From 677dc9baf9af0dec9a9e65f5988adb32234512e6 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Tue, 22 Nov 2022 05:11:50 -0500 Subject: [PATCH] output,xwayland: Add support for _NET_WM_SUPPORT_PARTIAL Account for space taken up by XWayland panels (as indicated by the _NET_WM_SUPPORT_PARTIAL property) in the usable_area calculation. This makes it possible to use labwc in a "transitional" setup, where it replaces the X11 window manager and compositor, but most other parts of a existing X11 desktop environment can still be used via XWayland. (Some remaining drawbacks of such a setup would be the lack of desktop icons, and native Wayland clients not showing up in X11-based taskbars.) --- include/view.h | 2 + include/xwayland.h | 5 ++ src/output.c | 11 +++++ src/xwayland.c | 112 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 130 insertions(+) diff --git a/include/view.h b/include/view.h index 471e52dd..0cd17192 100644 --- a/include/view.h +++ b/include/view.h @@ -55,6 +55,8 @@ enum view_wants_focus { }; struct view; +struct wlr_output; +struct wlr_output_layout; struct wlr_surface; /* Basic size hints (subset of XSizeHints from X11) */ diff --git a/include/xwayland.h b/include/xwayland.h index 40b831c8..d8dfb68a 100644 --- a/include/xwayland.h +++ b/include/xwayland.h @@ -33,6 +33,7 @@ struct xwayland_view { struct wl_listener set_class; struct wl_listener set_decorations; struct wl_listener set_override_redirect; + struct wl_listener set_strut_partial; /* Not (yet) implemented */ /* struct wl_listener set_role; */ @@ -52,5 +53,9 @@ void xwayland_server_init(struct server *server, struct wlr_compositor *compositor); void xwayland_server_finish(struct server *server); +void xwayland_adjust_usable_area(struct view *view, + struct wlr_output_layout *layout, struct wlr_output *output, + struct wlr_box *usable); + #endif /* HAVE_XWAYLAND */ #endif /* LABWC_XWAYLAND_H */ diff --git a/src/output.c b/src/output.c index f7f95ab1..56c208fa 100644 --- a/src/output.c +++ b/src/output.c @@ -23,6 +23,7 @@ #include "node.h" #include "regions.h" #include "view.h" +#include "xwayland.h" static void output_frame_notify(struct wl_listener *listener, void *data) @@ -519,6 +520,16 @@ update_usable_area(struct output *output) struct wlr_box old = output->usable_area; layers_arrange(output); +#if HAVE_XWAYLAND + struct view *view; + wl_list_for_each(view, &output->server->views, link) { + if (view->mapped && view->type == LAB_XWAYLAND_VIEW) { + xwayland_adjust_usable_area(view, + output->server->output_layout, + output->wlr_output, &output->usable_area); + } + } +#endif return !wlr_box_equal(&old, &output->usable_area); } diff --git a/src/xwayland.c b/src/xwayland.c index 020c1ece..efd5fe24 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -279,6 +279,7 @@ handle_destroy(struct wl_listener *listener, void *data) wl_list_remove(&xwayland_view->set_class.link); wl_list_remove(&xwayland_view->set_decorations.link); wl_list_remove(&xwayland_view->set_override_redirect.link); + wl_list_remove(&xwayland_view->set_strut_partial.link); view_destroy(view); } @@ -442,6 +443,18 @@ handle_set_override_redirect(struct wl_listener *listener, void *data) xwayland_unmanaged_create(server, xsurface, mapped); } +static void +handle_set_strut_partial(struct wl_listener *listener, void *data) +{ + struct xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, set_strut_partial); + struct view *view = &xwayland_view->base; + + if (view->mapped) { + output_update_all_usable_areas(view->server, false); + } +} + static void set_initial_position(struct view *view, struct wlr_xwayland_surface *xwayland_surface) @@ -596,6 +609,11 @@ xwayland_view_map(struct view *view) view_impl_map(view); view->been_mapped = true; + + /* Update usable area to account for XWayland "struts" (panels) */ + if (xwayland_surface->strut_partial) { + output_update_all_usable_areas(view->server, false); + } } static void @@ -609,6 +627,11 @@ xwayland_view_unmap(struct view *view, bool client_request) wlr_scene_node_set_enabled(&view->scene_tree->node, false); view_impl_unmap(view); + /* Update usable area to account for XWayland "struts" (panels) */ + if (xwayland_surface_from_view(view)->strut_partial) { + output_update_all_usable_areas(view->server, false); + } + /* * If the view was explicitly unmapped by the client (rather * than just minimized), destroy the foreign toplevel handle so @@ -798,6 +821,7 @@ xwayland_view_create(struct server *server, CONNECT_SIGNAL(xsurface, xwayland_view, set_class); CONNECT_SIGNAL(xsurface, xwayland_view, set_decorations); CONNECT_SIGNAL(xsurface, xwayland_view, set_override_redirect); + CONNECT_SIGNAL(xsurface, xwayland_view, set_strut_partial); wl_list_insert(&view->server->views, &view->link); @@ -880,3 +904,91 @@ xwayland_server_finish(struct server *server) server->xwayland = NULL; wlr_xwayland_destroy(xwayland); } + +static bool +intervals_overlap(int start_a, int end_a, int start_b, int end_b) +{ + /* check for empty intervals */ + if (end_a <= start_a || end_b <= start_b) { + return false; + } + + return start_a < start_b ? + start_b < end_a : /* B starts within A */ + start_a < end_b; /* A starts within B */ +} + +/* + * Subtract the area of an XWayland view (e.g. panel) from the usable + * area of the output based on _NET_WM_STRUT_PARTIAL property. + */ +void +xwayland_adjust_usable_area(struct view *view, struct wlr_output_layout *layout, + struct wlr_output *output, struct wlr_box *usable) +{ + assert(view); + assert(layout); + assert(output); + assert(usable); + + if (view->type != LAB_XWAYLAND_VIEW) { + return; + } + + xcb_ewmh_wm_strut_partial_t *strut = + xwayland_surface_from_view(view)->strut_partial; + if (!strut) { + return; + } + + /* these are layout coordinates */ + struct wlr_box lb = { 0 }; + wlr_output_layout_get_box(layout, NULL, &lb); + struct wlr_box ob = { 0 }; + wlr_output_layout_get_box(layout, output, &ob); + + /* + * strut->right/bottom are offsets from the lower right corner + * of the X11 screen, which should generally correspond with the + * lower right corner of the output layout + */ + double strut_left = strut->left; + double strut_right = (lb.x + lb.width) - strut->right; + double strut_top = strut->top; + double strut_bottom = (lb.y + lb.height) - strut->bottom; + + /* convert layout to output coordinates */ + wlr_output_layout_output_coords(layout, output, + &strut_left, &strut_top); + wlr_output_layout_output_coords(layout, output, + &strut_right, &strut_bottom); + + /* deal with right/bottom rather than width/height */ + int usable_right = usable->x + usable->width; + int usable_bottom = usable->y + usable->height; + + /* here we mix output and layout coordinates; be careful */ + if (strut_left > usable->x && strut_left < usable_right + && intervals_overlap(ob.y, ob.y + ob.height, + strut->left_start_y, strut->left_end_y + 1)) { + usable->x = strut_left; + } + if (strut_right > usable->x && strut_right < usable_right + && intervals_overlap(ob.y, ob.y + ob.height, + strut->right_start_y, strut->right_end_y + 1)) { + usable_right = strut_right; + } + if (strut_top > usable->y && strut_top < usable_bottom + && intervals_overlap(ob.x, ob.x + ob.width, + strut->top_start_x, strut->top_end_x + 1)) { + usable->y = strut_top; + } + if (strut_bottom > usable->y && strut_bottom < usable_bottom + && intervals_overlap(ob.x, ob.x + ob.width, + strut->bottom_start_x, strut->bottom_end_x + 1)) { + usable_bottom = strut_bottom; + } + + usable->width = usable_right - usable->x; + usable->height = usable_bottom - usable->y; +}