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/seat.c b/seat.c index 5f659a4..3d841d1 100644 --- a/seat.c +++ b/seat.c @@ -945,6 +945,8 @@ seat_set_focus(struct cg_seat *seat, struct cg_view *view) if (prev_view) { view_activate(prev_view, false); + if (prev_view->foreign_toplevel_handle) + wlr_foreign_toplevel_handle_v1_set_activated(prev_view->foreign_toplevel_handle, false); } /* Move the view to the front, but only if it isn't a @@ -955,6 +957,10 @@ seat_set_focus(struct cg_seat *seat, struct cg_view *view) } view_activate(view, true); + + if (view->foreign_toplevel_handle) + wlr_foreign_toplevel_handle_v1_set_activated(view->foreign_toplevel_handle, true); + char *title = view_get_title(view); struct cg_output *output; wl_list_for_each (output, &server->outputs, link) { diff --git a/server.h b/server.h index 00c2a61..f88c5ef 100644 --- a/server.h +++ b/server.h @@ -4,6 +4,7 @@ #include "config.h" #include +#include #include #include #include @@ -61,6 +62,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.h b/view.h index 9b2ab75..45e86c8 100644 --- a/view.h +++ b/view.h @@ -32,6 +32,8 @@ struct cg_view { enum cg_view_type type; const struct cg_view_impl *impl; + + struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel_handle; }; struct cg_view_impl { diff --git a/xdg_shell.c b/xdg_shell.c index 5493918..315ae04 100644 --- a/xdg_shell.c +++ b/xdg_shell.c @@ -10,10 +10,12 @@ #include #include #include +#include #include #include #include +#include "seat.h" #include "server.h" #include "view.h" #include "xdg_shell.h" @@ -183,21 +185,61 @@ destroy(struct cg_view *view) free(xdg_shell_view); } +static void +xdg_toplevel_set_fullscreen(struct cg_xdg_shell_view *xdg_shell_view, bool fullscreen) +{ + 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, fullscreen); + + if (xdg_shell_view->view.foreign_toplevel_handle) { + wlr_foreign_toplevel_handle_v1_set_fullscreen(xdg_shell_view->view.foreign_toplevel_handle, fullscreen); + } +} + 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); - /** - * Certain clients do not like figuring out their own window geometry if they - * display in fullscreen mode, so we set it here. - */ - 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); + xdg_toplevel_set_fullscreen(xdg_shell_view, xdg_shell_view->xdg_toplevel->requested.fullscreen); +} - wlr_xdg_toplevel_set_fullscreen(xdg_shell_view->xdg_toplevel, - xdg_shell_view->xdg_toplevel->requested.fullscreen); +static void +handle_foreign_toplevel_request_activate(struct wl_listener *listener, void *data) +{ + struct cg_foreign_toplevel_handle *foreign_toplevel = + wl_container_of(listener, foreign_toplevel, request_activate); + struct cg_xdg_shell_view *xdg_shell_view = foreign_toplevel->view; + struct cg_view *view = &xdg_shell_view->view; + + if (view->scene_tree) { + wlr_scene_node_raise_to_top(&view->scene_tree->node); + } + + seat_set_focus(view->server->seat, view); +} + +static void +handle_foreign_toplevel_request_close(struct wl_listener *listener, void *data) +{ + struct cg_foreign_toplevel_handle *foreign_toplevel = + wl_container_of(listener, foreign_toplevel, request_close); + struct cg_xdg_shell_view *xdg_shell_view = foreign_toplevel->view; + + wlr_xdg_toplevel_send_close(xdg_shell_view->xdg_toplevel); +} + +static void +handle_foreign_toplevel_request_fullscreen(struct wl_listener *listener, void *data) +{ + struct cg_foreign_toplevel_handle *foreign_toplevel = + wl_container_of(listener, foreign_toplevel, request_fullscreen); + struct cg_xdg_shell_view *xdg_shell_view = foreign_toplevel->view; + struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data; + + xdg_toplevel_set_fullscreen(xdg_shell_view, event->fullscreen); } static void @@ -206,6 +248,10 @@ handle_xdg_toplevel_unmap(struct wl_listener *listener, void *data) struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, unmap); struct cg_view *view = &xdg_shell_view->view; + if (view->foreign_toplevel_handle) { + wlr_foreign_toplevel_handle_v1_set_activated(view->foreign_toplevel_handle, false); + } + view_unmap(view); } @@ -216,6 +262,14 @@ 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); + + if (view->foreign_toplevel_handle) { + 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 @@ -240,6 +294,19 @@ handle_xdg_toplevel_destroy(struct wl_listener *listener, void *data) struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, destroy); struct cg_view *view = &xdg_shell_view->view; + if (xdg_shell_view->foreign_toplevel) { + wl_list_remove(&xdg_shell_view->foreign_toplevel->request_activate.link); + wl_list_remove(&xdg_shell_view->foreign_toplevel->request_close.link); + wl_list_remove(&xdg_shell_view->foreign_toplevel->request_fullscreen.link); + free(xdg_shell_view->foreign_toplevel); + xdg_shell_view->foreign_toplevel = NULL; + } + + if (view->foreign_toplevel_handle) { + wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel_handle); + view->foreign_toplevel_handle = NULL; + } + wl_list_remove(&xdg_shell_view->commit.link); wl_list_remove(&xdg_shell_view->map.link); wl_list_remove(&xdg_shell_view->unmap.link); @@ -275,6 +342,9 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data) view_init(&xdg_shell_view->view, server, CAGE_XDG_SHELL_VIEW, &xdg_shell_view_impl); xdg_shell_view->xdg_toplevel = toplevel; + xdg_shell_view->view.foreign_toplevel_handle = + wlr_foreign_toplevel_handle_v1_create(server->foreign_toplevel_manager); + xdg_shell_view->commit.notify = handle_xdg_toplevel_commit; wl_signal_add(&toplevel->base->surface->events.commit, &xdg_shell_view->commit); xdg_shell_view->map.notify = handle_xdg_toplevel_map; @@ -286,6 +356,27 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data) xdg_shell_view->request_fullscreen.notify = handle_xdg_toplevel_request_fullscreen; wl_signal_add(&toplevel->events.request_fullscreen, &xdg_shell_view->request_fullscreen); + if (xdg_shell_view->view.foreign_toplevel_handle) { + struct cg_foreign_toplevel_handle *foreign_toplevel = + calloc(1, sizeof(struct cg_foreign_toplevel_handle)); + if (!foreign_toplevel) { + wlr_log(WLR_ERROR, "Failed to allocate foreign toplevel handle"); + } else { + foreign_toplevel->view = xdg_shell_view; + xdg_shell_view->foreign_toplevel = foreign_toplevel; + + foreign_toplevel->request_activate.notify = handle_foreign_toplevel_request_activate; + wl_signal_add(&xdg_shell_view->view.foreign_toplevel_handle->events.request_activate, + &foreign_toplevel->request_activate); + foreign_toplevel->request_close.notify = handle_foreign_toplevel_request_close; + wl_signal_add(&xdg_shell_view->view.foreign_toplevel_handle->events.request_close, + &foreign_toplevel->request_close); + foreign_toplevel->request_fullscreen.notify = handle_foreign_toplevel_request_fullscreen; + wl_signal_add(&xdg_shell_view->view.foreign_toplevel_handle->events.request_fullscreen, + &foreign_toplevel->request_fullscreen); + } + } + toplevel->base->data = xdg_shell_view; } diff --git a/xdg_shell.h b/xdg_shell.h index f819549..609f938 100644 --- a/xdg_shell.h +++ b/xdg_shell.h @@ -7,6 +7,14 @@ #include "view.h" +struct cg_foreign_toplevel_handle { + struct cg_xdg_shell_view *view; + + struct wl_listener request_activate; + struct wl_listener request_close; + struct wl_listener request_fullscreen; +}; + struct cg_xdg_shell_view { struct cg_view view; struct wlr_xdg_toplevel *xdg_toplevel; @@ -16,6 +24,8 @@ struct cg_xdg_shell_view { struct wl_listener unmap; struct wl_listener map; struct wl_listener request_fullscreen; + + struct cg_foreign_toplevel_handle *foreign_toplevel; }; struct cg_xdg_decoration {