diff --git a/include/wlr/xwayland.h b/include/wlr/xwayland.h index 66622de3..1fad54d1 100644 --- a/include/wlr/xwayland.h +++ b/include/wlr/xwayland.h @@ -111,6 +111,9 @@ struct wlr_xwayland_surface { uint32_t hints_urgency; struct wlr_xwayland_surface_size_hints *size_hints; + bool pinging; + struct wl_event_source *ping_timer; + // _NET_WM_STATE bool fullscreen; bool maximized_vert; @@ -133,6 +136,7 @@ struct wlr_xwayland_surface { struct wl_signal set_parent; struct wl_signal set_pid; struct wl_signal set_window_type; + struct wl_signal ping_timeout; } events; struct wl_listener surface_destroy; @@ -188,6 +192,8 @@ bool wlr_xwayland_surface_is_unmanaged( bool wlr_surface_is_xwayland_surface(struct wlr_surface *surface); struct wlr_xwayland_surface *wlr_xwayland_surface_from_wlr_surface( - struct wlr_surface *surface); + struct wlr_surface *surface); + +void wlr_xwayland_surface_ping(struct wlr_xwayland_surface *surface); #endif diff --git a/include/xwayland/xwm.h b/include/xwayland/xwm.h index c733c13c..82bf726c 100644 --- a/include/xwayland/xwm.h +++ b/include/xwayland/xwm.h @@ -43,6 +43,7 @@ enum atom_name { _NET_WM_STATE_FULLSCREEN, _NET_WM_STATE_MAXIMIZED_VERT, _NET_WM_STATE_MAXIMIZED_HORZ, + _NET_WM_PING, WM_STATE, CLIPBOARD, PRIMARY, @@ -90,6 +91,7 @@ struct wlr_xwm { struct wlr_xwayland *xwayland; struct wl_event_source *event_source; struct wlr_seat *seat; + uint32_t ping_timeout; xcb_atom_t atoms[ATOM_LAST]; xcb_connection_t *xcb_conn; diff --git a/rootston/xwayland.c b/rootston/xwayland.c index 92f37362..27a27b65 100644 --- a/rootston/xwayland.c +++ b/rootston/xwayland.c @@ -260,6 +260,7 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { struct wlr_xwayland_surface *surface = data; wlr_log(L_DEBUG, "new xwayland surface: title=%s, class=%s, instance=%s", surface->title, surface->class, surface->instance); + wlr_xwayland_surface_ping(surface); struct roots_xwayland_surface *roots_surface = calloc(1, sizeof(struct roots_xwayland_surface)); diff --git a/xwayland/xwm.c b/xwayland/xwm.c index 6b1038cf..bad40117 100644 --- a/xwayland/xwm.c +++ b/xwayland/xwm.c @@ -42,6 +42,7 @@ const char *atom_map[ATOM_LAST] = { "_NET_WM_STATE_FULLSCREEN", "_NET_WM_STATE_MAXIMIZED_VERT", "_NET_WM_STATE_MAXIMIZED_HORZ", + "_NET_WM_PING", "WM_STATE", "CLIPBOARD", "PRIMARY", @@ -100,6 +101,14 @@ static struct wlr_xwayland_surface *lookup_surface(struct wlr_xwm *xwm, return NULL; } +static int xwayland_surface_handle_ping_timeout(void *data) { + struct wlr_xwayland_surface *surface = data; + + wlr_signal_emit_safe(&surface->events.ping_timeout, surface); + surface->pinging = false; + return 1; +} + static struct wlr_xwayland_surface *wlr_xwayland_surface_create( struct wlr_xwm *xwm, xcb_window_t window_id, int16_t x, int16_t y, uint16_t width, uint16_t height, bool override_redirect) { @@ -143,16 +152,25 @@ static struct wlr_xwayland_surface *wlr_xwayland_surface_create( wl_signal_init(&surface->events.set_parent); wl_signal_init(&surface->events.set_pid); wl_signal_init(&surface->events.set_window_type); + wl_signal_init(&surface->events.ping_timeout); xcb_get_geometry_reply_t *geometry_reply = xcb_get_geometry_reply(xwm->xcb_conn, geometry_cookie, NULL); - if (geometry_reply != NULL) { surface->has_alpha = geometry_reply->depth == 32; } - free(geometry_reply); + struct wl_display *display = xwm->xwayland->wl_display; + struct wl_event_loop *loop = wl_display_get_event_loop(display); + surface->ping_timer = wl_event_loop_add_timer(loop, + xwayland_surface_handle_ping_timeout, surface); + if (surface->ping_timer == NULL) { + free(surface); + wlr_log(L_ERROR, "Could not add timer to event loop"); + return NULL; + } + return surface; } @@ -163,6 +181,27 @@ static void xwm_set_net_active_window(struct wlr_xwm *xwm, xwm->atoms[WINDOW], 32, 1, &window); } +static void xwm_send_wm_message(struct wlr_xwayland_surface *surface, + xcb_client_message_data_t *data) { + struct wlr_xwm *xwm = surface->xwm; + + xcb_client_message_event_t event = { + .response_type = XCB_CLIENT_MESSAGE, + .format = 32, + .sequence = 0, + .window = surface->window_id, + .type = xwm->atoms[WM_PROTOCOLS], + .data = *data, + }; + + xcb_send_event(xwm->xcb_conn, + 0, // propagate + surface->window_id, + XCB_EVENT_MASK_NO_EVENT, + (const char *)&event); + xcb_flush(xwm->xcb_conn); +} + static void xwm_send_focus_window(struct wlr_xwm *xwm, struct wlr_xwayland_surface *xsurface) { if (!xsurface) { @@ -174,16 +213,10 @@ static void xwm_send_focus_window(struct wlr_xwm *xwm, return; } - xcb_client_message_event_t client_message; - client_message.response_type = XCB_CLIENT_MESSAGE; - client_message.format = 32; - client_message.window = xsurface->window_id; - client_message.type = xwm->atoms[WM_PROTOCOLS]; - client_message.data.data32[0] = xwm->atoms[WM_TAKE_FOCUS]; - client_message.data.data32[1] = XCB_TIME_CURRENT_TIME; - - xcb_send_event(xwm->xcb_conn, 0, xsurface->window_id, - XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (char*)&client_message); + 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_set_input_focus(xwm->xcb_conn, XCB_INPUT_FOCUS_POINTER_ROOT, xsurface->window_id, XCB_CURRENT_TIME); @@ -968,6 +1001,32 @@ static void xwm_handle_net_wm_state_message(struct wlr_xwm *xwm, } } +static void xwm_handle_wm_protocols_message(struct wlr_xwm *xwm, + xcb_client_message_event_t *ev) { + xcb_atom_t type = ev->data.data32[0]; + + if (type == xwm->atoms[_NET_WM_PING]) { + xcb_window_t window_id = ev->data.data32[2]; + + struct wlr_xwayland_surface *surface = lookup_surface(xwm, window_id); + if (surface == NULL) { + return; + } + + if (!surface->pinging) { + return; + } + + wl_event_source_timer_update(surface->ping_timer, 0); + surface->pinging = false; + } else { + char *type_name = xwm_get_atom_name(xwm, type); + wlr_log(L_DEBUG, "unhandled WM_PROTOCOLS client message %u (%s)", + type, type_name); + free(type_name); + } +} + static void xwm_handle_client_message(struct wlr_xwm *xwm, xcb_client_message_event_t *ev) { wlr_log(L_DEBUG, "XCB_CLIENT_MESSAGE (%u)", ev->window); @@ -978,6 +1037,8 @@ static void xwm_handle_client_message(struct wlr_xwm *xwm, xwm_handle_net_wm_state_message(xwm, ev); } else if (ev->type == xwm->atoms[_NET_WM_MOVERESIZE]) { xwm_handle_net_wm_moveresize_message(xwm, ev); + } else if (ev->type == xwm->atoms[WM_PROTOCOLS]) { + xwm_handle_wm_protocols_message(xwm, ev); } else if (!xwm_handle_selection_client_message(xwm, ev)) { char *type_name = xwm_get_atom_name(xwm, ev->type); wlr_log(L_DEBUG, "unhandled x11 client message %u (%s)", ev->type, @@ -1198,23 +1259,14 @@ void wlr_xwayland_surface_close(struct wlr_xwayland_surface *xsurface) { } if (supports_delete) { - xcb_client_message_event_t ev = {0}; - ev.response_type = XCB_CLIENT_MESSAGE; - ev.window = xsurface->window_id; - ev.format = 32; - ev.sequence = 0; - ev.type = xwm->atoms[WM_PROTOCOLS]; - ev.data.data32[0] = xwm->atoms[WM_DELETE_WINDOW]; - ev.data.data32[1] = XCB_CURRENT_TIME; - xcb_send_event(xwm->xcb_conn, 0, - xsurface->window_id, - XCB_EVENT_MASK_NO_EVENT, - (char *)&ev); + xcb_client_message_data_t message_data = {0}; + message_data.data32[0] = xwm->atoms[WM_DELETE_WINDOW]; + message_data.data32[1] = XCB_CURRENT_TIME; + xwm_send_wm_message(xsurface, &message_data); } else { xcb_kill_client(xwm->xcb_conn, xsurface->window_id); + xcb_flush(xwm->xcb_conn); } - - xcb_flush(xwm->xcb_conn); } void xwm_destroy(struct wlr_xwm *xwm) { @@ -1463,6 +1515,7 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland) { xwm->xwayland = wlr_xwayland; wl_list_init(&xwm->surfaces); wl_list_init(&xwm->unpaired_surfaces); + xwm->ping_timeout = 10000; xwm->xcb_conn = xcb_connect_to_fd(wlr_xwayland->wm_fd[0], NULL); @@ -1599,3 +1652,16 @@ bool wlr_xwayland_surface_is_unmanaged( return false; } + +void wlr_xwayland_surface_ping(struct wlr_xwayland_surface *surface) { + xcb_client_message_data_t data = { 0 }; + data.data32[0] = surface->xwm->atoms[_NET_WM_PING]; + data.data32[1] = XCB_CURRENT_TIME; + data.data32[2] = surface->window_id; + + xwm_send_wm_message(surface, &data); + + wl_event_source_timer_update(surface->ping_timer, + surface->xwm->ping_timeout); + surface->pinging = true; +}