From 415b9e10ff574cc4843533607ad9ef4f1e916dce Mon Sep 17 00:00:00 2001 From: Scott Moreau Date: Sun, 21 Jun 2026 10:15:31 -0600 Subject: [PATCH 1/2] stream-chooser: Only enable capture if needed protocols are advertised --- src/stream-chooser/stream-chooser.cpp | 45 ++++++++++++++++----------- src/stream-chooser/stream-chooser.hpp | 3 -- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/stream-chooser/stream-chooser.cpp b/src/stream-chooser/stream-chooser.cpp index 38f36ad6..a2cc5649 100644 --- a/src/stream-chooser/stream-chooser.cpp +++ b/src/stream-chooser/stream-chooser.cpp @@ -120,7 +120,6 @@ static void registry_add_object(void *data, wl_registry *registry, uint32_t name wl_registry_bind(registry, name, &ext_foreign_toplevel_list_v1_interface, version); - WayfireStreamChooserApp::getInstance().has_foreign_toplevel_list = true; WayfireStreamChooserApp::getInstance().set_toplevel_list(list); ext_foreign_toplevel_list_v1_add_listener(list, &toplevel_list_v1_impl, NULL); @@ -128,14 +127,12 @@ static void registry_add_object(void *data, wl_registry *registry, uint32_t name { auto manager = (ext_image_copy_capture_manager_v1*)wl_registry_bind(registry, name, &ext_image_copy_capture_manager_v1_interface, version); - WayfireStreamChooserApp::getInstance().has_image_copy_capture = true; WayfireStreamChooserApp::getInstance().set_copy_capture_manager(manager); } else if (strcmp(interface, ext_foreign_toplevel_image_capture_source_manager_v1_interface.name) == 0) { auto toplevel_capture_manager = (ext_foreign_toplevel_image_capture_source_manager_v1*)wl_registry_bind(registry, name, &ext_foreign_toplevel_image_capture_source_manager_v1_interface, version); - WayfireStreamChooserApp::getInstance().has_image_capture_source = true; WayfireStreamChooserApp::getInstance().set_toplevel_capture_manager(toplevel_capture_manager); } else if (strcmp(interface, ext_output_image_capture_source_manager_v1_interface.name) == 0) { @@ -281,24 +278,45 @@ void WayfireStreamChooserApp::activate() wl_display_roundtrip(display); wl_registry_destroy(registry); - if (!has_image_copy_capture) + bool toplevel_capture = true; + bool output_capture = true; + if (!this->manager) { std::cerr << "Compositor has not advertised ext-image-copy-capture-v1" << std::endl; + toplevel_capture = false; } - if (!has_foreign_toplevel_list) + if (!this->list) { std::cerr << "Compositor has not advertised ext-foreign-toplevel-list-v1" << std::endl; + toplevel_capture = false; } - if (!has_image_capture_source) + if (!this->output_capture_manager) { std::cerr << "Compositor has not advertised ext-image-capture-source-v1" << std::endl; + output_capture = false; } - window_label.set_sensitive(false); - window_label.set_tooltip_text("This compositor does not currently support sharing individual windows"); - notebook.set_current_page(1); + if (!this->toplevel_capture_manager) + { + std::cerr << "Compositor has not advertised ext-foreign-toplevel-image-capture-source-v1" << + std::endl; + toplevel_capture = false; + } + + if (!toplevel_capture) + { + if (!output_capture) + { + std::cerr << "No capture protocols supported, nothing to do." << std::endl; + exit(EXIT_FAILURE); + } + + window_label.set_sensitive(false); + window_label.set_tooltip_text("This compositor does not currently support sharing individual windows"); + notebook.set_current_page(1); + } /* Get output list */ auto gtkdisplay = Gdk::Display::get_default(); @@ -357,7 +375,6 @@ void WayfireStreamChooserApp::activate() window.present(); } -static bool first_toplevel = true; void WayfireStreamChooserApp::add_toplevel(ext_foreign_toplevel_handle_v1 *handle) { toplevels.emplace(handle, new WayfireChooserTopLevel(handle)); @@ -367,14 +384,6 @@ void WayfireStreamChooserApp::add_toplevel(ext_foreign_toplevel_handle_v1 *handl auto child = window_list.get_child_at_index(0); window_list.select_child(*child); } - - window_label.set_sensitive(true); - window_label.set_tooltip_text(""); - if (first_toplevel) - { - first_toplevel = false; - notebook.set_current_page(0); - } } void WayfireStreamChooserApp::remove_toplevel(WayfireChooserTopLevel *toplevel) diff --git a/src/stream-chooser/stream-chooser.hpp b/src/stream-chooser/stream-chooser.hpp index bb2b4e69..3b3801fa 100644 --- a/src/stream-chooser/stream-chooser.hpp +++ b/src/stream-chooser/stream-chooser.hpp @@ -34,9 +34,6 @@ class WayfireStreamChooserApp : public Gtk::Application public: Gtk::Notebook notebook; Gtk::FlowBox window_list, screen_list; - bool has_foreign_toplevel_list = false; - bool has_image_copy_capture = false; - bool has_image_capture_source = false; std::string drm_device_name; int drm_fd = -1; gbm_device *gbm_device_ptr = nullptr; From 04f2d68280d8566d7e55c7a11425845941672929 Mon Sep 17 00:00:00 2001 From: Scott Moreau Date: Sun, 21 Jun 2026 10:26:34 -0600 Subject: [PATCH 2/2] stream-chooser: Fix gbm bo fd leak --- src/stream-chooser/toplevelwidget.cpp | 68 ++++++++++++--------------- src/stream-chooser/toplevelwidget.hpp | 2 +- 2 files changed, 32 insertions(+), 38 deletions(-) diff --git a/src/stream-chooser/toplevelwidget.cpp b/src/stream-chooser/toplevelwidget.cpp index 875d27eb..380744e2 100644 --- a/src/stream-chooser/toplevelwidget.cpp +++ b/src/stream-chooser/toplevelwidget.cpp @@ -123,22 +123,24 @@ static void frame_handle_presentation_time(void*, {} static void frame_handle_ready(void *data, - struct ext_image_copy_capture_frame_v1*) + struct ext_image_copy_capture_frame_v1 *frame) { WayfireChooserTopLevel *toplevel = (WayfireChooserTopLevel*)data; + toplevel->buffer_ready(); - toplevel->frame_in_flight = false; + + ext_image_copy_capture_frame_v1_destroy(frame); + toplevel->frame = nullptr; } static void frame_handle_failed(void *data, - struct ext_image_copy_capture_frame_v1 *handle, + struct ext_image_copy_capture_frame_v1 *frame, uint32_t reason) { WayfireChooserTopLevel *toplevel = (WayfireChooserTopLevel*)data; - std::cerr << "Failed to copy frame because reason: " << reason << std::endl; - ext_image_copy_capture_frame_v1_destroy(handle); + + ext_image_copy_capture_frame_v1_destroy(frame); toplevel->frame = nullptr; - toplevel->frame_in_flight = false; } static const struct ext_image_copy_capture_frame_v1_listener frame_listener = { @@ -149,23 +151,6 @@ static const struct ext_image_copy_capture_frame_v1_listener frame_listener = { .failed = frame_handle_failed, }; -static void dmabuf_created(void *data, struct zwp_linux_buffer_params_v1*, - struct wl_buffer *wl_buffer) -{ - auto toplevel = (WayfireChooserTopLevel*)data; - toplevel->buffer->buffer = wl_buffer; -} - -static void dmabuf_failed(void*, struct zwp_linux_buffer_params_v1*) -{ - std::cerr << "Failed to create dmabuf" << std::endl; -} - -static const struct zwp_linux_buffer_params_v1_listener params_listener = { - .created = dmabuf_created, - .failed = dmabuf_failed, -}; - static void frame_handle_linux_dmabuf(uint32_t width, uint32_t height, WayfireChooserTopLevel *toplevel) { auto format = (toplevel->current_buffer_format == WL_SHM_FORMAT_XRGB8888) ? @@ -173,12 +158,24 @@ static void frame_handle_linux_dmabuf(uint32_t width, uint32_t height, WayfireCh auto buffer = toplevel->buffer; + if (buffer->gbm_fd > 0) + { + close(buffer->gbm_fd); + buffer->gbm_fd = -1; + } + if (buffer->bo) { gbm_bo_destroy(buffer->bo); buffer->bo = nullptr; } + if (buffer->buffer) + { + wl_buffer_destroy(buffer->buffer); + buffer->buffer = nullptr; + } + if (buffer->params) { zwp_linux_buffer_params_v1_destroy(buffer->params); @@ -209,14 +206,14 @@ static void frame_handle_linux_dmabuf(uint32_t width, uint32_t height, WayfireCh buffer->params = zwp_linux_dmabuf_v1_create_params(WayfireStreamChooserApp::getInstance().dmabuf); uint64_t mod = gbm_bo_get_modifier(buffer->bo); + buffer->gbm_fd = gbm_bo_get_fd(buffer->bo); zwp_linux_buffer_params_v1_add(buffer->params, - gbm_bo_get_fd(buffer->bo), 0, + buffer->gbm_fd, 0, gbm_bo_get_offset(buffer->bo, 0), gbm_bo_get_stride(buffer->bo), mod >> 32, mod & 0xffffffff); - zwp_linux_buffer_params_v1_add_listener(buffer->params, ¶ms_listener, toplevel); - zwp_linux_buffer_params_v1_create(buffer->params, w, h, format, 0); + buffer->buffer = zwp_linux_buffer_params_v1_create_immed(buffer->params, w, h, format, 0); } void WayfireChooserTopLevel::start_toplevel_source_ssession() @@ -265,11 +262,6 @@ void WayfireChooserTopLevel::frame_request() return; } - if (frame_in_flight) - { - return; - } - if (frame) { ext_image_copy_capture_frame_v1_destroy(frame); @@ -283,7 +275,6 @@ void WayfireChooserTopLevel::frame_request() ext_image_copy_capture_frame_v1_attach_buffer(buffer->frame, buffer->buffer); ext_image_copy_capture_frame_v1_damage_buffer(buffer->frame, 0, 0, buffer->width, buffer->height); ext_image_copy_capture_frame_v1_capture(buffer->frame); - frame_in_flight = true; } void WayfireChooserTopLevel::buffer_ready() @@ -309,10 +300,10 @@ void WayfireChooserTopLevel::buffer_ready() std::shared_ptr bytes = 0; size_t size = stride * buffer->height; bytes = Glib::Bytes::create((unsigned char*)data, size); + gbm_bo_unmap(buffer->bo, map_data); if (!bytes) { - gbm_bo_unmap(buffer->bo, map_data); return; } @@ -326,8 +317,6 @@ void WayfireChooserTopLevel::buffer_ready() auto texture = builder->build(); screenshot.set_paintable(texture); - - gbm_bo_unmap(buffer->bo, map_data); } void WayfireChooserTopLevel::stream() @@ -486,9 +475,9 @@ WayfireChooserTopLevel::~WayfireChooserTopLevel() ext_image_copy_capture_session_v1_destroy(recording_session); } - if (buffer->buffer) + if (buffer->gbm_fd > 0) { - wl_buffer_destroy(buffer->buffer); + close(buffer->gbm_fd); } if (buffer->bo) @@ -496,6 +485,11 @@ WayfireChooserTopLevel::~WayfireChooserTopLevel() gbm_bo_destroy(buffer->bo); } + if (buffer->buffer) + { + wl_buffer_destroy(buffer->buffer); + } + if (buffer->params) { zwp_linux_buffer_params_v1_destroy(buffer->params); diff --git a/src/stream-chooser/toplevelwidget.hpp b/src/stream-chooser/toplevelwidget.hpp index a4746463..3965e955 100644 --- a/src/stream-chooser/toplevelwidget.hpp +++ b/src/stream-chooser/toplevelwidget.hpp @@ -14,6 +14,7 @@ struct toplevel_buffer int height = 0; int stride = 0; gbm_bo *bo = nullptr; + int gbm_fd = -1; wl_buffer *buffer = nullptr; zwp_linux_buffer_params_v1 *params = nullptr; ext_image_copy_capture_frame_v1 *frame = NULL; @@ -47,7 +48,6 @@ class WayfireChooserTopLevel : public Gtk::Box ext_foreign_toplevel_handle_v1 *handle = nullptr; std::shared_ptr buffer = nullptr; ext_image_copy_capture_frame_v1 *frame = NULL; - bool frame_in_flight = false; WayfireChooserTopLevel(ext_foreign_toplevel_handle_v1 *handle); ~WayfireChooserTopLevel(); void commit();