diff --git a/include/sway/output.h b/include/sway/output.h index e6ca0d024..19fc5e990 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -32,6 +32,10 @@ struct sway_output { struct wl_list link; pid_t bg_pid; + + struct { + struct wl_signal destroy; + } events; }; void output_damage_whole(struct sway_output *output); diff --git a/include/sway/server.h b/include/sway/server.h index 0efc6baa2..1e1aa3cc0 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -11,8 +11,8 @@ #include #include // TODO WLR: make Xwayland optional -#include #include "list.h" +#include "sway/xwayland.h" struct sway_server { struct wl_display *wl_display; @@ -38,12 +38,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; bool debug_txn_timings; 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/commands/input.c b/sway/commands/input.c index 22a0bb7c5..678c57c43 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -31,7 +31,7 @@ static struct cmd_handler input_handlers[] = { struct cmd_results *cmd_input(int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 1))) { + if ((error = checkarg(argc, "input", EXPECTED_AT_LEAST, 2))) { return error; } diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 82bccf68b..55cbdff00 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -62,46 +62,56 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { wordexp_t p; char *src = join_args(argv, j); if (wordexp(src, &p, 0) != 0 || p.we_wordv[0] == NULL) { - return cmd_results_new(CMD_INVALID, "output", - "Invalid syntax (%s).", src); + struct cmd_results *cmd_res = cmd_results_new(CMD_INVALID, "output", + "Invalid syntax (%s)", src); + free(src); + wordfree(&p); + return cmd_res; } free(src); - src = p.we_wordv[0]; + src = strdup(p.we_wordv[0]); + wordfree(&p); + if (!src) { + wlr_log(L_ERROR, "Failed to duplicate string"); + return cmd_results_new(CMD_FAILURE, "output", + "Unable to allocate resource"); + } + if (config->reading && *src != '/') { + // src file is inside configuration dir + char *conf = strdup(config->current_config); - if (conf) { - char *conf_path = dirname(conf); - src = malloc(strlen(conf_path) + strlen(src) + 2); - if (!src) { - free(conf); - wordfree(&p); - wlr_log(L_ERROR, - "Unable to allocate resource: Not enough memory"); - return cmd_results_new(CMD_FAILURE, "output", + if(!conf) { + wlr_log(L_ERROR, "Failed to duplicate string"); + return cmd_results_new(CMD_FAILURE, "output", "Unable to allocate resources"); - } - sprintf(src, "%s/%s", conf_path, p.we_wordv[0]); - free(conf); - } else { - wlr_log(L_ERROR, "Unable to allocate background source"); } + + char *conf_path = dirname(conf); + char *rel_path = src; + src = malloc(strlen(conf_path) + strlen(src) + 2); + if (!src) { + free(rel_path); + free(conf); + wlr_log(L_ERROR, "Unable to allocate memory"); + return cmd_results_new(CMD_FAILURE, "output", + "Unable to allocate resources"); + } + + sprintf(src, "%s/%s", conf_path, rel_path); + free(rel_path); + free(conf); } if (access(src, F_OK) == -1) { struct cmd_results *cmd_res = cmd_results_new(CMD_FAILURE, "output", "Unable to access background file '%s': %s", src, strerror(errno)); free(src); - wordfree(&p); return cmd_res; } - output->background = strdup(src); + output->background = src; output->background_option = strdup(mode); - if (src != p.we_wordv[0]) { - free(src); - } - wordfree(&p); - argc -= j + 1; argv += j + 1; } diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index fe5fc3168..ff37bbf17 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -219,6 +219,8 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) { struct sway_layer_surface *sway_layer = wl_container_of(listener, sway_layer, output_destroy); wl_list_remove(&sway_layer->output_destroy.link); + wl_list_remove(&sway_layer->link); + wl_list_init(&sway_layer->link); sway_layer->layer_surface->output = NULL; wlr_layer_surface_close(sway_layer->layer_surface); } @@ -350,10 +352,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { wl_signal_add(&layer_surface->surface->events.commit, &sway_layer->surface_commit); - sway_layer->output_destroy.notify = handle_output_destroy; - wl_signal_add(&layer_surface->output->events.destroy, - &sway_layer->output_destroy); - sway_layer->destroy.notify = handle_destroy; wl_signal_add(&layer_surface->events.destroy, &sway_layer->destroy); sway_layer->map.notify = handle_map; @@ -366,6 +364,9 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { layer_surface->data = sway_layer; struct sway_output *output = layer_surface->output->data; + sway_layer->output_destroy.notify = handle_output_destroy; + wl_signal_add(&output->events.destroy, &sway_layer->output_destroy); + wl_list_insert(&output->layers[layer_surface->layer], &sway_layer->link); // Temporarily set the layer's current state to client_pending diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 34fefaa9e..69d0bdd47 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -1263,6 +1263,8 @@ static void damage_handle_destroy(struct wl_listener *listener, void *data) { static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, destroy); + wl_signal_emit(&output->events.destroy, output); + if (output->swayc) { container_destroy(output->swayc); } @@ -1343,6 +1345,7 @@ void output_enable(struct sway_output *output) { for (size_t i = 0; i < len; ++i) { wl_list_init(&output->layers[i]); } + wl_signal_init(&output->events.destroy); input_manager_configure_xcursor(input_manager); diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index b076d7726..0f45399de 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -117,11 +117,12 @@ static void set_fullscreen(struct sway_view *view, bool fullscreen) { } static bool wants_floating(struct sway_view *view) { - struct wlr_xdg_toplevel_state *state = - &view->wlr_xdg_surface->toplevel->current; - return state->min_width != 0 && state->min_height != 0 + struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_surface->toplevel; + struct wlr_xdg_toplevel_state *state = &toplevel->current; + return (state->min_width != 0 && state->min_height != 0 && state->min_width == state->max_width - && state->min_height == state->max_height; + && state->min_height == state->max_height) + || toplevel->parent; } static void for_each_surface(struct sway_view *view, diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 7320e629e..b296f1a87 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -117,11 +117,13 @@ static void set_fullscreen(struct sway_view *view, bool fullscreen) { } static bool wants_floating(struct sway_view *view) { - struct wlr_xdg_toplevel_v6_state *state = - &view->wlr_xdg_surface_v6->toplevel->current; - return state->min_width != 0 && state->min_height != 0 + struct wlr_xdg_toplevel_v6 *toplevel = + view->wlr_xdg_surface_v6->toplevel; + struct wlr_xdg_toplevel_v6_state *state = &toplevel->current; + return (state->min_width != 0 && state->min_height != 0 && state->min_width == state->max_width - && state->min_height == state->max_height; + && state->min_height == state->max_height) + || toplevel->parent; } static void for_each_surface(struct sway_view *view, diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 854da006c..023fb2a75 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -17,6 +17,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 = @@ -63,7 +71,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); } @@ -200,15 +209,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; } @@ -324,9 +350,14 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { ev->width, ev->height); return; } - // TODO: Let floating views do whatever - configure(view, view->swayc->current.view_x, view->swayc->current.view_y, - view->swayc->current.view_width, view->swayc->current.view_height); + if (container_is_floating(view->swayc)) { + configure(view, view->swayc->current.view_x, + view->swayc->current.view_y, ev->width, ev->height); + } else { + configure(view, view->swayc->current.view_x, + view->swayc->current.view_y, view->swayc->current.view_width, + view->swayc->current.view_height); + } } static void handle_request_fullscreen(struct wl_listener *listener, void *data) { @@ -431,3 +462,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/cursor.c b/sway/input/cursor.c index 37a877568..944e35aac 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -174,10 +174,13 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, seat_set_focus_warp(seat, c, false); } } else if (c->type == C_VIEW) { - // Focus c if both of the following are true: + // Focus c if the following are true: // - cursor is over a new view, i.e. entered a new window; and - // - the new view is visible, i.e. not hidden in a stack or tab. - if (c != prev_c && view_is_visible(c->sway_view)) { + // - the new view is visible, i.e. not hidden in a stack or tab; and + // - the seat does not have a keyboard grab + if (!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) && + c != prev_c && + view_is_visible(c->sway_view)) { seat_set_focus_warp(seat, c, false); } else { struct sway_container *next_focus = diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 9e093828f..ec149d060 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -64,12 +64,12 @@ static void update_shortcut_state(struct sway_shortcut_state *state, bool last_key_was_a_modifier = raw_modifiers != state->last_raw_modifiers; state->last_raw_modifiers = raw_modifiers; - if (event->state == WLR_KEY_PRESSED) { - if (last_key_was_a_modifier && state->last_keycode) { - // Last pressed key before this one was a modifier - state_erase_key(state, state->last_keycode); - } + if (last_key_was_a_modifier && state->last_keycode) { + // Last pressed key before this one was a modifier + state_erase_key(state, state->last_keycode); + } + if (event->state == WLR_KEY_PRESSED) { // Add current key to set; there may be duplicates state_add_key(state, event->keycode, new_key); state->last_keycode = event->keycode; diff --git a/sway/input/seat.c b/sway/input/seat.c index 436d18e21..2c2087da4 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 c461b0ff4..9ff3f05f1 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -153,6 +153,7 @@ sway_deps = [ server_protos, wayland_server, wlroots, + xcb, xkbcommon, ] diff --git a/sway/server.c b/sway/server.c index 884d971ef..f5700c09c 100644 --- a/sway/server.c +++ b/sway/server.c @@ -19,13 +19,12 @@ #include #include // TODO WLR: make Xwayland optional -#include #include "list.h" #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_privileged_prepare(struct sway_server *server) { wlr_log(L_DEBUG, "Preparing Wayland server initialization"); @@ -79,20 +78,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); }