xwayland: set _NET_WORKAREA property based on usable area

XWayland clients use the _NET_WORKAREA root window property to determine
how much of the screen is not covered by panels/docks. The property is
used for example by Qt to determine areas of the screen that popup menus
should not overlap (see QScreen::availableVirtualGeometry).

Depends on wlroots MR:
https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4406

v2: prevent calling wlr_xwayland_set_workareas() too early
v3: fix segfault at exit (server->xwayland == NULL)
This commit is contained in:
John Lindgren 2023-10-24 13:49:15 -04:00 committed by Johan Malm
parent 5a2df57363
commit 6b8c79748a
3 changed files with 89 additions and 0 deletions

View file

@ -58,5 +58,7 @@ void xwayland_server_init(struct server *server,
struct wlr_compositor *compositor); struct wlr_compositor *compositor);
void xwayland_server_finish(struct server *server); void xwayland_server_finish(struct server *server);
void xwayland_update_workarea(struct server *server);
#endif /* HAVE_XWAYLAND */ #endif /* HAVE_XWAYLAND */
#endif /* LABWC_XWAYLAND_H */ #endif /* LABWC_XWAYLAND_H */

View file

@ -24,6 +24,7 @@
#include "node.h" #include "node.h"
#include "regions.h" #include "regions.h"
#include "view.h" #include "view.h"
#include "xwayland.h"
static void static void
output_frame_notify(struct wl_listener *listener, void *data) output_frame_notify(struct wl_listener *listener, void *data)
@ -610,6 +611,9 @@ output_update_usable_area(struct output *output)
{ {
if (update_usable_area(output)) { if (update_usable_area(output)) {
regions_update_geometry(output); regions_update_geometry(output);
#if HAVE_XWAYLAND
xwayland_update_workarea(output->server);
#endif
desktop_arrange_all_views(output->server); desktop_arrange_all_views(output->server);
} }
} }
@ -629,6 +633,9 @@ output_update_all_usable_areas(struct server *server, bool layout_changed)
} }
} }
if (usable_area_changed || layout_changed) { if (usable_area_changed || layout_changed) {
#if HAVE_XWAYLAND
xwayland_update_workarea(server);
#endif
desktop_arrange_all_views(server); desktop_arrange_all_views(server);
} }
} }

View file

@ -839,6 +839,7 @@ handle_ready(struct wl_listener *listener, void *data)
struct server *server = struct server *server =
wl_container_of(listener, server, xwayland_ready); wl_container_of(listener, server, xwayland_ready);
wlr_xwayland_set_seat(server->xwayland, server->seat.seat); wlr_xwayland_set_seat(server->xwayland, server->seat.seat);
xwayland_update_workarea(server);
} }
void void
@ -928,3 +929,82 @@ xwayland_server_finish(struct server *server)
server->xwayland = NULL; server->xwayland = NULL;
wlr_xwayland_destroy(xwayland); wlr_xwayland_destroy(xwayland);
} }
void
xwayland_update_workarea(struct server *server)
{
/*
* Do nothing if called during destroy or before xwayland is ready.
* This function will be called again from the ready signal handler.
*/
if (!server->xwayland || !server->xwayland->xwm) {
return;
}
struct wlr_box lb;
wlr_output_layout_get_box(server->output_layout, NULL, &lb);
/* Compute outer edges of layout (excluding negative regions) */
int layout_left = MAX(0, lb.x);
int layout_right = MAX(0, lb.x + lb.width);
int layout_top = MAX(0, lb.y);
int layout_bottom = MAX(0, lb.y + lb.height);
/* Workarea is initially the entire layout */
int workarea_left = layout_left;
int workarea_right = layout_right;
int workarea_top = layout_top;
int workarea_bottom = layout_bottom;
struct output *output;
wl_list_for_each(output, &server->outputs, link) {
if (!output_is_usable(output)) {
continue;
}
struct wlr_box ob;
wlr_output_layout_get_box(server->output_layout,
output->wlr_output, &ob);
/* Compute edges of output */
int output_left = ob.x;
int output_right = ob.x + ob.width;
int output_top = ob.y;
int output_bottom = ob.y + ob.height;
/* Compute edges of usable area */
int usable_left = output_left + output->usable_area.x;
int usable_right = usable_left + output->usable_area.width;
int usable_top = output_top + output->usable_area.y;
int usable_bottom = usable_top + output->usable_area.height;
/*
* Only adjust workarea edges for output edges that are
* aligned with outer edges of layout
*/
if (output_left == layout_left) {
workarea_left = MAX(workarea_left, usable_left);
}
if (output_right == layout_right) {
workarea_right = MIN(workarea_right, usable_right);
}
if (output_top == layout_top) {
workarea_top = MAX(workarea_top, usable_top);
}
if (output_bottom == layout_bottom) {
workarea_bottom = MIN(workarea_bottom, usable_bottom);
}
}
/*
* Set _NET_WORKAREA property. We don't report virtual desktops
* to XWayland, so we set only one workarea.
*/
struct wlr_box workarea = {
.x = workarea_left,
.y = workarea_top,
.width = workarea_right - workarea_left,
.height = workarea_bottom - workarea_top,
};
wlr_xwayland_set_workareas(server->xwayland, &workarea, 1);
}