diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index 1085cb14..a3e7132d 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -40,6 +40,7 @@ enum atom_name { NET_ACTIVE_WINDOW, NET_WM_MOVERESIZE, NET_SUPPORTING_WM_CHECK, + NET_WM_STATE_FOCUSED, NET_WM_STATE_MODAL, NET_WM_STATE_FULLSCREEN, NET_WM_STATE_MAXIMIZED_VERT, diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 692c0f8b..88f4c3c5 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -38,6 +38,7 @@ const char *atom_map[ATOM_LAST] = { [NET_ACTIVE_WINDOW] = "_NET_ACTIVE_WINDOW", [NET_WM_MOVERESIZE] = "_NET_WM_MOVERESIZE", [NET_SUPPORTING_WM_CHECK] = "_NET_SUPPORTING_WM_CHECK", + [NET_WM_STATE_FOCUSED] = "_NET_WM_STATE_FOCUSED", [NET_WM_STATE_MODAL] = "_NET_WM_STATE_MODAL", [NET_WM_STATE_FULLSCREEN] = "_NET_WM_STATE_FULLSCREEN", [NET_WM_STATE_MAXIMIZED_VERT] = "_NET_WM_STATE_MAXIMIZED_VERT", @@ -238,8 +239,20 @@ static void xwm_set_net_client_list(struct wlr_xwm *xwm) { XCB_ATOM_WINDOW, 32, mapped_surfaces, windows); } -static void xwm_send_focus_window(struct wlr_xwm *xwm, +static void xsurface_set_net_wm_state(struct wlr_xwayland_surface *xsurface); + +static void xwm_set_focus_window(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface) { + struct wlr_xwayland_surface *unfocus_surface = xwm->focus_surface; + + // We handle cases where focus_surface == xsurface because we + // want to be able to deny FocusIn events. + xwm->focus_surface = xsurface; + + if (unfocus_surface) { + xsurface_set_net_wm_state(unfocus_surface); + } + if (!xsurface) { xcb_set_input_focus_checked(xwm->xcb_conn, XCB_INPUT_FOCUS_POINTER_ROOT, @@ -270,6 +283,8 @@ static void xwm_send_focus_window(struct wlr_xwm *xwm, 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); } static void xwm_surface_activate(struct wlr_xwm *xwm, @@ -285,9 +300,7 @@ static void xwm_surface_activate(struct wlr_xwm *xwm, xwm_set_net_active_window(xwm, XCB_WINDOW_NONE); } - xwm_send_focus_window(xwm, xsurface); - - xwm->focus_surface = xsurface; + xwm_set_focus_window(xwm, xsurface); xcb_flush(xwm->xcb_conn); } @@ -295,7 +308,7 @@ static void xwm_surface_activate(struct wlr_xwm *xwm, static void xsurface_set_net_wm_state(struct wlr_xwayland_surface *xsurface) { struct wlr_xwm *xwm = xsurface->xwm; - uint32_t property[5]; + uint32_t property[6]; size_t i = 0; if (xsurface->modal) { property[i++] = xwm->atoms[NET_WM_STATE_MODAL]; @@ -312,6 +325,9 @@ static void xsurface_set_net_wm_state(struct wlr_xwayland_surface *xsurface) { if (xsurface->minimized) { property[i++] = xwm->atoms[NET_WM_STATE_HIDDEN]; } + if (xsurface == xwm->focus_surface) { + property[i++] = xwm->atoms[NET_WM_STATE_FOCUSED]; + } assert(i <= sizeof(property) / sizeof(property[0])); xcb_change_property(xwm->xcb_conn, @@ -1299,10 +1315,10 @@ static void xwm_handle_focus_in(struct wlr_xwm *xwm, struct wlr_xwayland_surface *requested_focus = lookup_surface(xwm, ev->event); if (xwm->focus_surface && requested_focus && requested_focus->pid == xwm->focus_surface->pid) { - xwm->focus_surface = requested_focus; + xwm_set_focus_window(xwm, requested_focus); + } else { + xwm_set_focus_window(xwm, xwm->focus_surface); } - - xwm_send_focus_window(xwm, xwm->focus_surface); } static void xwm_handle_xcb_error(struct wlr_xwm *xwm, xcb_value_error_t *ev) { @@ -1817,6 +1833,7 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { xwm->atoms[NET_WM_STATE], xwm->atoms[NET_ACTIVE_WINDOW], xwm->atoms[NET_WM_MOVERESIZE], + xwm->atoms[NET_WM_STATE_FOCUSED], xwm->atoms[NET_WM_STATE_MODAL], xwm->atoms[NET_WM_STATE_FULLSCREEN], xwm->atoms[NET_WM_STATE_MAXIMIZED_VERT],