Ask Wayland what monitor outputs are available

This commit is contained in:
Loki
2021-08-23 18:22:59 +02:00
parent 03d572fe10
commit 05dcff4f87
7 changed files with 351 additions and 1 deletions

3
.gitmodules vendored
View File

@@ -10,3 +10,6 @@
[submodule "third-party/miniupnp"]
path = third-party/miniupnp
url = https://github.com/miniupnp/miniupnp
[submodule "third-party/wayland-protocols"]
path = third-party/wayland-protocols
url = https://github.com/wayland-project/wayland-protocols.git

View File

@@ -104,6 +104,7 @@ else()
option(SUNSHINE_ENABLE_DRM "Enable KMS grab if available" ON)
option(SUNSHINE_ENABLE_X11 "Enable X11 grab if available" ON)
option(SUNSHINE_ENABLE_WAYLAND "Enable building wayland specific code" ON)
if(${SUNSHINE_ENABLE_X11})
find_package(X11)
@@ -111,6 +112,9 @@ else()
if(${SUNSHINE_ENABLE_DRM})
find_package(LIBDRM)
endif()
if(${SUNSHINE_ENABLE_WAYLAND})
find_package(Wayland)
endif()
find_package(FFMPEG REQUIRED)
@@ -126,8 +130,40 @@ else()
list(APPEND PLATFORM_TARGET_FILES sunshine/platform/linux/kmsgrab.cpp)
list(APPEND SUNSHINE_DEFINITIONS EGL_NO_X11=1)
endif()
if(WAYLAND_FOUND)
macro(genWayland FILEPATH FILENAME)
message("wayland-scanner private-code ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${FILEPATH}/${FILENAME}.xml ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.c")
message("wayland-scanner client-header ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${FILEPATH}/${FILENAME}.xml ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.h")
execute_process(
COMMAND wayland-scanner private-code ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${FILEPATH}/${FILENAME}.xml ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.c
COMMAND wayland-scanner client-header ${CMAKE_SOURCE_DIR}/third-party/wayland-protocols/${FILEPATH}/${FILENAME}.xml ${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.h
RESULT_VARIABLE EXIT_INT
)
if(NOT ${EXIT_INT} EQUAL 0)
message(FATAL_ERROR "wayland-scanner failed")
endif()
list(APPEND PLATFORM_TARGET_FILES
${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.c
${CMAKE_BINARY_DIR}/generated-src/${FILENAME}.h
)
endmacro()
genWayland(unstable/xdg-output xdg-output-unstable-v1)
include_directories(
${WAYLAND_INCLUDE_DIRS}
${CMAKE_BINARY_DIR}/generated-src
)
list(APPEND PLATFORM_LIBRARIES ${WAYLAND_LIBRARIES})
list(APPEND PLATFORM_TARGET_FILES sunshine/platform/linux/wayland.h)
list(APPEND PLATFORM_TARGET_FILES sunshine/platform/linux/wayland.cpp)
endif()
if(NOT X11_FOUND AND NOT LIBDRM_FOUND)
message(FATAL "Couldn't find either x11 or libdrm")
message(FATAL_ERROR "Couldn't find either x11 or libdrm")
endif()
list(APPEND PLATFORM_TARGET_FILES

78
cmake/FindWayland.cmake Normal file
View File

@@ -0,0 +1,78 @@
# Try to find Wayland on a Unix system
#
# This will define:
#
# WAYLAND_FOUND - True if Wayland is found
# WAYLAND_LIBRARIES - Link these to use Wayland
# WAYLAND_INCLUDE_DIRS - Include directory for Wayland
# WAYLAND_DEFINITIONS - Compiler flags for using Wayland
#
# In addition the following more fine grained variables will be defined:
#
# Wayland_Client_FOUND WAYLAND_CLIENT_INCLUDE_DIRS WAYLAND_CLIENT_LIBRARIES
# Wayland_Server_FOUND WAYLAND_SERVER_INCLUDE_DIRS WAYLAND_SERVER_LIBRARIES
# Wayland_EGL_FOUND WAYLAND_EGL_INCLUDE_DIRS WAYLAND_EGL_LIBRARIES
# Wayland_Cursor_FOUND WAYLAND_CURSOR_INCLUDE_DIRS WAYLAND_CURSOR_LIBRARIES
#
# Copyright (c) 2013 Martin Gräßlin <mgraesslin@kde.org>
# 2020 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
IF (NOT WIN32)
# Use pkg-config to get the directories and then use these values
# in the find_path() and find_library() calls
find_package(PkgConfig)
PKG_CHECK_MODULES(PKG_WAYLAND QUIET wayland-client wayland-server wayland-egl wayland-cursor)
set(WAYLAND_DEFINITIONS ${PKG_WAYLAND_CFLAGS})
find_path(WAYLAND_CLIENT_INCLUDE_DIRS NAMES wayland-client.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
find_library(WAYLAND_CLIENT_LIBRARIES NAMES wayland-client HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
if(WAYLAND_CLIENT_INCLUDE_DIRS AND WAYLAND_CLIENT_LIBRARIES)
set(Wayland_Client_FOUND TRUE)
else()
set(Wayland_Client_FOUND FALSE)
endif()
mark_as_advanced(WAYLAND_CLIENT_INCLUDE_DIRS WAYLAND_CLIENT_LIBRARIES)
find_path(WAYLAND_CURSOR_INCLUDE_DIRS NAMES wayland-cursor.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
find_library(WAYLAND_CURSOR_LIBRARIES NAMES wayland-cursor HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
if(WAYLAND_CURSOR_INCLUDE_DIRS AND WAYLAND_CURSOR_LIBRARIES)
set(Wayland_Cursor_FOUND TRUE)
else()
set(Wayland_Cursor_FOUND FALSE)
endif()
mark_as_advanced(WAYLAND_CURSOR_INCLUDE_DIRS WAYLAND_CURSOR_LIBRARIES)
find_path(WAYLAND_EGL_INCLUDE_DIRS NAMES wayland-egl.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
find_library(WAYLAND_EGL_LIBRARIES NAMES wayland-egl HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
if(WAYLAND_EGL_INCLUDE_DIRS AND WAYLAND_EGL_LIBRARIES)
set(Wayland_EGL_FOUND TRUE)
else()
set(Wayland_EGL_FOUND FALSE)
endif()
mark_as_advanced(WAYLAND_EGL_INCLUDE_DIRS WAYLAND_EGL_LIBRARIES)
find_path(WAYLAND_SERVER_INCLUDE_DIRS NAMES wayland-server.h HINTS ${PKG_WAYLAND_INCLUDE_DIRS})
find_library(WAYLAND_SERVER_LIBRARIES NAMES wayland-server HINTS ${PKG_WAYLAND_LIBRARY_DIRS})
if(WAYLAND_SERVER_INCLUDE_DIRS AND WAYLAND_SERVER_LIBRARIES)
set(Wayland_Server_FOUND TRUE)
else()
set(Wayland_Server_FOUND FALSE)
endif()
mark_as_advanced(WAYLAND_SERVER_INCLUDE_DIRS WAYLAND_SERVER_LIBRARIES)
set(WAYLAND_INCLUDE_DIRS ${WAYLAND_CLIENT_INCLUDE_DIRS} ${WAYLAND_SERVER_INCLUDE_DIRS} ${WAYLAND_EGL_INCLUDE_DIRS} ${WAYLAND_CURSOR_INCLUDE_DIRS})
set(WAYLAND_LIBRARIES ${WAYLAND_CLIENT_LIBRARIES} ${WAYLAND_SERVER_LIBRARIES} ${WAYLAND_EGL_LIBRARIES} ${WAYLAND_CURSOR_LIBRARIES})
mark_as_advanced(WAYLAND_INCLUDE_DIRS WAYLAND_LIBRARIES)
list(REMOVE_DUPLICATES WAYLAND_INCLUDE_DIRS)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Wayland REQUIRED_VARS WAYLAND_LIBRARIES WAYLAND_INCLUDE_DIRS HANDLE_COMPONENTS)
ENDIF ()

View File

@@ -26,6 +26,8 @@
#include "upnp.h"
#include "video.h"
#include "platform/linux/wayland.h"
#include "platform/common.h"
extern "C" {
#include <libavutil/log.h>
@@ -239,6 +241,9 @@ int main(int argc, char *argv[]) {
shutdown_event->raise(true);
});
wl::test();
return 0;
proc::refresh(config::stream.file_apps);
auto deinit_guard = platf::init();

View File

@@ -0,0 +1,143 @@
#include <wayland-client.h>
#include <wayland-util.h>
#include <cstdlib>
#include "sunshine/main.h"
#include "sunshine/platform/common.h"
#include "sunshine/utility.h"
#include "wayland.h"
extern const wl_interface wl_output_interface;
using namespace std::literals;
// Disable warning for converting incompatible functions
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
namespace wl {
void test() {
display_t display;
if(display.init()) {
return;
}
interface_t interface { display.registry() };
display.roundtrip();
for(auto &monitor : interface.monitors) {
monitor->listen(interface.output_manager);
}
display.roundtrip();
}
int display_t::init(const char *display_name) {
if(!display_name) {
display_name = std::getenv("WAYLAND_DISPLAY");
}
if(!display_name) {
BOOST_LOG(error) << "Environment variable WAYLAND_DISPLAY has not been defined"sv;
return -1;
}
display_internal.reset(wl_display_connect(display_name));
if(!display_internal) {
BOOST_LOG(error) << "Couldn't connect to Wayland display: "sv << display_name;
return -1;
}
BOOST_LOG(info) << "Found display ["sv << display_name << ']';
return 0;
}
void display_t::roundtrip() {
wl_display_roundtrip(display_internal.get());
}
wl_registry *display_t::registry() {
return wl_display_get_registry(display_internal.get());
}
inline monitor_t::monitor_t(wl_output *output) : output { output } {}
inline void monitor_t::xdg_name(zxdg_output_v1 *, const char *name) {
this->name = name;
BOOST_LOG(info) << "Name: "sv << this->name;
}
inline void monitor_t::xdg_description(zxdg_output_v1 *, const char *description) {
this->description = description;
BOOST_LOG(info) << "Found monitor: "sv << this->description;
}
inline void monitor_t::xdg_position(zxdg_output_v1 *, std::int32_t x, std::int32_t y) {
viewport.offset_x = x;
viewport.offset_y = y;
BOOST_LOG(info) << "Offset: "sv << x << 'x' << y;
}
inline void monitor_t::xdg_size(zxdg_output_v1 *, std::int32_t width, std::int32_t height) {
viewport.width = width;
viewport.height = height;
BOOST_LOG(info) << "Resolution: "sv << width << 'x' << height;
}
inline void monitor_t::xdg_done(zxdg_output_v1 *) {
BOOST_LOG(info) << "All info about monitor ["sv << name << "] has been send"sv;
}
inline void monitor_t::listen(zxdg_output_manager_v1 *output_manager) {
auto xdg_output = zxdg_output_manager_v1_get_xdg_output(output_manager, output);
#define CLASS_CALL(x, y) x = (decltype(x))&y
CLASS_CALL(listener.name, monitor_t::xdg_name);
CLASS_CALL(listener.logical_size, monitor_t::xdg_size);
CLASS_CALL(listener.logical_position, monitor_t::xdg_position);
CLASS_CALL(listener.done, monitor_t::xdg_done);
CLASS_CALL(listener.description, monitor_t::xdg_description);
#undef CLASS_CALL
zxdg_output_v1_add_listener(xdg_output, &listener, this);
}
inline interface_t::interface_t(wl_registry *registry)
: output_manager { nullptr }, listener {
(decltype(wl_registry_listener::global))&interface_t::add_interface,
(decltype(wl_registry_listener::global_remove))&interface_t::del_interface,
} {
wl_registry_add_listener(registry, &listener, this);
}
inline void interface_t::add_interface(wl_registry *registry, std::uint32_t id, const char *interface, std::uint32_t version) {
BOOST_LOG(debug) << "Available interface: "sv << interface << '(' << id << ") version "sv << version;
if(!std::strcmp(interface, wl_output_interface.name)) {
BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version;
monitors.emplace_back(
std::make_unique<monitor_t>(
(wl_output *)wl_registry_bind(registry, id, &wl_output_interface, version)));
}
else if(!std::strcmp(interface, zxdg_output_manager_v1_interface.name)) {
BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version;
output_manager = (zxdg_output_manager_v1 *)wl_registry_bind(registry, id, &zxdg_output_manager_v1_interface, version);
}
}
inline void interface_t::del_interface(wl_registry *registry, uint32_t id) {
BOOST_LOG(info) << "Delete: "sv << id;
}
} // namespace wl
#pragma GCC diagnostic pop

View File

@@ -0,0 +1,84 @@
#ifndef SUNSHINE_WAYLAND_H
#define SUNSHINE_WAYLAND_H
#include <xdg-output-unstable-v1.h>
namespace wl {
using display_internal_t = util::safe_ptr<wl_display, wl_display_disconnect>;
class monitor_t {
public:
monitor_t(monitor_t &&) = delete;
monitor_t(const monitor_t &) = delete;
monitor_t &operator=(const monitor_t &) = delete;
monitor_t &operator=(monitor_t &&) = delete;
monitor_t(wl_output *output);
void xdg_name(zxdg_output_v1 *, const char *name);
void xdg_description(zxdg_output_v1 *, const char *description);
void xdg_position(zxdg_output_v1 *, std::int32_t x, std::int32_t y);
void xdg_size(zxdg_output_v1 *, std::int32_t width, std::int32_t height);
void xdg_done(zxdg_output_v1 *);
void listen(zxdg_output_manager_v1 *output_manager);
wl_output *output;
std::string name;
std::string description;
platf::touch_port_t viewport;
zxdg_output_v1_listener listener;
};
class interface_t {
struct bind_t {
std::uint32_t id;
std::uint32_t version;
};
public:
interface_t(interface_t &&) = delete;
interface_t(const interface_t &) = delete;
interface_t &operator=(const interface_t &) = delete;
interface_t &operator=(interface_t &&) = delete;
interface_t(wl_registry *registry);
std::vector<std::unique_ptr<monitor_t>> monitors;
zxdg_output_manager_v1 *output_manager;
private:
void add_interface(wl_registry *registry, std::uint32_t id, const char *interface, std::uint32_t version);
void del_interface(wl_registry *registry, uint32_t id);
wl_registry_listener listener;
};
class display_t {
public:
/**
* Initialize display with display_name
* If display_name == nullptr -> display_name = std::getenv("WAYLAND_DISPLAY")
*/
int init(const char *display_name = nullptr);
// Roundtrip with Wayland connection
void roundtrip();
// Get the registry associated with the display
// No need to manually free the registry
wl_registry *registry();
private:
display_internal_t display_internal;
};
void test();
} // namespace wl
#endif