diff --git a/include/sway/server.h b/include/sway/server.h index 65d96e7a4..96cad69d0 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -11,7 +11,7 @@ #include #include // TODO WLR: make Xwayland optional -#include +#include "sway/xwayland.h" struct sway_server { struct wl_display *wl_display; @@ -37,12 +37,9 @@ struct sway_server { struct wlr_xdg_shell *xdg_shell; struct wl_listener xdg_shell_surface; - struct wlr_xwayland *xwayland; - struct wlr_xcursor_manager *xcursor_manager; + struct sway_xwayland xwayland; struct wl_listener xwayland_surface; - - struct wlr_wl_shell *wl_shell; - struct wl_listener wl_shell_surface; + struct wl_listener xwayland_ready; }; struct sway_server server; diff --git a/include/sway/xwayland.h b/include/sway/xwayland.h new file mode 100644 index 000000000..78d1053b6 --- /dev/null +++ b/include/sway/xwayland.h @@ -0,0 +1,25 @@ +#ifndef SWAY_XWAYLAND_H +#define SWAY_XWAYLAND_H + +#include +#include + +enum atom_name { + NET_WM_WINDOW_TYPE_DIALOG, + NET_WM_WINDOW_TYPE_UTILITY, + NET_WM_WINDOW_TYPE_TOOLBAR, + NET_WM_WINDOW_TYPE_SPLASH, + NET_WM_STATE_MODAL, + ATOM_LAST, +}; + +struct sway_xwayland { + struct wlr_xwayland *wlr_xwayland; + struct wlr_xcursor_manager *xcursor_manager; + + xcb_atom_t atoms[ATOM_LAST]; +}; + +void handle_xwayland_ready(struct wl_listener *listener, void *data); + +#endif diff --git a/meson.build b/meson.build index d4ee1a118..1d40581aa 100644 --- a/meson.build +++ b/meson.build @@ -43,7 +43,8 @@ systemd = dependency('libsystemd', required: false) elogind = dependency('libelogind', required: false) math = cc.find_library('m') rt = cc.find_library('rt') -git = find_program('git', required: false) +xcb = dependency('xcb') +git = find_program('git', required: false) conf_data = configuration_data() diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 6447b711e..2c3848cd2 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -15,6 +15,14 @@ #include "sway/tree/layout.h" #include "sway/tree/view.h" +static const char *atom_map[ATOM_LAST] = { + "_NET_WM_WINDOW_TYPE_DIALOG", + "_NET_WM_WINDOW_TYPE_UTILITY", + "_NET_WM_WINDOW_TYPE_TOOLBAR", + "_NET_WM_WINDOW_TYPE_SPLASH", + "_NET_WM_STATE_MODAL", +}; + static void unmanaged_handle_request_configure(struct wl_listener *listener, void *data) { struct sway_xwayland_unmanaged *surface = @@ -61,7 +69,8 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) { if (!wlr_xwayland_surface_is_unmanaged(xsurface)) { struct sway_seat *seat = input_manager_current_seat(input_manager); - struct wlr_xwayland *xwayland = seat->input->server->xwayland; + struct wlr_xwayland *xwayland = + seat->input->server->xwayland.wlr_xwayland; wlr_xwayland_set_seat(xwayland, seat->wlr_seat); seat_set_focus_surface(seat, xsurface->surface); } @@ -199,15 +208,32 @@ static void set_fullscreen(struct sway_view *view, bool fullscreen) { } static bool wants_floating(struct sway_view *view) { - // TODO: - // We want to return true if the window type contains any of these: - // NET_WM_WINDOW_TYPE_DIALOG - // NET_WM_WINDOW_TYPE_UTILITY - // NET_WM_WINDOW_TYPE_TOOLBAR - // NET_WM_WINDOW_TYPE_SPLASH - // - // We also want to return true if the NET_WM_STATE is MODAL. - // wlroots doesn't appear to provide all this information at the moment. + if (xwayland_view_from_view(view) == NULL) { + return false; + } + struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; + struct sway_xwayland *xwayland = &server.xwayland; + + // TODO: return true if the NET_WM_STATE is MODAL + + for (size_t i = 0; i < surface->window_type_len; ++i) { + xcb_atom_t type = surface->window_type[i]; + if (type == xwayland->atoms[NET_WM_WINDOW_TYPE_DIALOG] || + type == xwayland->atoms[NET_WM_WINDOW_TYPE_UTILITY] || + type == xwayland->atoms[NET_WM_WINDOW_TYPE_TOOLBAR] || + type == xwayland->atoms[NET_WM_WINDOW_TYPE_SPLASH]) { + return true; + } + } + + struct wlr_xwayland_surface_size_hints *size_hints = surface->size_hints; + if (size_hints != NULL && + size_hints->min_width != 0 && size_hints->min_height != 0 && + size_hints->max_width == size_hints->min_width && + size_hints->max_height == size_hints->min_height) { + return true; + } + return false; } @@ -411,3 +437,40 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { wl_signal_add(&xsurface->events.map, &xwayland_view->map); xwayland_view->map.notify = handle_map; } + +void handle_xwayland_ready(struct wl_listener *listener, void *data) { + struct sway_server *server = + wl_container_of(listener, server, xwayland_ready); + struct sway_xwayland *xwayland = &server->xwayland; + + xcb_connection_t *xcb_conn = xcb_connect(NULL, NULL); + int err = xcb_connection_has_error(xcb_conn); + if (err) { + wlr_log(L_ERROR, "XCB connect failed: %d", err); + return; + } + + xcb_intern_atom_cookie_t cookies[ATOM_LAST]; + for (size_t i = 0; i < ATOM_LAST; i++) { + cookies[i] = + xcb_intern_atom(xcb_conn, 0, strlen(atom_map[i]), atom_map[i]); + } + for (size_t i = 0; i < ATOM_LAST; i++) { + xcb_generic_error_t *error = NULL; + xcb_intern_atom_reply_t *reply = + xcb_intern_atom_reply(xcb_conn, cookies[i], &error); + if (reply != NULL && error == NULL) { + xwayland->atoms[i] = reply->atom; + } + free(reply); + + if (error != NULL) { + wlr_log(L_ERROR, "could not resolve atom %s, X11 error code %d", + atom_map[i], error->error_code); + free(error); + break; + } + } + + xcb_disconnect(xcb_conn); +} diff --git a/sway/input/seat.c b/sway/input/seat.c index 1ea364666..1c2434b04 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -99,7 +99,8 @@ static void seat_send_focus(struct sway_container *con, if (con->type == C_VIEW && seat_is_input_allowed(seat, con->sway_view->surface)) { if (con->sway_view->type == SWAY_VIEW_XWAYLAND) { - struct wlr_xwayland *xwayland = seat->input->server->xwayland; + struct wlr_xwayland *xwayland = + seat->input->server->xwayland.wlr_xwayland; wlr_xwayland_set_seat(xwayland, seat->wlr_seat); } struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); diff --git a/sway/meson.build b/sway/meson.build index 0da67ed7d..2cf90b118 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -152,6 +152,7 @@ sway_deps = [ server_protos, wayland_server, wlroots, + xcb, xkbcommon, ] diff --git a/sway/server.c b/sway/server.c index 824b1d8e2..878b530d2 100644 --- a/sway/server.c +++ b/sway/server.c @@ -18,12 +18,11 @@ #include #include // TODO WLR: make Xwayland optional -#include #include "sway/config.h" #include "sway/input/input-manager.h" #include "sway/server.h" #include "sway/tree/layout.h" - +#include "sway/xwayland.h" bool server_init(struct sway_server *server) { wlr_log(L_DEBUG, "Initializing Wayland server"); @@ -72,20 +71,23 @@ bool server_init(struct sway_server *server) { server->xdg_shell_surface.notify = handle_xdg_shell_surface; // TODO make xwayland optional - server->xwayland = + server->xwayland.wlr_xwayland = wlr_xwayland_create(server->wl_display, server->compositor, true); - wl_signal_add(&server->xwayland->events.new_surface, + wl_signal_add(&server->xwayland.wlr_xwayland->events.new_surface, &server->xwayland_surface); server->xwayland_surface.notify = handle_xwayland_surface; + wl_signal_add(&server->xwayland.wlr_xwayland->events.ready, + &server->xwayland_ready); + server->xwayland_ready.notify = handle_xwayland_ready; // TODO: configurable cursor theme and size - server->xcursor_manager = wlr_xcursor_manager_create(NULL, 24); - wlr_xcursor_manager_load(server->xcursor_manager, 1); + server->xwayland.xcursor_manager = wlr_xcursor_manager_create(NULL, 24); + wlr_xcursor_manager_load(server->xwayland.xcursor_manager, 1); struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor( - server->xcursor_manager, "left_ptr", 1); + server->xwayland.xcursor_manager, "left_ptr", 1); if (xcursor != NULL) { struct wlr_xcursor_image *image = xcursor->images[0]; - wlr_xwayland_set_cursor(server->xwayland, image->buffer, + wlr_xwayland_set_cursor(server->xwayland.wlr_xwayland, image->buffer, image->width * 4, image->width, image->height, image->hotspot_x, image->hotspot_y); }