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.
This commit is contained in:
parent
699d724000
commit
ae2f3ecb68
|
@ -146,6 +146,7 @@ struct wlr_xwayland_surface {
|
||||||
uint32_t surface_id;
|
uint32_t surface_id;
|
||||||
|
|
||||||
struct wl_list link;
|
struct wl_list link;
|
||||||
|
struct wl_list stack_link;
|
||||||
struct wl_list unpaired_link;
|
struct wl_list unpaired_link;
|
||||||
|
|
||||||
struct wlr_surface *surface;
|
struct wlr_surface *surface;
|
||||||
|
|
|
@ -80,6 +80,7 @@ enum atom_name {
|
||||||
DND_ACTION_ASK,
|
DND_ACTION_ASK,
|
||||||
DND_ACTION_PRIVATE,
|
DND_ACTION_PRIVATE,
|
||||||
NET_CLIENT_LIST,
|
NET_CLIENT_LIST,
|
||||||
|
NET_CLIENT_LIST_STACKING,
|
||||||
ATOM_LAST // keep last
|
ATOM_LAST // keep last
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -106,7 +107,10 @@ struct wlr_xwm {
|
||||||
|
|
||||||
struct wlr_xwayland_surface *focus_surface;
|
struct wlr_xwayland_surface *focus_surface;
|
||||||
|
|
||||||
|
// Surfaces in creation order
|
||||||
struct wl_list surfaces; // wlr_xwayland_surface::link
|
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 wl_list unpaired_surfaces; // wlr_xwayland_surface::unpaired_link
|
||||||
|
|
||||||
struct wlr_drag *drag;
|
struct wlr_drag *drag;
|
||||||
|
|
|
@ -85,6 +85,7 @@ const char *const atom_map[ATOM_LAST] = {
|
||||||
[DND_ACTION_ASK] = "XdndActionAsk",
|
[DND_ACTION_ASK] = "XdndActionAsk",
|
||||||
[DND_ACTION_PRIVATE] = "XdndActionPrivate",
|
[DND_ACTION_PRIVATE] = "XdndActionPrivate",
|
||||||
[NET_CLIENT_LIST] = "_NET_CLIENT_LIST",
|
[NET_CLIENT_LIST] = "_NET_CLIENT_LIST",
|
||||||
|
[NET_CLIENT_LIST_STACKING] = "_NET_CLIENT_LIST_STACKING",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct wlr_surface_role xwayland_surface_role;
|
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->height = height;
|
||||||
surface->override_redirect = override_redirect;
|
surface->override_redirect = override_redirect;
|
||||||
wl_list_init(&surface->children);
|
wl_list_init(&surface->children);
|
||||||
|
wl_list_init(&surface->stack_link);
|
||||||
wl_list_init(&surface->parent_link);
|
wl_list_init(&surface->parent_link);
|
||||||
wl_signal_init(&surface->events.destroy);
|
wl_signal_init(&surface->events.destroy);
|
||||||
wl_signal_init(&surface->events.request_configure);
|
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) {
|
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;
|
size_t mapped_surfaces = 0;
|
||||||
struct wlr_xwayland_surface *surface;
|
struct wlr_xwayland_surface *surface;
|
||||||
wl_list_for_each(surface, &xwm->surfaces, link) {
|
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);
|
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 xsurface_set_net_wm_state(struct wlr_xwayland_surface *xsurface);
|
||||||
|
|
||||||
static void xwm_set_focus_window(struct wlr_xwm *xwm,
|
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;
|
xwm->last_focus_seq = cookie.sequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t values[1];
|
wlr_xwayland_surface_restack(xsurface, NULL, XCB_STACK_MODE_ABOVE);
|
||||||
values[0] = XCB_STACK_MODE_ABOVE;
|
|
||||||
xcb_configure_window(xwm->xcb_conn, xsurface->window_id,
|
|
||||||
XCB_CONFIG_WINDOW_STACK_MODE, values);
|
|
||||||
|
|
||||||
xsurface_set_net_wm_state(xsurface);
|
xsurface_set_net_wm_state(xsurface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -358,6 +378,7 @@ static void xwayland_surface_destroy(
|
||||||
}
|
}
|
||||||
|
|
||||||
wl_list_remove(&xsurface->link);
|
wl_list_remove(&xsurface->link);
|
||||||
|
wl_list_remove(&xsurface->stack_link);
|
||||||
wl_list_remove(&xsurface->parent_link);
|
wl_list_remove(&xsurface->parent_link);
|
||||||
|
|
||||||
struct wlr_xwayland_surface *child, *next;
|
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);
|
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,
|
static void xwm_handle_map_request(struct wlr_xwm *xwm,
|
||||||
xcb_map_request_event_t *ev) {
|
xcb_map_request_event_t *ev) {
|
||||||
struct wlr_xwayland_surface *xsurface = lookup_surface(xwm, ev->window);
|
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_wm_state(xsurface, XCB_ICCCM_WM_STATE_NORMAL);
|
||||||
xsurface_set_net_wm_state(xsurface);
|
xsurface_set_net_wm_state(xsurface);
|
||||||
|
|
||||||
uint32_t values[1];
|
wlr_xwayland_surface_restack(xsurface, NULL, XCB_STACK_MODE_BELOW);
|
||||||
values[0] = XCB_STACK_MODE_BELOW;
|
|
||||||
xcb_configure_window(xwm->xcb_conn, ev->window,
|
|
||||||
XCB_CONFIG_WINDOW_STACK_MODE, values);
|
|
||||||
|
|
||||||
xcb_map_window(xwm->xcb_conn, ev->window);
|
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,
|
void wlr_xwayland_surface_configure(struct wlr_xwayland_surface *xsurface,
|
||||||
int16_t x, int16_t y, uint16_t width, uint16_t height) {
|
int16_t x, int16_t y, uint16_t width, uint16_t height) {
|
||||||
xsurface->x = x;
|
xsurface->x = x;
|
||||||
|
@ -1878,6 +1919,7 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) {
|
||||||
|
|
||||||
xwm->xwayland = xwayland;
|
xwm->xwayland = xwayland;
|
||||||
wl_list_init(&xwm->surfaces);
|
wl_list_init(&xwm->surfaces);
|
||||||
|
wl_list_init(&xwm->surfaces_in_stack_order);
|
||||||
wl_list_init(&xwm->unpaired_surfaces);
|
wl_list_init(&xwm->unpaired_surfaces);
|
||||||
xwm->ping_timeout = 10000;
|
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_MAXIMIZED_HORZ],
|
||||||
xwm->atoms[NET_WM_STATE_HIDDEN],
|
xwm->atoms[NET_WM_STATE_HIDDEN],
|
||||||
xwm->atoms[NET_CLIENT_LIST],
|
xwm->atoms[NET_CLIENT_LIST],
|
||||||
|
xwm->atoms[NET_CLIENT_LIST_STACKING],
|
||||||
};
|
};
|
||||||
xcb_change_property(xwm->xcb_conn,
|
xcb_change_property(xwm->xcb_conn,
|
||||||
XCB_PROP_MODE_REPLACE,
|
XCB_PROP_MODE_REPLACE,
|
||||||
|
|
Loading…
Reference in New Issue