mirror of
https://gitlab.freedesktop.org/wlroots/wlroots.git
synced 2024-11-21 22:52:20 +00:00
xwayland: add wlr_xwayland_surface_offer_focus()
In labwc, we have had trouble with XWayland windows using the Globally Active input model (see wlr_xwayland_icccm_input_model()). Under traditional X11, these windows do not expect to be given focus directly by the window manager; rather, the WM sends them a WM_TAKE_FOCUS message prompting the client to take focus voluntarily. Currently, these clients are difficult to support with wlroots, because wlr_xwayland_surface_activate() assumes the client window will always accept the keyboard focus after being sent WM_TAKE_FOCUS. Some Globally Active client windows (e.g. panels/toolbars) don't want to be focused. It's useless at best to focus them, and might even make them misbehave. Others do need keyboard focus to be functional -- and there doesn't seem to be any reliable way to know this in advance. Adding wlr_xwayland_surface_offer_focus() allows the compositor to send WM_TAKE_FOCUS to a client window supporting it and then see whether the client accepts or ignores the offer. If it accepts, the surface will emit the focus_in signal notifying the compositor that it has received focus. This is entirely opt-in. A compositor that doesn't want to use the new function can continue to call wlr_xwayland_surface_activate() directly just as before.
This commit is contained in:
parent
eb5312022a
commit
5083efe18b
@ -278,6 +278,18 @@ void wlr_xwayland_set_seat(struct wlr_xwayland *xwayland,
|
|||||||
struct wlr_xwayland_surface *wlr_xwayland_surface_try_from_wlr_surface(
|
struct wlr_xwayland_surface *wlr_xwayland_surface_try_from_wlr_surface(
|
||||||
struct wlr_surface *surface);
|
struct wlr_surface *surface);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offer focus by sending WM_TAKE_FOCUS to a client window supporting it.
|
||||||
|
* The client may accept or ignore the offer. If it accepts, the surface will
|
||||||
|
* emit the focus_in signal notifying the compositor that it has received focus.
|
||||||
|
*
|
||||||
|
* This is a more compatible method of giving focus to windows using the
|
||||||
|
* Globally Active input model (see wlr_xwayland_icccm_input_model()) than
|
||||||
|
* calling wlr_xwayland_surface_activate() unconditionally, since there is no
|
||||||
|
* reliable way to know in advance whether these windows want to be focused.
|
||||||
|
*/
|
||||||
|
void wlr_xwayland_surface_offer_focus(struct wlr_xwayland_surface *xsurface);
|
||||||
|
|
||||||
void wlr_xwayland_surface_ping(struct wlr_xwayland_surface *surface);
|
void wlr_xwayland_surface_ping(struct wlr_xwayland_surface *surface);
|
||||||
|
|
||||||
/** Metric to guess if an OR window should "receive" focus
|
/** Metric to guess if an OR window should "receive" focus
|
||||||
|
@ -113,6 +113,7 @@ struct wlr_xwm {
|
|||||||
struct wlr_xwm_selection dnd_selection;
|
struct wlr_xwm_selection dnd_selection;
|
||||||
|
|
||||||
struct wlr_xwayland_surface *focus_surface;
|
struct wlr_xwayland_surface *focus_surface;
|
||||||
|
struct wlr_xwayland_surface *offered_focus;
|
||||||
|
|
||||||
// Surfaces in creation order
|
// Surfaces in creation order
|
||||||
struct wl_list surfaces; // wlr_xwayland_surface.link
|
struct wl_list surfaces; // wlr_xwayland_surface.link
|
||||||
|
@ -395,12 +395,17 @@ static void xwm_set_focused_window(struct wlr_xwm *xwm,
|
|||||||
struct wlr_xwayland_surface *xsurface) {
|
struct wlr_xwayland_surface *xsurface) {
|
||||||
struct wlr_xwayland_surface *unfocus_surface = xwm->focus_surface;
|
struct wlr_xwayland_surface *unfocus_surface = xwm->focus_surface;
|
||||||
|
|
||||||
if (xwm->focus_surface == xsurface ||
|
if (xsurface && xsurface->override_redirect) {
|
||||||
(xsurface && xsurface->override_redirect)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
xwm->focus_surface = xsurface;
|
xwm->focus_surface = xsurface;
|
||||||
|
// cancel any pending focus offer
|
||||||
|
xwm->offered_focus = xsurface;
|
||||||
|
|
||||||
|
if (xsurface == unfocus_surface) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (unfocus_surface) {
|
if (unfocus_surface) {
|
||||||
xsurface_set_net_wm_state(unfocus_surface);
|
xsurface_set_net_wm_state(unfocus_surface);
|
||||||
@ -414,13 +419,34 @@ static void xwm_set_focused_window(struct wlr_xwm *xwm,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wlr_xwayland_surface_offer_focus(struct wlr_xwayland_surface *xsurface) {
|
||||||
|
if (!xsurface || xsurface->override_redirect) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_xwm *xwm = xsurface->xwm;
|
||||||
|
if (!xwm_atoms_contains(xwm, xsurface->protocols,
|
||||||
|
xsurface->protocols_len, WM_TAKE_FOCUS)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
xwm->offered_focus = xsurface;
|
||||||
|
|
||||||
|
xcb_client_message_data_t message_data = { 0 };
|
||||||
|
message_data.data32[0] = xwm->atoms[WM_TAKE_FOCUS];
|
||||||
|
message_data.data32[1] = XCB_TIME_CURRENT_TIME;
|
||||||
|
xwm_send_wm_message(xsurface, &message_data, XCB_EVENT_MASK_NO_EVENT);
|
||||||
|
|
||||||
|
xcb_flush(xwm->xcb_conn);
|
||||||
|
}
|
||||||
|
|
||||||
static void xwm_surface_activate(struct wlr_xwm *xwm,
|
static void xwm_surface_activate(struct wlr_xwm *xwm,
|
||||||
struct wlr_xwayland_surface *xsurface) {
|
struct wlr_xwayland_surface *xsurface) {
|
||||||
if (xsurface && xsurface->override_redirect) {
|
if (xsurface && xsurface->override_redirect) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xsurface != xwm->focus_surface) {
|
if (xsurface != xwm->focus_surface && xsurface != xwm->offered_focus) {
|
||||||
xwm_focus_window(xwm, xsurface);
|
xwm_focus_window(xwm, xsurface);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -503,6 +529,9 @@ static void xwayland_surface_destroy(struct wlr_xwayland_surface *xsurface) {
|
|||||||
if (xsurface == xsurface->xwm->focus_surface) {
|
if (xsurface == xsurface->xwm->focus_surface) {
|
||||||
xwm_surface_activate(xsurface->xwm, NULL);
|
xwm_surface_activate(xsurface->xwm, NULL);
|
||||||
}
|
}
|
||||||
|
if (xsurface == xsurface->xwm->offered_focus) {
|
||||||
|
xsurface->xwm->offered_focus = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
wl_list_remove(&xsurface->link);
|
wl_list_remove(&xsurface->link);
|
||||||
wl_list_remove(&xsurface->parent_link);
|
wl_list_remove(&xsurface->parent_link);
|
||||||
@ -1623,7 +1652,8 @@ static void xwm_handle_focus_in(struct wlr_xwm *xwm,
|
|||||||
// Allow focus changes between surfaces belonging to the same
|
// Allow focus changes between surfaces belonging to the same
|
||||||
// application. Steam for example relies on this:
|
// application. Steam for example relies on this:
|
||||||
// https://github.com/swaywm/sway/issues/1865
|
// https://github.com/swaywm/sway/issues/1865
|
||||||
if (xsurface && xwm->focus_surface && xsurface->pid == xwm->focus_surface->pid) {
|
if (xsurface && ((xwm->focus_surface && xsurface->pid == xwm->focus_surface->pid) ||
|
||||||
|
(xwm->offered_focus && xsurface->pid == xwm->offered_focus->pid))) {
|
||||||
xwm_set_focused_window(xwm, xsurface);
|
xwm_set_focused_window(xwm, xsurface);
|
||||||
wl_signal_emit_mutable(&xsurface->events.focus_in, NULL);
|
wl_signal_emit_mutable(&xsurface->events.focus_in, NULL);
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user