mirror of
https://github.com/LizardByte/Sunshine.git
synced 2025-08-10 00:52:16 +00:00
Ask Wayland what monitor outputs are available
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -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
|
||||
|
||||
@@ -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
78
cmake/FindWayland.cmake
Normal 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 ()
|
||||
@@ -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();
|
||||
|
||||
143
sunshine/platform/linux/wayland.cpp
Normal file
143
sunshine/platform/linux/wayland.cpp
Normal 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
|
||||
84
sunshine/platform/linux/wayland.h
Normal file
84
sunshine/platform/linux/wayland.h
Normal 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
|
||||
1
third-party/wayland-protocols
vendored
Submodule
1
third-party/wayland-protocols
vendored
Submodule
Submodule third-party/wayland-protocols added at 7dffa6f346
Reference in New Issue
Block a user