]> git.mdlowis.com Git - proto/labwc.git/commitdiff
output,xwayland: Add support for _NET_WM_STRUT_PARTIAL
authorJohn Lindgren <john@jlindgren.net>
Tue, 22 Nov 2022 10:11:50 +0000 (05:11 -0500)
committerJohan Malm <johanmalm@users.noreply.github.com>
Wed, 29 Nov 2023 06:48:31 +0000 (06:48 +0000)
Account for space taken up by XWayland panels (as indicated by the
_NET_WM_STRUT_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/xwayland.h
src/output.c
src/xwayland.c

index 129a03215f3d8f600311bce9107abbb912e4056d..e452dbe35ab646a9fed578540eebaeb67f3bbf5f 100644 (file)
@@ -6,6 +6,8 @@
 #include "view.h"
 
 struct wlr_compositor;
+struct wlr_output;
+struct wlr_output_layout;
 
 struct xwayland_unmanaged {
        struct server *server;
@@ -37,6 +39,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; */
@@ -58,6 +61,10 @@ 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);
+
 void xwayland_update_workarea(struct server *server);
 
 #endif /* HAVE_XWAYLAND */
index 3474811364c3a31e8849d25c6030af29ec9d9bc9..003be96383165f0cd77c341a6d0f88ea145f95e2 100644 (file)
@@ -603,6 +603,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);
 }
 
index 2719102a0ac90f80c74f1c1df58f91481df123bc..ac2c715871b7424f043b328e684607c0fb89936d 100644 (file)
@@ -291,6 +291,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);
 }
@@ -463,6 +464,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)
@@ -605,6 +618,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
@@ -618,6 +636,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
@@ -806,6 +829,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);
 
@@ -930,6 +954,94 @@ xwayland_server_finish(struct server *server)
        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;
+}
+
 void
 xwayland_update_workarea(struct server *server)
 {