From 17f7c1b7820e4f85913ec70ab3c76f238c15c54c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 12 Jun 2025 00:14:50 +0200 Subject: [PATCH 1/5] build: set wrap_mode=nodownload in default options This can be surprising (e.g. in CI, this can download source code instead of using system libraries) and users can easily turn it back on if desired. --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 05e518818..eab1348ae 100644 --- a/meson.build +++ b/meson.build @@ -8,6 +8,7 @@ project( 'c_std=c11', 'warning_level=2', 'werror=true', + 'wrap_mode=nodownload', ], ) From 4b15b3427fd0835247d0a2e188c00f1d2aa04484 Mon Sep 17 00:00:00 2001 From: hwsmm <9151706+hwsmm@users.noreply.github.com> Date: Mon, 16 Jun 2025 00:41:19 +0900 Subject: [PATCH 2/5] Rename get_current_time_msec to get_current_time_in_msec and move to util.c get_current_time_msec conflicts with a function with the same name in wlroots. --- common/util.c | 6 ++++++ include/util.h | 2 ++ sway/input/cursor.c | 10 ++-------- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/common/util.c b/common/util.c index 7c492bcbf..ae1fe4431 100644 --- a/common/util.c +++ b/common/util.c @@ -141,3 +141,9 @@ bool sway_set_cloexec(int fd, bool cloexec) { } return true; } + +uint32_t get_current_time_in_msec(void) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + return now.tv_sec * 1000 + now.tv_nsec / 1000000; +} diff --git a/include/util.h b/include/util.h index f887d4895..92f5916cf 100644 --- a/include/util.h +++ b/include/util.h @@ -61,4 +61,6 @@ const char *sway_wl_output_subpixel_to_string(enum wl_output_subpixel subpixel); bool sway_set_cloexec(int fd, bool cloexec); +uint32_t get_current_time_in_msec(void); + #endif diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 1fd57ec4e..aeab558cb 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -32,12 +32,6 @@ #include "sway/tree/workspace.h" #include "wlr-layer-shell-unstable-v1-protocol.h" -static uint32_t get_current_time_msec(void) { - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - return now.tv_sec * 1000 + now.tv_nsec / 1000000; -} - /** * Returns the node at the cursor's position. If there is a surface at that * location, it is stored in **surface (it may not be a view). @@ -144,7 +138,7 @@ struct sway_node *node_at_coords( } void cursor_rebase(struct sway_cursor *cursor) { - uint32_t time_msec = get_current_time_msec(); + uint32_t time_msec = get_current_time_in_msec(); seatop_rebase(cursor->seat, time_msec); } @@ -359,7 +353,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, struct wlr_input_device *device, uint32_t time_msec, uint32_t button, enum wl_pointer_button_state state) { if (time_msec == 0) { - time_msec = get_current_time_msec(); + time_msec = get_current_time_in_msec(); } seatop_button(cursor->seat, time_msec, device, button, state); From 25ea1a0af287018da1dbb34fe92e6c8ea3b8a88d Mon Sep 17 00:00:00 2001 From: hwsmm <9151706+hwsmm@users.noreply.github.com> Date: Mon, 16 Jun 2025 00:41:44 +0900 Subject: [PATCH 3/5] seatop_default: Call seatop_rebase with proper timestamp --- sway/input/seatop_default.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index a2e99d2bc..19645b4c9 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -16,6 +16,7 @@ #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "log.h" +#include "util.h" #if WLR_HAS_XWAYLAND #include "sway/xwayland.h" #endif @@ -1148,5 +1149,7 @@ void seatop_begin_default(struct sway_seat *seat) { seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - seatop_rebase(seat, 0); + + uint32_t time_msec = get_current_time_in_msec(); + seatop_rebase(seat, time_msec); } From eb8acfd7b1cd0eeab75fe699e6aa0751150e80a2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 5 Jun 2025 13:15:38 +0200 Subject: [PATCH 4/5] Stop using wlr_scene_buffer_send_frame_done() That function now takes the output as input. We don't always have the output at hand, so use the function operating on a wlr_scene_surface instead. --- sway/desktop/output.c | 19 +++++++++++++------ sway/tree/view.c | 6 +++++- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index e6fe2ee4d..12dc9cc7a 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -97,11 +97,11 @@ struct buffer_timer { }; static int handle_buffer_timer(void *data) { - struct wlr_scene_buffer *buffer = data; + struct wlr_scene_surface *scene_surface = data; struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - wlr_scene_buffer_send_frame_done(buffer, &now); + wlr_scene_surface_send_frame_done(scene_surface, &now); return 0; } @@ -114,7 +114,9 @@ static void handle_buffer_timer_destroy(struct wl_listener *listener, free(timer); } -static struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_buffer *buffer) { +static struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_surface *scene_surface) { + struct wlr_scene_buffer *buffer = scene_surface->buffer; + struct buffer_timer *timer = scene_descriptor_try_get(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER); if (timer) { @@ -127,7 +129,7 @@ static struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_buffer * } timer->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, - handle_buffer_timer, buffer); + handle_buffer_timer, scene_surface); if (!timer->frame_done_timer) { free(timer); return NULL; @@ -151,6 +153,11 @@ static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, return; } + struct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(buffer); + if (scene_surface == NULL) { + return; + } + struct wlr_scene_node *current = &buffer->node; while (true) { struct sway_view *view = scene_descriptor_try_get(current, @@ -173,13 +180,13 @@ static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, struct buffer_timer *timer = NULL; if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) { - timer = buffer_timer_get_or_create(buffer); + timer = buffer_timer_get_or_create(scene_surface); } if (timer) { wl_event_source_timer_update(timer->frame_done_timer, delay); } else { - wlr_scene_buffer_send_frame_done(buffer, &data->when); + wlr_scene_surface_send_frame_done(scene_surface, &data->when); } } diff --git a/sway/tree/view.c b/sway/tree/view.c index 9a5cf06e0..742a66c2d 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -1242,7 +1242,11 @@ bool view_can_tear(struct sway_view *view) { static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer, int x, int y, void *data) { struct timespec *when = data; - wl_signal_emit_mutable(&scene_buffer->events.frame_done, when); + struct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(scene_buffer); + if (scene_surface == NULL) { + return; + } + wlr_scene_surface_send_frame_done(scene_surface, when); } void view_send_frame_done(struct sway_view *view) { From 170c9c9525f54e8c1ba03847d5f9b01fc24b8c89 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 25 May 2025 19:38:05 +0200 Subject: [PATCH 5/5] Add support for toplevel capture References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5078 --- include/sway/server.h | 3 +++ include/sway/tree/view.h | 11 ++++++++++- sway/desktop/xdg_shell.c | 16 ++++++++++++---- sway/desktop/xwayland.c | 6 ++++++ sway/server.c | 22 ++++++++++++++++++++++ sway/tree/view.c | 7 +++++++ 6 files changed, 60 insertions(+), 5 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index 8e859702a..72bccd70c 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -112,6 +112,9 @@ struct sway_server { struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager_v1; struct wlr_security_context_manager_v1 *security_context_manager_v1; + struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 *ext_foreign_toplevel_image_capture_source_manager_v1; + struct wl_listener new_foreign_toplevel_capture_request; + struct wlr_xdg_activation_v1 *xdg_activation_v1; struct wl_listener xdg_activation_v1_request_activate; struct wl_listener xdg_activation_v1_new_token; diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 9f084eeb4..6151a0234 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -69,6 +69,9 @@ struct sway_view { struct wlr_scene_tree *content_tree; struct wlr_scene_tree *saved_surface_tree; + struct wlr_scene *image_capture_scene; + struct wlr_ext_image_capture_source_v1 *image_capture_source; + struct sway_container *container; // NULL if unmapped and transactions finished struct wlr_surface *surface; // NULL for unmapped views struct sway_xdg_decoration *xdg_decoration; @@ -124,6 +127,8 @@ struct sway_view { struct sway_xdg_shell_view { struct sway_view view; + struct wlr_scene_tree *image_capture_tree; + struct wl_listener commit; struct wl_listener request_move; struct wl_listener request_resize; @@ -142,6 +147,8 @@ struct sway_xwayland_view { struct wlr_scene_tree *surface_tree; + struct wlr_scene_surface *image_capture_scene_surface; + struct wl_listener commit; struct wl_listener request_move; struct wl_listener request_resize; @@ -192,10 +199,12 @@ struct sway_popup_desc { struct sway_xdg_popup { struct sway_view *view; + struct wlr_xdg_popup *wlr_xdg_popup; struct wlr_scene_tree *scene_tree; struct wlr_scene_tree *xdg_surface_tree; - struct wlr_xdg_popup *wlr_xdg_popup; + + struct wlr_scene_tree *image_capture_tree; struct sway_popup_desc desc; diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 3aed4ec7e..3852806e4 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -20,13 +20,13 @@ static struct sway_xdg_popup *popup_create( struct wlr_xdg_popup *wlr_popup, struct sway_view *view, - struct wlr_scene_tree *parent); + struct wlr_scene_tree *parent, struct wlr_scene_tree *image_capture_parent); static void popup_handle_new_popup(struct wl_listener *listener, void *data) { struct sway_xdg_popup *popup = wl_container_of(listener, popup, new_popup); struct wlr_xdg_popup *wlr_popup = data; - popup_create(wlr_popup, popup->view, popup->xdg_surface_tree); + popup_create(wlr_popup, popup->view, popup->xdg_surface_tree, popup->image_capture_tree); } static void popup_handle_destroy(struct wl_listener *listener, void *data) { @@ -77,7 +77,8 @@ static void popup_handle_reposition(struct wl_listener *listener, void *data) { } static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup, - struct sway_view *view, struct wlr_scene_tree *parent) { + struct sway_view *view, struct wlr_scene_tree *parent, + struct wlr_scene_tree *image_capture_parent) { struct wlr_xdg_surface *xdg_surface = wlr_popup->base; struct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup)); @@ -113,6 +114,11 @@ static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup, return NULL; } + popup->image_capture_tree = wlr_scene_xdg_surface_create(image_capture_parent, xdg_surface); + if (popup->image_capture_tree == NULL) { + return NULL; + } + popup->wlr_xdg_popup = xdg_surface->popup; struct sway_xdg_shell_view *shell_view = wl_container_of(view, shell_view, view); @@ -359,7 +365,7 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { struct wlr_xdg_popup *wlr_popup = data; struct sway_xdg_popup *popup = popup_create(wlr_popup, - &xdg_shell_view->view, root->layers.popup); + &xdg_shell_view->view, root->layers.popup, xdg_shell_view->image_capture_tree); if (!popup) { return; } @@ -570,6 +576,8 @@ void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) { wl_signal_add(&xdg_toplevel->events.destroy, &xdg_shell_view->destroy); wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base); + xdg_shell_view->image_capture_tree = + wlr_scene_xdg_surface_create(&xdg_shell_view->view.image_capture_scene->tree, xdg_toplevel->base); xdg_toplevel->base->data = xdg_shell_view; } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index b83537a0a..76e63ce1f 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -497,6 +497,9 @@ static void handle_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&xwayland_view->commit.link); wl_list_remove(&xwayland_view->surface_tree_destroy.link); + wlr_scene_node_destroy(&xwayland_view->image_capture_scene_surface->buffer->node); + xwayland_view->image_capture_scene_surface = NULL; + if (xwayland_view->surface_tree) { wlr_scene_node_destroy(&xwayland_view->surface_tree->node); xwayland_view->surface_tree = NULL; @@ -537,6 +540,9 @@ static void handle_map(struct wl_listener *listener, void *data) { &xwayland_view->surface_tree_destroy); } + xwayland_view->image_capture_scene_surface = + wlr_scene_surface_create(&xwayland_view->view.image_capture_scene->tree, xsurface->surface); + transaction_commit_dirty(); } diff --git a/sway/server.c b/sway/server.c index e8a6ce64f..6ca5906df 100644 --- a/sway/server.c +++ b/sway/server.c @@ -232,6 +232,21 @@ static void handle_renderer_lost(struct wl_listener *listener, void *data) { server->recreating_renderer = wl_event_loop_add_idle(server->wl_event_loop, do_renderer_recreate, server); } +static void handle_new_foreign_toplevel_capture_request(struct wl_listener *listener, void *data) { + struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request = data; + struct sway_view *view = request->toplevel_handle->data; + + if (view->image_capture_source == NULL) { + view->image_capture_source = wlr_ext_image_capture_source_v1_create_with_scene_node( + &view->image_capture_scene->tree.node, server.wl_event_loop, server.allocator, server.renderer); + if (view->image_capture_source == NULL) { + return; + } + } + + wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept(request, view->image_capture_source); +} + bool server_init(struct sway_server *server) { sway_log(SWAY_DEBUG, "Initializing Wayland server"); server->wl_display = wl_display_create(); @@ -395,6 +410,12 @@ bool server_init(struct sway_server *server) { wlr_content_type_manager_v1_create(server->wl_display, 1); wlr_fractional_scale_manager_v1_create(server->wl_display, 1); + server->ext_foreign_toplevel_image_capture_source_manager_v1 = + wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(server->wl_display, 1); + server->new_foreign_toplevel_capture_request.notify = handle_new_foreign_toplevel_capture_request; + wl_signal_add(&server->ext_foreign_toplevel_image_capture_source_manager_v1->events.new_request, + &server->new_foreign_toplevel_capture_request); + server->tearing_control_v1 = wlr_tearing_control_manager_v1_create(server->wl_display, 1); server->tearing_control_new_object.notify = handle_new_tearing_hint; @@ -488,6 +509,7 @@ void server_fini(struct sway_server *server) { wl_list_remove(&server->xdg_activation_v1_request_activate.link); wl_list_remove(&server->xdg_activation_v1_new_token.link); wl_list_remove(&server->request_set_cursor_shape.link); + wl_list_remove(&server->new_foreign_toplevel_capture_request.link); input_manager_finish(server->input); // TODO: free sway-specific resources diff --git a/sway/tree/view.c b/sway/tree/view.c index 742a66c2d..eca5fcc56 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -49,6 +49,11 @@ bool view_init(struct sway_view *view, enum sway_view_type type, failed = true; } + view->image_capture_scene = wlr_scene_create(); + if (view->image_capture_scene == NULL) { + failed = true; + } + if (failed) { wlr_scene_node_destroy(&view->scene_tree->node); return false; @@ -81,6 +86,7 @@ void view_destroy(struct sway_view *view) { list_free(view->executed_criteria); view_assign_ctx(view, NULL); + wlr_scene_node_destroy(&view->image_capture_scene->tree.node); wlr_scene_node_destroy(&view->scene_tree->node); if (view->impl->destroy) { view->impl->destroy(view); @@ -815,6 +821,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, }; view->ext_foreign_toplevel = wlr_ext_foreign_toplevel_handle_v1_create(server.foreign_toplevel_list, &foreign_toplevel_state); + view->ext_foreign_toplevel->data = view; view->foreign_toplevel = wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager);