diff --git a/include/labwc.h b/include/labwc.h index 78b8b3b6..70cef947 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -220,7 +220,8 @@ struct server { struct wl_listener xdg_toplevel_decoration; #if HAVE_XWAYLAND struct wlr_xwayland *xwayland; - struct wl_listener xwayland_ready; + struct wl_listener xwayland_server_ready; + struct wl_listener xwayland_xwm_ready; struct wl_listener xwayland_new_surface; #endif diff --git a/include/xwayland.h b/include/xwayland.h index e452dbe3..6009ad9d 100644 --- a/include/xwayland.h +++ b/include/xwayland.h @@ -2,13 +2,61 @@ #ifndef LABWC_XWAYLAND_H #define LABWC_XWAYLAND_H #include "config.h" + #if HAVE_XWAYLAND +#include +#include +#include +#include "common/macros.h" #include "view.h" struct wlr_compositor; struct wlr_output; struct wlr_output_layout; +enum atom { + /* https://specifications.freedesktop.org/wm-spec/wm-spec-1.4.html#idm45649101374512 */ + NET_WM_WINDOW_TYPE_DESKTOP = 0, + NET_WM_WINDOW_TYPE_DOCK, + NET_WM_WINDOW_TYPE_TOOLBAR, + NET_WM_WINDOW_TYPE_MENU, + NET_WM_WINDOW_TYPE_UTILITY, + NET_WM_WINDOW_TYPE_SPLASH, + NET_WM_WINDOW_TYPE_DIALOG, + NET_WM_WINDOW_TYPE_DROPDOWN_MENU, + NET_WM_WINDOW_TYPE_POPUP_MENU, + NET_WM_WINDOW_TYPE_TOOLTIP, + NET_WM_WINDOW_TYPE_NOTIFICATION, + NET_WM_WINDOW_TYPE_COMBO, + NET_WM_WINDOW_TYPE_DND, + NET_WM_WINDOW_TYPE_NORMAL, + + ATOM_LEN +}; + +static const char * const atom_names[] = { + "_NET_WM_WINDOW_TYPE_DESKTOP", + "_NET_WM_WINDOW_TYPE_DOCK", + "_NET_WM_WINDOW_TYPE_TOOLBAR", + "_NET_WM_WINDOW_TYPE_MENU", + "_NET_WM_WINDOW_TYPE_UTILITY", + "_NET_WM_WINDOW_TYPE_SPLASH", + "_NET_WM_WINDOW_TYPE_DIALOG", + "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU", + "_NET_WM_WINDOW_TYPE_POPUP_MENU", + "_NET_WM_WINDOW_TYPE_TOOLTIP", + "_NET_WM_WINDOW_TYPE_NOTIFICATION", + "_NET_WM_WINDOW_TYPE_COMBO", + "_NET_WM_WINDOW_TYPE_DND", + "_NET_WM_WINDOW_TYPE_NORMAL", +}; + +static_assert( + ARRAY_SIZE(atom_names) == ATOM_LEN, + "Xwayland atoms out of sync"); + +extern xcb_atom_t atoms[ATOM_LEN]; + struct xwayland_unmanaged { struct server *server; struct wlr_xwayland_surface *xwayland_surface; @@ -40,10 +88,10 @@ struct xwayland_view { struct wl_listener set_decorations; struct wl_listener set_override_redirect; struct wl_listener set_strut_partial; + struct wl_listener set_window_type; /* Not (yet) implemented */ /* struct wl_listener set_role; */ -/* struct wl_listener set_window_type; */ /* struct wl_listener set_hints; */ }; @@ -57,6 +105,9 @@ void xwayland_adjust_stacking_order(struct server *server); struct wlr_xwayland_surface *xwayland_surface_from_view(struct view *view); +bool xwayland_surface_contains_window_type( + struct wlr_xwayland_surface *surface, enum atom window_type); + void xwayland_server_init(struct server *server, struct wlr_compositor *compositor); void xwayland_server_finish(struct server *server); diff --git a/src/xwayland.c b/src/xwayland.c index 60ce5274..6c954dee 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -15,8 +15,23 @@ #include "workspaces.h" #include "xwayland.h" +xcb_atom_t atoms[ATOM_LEN] = {0}; + static void xwayland_view_unmap(struct view *view, bool client_request); +bool +xwayland_surface_contains_window_type( + struct wlr_xwayland_surface *surface, enum atom window_type) +{ + assert(surface); + for (size_t i = 0; i < surface->window_type_len; i++) { + if (surface->window_type[i] == atoms[window_type]) { + return true; + } + } + return false; +} + static struct view_size_hints xwayland_view_get_size_hints(struct view *view) { @@ -313,6 +328,7 @@ handle_destroy(struct wl_listener *listener, void *data) wl_list_remove(&xwayland_view->set_decorations.link); wl_list_remove(&xwayland_view->set_override_redirect.link); wl_list_remove(&xwayland_view->set_strut_partial.link); + wl_list_remove(&xwayland_view->set_window_type.link); view_destroy(view); } @@ -478,6 +494,12 @@ handle_set_decorations(struct wl_listener *listener, void *data) view_set_decorations(view, want_deco(xwayland_view->xwayland_surface)); } +static void +handle_set_window_type(struct wl_listener *listener, void *data) +{ + /* Intentionally left blank */ +} + static void handle_set_override_redirect(struct wl_listener *listener, void *data) { @@ -865,6 +887,7 @@ xwayland_view_create(struct server *server, CONNECT_SIGNAL(xsurface, xwayland_view, set_decorations); CONNECT_SIGNAL(xsurface, xwayland_view, set_override_redirect); CONNECT_SIGNAL(xsurface, xwayland_view, set_strut_partial); + CONNECT_SIGNAL(xsurface, xwayland_view, set_window_type); wl_list_insert(&view->server->views, &view->link); @@ -896,10 +919,62 @@ handle_new_surface(struct wl_listener *listener, void *data) } static void -handle_ready(struct wl_listener *listener, void *data) +sync_atoms(xcb_connection_t *xcb_conn) +{ + assert(xcb_conn); + + wlr_log(WLR_DEBUG, "Syncing X11 atoms"); + xcb_intern_atom_cookie_t cookies[ATOM_LEN]; + + /* First request everything and then loop over the results to reduce latency */ + for (size_t i = 0; i < ATOM_LEN; i++) { + cookies[i] = xcb_intern_atom(xcb_conn, 0, + strlen(atom_names[i]), atom_names[i]); + } + + for (size_t i = 0; i < ATOM_LEN; i++) { + xcb_generic_error_t *err = NULL; + xcb_intern_atom_reply_t *reply = + xcb_intern_atom_reply(xcb_conn, cookies[i], &err); + if (reply) { + atoms[i] = reply->atom; + wlr_log(WLR_DEBUG, "Got X11 atom for %s: %u", + atom_names[i], reply->atom); + } + if (err) { + wlr_log(WLR_INFO, "Failed to get X11 atom for %s", + atom_names[i]); + } + free(reply); + free(err); + } +} + +static void +handle_server_ready(struct wl_listener *listener, void *data) +{ + xcb_connection_t *xcb_conn = xcb_connect(NULL, NULL); + if (xcb_connection_has_error(xcb_conn)) { + wlr_log(WLR_ERROR, "Failed to create xcb connection"); + + /* Just clear all existing atoms */ + for (size_t i = 0; i < ATOM_LEN; i++) { + atoms[i] = XCB_ATOM_NONE; + } + return; + } + + wlr_log(WLR_DEBUG, "Connected to xwayland"); + sync_atoms(xcb_conn); + wlr_log(WLR_DEBUG, "Disconnecting from xwayland"); + xcb_disconnect(xcb_conn); +} + +static void +handle_xwm_ready(struct wl_listener *listener, void *data) { struct server *server = - wl_container_of(listener, server, xwayland_ready); + wl_container_of(listener, server, xwayland_xwm_ready); wlr_xwayland_set_seat(server->xwayland, server->seat.seat); xwayland_update_workarea(server); } @@ -917,9 +992,13 @@ xwayland_server_init(struct server *server, struct wlr_compositor *compositor) wl_signal_add(&server->xwayland->events.new_surface, &server->xwayland_new_surface); - server->xwayland_ready.notify = handle_ready; + server->xwayland_server_ready.notify = handle_server_ready; + wl_signal_add(&server->xwayland->server->events.ready, + &server->xwayland_server_ready); + + server->xwayland_xwm_ready.notify = handle_xwm_ready; wl_signal_add(&server->xwayland->events.ready, - &server->xwayland_ready); + &server->xwayland_xwm_ready); if (setenv("DISPLAY", server->xwayland->display_name, true) < 0) { wlr_log_errno(WLR_ERROR, "unable to set DISPLAY for xwayland");