From b9bd1f5dd1ca4a692f4bc328a4e6c8261f7de6cb Mon Sep 17 00:00:00 2001 From: gorgbus Date: Fri, 4 Apr 2025 21:34:57 +0200 Subject: [PATCH 1/6] Swapped wlr-export-dmabuf-unstable-v1 for wlr-screencopy-unstable-v1 --- cmake/compile_definitions/linux.cmake | 5 +- src/platform/linux/wayland.cpp | 337 ++++++++++++++++++++++---- src/platform/linux/wayland.h | 176 ++++++-------- 3 files changed, 360 insertions(+), 158 deletions(-) diff --git a/cmake/compile_definitions/linux.cmake b/cmake/compile_definitions/linux.cmake index 62ca61a64d5..152466d806c 100644 --- a/cmake/compile_definitions/linux.cmake +++ b/cmake/compile_definitions/linux.cmake @@ -137,7 +137,8 @@ if(WAYLAND_FOUND) endif() GEN_WAYLAND("${WAYLAND_PROTOCOLS_DIR}" "unstable/xdg-output" xdg-output-unstable-v1) - GEN_WAYLAND("${CMAKE_SOURCE_DIR}/third-party/wlr-protocols" "unstable" wlr-export-dmabuf-unstable-v1) + GEN_WAYLAND("${WAYLAND_PROTOCOLS_DIR}" "unstable/linux-dmabuf" linux-dmabuf-unstable-v1) + GEN_WAYLAND("${CMAKE_SOURCE_DIR}/third-party/wlr-protocols" "unstable" wlr-screencopy-unstable-v1) include_directories( SYSTEM @@ -145,7 +146,7 @@ if(WAYLAND_FOUND) ${CMAKE_BINARY_DIR}/generated-src ) - list(APPEND PLATFORM_LIBRARIES ${WAYLAND_LIBRARIES}) + list(APPEND PLATFORM_LIBRARIES ${WAYLAND_LIBRARIES} gbm) list(APPEND PLATFORM_TARGET_FILES "${CMAKE_SOURCE_DIR}/src/platform/linux/wlgrab.cpp" "${CMAKE_SOURCE_DIR}/src/platform/linux/wayland.h" diff --git a/src/platform/linux/wayland.cpp b/src/platform/linux/wayland.cpp index c6e02d7284e..8ea1ef76fcb 100644 --- a/src/platform/linux/wayland.cpp +++ b/src/platform/linux/wayland.cpp @@ -9,6 +9,11 @@ #include #include #include +#include +#include +#include +#include +#include // local includes #include "graphics.h" @@ -37,6 +42,12 @@ namespace wl { #define CLASS_CALL(c, m) classCall + // Define buffer params listener + static const struct zwp_linux_buffer_params_v1_listener params_listener = { + .created = dmabuf_t::buffer_params_created, + .failed = dmabuf_t::buffer_params_failed + }; + int display_t::init(const char *display_name) { if (!display_name) { display_name = std::getenv("WAYLAND_DISPLAY"); @@ -136,7 +147,13 @@ namespace wl { BOOST_LOG(info) << "Logical size: "sv << width << 'x' << height; } - void monitor_t::wl_mode(wl_output *wl_output, std::uint32_t flags, std::int32_t width, std::int32_t height, std::int32_t refresh) { + void monitor_t::wl_mode( + wl_output *wl_output, + std::uint32_t flags, + std::int32_t width, + std::int32_t height, + std::int32_t refresh + ) { viewport.width = width; viewport.height = height; @@ -151,6 +168,8 @@ namespace wl { interface_t::interface_t() noexcept : + screencopy_manager {nullptr}, + dmabuf_interface {nullptr}, output_manager {nullptr}, listener { &CLASS_CALL(interface_t, add_interface), @@ -162,7 +181,12 @@ namespace wl { wl_registry_add_listener(registry, &listener, this); } - void interface_t::add_interface(wl_registry *registry, std::uint32_t id, const char *interface, std::uint32_t version) { + 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)) { @@ -177,11 +201,16 @@ namespace wl { output_manager = (zxdg_output_manager_v1 *) wl_registry_bind(registry, id, &zxdg_output_manager_v1_interface, version); this->interface[XDG_OUTPUT] = true; - } else if (!std::strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name)) { + } else if (!std::strcmp(interface, zwlr_screencopy_manager_v1_interface.name)) { BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version; - dmabuf_manager = (zwlr_export_dmabuf_manager_v1 *) wl_registry_bind(registry, id, &zwlr_export_dmabuf_manager_v1_interface, version); + screencopy_manager = (zwlr_screencopy_manager_v1 *) wl_registry_bind(registry, id, &zwlr_screencopy_manager_v1_interface, version); this->interface[WLR_EXPORT_DMABUF] = true; + } else if (!std::strcmp(interface, zwp_linux_dmabuf_v1_interface.name)) { + BOOST_LOG(info) << "Found interface: "sv << interface << '(' << id << ") version "sv << version; + dmabuf_interface = (zwp_linux_dmabuf_v1 *) wl_registry_bind(registry, id, &zwp_linux_dmabuf_v1_interface, version); + + this->interface[LINUX_DMABUF] = true; } } @@ -189,94 +218,304 @@ namespace wl { BOOST_LOG(info) << "Delete: "sv << id; } + // Initialize GBM + bool dmabuf_t::init_gbm() { + if (gbm_device) return true; + + // Find render node + drmDevice *devices[16]; + int n = drmGetDevices2(0, devices, 16); + if (n <= 0) { + BOOST_LOG(error) << "No DRM devices found"sv; + return false; + } + + int drm_fd = -1; + for (int i = 0; i < n; i++) { + if (devices[i]->available_nodes & (1 << DRM_NODE_RENDER)) { + drm_fd = open(devices[i]->nodes[DRM_NODE_RENDER], O_RDWR); + if (drm_fd >= 0) break; + } + } + drmFreeDevices(devices, n); + + if (drm_fd < 0) { + BOOST_LOG(error) << "Failed to open DRM render node"sv; + return false; + } + + gbm_device = gbm_create_device(drm_fd); + if (!gbm_device) { + close(drm_fd); + BOOST_LOG(error) << "Failed to create GBM device"sv; + return false; + } + + return true; + } + + // Cleanup GBM + void dmabuf_t::cleanup_gbm() { + if (current_bo) { + gbm_bo_destroy(current_bo); + current_bo = nullptr; + } + + if (current_wl_buffer) { + wl_buffer_destroy(current_wl_buffer); + current_wl_buffer = nullptr; + } + } + dmabuf_t::dmabuf_t(): status {READY}, frames {}, current_frame {&frames[0]}, listener { - &CLASS_CALL(dmabuf_t, frame), - &CLASS_CALL(dmabuf_t, object), + &CLASS_CALL(dmabuf_t, buffer), + &CLASS_CALL(dmabuf_t, flags), &CLASS_CALL(dmabuf_t, ready), - &CLASS_CALL(dmabuf_t, cancel) + &CLASS_CALL(dmabuf_t, failed), + &CLASS_CALL(dmabuf_t, damage), + &CLASS_CALL(dmabuf_t, linux_dmabuf), + &CLASS_CALL(dmabuf_t, buffer_done), } { } - void dmabuf_t::listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, wl_output *output, bool blend_cursor) { - auto frame = zwlr_export_dmabuf_manager_v1_capture_output(dmabuf_manager, blend_cursor, output); - zwlr_export_dmabuf_frame_v1_add_listener(frame, &listener, this); - + // Start capture + void dmabuf_t::listen( + zwlr_screencopy_manager_v1 *screencopy_manager, + zwp_linux_dmabuf_v1 *dmabuf_interface, + wl_output *output, + bool blend_cursor + ) { + this->dmabuf_interface = dmabuf_interface; + // Reset state + shm_info.supported = false; + dmabuf_info.supported = false; + + // Create new frame + auto frame = zwlr_screencopy_manager_v1_capture_output( + screencopy_manager, blend_cursor ? 1 : 0, output); + + // Store frame data pointer for callbacks + zwlr_screencopy_frame_v1_set_user_data(frame, this); + + // Add listener + zwlr_screencopy_frame_v1_add_listener(frame, &listener, this); + status = WAITING; } dmabuf_t::~dmabuf_t() { + cleanup_gbm(); + for (auto &frame : frames) { frame.destroy(); } + + if (gbm_device) { + // We should close the DRM FD, but it's owned by GBM + gbm_device_destroy(gbm_device); + gbm_device = nullptr; + } } - void dmabuf_t::frame( - zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t width, - std::uint32_t height, - std::uint32_t x, - std::uint32_t y, - std::uint32_t buffer_flags, - std::uint32_t flags, + // Buffer format callback + void dmabuf_t::buffer( + zwlr_screencopy_frame_v1 *frame, + uint32_t format, + uint32_t width, + uint32_t height, + uint32_t stride + ) { + shm_info.supported = true; + shm_info.format = format; + shm_info.width = width; + shm_info.height = height; + shm_info.stride = stride; + + BOOST_LOG(debug) << "Screencopy supports SHM format: "sv << format; + } + + // DMA-BUF format callback + void dmabuf_t::linux_dmabuf( + zwlr_screencopy_frame_v1 *frame, std::uint32_t format, - std::uint32_t high, - std::uint32_t low, - std::uint32_t obj_count + std::uint32_t width, + std::uint32_t height ) { - auto next_frame = get_next_frame(); + dmabuf_info.supported = true; + dmabuf_info.format = format; + dmabuf_info.width = width; + dmabuf_info.height = height; + + BOOST_LOG(debug) << "Screencopy supports DMA-BUF format: "sv << format; + } - next_frame->sd.fourcc = format; - next_frame->sd.width = width; - next_frame->sd.height = height; - next_frame->sd.modifier = (((std::uint64_t) high) << 32) | low; + // Flags callback + void dmabuf_t::flags(zwlr_screencopy_frame_v1 *frame, std::uint32_t flags) { + y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT; + BOOST_LOG(debug) << "Frame flags: "sv << flags << (y_invert ? " (y_invert)" : ""); } - void dmabuf_t::object( - zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t index, - std::int32_t fd, - std::uint32_t size, - std::uint32_t offset, - std::uint32_t stride, - std::uint32_t plane_index - ) { + // DMA-BUF creation helper + void dmabuf_t::create_and_copy_dmabuf(zwlr_screencopy_frame_v1 *frame) { + if (!init_gbm()) { + BOOST_LOG(error) << "Failed to initialize GBM"sv; + zwlr_screencopy_frame_v1_destroy(frame); + status = REINIT; + return; + } + + // Create GBM buffer + current_bo = gbm_bo_create(gbm_device, dmabuf_info.width, dmabuf_info.height, + dmabuf_info.format, GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING); + if (!current_bo) { + BOOST_LOG(error) << "Failed to create GBM buffer"sv; + zwlr_screencopy_frame_v1_destroy(frame); + status = REINIT; + return; + } + + // Get buffer info + int fd = gbm_bo_get_fd(current_bo); + if (fd < 0) { + BOOST_LOG(error) << "Failed to get buffer FD"sv; + gbm_bo_destroy(current_bo); + current_bo = nullptr; + zwlr_screencopy_frame_v1_destroy(frame); + status = REINIT; + return; + } + + uint32_t stride = gbm_bo_get_stride(current_bo); + uint64_t modifier = gbm_bo_get_modifier(current_bo); + + // Store in surface descriptor for later use auto next_frame = get_next_frame(); + next_frame->sd.fds[0] = fd; + next_frame->sd.pitches[0] = stride; + next_frame->sd.offsets[0] = 0; + next_frame->sd.modifier = modifier; + + // Create linux-dmabuf buffer + auto params = zwp_linux_dmabuf_v1_create_params(dmabuf_interface); + zwp_linux_buffer_params_v1_add(params, fd, 0, 0, stride, + modifier >> 32, modifier & 0xffffffff); + + // Add listener for buffer creation + zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, frame); + + // Create Wayland buffer (async - callback will handle copy) + zwp_linux_buffer_params_v1_create(params, dmabuf_info.width, dmabuf_info.height, + dmabuf_info.format, 0); + } - next_frame->sd.fds[plane_index] = fd; - next_frame->sd.pitches[plane_index] = stride; - next_frame->sd.offsets[plane_index] = offset; + // Buffer done callback - time to create buffer + void dmabuf_t::buffer_done(zwlr_screencopy_frame_v1 *frame) { + auto next_frame = get_next_frame(); + + // Prefer DMA-BUF if supported + if (dmabuf_info.supported && dmabuf_interface) { + // Store format info first + next_frame->sd.fourcc = dmabuf_info.format; + next_frame->sd.width = dmabuf_info.width; + next_frame->sd.height = dmabuf_info.height; + + // Create and start copy + create_and_copy_dmabuf(frame); + } + else if (shm_info.supported) { + // SHM fallback would go here + BOOST_LOG(warning) << "SHM capture not implemented"sv; + zwlr_screencopy_frame_v1_destroy(frame); + status = REINIT; + } + else { + BOOST_LOG(error) << "No supported buffer types"sv; + zwlr_screencopy_frame_v1_destroy(frame); + status = REINIT; + } + } + + // Buffer params created callback + void dmabuf_t::buffer_params_created( + void *data, + struct zwp_linux_buffer_params_v1 *params, + struct wl_buffer *buffer + ) { + auto frame = static_cast(data); + auto self = static_cast(zwlr_screencopy_frame_v1_get_user_data(frame)); + + // Store for cleanup + self->current_wl_buffer = buffer; + + // Start the actual copy + zwlr_screencopy_frame_v1_copy(frame, buffer); + } + + // Buffer params failed callback + void dmabuf_t::buffer_params_failed( + void *data, + struct zwp_linux_buffer_params_v1 *params + ) { + auto frame = static_cast(data); + auto self = static_cast(zwlr_screencopy_frame_v1_get_user_data(frame)); + + BOOST_LOG(error) << "Failed to create buffer from params"sv; + self->cleanup_gbm(); + + zwlr_screencopy_frame_v1_destroy(frame); + self->status = REINIT; } + // Ready callback void dmabuf_t::ready( - zwlr_export_dmabuf_frame_v1 *frame, + zwlr_screencopy_frame_v1 *frame, std::uint32_t tv_sec_hi, std::uint32_t tv_sec_lo, std::uint32_t tv_nsec ) { - zwlr_export_dmabuf_frame_v1_destroy(frame); - + BOOST_LOG(debug) << "Frame ready"sv; + + // Frame is ready for use, GBM buffer now contains screen content current_frame->destroy(); current_frame = get_next_frame(); + + // Keep the GBM buffer alive but destroy the Wayland objects + if (current_wl_buffer) { + wl_buffer_destroy(current_wl_buffer); + current_wl_buffer = nullptr; + } + cleanup_gbm(); + + zwlr_screencopy_frame_v1_destroy(frame); status = READY; } - void dmabuf_t::cancel( - zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t reason - ) { - zwlr_export_dmabuf_frame_v1_destroy(frame); - + // Failed callback + void dmabuf_t::failed(zwlr_screencopy_frame_v1 *frame) { + BOOST_LOG(error) << "Frame capture failed"sv; + + // Clean up resources + cleanup_gbm(); auto next_frame = get_next_frame(); next_frame->destroy(); - + + zwlr_screencopy_frame_v1_destroy(frame); status = REINIT; } + void dmabuf_t::damage( + zwlr_screencopy_frame_v1 *frame, + std::uint32_t x, + std::uint32_t y, + std::uint32_t width, + std::uint32_t height + ) {}; + void frame_t::destroy() { for (auto x = 0; x < 4; ++x) { if (sd.fds[x] >= 0) { diff --git a/src/platform/linux/wayland.h b/src/platform/linux/wayland.h index 6a6986f6748..25a0d11e0b8 100644 --- a/src/platform/linux/wayland.h +++ b/src/platform/linux/wayland.h @@ -8,17 +8,14 @@ #include #ifdef SUNSHINE_BUILD_WAYLAND - #include - #include +#include +#include +#include #endif // local includes #include "graphics.h" -/** - * The classes defined in this macro block should only be used by - * cpp files whose compilation depends on SUNSHINE_BUILD_WAYLAND - */ #ifdef SUNSHINE_BUILD_WAYLAND namespace wl { @@ -27,117 +24,95 @@ namespace wl { class frame_t { public: frame_t(); - egl::surface_descriptor_t sd; - void destroy(); + + egl::surface_descriptor_t sd; }; class dmabuf_t { public: enum status_e { WAITING, ///< Waiting for a frame - READY, ///< Frame is ready - REINIT, ///< Reinitialize the frame + READY, ///< Frame is ready + REINIT ///< Reinitialize the frame }; + dmabuf_t(); + ~dmabuf_t(); + dmabuf_t(dmabuf_t &&) = delete; dmabuf_t(const dmabuf_t &) = delete; - dmabuf_t &operator=(const dmabuf_t &) = delete; dmabuf_t &operator=(dmabuf_t &&) = delete; - dmabuf_t(); - - void listen(zwlr_export_dmabuf_manager_v1 *dmabuf_manager, wl_output *output, bool blend_cursor = false); - - ~dmabuf_t(); - - void frame( - zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t width, - std::uint32_t height, - std::uint32_t x, - std::uint32_t y, - std::uint32_t buffer_flags, - std::uint32_t flags, - std::uint32_t format, - std::uint32_t high, - std::uint32_t low, - std::uint32_t obj_count - ); - - void object( - zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t index, - std::int32_t fd, - std::uint32_t size, - std::uint32_t offset, - std::uint32_t stride, - std::uint32_t plane_index - ); - - void ready( - zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t tv_sec_hi, - std::uint32_t tv_sec_lo, - std::uint32_t tv_nsec - ); - - void cancel( - zwlr_export_dmabuf_frame_v1 *frame, - std::uint32_t reason - ); - - inline frame_t *get_next_frame() { - return current_frame == &frames[0] ? &frames[1] : &frames[0]; - } + void listen(zwlr_screencopy_manager_v1 *screencopy_manager, zwp_linux_dmabuf_v1 *dmabuf_interface, wl_output *output, bool blend_cursor = false); + static void buffer_params_created(void *data, struct zwp_linux_buffer_params_v1 *params, struct wl_buffer *wl_buffer); + static void buffer_params_failed(void *data, struct zwp_linux_buffer_params_v1 *params); + void buffer(zwlr_screencopy_frame_v1 *frame, std::uint32_t format, std::uint32_t width, std::uint32_t height, std::uint32_t stride); + void linux_dmabuf(zwlr_screencopy_frame_v1 *frame, std::uint32_t format, std::uint32_t width, std::uint32_t height); + void buffer_done(zwlr_screencopy_frame_v1 *frame); + void flags(zwlr_screencopy_frame_v1 *frame, std::uint32_t flags); + void damage(zwlr_screencopy_frame_v1 *frame, std::uint32_t x, std::uint32_t y, std::uint32_t width, std::uint32_t height); + void ready(zwlr_screencopy_frame_v1 *frame, std::uint32_t tv_sec_hi, std::uint32_t tv_sec_lo, std::uint32_t tv_nsec); + void failed(zwlr_screencopy_frame_v1 *frame); + frame_t *get_next_frame() { return current_frame == &frames[0] ? &frames[1] : &frames[0]; } status_e status; - std::array frames; frame_t *current_frame; + zwlr_screencopy_frame_v1_listener listener; - zwlr_export_dmabuf_frame_v1_listener listener; + private: + bool init_gbm(); + void cleanup_gbm(); + void create_and_copy_dmabuf(zwlr_screencopy_frame_v1 *frame); + + zwp_linux_dmabuf_v1 *dmabuf_interface{nullptr}; + struct { + bool supported{false}; + std::uint32_t format; + std::uint32_t width; + std::uint32_t height; + std::uint32_t stride; + } shm_info; + + struct { + bool supported{false}; + std::uint32_t format; + std::uint32_t width; + std::uint32_t height; + } dmabuf_info; + + struct gbm_device *gbm_device{nullptr}; + struct gbm_bo *current_bo{nullptr}; + struct wl_buffer *current_wl_buffer{nullptr}; + bool y_invert{false}; }; class monitor_t { public: + explicit monitor_t(wl_output *output); + 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 listen(zxdg_output_manager_v1 *output_manager); 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 wl_geometry(wl_output *wl_output, std::int32_t x, std::int32_t y, std::int32_t physical_width, std::int32_t physical_height, std::int32_t subpixel, const char *make, const char *model, std::int32_t transform) { - } - + void xdg_done(zxdg_output_v1 *) {} + void wl_geometry(wl_output *wl_output, std::int32_t x, std::int32_t y, std::int32_t physical_width, std::int32_t physical_height, std::int32_t subpixel, const char *make, const char *model, std::int32_t transform) {} void wl_mode(wl_output *wl_output, std::uint32_t flags, std::int32_t width, std::int32_t height, std::int32_t refresh); - - void wl_done(wl_output *wl_output) { - } - - void wl_scale(wl_output *wl_output, std::int32_t factor) { - } - - void listen(zxdg_output_manager_v1 *output_manager); + void wl_done(wl_output *wl_output) {} + void wl_scale(wl_output *wl_output, std::int32_t factor) {} wl_output *output; - std::string name; std::string description; - platf::touch_port_t viewport; - wl_output_listener wl_listener; zxdg_output_v1_listener xdg_listener; }; @@ -150,36 +125,32 @@ namespace wl { public: enum interface_e { - XDG_OUTPUT, ///< xdg-output - WLR_EXPORT_DMABUF, ///< Export dmabuf - MAX_INTERFACES, ///< Maximum number of interfaces + XDG_OUTPUT, ///< xdg-output + WLR_EXPORT_DMABUF, ///< screencopy manager + LINUX_DMABUF, ///< linux-dmabuf protocol + MAX_INTERFACES }; + interface_t() noexcept; + 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() noexcept; - void listen(wl_registry *registry); + bool operator[](interface_e bit) const { return interface[bit]; } std::vector> monitors; - - zwlr_export_dmabuf_manager_v1 *dmabuf_manager; - zxdg_output_manager_v1 *output_manager; - - bool operator[](interface_e bit) const { - return interface[bit]; - } + zwlr_screencopy_manager_v1 *screencopy_manager{nullptr}; + zwp_linux_dmabuf_v1 *dmabuf_interface{nullptr}; + zxdg_output_manager_v1 *output_manager{nullptr}; 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); std::bitset interface; - wl_registry_listener listener; }; @@ -202,7 +173,7 @@ namespace wl { // Get the registry associated with the display // No need to manually free the registry wl_registry *registry(); - + inline display_internal_t::pointer get() { return display_internal.get(); } @@ -212,7 +183,6 @@ namespace wl { }; std::vector> monitors(const char *display_name = nullptr); - int init(); } // namespace wl #else @@ -223,30 +193,22 @@ struct zxdg_output_manager_v1; namespace wl { class monitor_t { public: + monitor_t(wl_output *output); + 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 listen(zxdg_output_manager_v1 *output_manager); wl_output *output; - std::string name; std::string description; - platf::touch_port_t viewport; }; - inline std::vector> monitors(const char *display_name = nullptr) { - return {}; - } - - inline int init() { - return -1; - } + inline std::vector> monitors(const char *display_name = nullptr) { return {}; } + inline int init() { return -1; } } // namespace wl #endif From 97f1dbca971b91b858aafaf6ae9b5746d1a0f0ff Mon Sep 17 00:00:00 2001 From: gorgbus Date: Fri, 4 Apr 2025 21:40:39 +0200 Subject: [PATCH 2/6] fix --- src/platform/linux/wlgrab.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/linux/wlgrab.cpp b/src/platform/linux/wlgrab.cpp index b867b90838b..9ef3e09f9c6 100644 --- a/src/platform/linux/wlgrab.cpp +++ b/src/platform/linux/wlgrab.cpp @@ -90,7 +90,7 @@ namespace wl { auto to = std::chrono::steady_clock::now() + timeout; // Dispatch events until we get a new frame or the timeout expires - dmabuf.listen(interface.dmabuf_manager, output, cursor); + dmabuf.listen(interface.screencopy_manager, interface.dmabuf_interface, output, cursor); do { auto remaining_time_ms = std::chrono::duration_cast(to - std::chrono::steady_clock::now()); if (remaining_time_ms.count() < 0 || !display.dispatch(remaining_time_ms)) { From ce0a1b31e4eeedd82869e6ae0fcce89df0511a09 Mon Sep 17 00:00:00 2001 From: gorgbus Date: Sat, 5 Apr 2025 14:35:16 +0200 Subject: [PATCH 3/6] fix debian/ubuntu and fedora build --- packaging/linux/fedora/Sunshine.spec | 1 + scripts/linux_build.sh | 2 ++ 2 files changed, 3 insertions(+) diff --git a/packaging/linux/fedora/Sunshine.spec b/packaging/linux/fedora/Sunshine.spec index 910c4fce746..3becff3be1a 100644 --- a/packaging/linux/fedora/Sunshine.spec +++ b/packaging/linux/fedora/Sunshine.spec @@ -35,6 +35,7 @@ BuildRequires: libXrandr-devel BuildRequires: libXtst-devel BuildRequires: git BuildRequires: mesa-libGL-devel +BuildRequires: mesa-libgbm-devel BuildRequires: miniupnpc-devel BuildRequires: npm BuildRequires: numactl-devel diff --git a/scripts/linux_build.sh b/scripts/linux_build.sh index e6a62d7e8c0..4cb071ecb6e 100644 --- a/scripts/linux_build.sh +++ b/scripts/linux_build.sh @@ -105,6 +105,7 @@ function add_debain_based_deps() { "libcurl4-openssl-dev" "libdrm-dev" # KMS "libevdev-dev" + "libgbm-dev" "libminiupnpc-dev" "libnotify-dev" "libnuma-dev" @@ -175,6 +176,7 @@ function add_fedora_deps() { "libXrandr-devel" # X11 "libXtst-devel" # X11 "mesa-libGL-devel" + "mesa-libgbm-devel" "miniupnpc-devel" "ninja-build" "npm" From a6d32fb1592ca9b79fa2a23bcd998ba3d9703bae Mon Sep 17 00:00:00 2001 From: gorgbus Date: Sat, 5 Apr 2025 14:49:25 +0200 Subject: [PATCH 4/6] fix lint --- src/platform/linux/wayland.cpp | 110 +++++++++++++++++---------------- src/platform/linux/wayland.h | 75 ++++++++++++++-------- 2 files changed, 104 insertions(+), 81 deletions(-) diff --git a/src/platform/linux/wayland.cpp b/src/platform/linux/wayland.cpp index 8ea1ef76fcb..93654baa15c 100644 --- a/src/platform/linux/wayland.cpp +++ b/src/platform/linux/wayland.cpp @@ -6,14 +6,14 @@ #include // platform includes +#include +#include +#include #include +#include #include #include -#include #include -#include -#include -#include // local includes #include "graphics.h" @@ -220,8 +220,10 @@ namespace wl { // Initialize GBM bool dmabuf_t::init_gbm() { - if (gbm_device) return true; - + if (gbm_device) { + return true; + } + // Find render node drmDevice *devices[16]; int n = drmGetDevices2(0, devices, 16); @@ -229,28 +231,30 @@ namespace wl { BOOST_LOG(error) << "No DRM devices found"sv; return false; } - + int drm_fd = -1; for (int i = 0; i < n; i++) { if (devices[i]->available_nodes & (1 << DRM_NODE_RENDER)) { drm_fd = open(devices[i]->nodes[DRM_NODE_RENDER], O_RDWR); - if (drm_fd >= 0) break; + if (drm_fd >= 0) { + break; + } } } drmFreeDevices(devices, n); - + if (drm_fd < 0) { BOOST_LOG(error) << "Failed to open DRM render node"sv; return false; } - + gbm_device = gbm_create_device(drm_fd); if (!gbm_device) { close(drm_fd); BOOST_LOG(error) << "Failed to create GBM device"sv; return false; } - + return true; } @@ -260,7 +264,7 @@ namespace wl { gbm_bo_destroy(current_bo); current_bo = nullptr; } - + if (current_wl_buffer) { wl_buffer_destroy(current_wl_buffer); current_wl_buffer = nullptr; @@ -293,27 +297,30 @@ namespace wl { // Reset state shm_info.supported = false; dmabuf_info.supported = false; - + // Create new frame auto frame = zwlr_screencopy_manager_v1_capture_output( - screencopy_manager, blend_cursor ? 1 : 0, output); - + screencopy_manager, + blend_cursor ? 1 : 0, + output + ); + // Store frame data pointer for callbacks zwlr_screencopy_frame_v1_set_user_data(frame, this); - + // Add listener zwlr_screencopy_frame_v1_add_listener(frame, &listener, this); - + status = WAITING; } dmabuf_t::~dmabuf_t() { cleanup_gbm(); - + for (auto &frame : frames) { frame.destroy(); } - + if (gbm_device) { // We should close the DRM FD, but it's owned by GBM gbm_device_destroy(gbm_device); @@ -334,7 +341,7 @@ namespace wl { shm_info.width = width; shm_info.height = height; shm_info.stride = stride; - + BOOST_LOG(debug) << "Screencopy supports SHM format: "sv << format; } @@ -349,7 +356,7 @@ namespace wl { dmabuf_info.format = format; dmabuf_info.width = width; dmabuf_info.height = height; - + BOOST_LOG(debug) << "Screencopy supports DMA-BUF format: "sv << format; } @@ -367,17 +374,16 @@ namespace wl { status = REINIT; return; } - + // Create GBM buffer - current_bo = gbm_bo_create(gbm_device, dmabuf_info.width, dmabuf_info.height, - dmabuf_info.format, GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING); + current_bo = gbm_bo_create(gbm_device, dmabuf_info.width, dmabuf_info.height, dmabuf_info.format, GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING); if (!current_bo) { BOOST_LOG(error) << "Failed to create GBM buffer"sv; zwlr_screencopy_frame_v1_destroy(frame); status = REINIT; return; } - + // Get buffer info int fd = gbm_bo_get_fd(current_bo); if (fd < 0) { @@ -388,69 +394,65 @@ namespace wl { status = REINIT; return; } - + uint32_t stride = gbm_bo_get_stride(current_bo); uint64_t modifier = gbm_bo_get_modifier(current_bo); - + // Store in surface descriptor for later use auto next_frame = get_next_frame(); next_frame->sd.fds[0] = fd; next_frame->sd.pitches[0] = stride; next_frame->sd.offsets[0] = 0; next_frame->sd.modifier = modifier; - + // Create linux-dmabuf buffer auto params = zwp_linux_dmabuf_v1_create_params(dmabuf_interface); - zwp_linux_buffer_params_v1_add(params, fd, 0, 0, stride, - modifier >> 32, modifier & 0xffffffff); - + zwp_linux_buffer_params_v1_add(params, fd, 0, 0, stride, modifier >> 32, modifier & 0xffffffff); + // Add listener for buffer creation zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, frame); - + // Create Wayland buffer (async - callback will handle copy) - zwp_linux_buffer_params_v1_create(params, dmabuf_info.width, dmabuf_info.height, - dmabuf_info.format, 0); + zwp_linux_buffer_params_v1_create(params, dmabuf_info.width, dmabuf_info.height, dmabuf_info.format, 0); } // Buffer done callback - time to create buffer void dmabuf_t::buffer_done(zwlr_screencopy_frame_v1 *frame) { auto next_frame = get_next_frame(); - + // Prefer DMA-BUF if supported if (dmabuf_info.supported && dmabuf_interface) { // Store format info first next_frame->sd.fourcc = dmabuf_info.format; next_frame->sd.width = dmabuf_info.width; next_frame->sd.height = dmabuf_info.height; - + // Create and start copy create_and_copy_dmabuf(frame); - } - else if (shm_info.supported) { + } else if (shm_info.supported) { // SHM fallback would go here BOOST_LOG(warning) << "SHM capture not implemented"sv; zwlr_screencopy_frame_v1_destroy(frame); status = REINIT; - } - else { + } else { BOOST_LOG(error) << "No supported buffer types"sv; zwlr_screencopy_frame_v1_destroy(frame); status = REINIT; } } - + // Buffer params created callback void dmabuf_t::buffer_params_created( void *data, struct zwp_linux_buffer_params_v1 *params, struct wl_buffer *buffer ) { - auto frame = static_cast(data); - auto self = static_cast(zwlr_screencopy_frame_v1_get_user_data(frame)); - + auto frame = static_cast(data); + auto self = static_cast(zwlr_screencopy_frame_v1_get_user_data(frame)); + // Store for cleanup self->current_wl_buffer = buffer; - + // Start the actual copy zwlr_screencopy_frame_v1_copy(frame, buffer); } @@ -460,12 +462,12 @@ namespace wl { void *data, struct zwp_linux_buffer_params_v1 *params ) { - auto frame = static_cast(data); - auto self = static_cast(zwlr_screencopy_frame_v1_get_user_data(frame)); - + auto frame = static_cast(data); + auto self = static_cast(zwlr_screencopy_frame_v1_get_user_data(frame)); + BOOST_LOG(error) << "Failed to create buffer from params"sv; self->cleanup_gbm(); - + zwlr_screencopy_frame_v1_destroy(frame); self->status = REINIT; } @@ -478,11 +480,11 @@ namespace wl { std::uint32_t tv_nsec ) { BOOST_LOG(debug) << "Frame ready"sv; - + // Frame is ready for use, GBM buffer now contains screen content current_frame->destroy(); current_frame = get_next_frame(); - + // Keep the GBM buffer alive but destroy the Wayland objects if (current_wl_buffer) { wl_buffer_destroy(current_wl_buffer); @@ -490,7 +492,7 @@ namespace wl { } cleanup_gbm(); - + zwlr_screencopy_frame_v1_destroy(frame); status = READY; } @@ -498,12 +500,12 @@ namespace wl { // Failed callback void dmabuf_t::failed(zwlr_screencopy_frame_v1 *frame) { BOOST_LOG(error) << "Frame capture failed"sv; - + // Clean up resources cleanup_gbm(); auto next_frame = get_next_frame(); next_frame->destroy(); - + zwlr_screencopy_frame_v1_destroy(frame); status = REINIT; } diff --git a/src/platform/linux/wayland.h b/src/platform/linux/wayland.h index 25a0d11e0b8..08d5acbd543 100644 --- a/src/platform/linux/wayland.h +++ b/src/platform/linux/wayland.h @@ -8,14 +8,18 @@ #include #ifdef SUNSHINE_BUILD_WAYLAND -#include -#include -#include + #include + #include + #include #endif // local includes #include "graphics.h" +/** + * The classes defined in this macro block should only be used by + * cpp files whose compilation depends on SUNSHINE_BUILD_WAYLAND + */ #ifdef SUNSHINE_BUILD_WAYLAND namespace wl { @@ -25,7 +29,7 @@ namespace wl { public: frame_t(); void destroy(); - + egl::surface_descriptor_t sd; }; @@ -33,8 +37,8 @@ namespace wl { public: enum status_e { WAITING, ///< Waiting for a frame - READY, ///< Frame is ready - REINIT ///< Reinitialize the frame + READY, ///< Frame is ready + REINIT, ///< Reinitialize the frame }; dmabuf_t(); @@ -55,7 +59,10 @@ namespace wl { void damage(zwlr_screencopy_frame_v1 *frame, std::uint32_t x, std::uint32_t y, std::uint32_t width, std::uint32_t height); void ready(zwlr_screencopy_frame_v1 *frame, std::uint32_t tv_sec_hi, std::uint32_t tv_sec_lo, std::uint32_t tv_nsec); void failed(zwlr_screencopy_frame_v1 *frame); - frame_t *get_next_frame() { return current_frame == &frames[0] ? &frames[1] : &frames[0]; } + + frame_t *get_next_frame() { + return current_frame == &frames[0] ? &frames[1] : &frames[0]; + } status_e status; std::array frames; @@ -67,26 +74,27 @@ namespace wl { void cleanup_gbm(); void create_and_copy_dmabuf(zwlr_screencopy_frame_v1 *frame); - zwp_linux_dmabuf_v1 *dmabuf_interface{nullptr}; + zwp_linux_dmabuf_v1 *dmabuf_interface {nullptr}; + struct { - bool supported{false}; + bool supported {false}; std::uint32_t format; std::uint32_t width; std::uint32_t height; std::uint32_t stride; } shm_info; - + struct { - bool supported{false}; + bool supported {false}; std::uint32_t format; std::uint32_t width; std::uint32_t height; } dmabuf_info; - - struct gbm_device *gbm_device{nullptr}; - struct gbm_bo *current_bo{nullptr}; - struct wl_buffer *current_wl_buffer{nullptr}; - bool y_invert{false}; + + struct gbm_device *gbm_device {nullptr}; + struct gbm_bo *current_bo {nullptr}; + struct wl_buffer *current_wl_buffer {nullptr}; + bool y_invert {false}; }; class monitor_t { @@ -103,10 +111,15 @@ namespace wl { 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 wl_geometry(wl_output *wl_output, std::int32_t x, std::int32_t y, std::int32_t physical_width, std::int32_t physical_height, std::int32_t subpixel, const char *make, const char *model, std::int32_t transform) {} + void wl_mode(wl_output *wl_output, std::uint32_t flags, std::int32_t width, std::int32_t height, std::int32_t refresh); + void wl_done(wl_output *wl_output) {} + void wl_scale(wl_output *wl_output, std::int32_t factor) {} wl_output *output; @@ -125,10 +138,10 @@ namespace wl { public: enum interface_e { - XDG_OUTPUT, ///< xdg-output + XDG_OUTPUT, ///< xdg-output WLR_EXPORT_DMABUF, ///< screencopy manager - LINUX_DMABUF, ///< linux-dmabuf protocol - MAX_INTERFACES + LINUX_DMABUF, ///< linux-dmabuf protocol + MAX_INTERFACES, ///< Maximum number of interfaces }; interface_t() noexcept; @@ -139,12 +152,15 @@ namespace wl { interface_t &operator=(interface_t &&) = delete; void listen(wl_registry *registry); - bool operator[](interface_e bit) const { return interface[bit]; } + + bool operator[](interface_e bit) const { + return interface[bit]; + } std::vector> monitors; - zwlr_screencopy_manager_v1 *screencopy_manager{nullptr}; - zwp_linux_dmabuf_v1 *dmabuf_interface{nullptr}; - zxdg_output_manager_v1 *output_manager{nullptr}; + zwlr_screencopy_manager_v1 *screencopy_manager {nullptr}; + zwp_linux_dmabuf_v1 *dmabuf_interface {nullptr}; + zxdg_output_manager_v1 *output_manager {nullptr}; private: void add_interface(wl_registry *registry, std::uint32_t id, const char *interface, std::uint32_t version); @@ -173,7 +189,7 @@ namespace wl { // Get the registry associated with the display // No need to manually free the registry wl_registry *registry(); - + inline display_internal_t::pointer get() { return display_internal.get(); } @@ -194,7 +210,7 @@ namespace wl { class monitor_t { public: monitor_t(wl_output *output); - + monitor_t(monitor_t &&) = delete; monitor_t(const monitor_t &) = delete; monitor_t &operator=(const monitor_t &) = delete; @@ -208,7 +224,12 @@ namespace wl { platf::touch_port_t viewport; }; - inline std::vector> monitors(const char *display_name = nullptr) { return {}; } - inline int init() { return -1; } + inline std::vector> monitors(const char *display_name = nullptr) { + return {}; + } + + inline int init() { + return -1; + } } // namespace wl #endif From b4868dd492f52af9e02a6e3a9b4410fdcc2ec679 Mon Sep 17 00:00:00 2001 From: gorgbus Date: Sun, 6 Apr 2025 04:04:24 +0200 Subject: [PATCH 5/6] fix(linux): homebrew build --- packaging/sunshine.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/packaging/sunshine.rb b/packaging/sunshine.rb index dd96392a82c..81e42b09456 100644 --- a/packaging/sunshine.rb +++ b/packaging/sunshine.rb @@ -51,6 +51,7 @@ class @PROJECT_NAME@ < Formula depends_on "libxinerama" depends_on "libxrandr" depends_on "libxtst" + depends_on "mesa" depends_on "numactl" depends_on "pulseaudio" depends_on "systemd" From 7907ab6a0a7625229c8763673781af411c17e3e7 Mon Sep 17 00:00:00 2001 From: gorgbus Date: Thu, 10 Apr 2025 21:46:10 +0200 Subject: [PATCH 6/6] Removed GBM_BO_USE_LINEAR flag --- src/platform/linux/wayland.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/linux/wayland.cpp b/src/platform/linux/wayland.cpp index 93654baa15c..4fa05a277dc 100644 --- a/src/platform/linux/wayland.cpp +++ b/src/platform/linux/wayland.cpp @@ -376,7 +376,7 @@ namespace wl { } // Create GBM buffer - current_bo = gbm_bo_create(gbm_device, dmabuf_info.width, dmabuf_info.height, dmabuf_info.format, GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING); + current_bo = gbm_bo_create(gbm_device, dmabuf_info.width, dmabuf_info.height, dmabuf_info.format, GBM_BO_USE_RENDERING); if (!current_bo) { BOOST_LOG(error) << "Failed to create GBM buffer"sv; zwlr_screencopy_frame_v1_destroy(frame);