From af0a3cf8a6092fcb5a2488f905cfed42cea6e2f4 Mon Sep 17 00:00:00 2001 From: GnSight Date: Wed, 1 Oct 2025 11:53:09 +0800 Subject: [PATCH] Add support for wlr-foreign-toplevel-management --- cage.c | 8 ++++++++ server.h | 2 ++ view.c | 43 +++++++++++++++++++++++++++++++++++++++---- view.h | 5 +++++ xdg_shell.c | 19 ++++++++++++++++--- xwayland.c | 15 +++++++++++++++ 6 files changed, 85 insertions(+), 7 deletions(-) diff --git a/cage.c b/cage.c index 9b7c510..0f03cd9 100644 --- a/cage.c +++ b/cage.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -527,6 +528,13 @@ main(int argc, char *argv[]) goto end; } + server.foreign_toplevel_manager = wlr_foreign_toplevel_manager_v1_create(server.wl_display); + if (!server.foreign_toplevel_manager) { + wlr_log(WLR_ERROR, "Unable to create the foreign toplevel manager"); + ret = 1; + goto end; + } + #if CAGE_HAS_XWAYLAND struct wlr_xcursor_manager *xcursor_manager = NULL; struct wlr_xwayland *xwayland = wlr_xwayland_create(server.wl_display, compositor, true); diff --git a/server.h b/server.h index 00c2a61..277b09e 100644 --- a/server.h +++ b/server.h @@ -61,6 +61,8 @@ struct cg_server { struct wlr_relative_pointer_manager_v1 *relative_pointer_manager; + struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; + bool xdg_decoration; bool allow_vt_switch; bool return_app_code; diff --git a/view.c b/view.c index 8cbeb5e..94fd75e 100644 --- a/view.c +++ b/view.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -50,6 +51,7 @@ void view_activate(struct cg_view *view, bool activate) { view->impl->activate(view, activate); + wlr_foreign_toplevel_handle_v1_set_activated(view->foreign_toplevel_handle, activate); } static bool @@ -115,20 +117,39 @@ view_unmap(struct cg_view *view) { wl_list_remove(&view->link); + wl_list_remove(&view->request_activate.link); + wl_list_remove(&view->request_close.link); + wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel_handle); + view->foreign_toplevel_handle = NULL; + wlr_scene_node_destroy(&view->scene_tree->node); view->wlr_surface->data = NULL; view->wlr_surface = NULL; } +void +handle_surface_request_activate(struct wl_listener *listener, void *data) +{ + struct cg_view *view = wl_container_of(listener, view, request_activate); + + wlr_scene_node_raise_to_top(&view->scene_tree->node); + seat_set_focus(view->server->seat, view); +} + +void +handle_surface_request_close(struct wl_listener *listener, void *data) +{ + struct cg_view *view = wl_container_of(listener, view, request_close); + view->impl->close(view); +} + void view_map(struct cg_view *view, struct wlr_surface *surface) { view->scene_tree = wlr_scene_subsurface_tree_create(&view->server->scene->tree, surface); - if (!view->scene_tree) { - wl_resource_post_no_memory(surface->resource); - return; - } + if (!view->scene_tree) + goto fail; view->scene_tree->node.data = view; view->wlr_surface = surface; @@ -144,7 +165,21 @@ view_map(struct cg_view *view, struct wlr_surface *surface) } wl_list_insert(&view->server->views, &view->link); + + view->foreign_toplevel_handle = wlr_foreign_toplevel_handle_v1_create(view->server->foreign_toplevel_manager); + if (!view->foreign_toplevel_handle) + goto fail; + + view->request_activate.notify = handle_surface_request_activate; + wl_signal_add(&view->foreign_toplevel_handle->events.request_activate, &view->request_activate); + view->request_close.notify = handle_surface_request_close; + wl_signal_add(&view->foreign_toplevel_handle->events.request_close, &view->request_close); + seat_set_focus(view->server->seat, view); + return; + +fail: + wl_resource_post_no_memory(surface->resource); } void diff --git a/view.h b/view.h index 9b2ab75..53a04f8 100644 --- a/view.h +++ b/view.h @@ -32,6 +32,10 @@ struct cg_view { enum cg_view_type type; const struct cg_view_impl *impl; + + struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel_handle; + struct wl_listener request_activate; + struct wl_listener request_close; }; struct cg_view_impl { @@ -41,6 +45,7 @@ struct cg_view_impl { bool (*is_transient_for)(struct cg_view *child, struct cg_view *parent); void (*activate)(struct cg_view *view, bool activate); void (*maximize)(struct cg_view *view, int output_width, int output_height); + void (*close)(struct cg_view *view); void (*destroy)(struct cg_view *view); }; diff --git a/xdg_shell.c b/xdg_shell.c index 5493918..a6fefd4 100644 --- a/xdg_shell.c +++ b/xdg_shell.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -183,10 +184,18 @@ destroy(struct cg_view *view) free(xdg_shell_view); } +static void +close(struct cg_view *view) +{ + struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); + wlr_xdg_toplevel_send_close(xdg_shell_view->xdg_toplevel); +} + static void handle_xdg_toplevel_request_fullscreen(struct wl_listener *listener, void *data) { struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_fullscreen); + bool fullscreen = xdg_shell_view->xdg_toplevel->requested.fullscreen; /** * Certain clients do not like figuring out their own window geometry if they @@ -195,9 +204,8 @@ handle_xdg_toplevel_request_fullscreen(struct wl_listener *listener, void *data) struct wlr_box layout_box; wlr_output_layout_get_box(xdg_shell_view->view.server->output_layout, NULL, &layout_box); wlr_xdg_toplevel_set_size(xdg_shell_view->xdg_toplevel, layout_box.width, layout_box.height); - - wlr_xdg_toplevel_set_fullscreen(xdg_shell_view->xdg_toplevel, - xdg_shell_view->xdg_toplevel->requested.fullscreen); + wlr_xdg_toplevel_set_fullscreen(xdg_shell_view->xdg_toplevel, fullscreen); + wlr_foreign_toplevel_handle_v1_set_fullscreen(xdg_shell_view->view.foreign_toplevel_handle, fullscreen); } static void @@ -216,6 +224,10 @@ handle_xdg_toplevel_map(struct wl_listener *listener, void *data) struct cg_view *view = &xdg_shell_view->view; view_map(view, xdg_shell_view->xdg_toplevel->base->surface); + + wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel_handle, xdg_shell_view->xdg_toplevel->title); + wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel_handle, xdg_shell_view->xdg_toplevel->app_id); + /* Activation state will be set by seat_set_focus */ } static void @@ -258,6 +270,7 @@ static const struct cg_view_impl xdg_shell_view_impl = { .activate = activate, .maximize = maximize, .destroy = destroy, + .close = close, }; void diff --git a/xwayland.c b/xwayland.c index 3df7a08..078a853 100644 --- a/xwayland.c +++ b/xwayland.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -102,12 +103,21 @@ destroy(struct cg_view *view) free(xwayland_view); } +static void +close(struct cg_view *view) +{ + struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view); + wlr_xwayland_surface_close(xwayland_view->xwayland_surface); +} + static void handle_xwayland_surface_request_fullscreen(struct wl_listener *listener, void *data) { struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, request_fullscreen); struct wlr_xwayland_surface *xwayland_surface = xwayland_view->xwayland_surface; wlr_xwayland_surface_set_fullscreen(xwayland_view->xwayland_surface, xwayland_surface->fullscreen); + wlr_foreign_toplevel_handle_v1_set_fullscreen(xwayland_view->view.foreign_toplevel_handle, + xwayland_surface->fullscreen); } static void @@ -131,6 +141,10 @@ handle_xwayland_surface_map(struct wl_listener *listener, void *data) } view_map(view, xwayland_view->xwayland_surface->surface); + + wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel_handle, xwayland_view->xwayland_surface->title); + wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel_handle, + xwayland_view->xwayland_surface->class); } static void @@ -154,6 +168,7 @@ static const struct cg_view_impl xwayland_view_impl = { .activate = activate, .maximize = maximize, .destroy = destroy, + .close = close, }; void