From ae2f3ecb6824e168083580a601af53d6c9544b5d Mon Sep 17 00:00:00 2001 From: Tudor Brindus Date: Sun, 2 May 2021 12:50:36 -0400 Subject: [PATCH] xwm: implement _NET_CLIENT_LIST_STACKING This property is present on all modern X11 instances. The nonpresence of it requires applications to fall back to XQueryTree-based logic to determine stacking logic (e.g., to determine what surface should get Xdnd events). These code paths are effectively untested nowadays, so this makes it more likely for wlroots to "break" applications. For instance, the XQueryTree fallback path has been broken in Chromium for the last 10 years. It's easy enough to maintain this property, so let's just do it. Fixes #2889. --- include/wlr/xwayland.h | 3 +- include/xwayland/xwm.h | 4 ++ xwayland/xwm.c | 97 ++++++++++++++++++++++++++++++------------ 3 files changed, 76 insertions(+), 28 deletions(-) diff --git a/include/wlr/xwayland.h b/include/wlr/xwayland.h index 7dcb3935..6a83e08a 100644 --- a/include/wlr/xwayland.h +++ b/include/wlr/xwayland.h @@ -121,7 +121,7 @@ struct wlr_xwayland_surface_size_hints { /** * This represents the input focus described as follows: - * + * * https://www.x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html#input_focus */ enum wlr_xwayland_icccm_input_model { @@ -146,6 +146,7 @@ struct wlr_xwayland_surface { uint32_t surface_id; struct wl_list link; + struct wl_list stack_link; struct wl_list unpaired_link; struct wlr_surface *surface; diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index 05d229ae..d564a0e2 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -80,6 +80,7 @@ enum atom_name { DND_ACTION_ASK, DND_ACTION_PRIVATE, NET_CLIENT_LIST, + NET_CLIENT_LIST_STACKING, ATOM_LAST // keep last }; @@ -106,7 +107,10 @@ struct wlr_xwm { struct wlr_xwayland_surface *focus_surface; + // Surfaces in creation order struct wl_list surfaces; // wlr_xwayland_surface::link + // Surfaces in bottom-to-top stacking order, for _NET_CLIENT_LIST_STACKING + struct wl_list surfaces_in_stack_order; // wlr_xwayland_surface::stack_link struct wl_list unpaired_surfaces; // wlr_xwayland_surface::unpaired_link struct wlr_drag *drag; diff --git a/xwayland/xwm.c b/xwayland/xwm.c index fb5879e3..845b98cc 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -85,6 +85,7 @@ const char *const atom_map[ATOM_LAST] = { [DND_ACTION_ASK] = "XdndActionAsk", [DND_ACTION_PRIVATE] = "XdndActionPrivate", [NET_CLIENT_LIST] = "_NET_CLIENT_LIST", + [NET_CLIENT_LIST_STACKING] = "_NET_CLIENT_LIST_STACKING", }; static const struct wlr_surface_role xwayland_surface_role; @@ -147,6 +148,7 @@ static struct wlr_xwayland_surface *xwayland_surface_create( surface->height = height; surface->override_redirect = override_redirect; wl_list_init(&surface->children); + wl_list_init(&surface->stack_link); wl_list_init(&surface->parent_link); wl_signal_init(&surface->events.destroy); wl_signal_init(&surface->events.request_configure); @@ -223,6 +225,10 @@ static void xwm_send_wm_message(struct wlr_xwayland_surface *surface, } static void xwm_set_net_client_list(struct wlr_xwm *xwm) { + // FIXME: _NET_CLIENT_LIST is expected to be ordered by map time, but the + // order of surfaces in `xwm->surfaces` is by creation time. The order of + // windows _NET_CLIENT_LIST exposed by wlroots is wrong. + size_t mapped_surfaces = 0; struct wlr_xwayland_surface *surface; wl_list_for_each(surface, &xwm->surfaces, link) { @@ -244,6 +250,24 @@ static void xwm_set_net_client_list(struct wlr_xwm *xwm) { XCB_ATOM_WINDOW, 32, mapped_surfaces, windows); } +static void xwm_set_net_client_list_stacking(struct wlr_xwm *xwm) { + size_t num_surfaces = wl_list_length(&xwm->surfaces_in_stack_order); + xcb_window_t windows[num_surfaces + 1]; + + // We store surfaces in top-to-bottom order because this is easier to reason + // about, but _NET_CLIENT_LIST_STACKING is supposed to be in bottom-to-top + // order, so iterate backwards through the list. + size_t i = 0; + struct wlr_xwayland_surface *xsurface; + wl_list_for_each(xsurface, &xwm->surfaces_in_stack_order, stack_link) { + windows[i++] = xsurface->window_id; + } + + xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE, xwm->screen->root, + xwm->atoms[NET_CLIENT_LIST_STACKING], XCB_ATOM_WINDOW, 32, num_surfaces, + windows); +} + static void xsurface_set_net_wm_state(struct wlr_xwayland_surface *xsurface); static void xwm_set_focus_window(struct wlr_xwm *xwm, @@ -285,11 +309,7 @@ static void xwm_set_focus_window(struct wlr_xwm *xwm, xwm->last_focus_seq = cookie.sequence; } - uint32_t values[1]; - values[0] = XCB_STACK_MODE_ABOVE; - xcb_configure_window(xwm->xcb_conn, xsurface->window_id, - XCB_CONFIG_WINDOW_STACK_MODE, values); - + wlr_xwayland_surface_restack(xsurface, NULL, XCB_STACK_MODE_ABOVE); xsurface_set_net_wm_state(xsurface); } @@ -358,6 +378,7 @@ static void xwayland_surface_destroy( } wl_list_remove(&xsurface->link); + wl_list_remove(&xsurface->stack_link); wl_list_remove(&xsurface->parent_link); struct wlr_xwayland_surface *child, *next; @@ -964,6 +985,47 @@ static void xsurface_set_wm_state(struct wlr_xwayland_surface *xsurface, sizeof(property) / sizeof(property[0]), property); } +void wlr_xwayland_surface_restack(struct wlr_xwayland_surface *xsurface, + struct wlr_xwayland_surface *sibling, enum xcb_stack_mode_t mode) { + struct wlr_xwm *xwm = xsurface->xwm; + uint32_t values[2]; + size_t idx = 0; + uint32_t flags = XCB_CONFIG_WINDOW_STACK_MODE; + + if (sibling != NULL) { + values[idx++] = sibling->window_id; + flags |= XCB_CONFIG_WINDOW_SIBLING; + } + values[idx++] = mode; + + xcb_configure_window(xwm->xcb_conn, xsurface->window_id, flags, values); + + wl_list_remove(&xsurface->stack_link); + + struct wl_list *node; + if (mode == XCB_STACK_MODE_ABOVE) { + if (sibling) { + node = &sibling->stack_link; + } else { + node = xwm->surfaces_in_stack_order.prev; + } + } else if (mode == XCB_STACK_MODE_BELOW) { + if (sibling) { + node = sibling->stack_link.prev; + } else { + node = &xwm->surfaces_in_stack_order; + } + } else { + // Not implementing XCB_STACK_MODE_TOP_IF | XCB_STACK_MODE_BOTTOM_IF | + // XCB_STACK_MODE_OPPOSITE. + abort(); + } + + wl_list_insert(node, &xsurface->stack_link); + xwm_set_net_client_list_stacking(xwm); + xcb_flush(xwm->xcb_conn); +} + static void xwm_handle_map_request(struct wlr_xwm *xwm, xcb_map_request_event_t *ev) { struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->window); @@ -974,11 +1036,7 @@ static void xwm_handle_map_request(struct wlr_xwm *xwm, xsurface_set_wm_state(xsurface, XCB_ICCCM_WM_STATE_NORMAL); xsurface_set_net_wm_state(xsurface); - uint32_t values[1]; - values[0] = XCB_STACK_MODE_BELOW; - xcb_configure_window(xwm->xcb_conn, ev->window, - XCB_CONFIG_WINDOW_STACK_MODE, values); - + wlr_xwayland_surface_restack(xsurface, NULL, XCB_STACK_MODE_BELOW); xcb_map_window(xwm->xcb_conn, ev->window); } @@ -1531,23 +1589,6 @@ void wlr_xwayland_surface_activate(struct wlr_xwayland_surface *xsurface, } } -void wlr_xwayland_surface_restack(struct wlr_xwayland_surface *surface, - struct wlr_xwayland_surface *sibling, enum xcb_stack_mode_t mode) { - struct wlr_xwm *xwm = surface->xwm; - uint32_t values[2]; - size_t idx = 0; - uint32_t flags = XCB_CONFIG_WINDOW_STACK_MODE; - - if (sibling != NULL) { - values[idx++] = sibling->window_id; - flags |= XCB_CONFIG_WINDOW_SIBLING; - } - values[idx++] = mode; - - xcb_configure_window(xwm->xcb_conn, surface->window_id, flags, values); - xcb_flush(xwm->xcb_conn); -} - void wlr_xwayland_surface_configure(struct wlr_xwayland_surface *xsurface, int16_t x, int16_t y, uint16_t width, uint16_t height) { xsurface->x = x; @@ -1878,6 +1919,7 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { xwm->xwayland = xwayland; wl_list_init(&xwm->surfaces); + wl_list_init(&xwm->surfaces_in_stack_order); wl_list_init(&xwm->unpaired_surfaces); xwm->ping_timeout = 10000; @@ -1937,6 +1979,7 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { xwm->atoms[NET_WM_STATE_MAXIMIZED_HORZ], xwm->atoms[NET_WM_STATE_HIDDEN], xwm->atoms[NET_CLIENT_LIST], + xwm->atoms[NET_CLIENT_LIST_STACKING], }; xcb_change_property(xwm->xcb_conn, XCB_PROP_MODE_REPLACE,