diff --git a/common/stringop.c b/common/stringop.c index d9ae9925..d2c91c24 100644 --- a/common/stringop.c +++ b/common/stringop.c @@ -401,3 +401,17 @@ char *argsep(char **stringp, const char *delim) { found: return start; } + +const char *strcasestr(const char *haystack, const char *needle) { + size_t needle_len = strlen(needle); + const char *pos = haystack; + const char *end = pos + strlen(haystack) - needle_len; + + while (pos <= end) { + if (strncasecmp(pos, needle, needle_len) == 0) { + return pos; + } + ++pos; + } + return NULL; +} diff --git a/include/stringop.h b/include/stringop.h index e7f58011..01bbdaa9 100644 --- a/include/stringop.h +++ b/include/stringop.h @@ -46,4 +46,6 @@ char *cmdsep(char **stringp, const char *delim); // Split string into 2 by delim, handle quotes char *argsep(char **stringp, const char *delim); +const char *strcasestr(const char *haystack, const char *needle); + #endif diff --git a/include/sway/commands.h b/include/sway/commands.h index 8e91c158..b0b5ed0f 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -117,6 +117,7 @@ sway_cmd cmd_floating_modifier; sway_cmd cmd_floating_scroll; sway_cmd cmd_focus; sway_cmd cmd_focus_follows_mouse; +sway_cmd cmd_focus_on_window_activation; sway_cmd cmd_focus_wrapping; sway_cmd cmd_font; sway_cmd cmd_for_window; diff --git a/include/sway/config.h b/include/sway/config.h index 18d10faa..4ee8c3c2 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -57,6 +57,16 @@ struct sway_mouse_binding { char *command; }; +/** + * Focus on window activation. + */ +enum sway_fowa { + FOWA_SMART, + FOWA_URGENT, + FOWA_FOCUS, + FOWA_NONE, +}; + /** * A "mode" of keybindings created via the `mode` command. */ @@ -340,6 +350,7 @@ struct sway_config { size_t font_height; bool pango_markup; size_t urgent_timeout; + enum sway_fowa focus_on_window_activation; // Flags bool focus_follows_mouse; diff --git a/include/sway/criteria.h b/include/sway/criteria.h index 7a1e547b..af12ffd7 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -31,7 +31,7 @@ struct criteria { uint32_t id; // X11 window ID #endif pcre *window_role; - uint32_t window_type; + enum atom_name window_type; bool floating; bool tiling; char urgent; // 'l' for latest or 'o' for oldest diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index f73ce571..382ab6b9 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -167,6 +167,7 @@ struct sway_xwayland_view { struct wl_listener request_maximize; struct wl_listener request_configure; struct wl_listener request_fullscreen; + struct wl_listener request_activate; struct wl_listener set_title; struct wl_listener set_class; struct wl_listener set_window_type; @@ -259,6 +260,11 @@ void view_autoconfigure(struct sway_view *view); void view_set_activated(struct sway_view *view, bool activated); +/** + * Called when the view requests to be focused. + */ +void view_request_activate(struct sway_view *view); + void view_set_tiled(struct sway_view *view, bool tiled); void view_close(struct sway_view *view); diff --git a/include/sway/xwayland.h b/include/sway/xwayland.h index 78d1053b..121edad3 100644 --- a/include/sway/xwayland.h +++ b/include/sway/xwayland.h @@ -5,6 +5,7 @@ #include enum atom_name { + NET_WM_WINDOW_TYPE_NORMAL, NET_WM_WINDOW_TYPE_DIALOG, NET_WM_WINDOW_TYPE_UTILITY, NET_WM_WINDOW_TYPE_TOOLBAR, diff --git a/meson.build b/meson.build index 57b024c8..253a4e96 100644 --- a/meson.build +++ b/meson.build @@ -106,7 +106,7 @@ endif add_project_arguments('-DSYSCONFDIR="/@0@/@1@"'.format(prefix, sysconfdir), language : 'c') -version = get_option('sway_version') +version = get_option('sway-version') if version != '' version = '"@0@"'.format(version) else @@ -120,6 +120,8 @@ else endif add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') +add_project_arguments('-D_LD_LIBRARY_PATH="@0@"'.format(get_option('ld-library-path')), language: 'c') + sway_inc = include_directories('include') subdir('include') @@ -168,7 +170,7 @@ install_data( install_dir: datadir + '/wayland-sessions' ) -if (get_option('default_wallpaper')) +if (get_option('default-wallpaper')) wallpaper_files = files( 'assets/Sway_Wallpaper_Blue_768x1024.png', 'assets/Sway_Wallpaper_Blue_768x1024_Portrait.png', @@ -184,7 +186,7 @@ if (get_option('default_wallpaper')) install_data(wallpaper_files, install_dir: wallpaper_install_dir) endif -if (get_option('zsh_completions')) +if (get_option('zsh-completions')) zsh_files = files( 'completions/zsh/_sway', 'completions/zsh/_swaylock', @@ -195,7 +197,7 @@ if (get_option('zsh_completions')) install_data(zsh_files, install_dir: zsh_install_dir) endif -if (get_option('bash_completions')) +if (get_option('bash-completions')) bash_files = files( 'completions/bash/sway', 'completions/bash/swayidle', @@ -207,7 +209,7 @@ if (get_option('bash_completions')) install_data(bash_files, install_dir: bash_install_dir) endif -if (get_option('fish_completions')) +if (get_option('fish-completions')) fish_files = files( 'completions/fish/sway.fish', 'completions/fish/swaylock.fish', diff --git a/meson_options.txt b/meson_options.txt index 5e54607f..50d646fd 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,6 +1,7 @@ -option('sway_version', type : 'string', description: 'The version string reported in `sway --version`.') -option('default_wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.') -option('zsh_completions', type: 'boolean', value: true, description: 'Install zsh shell completions.') -option('bash_completions', type: 'boolean', value: true, description: 'Install bash shell completions.') -option('fish_completions', type: 'boolean', value: true, description: 'Install fish shell completions.') +option('sway-version', type : 'string', description: 'The version string reported in `sway --version`.') +option('ld-library-path', type: 'string', value: '', description: 'The LD_LIBRARY_PATH environment variable.') +option('default-wallpaper', type: 'boolean', value: true, description: 'Install the default wallpaper.') +option('zsh-completions', type: 'boolean', value: true, description: 'Install zsh shell completions.') +option('bash-completions', type: 'boolean', value: true, description: 'Install bash shell completions.') +option('fish-completions', type: 'boolean', value: true, description: 'Install fish shell completions.') option('enable-xwayland', type: 'boolean', value: true, description: 'Enable support for X11 applications') diff --git a/sway/commands.c b/sway/commands.c index f321a874..9e0fda55 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -106,6 +106,7 @@ static struct cmd_handler handlers[] = { { "floating_modifier", cmd_floating_modifier }, { "focus", cmd_focus }, { "focus_follows_mouse", cmd_focus_follows_mouse }, + { "focus_on_window_activation", cmd_focus_on_window_activation }, { "focus_wrapping", cmd_focus_wrapping }, { "font", cmd_font }, { "for_window", cmd_for_window }, @@ -118,6 +119,8 @@ static struct cmd_handler handlers[] = { { "input", cmd_input }, { "mode", cmd_mode }, { "mouse_warping", cmd_mouse_warping }, + { "new_float", cmd_default_floating_border }, + { "new_window", cmd_default_border }, { "no_focus", cmd_no_focus }, { "output", cmd_output }, { "seat", cmd_seat }, diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 8270b958..b134c92f 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -310,7 +310,7 @@ void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) bool reload = false; // if this is a reload command we need to make a duplicate of the // binding since it will be gone after the reload has completed. - if (strcasecmp(binding->command, "reload") == 0) { + if (strcasestr(binding->command, "reload")) { reload = true; binding_copy = sway_binding_dup(binding); if (!binding_copy) { diff --git a/sway/commands/border.c b/sway/commands/border.c index 9c19e20a..9502c877 100644 --- a/sway/commands/border.c +++ b/sway/commands/border.c @@ -27,9 +27,6 @@ struct cmd_results *cmd_border(int argc, char **argv) { view->border = B_NORMAL; } else if (strcmp(argv[0], "pixel") == 0) { view->border = B_PIXEL; - if (argc == 2) { - view->border_thickness = atoi(argv[1]); - } } else if (strcmp(argv[0], "toggle") == 0) { view->border = (view->border + 1) % 3; } else { @@ -37,6 +34,9 @@ struct cmd_results *cmd_border(int argc, char **argv) { "Expected 'border ' " "or 'border pixel '"); } + if (argc == 2) { + view->border_thickness = atoi(argv[1]); + } if (container_is_floating(view->swayc)) { container_set_geometry_from_floating_view(view->swayc); diff --git a/sway/commands/focus_on_window_activation.c b/sway/commands/focus_on_window_activation.c new file mode 100644 index 00000000..1fb07918 --- /dev/null +++ b/sway/commands/focus_on_window_activation.c @@ -0,0 +1,25 @@ +#include "sway/commands.h" + +struct cmd_results *cmd_focus_on_window_activation(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "focus_on_window_activation", + EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (strcmp(argv[0], "smart") == 0) { + config->focus_on_window_activation = FOWA_SMART; + } else if (strcmp(argv[0], "urgent") == 0) { + config->focus_on_window_activation = FOWA_URGENT; + } else if (strcmp(argv[0], "focus") == 0) { + config->focus_on_window_activation = FOWA_FOCUS; + } else if (strcmp(argv[0], "none") == 0) { + config->focus_on_window_activation = FOWA_NONE; + } else { + return cmd_results_new(CMD_INVALID, "focus_on_window_activation", + "Expected " + "'focus_on_window_activation smart|urgent|focus|none'"); + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/config/output.c b/sway/config/output.c index 16ec9339..65f09258 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -196,6 +196,10 @@ void apply_output_config(struct output_config *oc, struct sway_container *output wlr_log(WLR_DEBUG, "Set %s mode to %dx%d (%f GHz)", oc->name, oc->width, oc->height, oc->refresh_rate); set_mode(wlr_output, oc->width, oc->height, oc->refresh_rate); + } else if (!wl_list_empty(&wlr_output->modes)) { + struct wlr_output_mode *mode = + wl_container_of(wlr_output->modes.prev, mode, link); + wlr_output_set_mode(wlr_output, mode); } if (oc && oc->scale > 0) { wlr_log(WLR_DEBUG, "Set %s scale to %f", oc->name, oc->scale); diff --git a/sway/criteria.c b/sway/criteria.c index 5452c4ee..13176fa1 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "sway/criteria.h" #include "sway/tree/container.h" @@ -25,7 +26,7 @@ bool criteria_is_empty(struct criteria *criteria) { && !criteria->id #endif && !criteria->window_role - && !criteria->window_type + && criteria->window_type == ATOM_LAST && !criteria->floating && !criteria->tiling && !criteria->urgent @@ -50,6 +51,23 @@ static int regex_cmp(const char *item, const pcre *regex) { return pcre_exec(regex, NULL, item, strlen(item), 0, 0, NULL, 0); } +static bool view_has_window_type(struct sway_view *view, enum atom_name name) { +#ifdef HAVE_XWAYLAND + if (view->type != SWAY_VIEW_XWAYLAND) { + return false; + } + struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; + struct sway_xwayland *xwayland = &server.xwayland; + xcb_atom_t desired_atom = xwayland->atoms[name]; + for (size_t i = 0; i < surface->window_type_len; ++i) { + if (surface->window_type[i] == desired_atom) { + return true; + } + } +#endif + return false; +} + static int cmp_urgent(const void *_a, const void *_b) { struct sway_view *a = *(void **)_a; struct sway_view *b = *(void **)_b; @@ -144,9 +162,8 @@ static bool criteria_matches_view(struct criteria *criteria, // TODO } - if (criteria->window_type) { - uint32_t type = view_get_window_type(view); - if (!type || type != criteria->window_type) { + if (criteria->window_type != ATOM_LAST) { + if (!view_has_window_type(view, criteria->window_type)) { return false; } } @@ -254,6 +271,21 @@ static bool generate_regex(pcre **regex, char *value) { return true; } +static enum atom_name parse_window_type(const char *type) { + if (strcasecmp(type, "normal") == 0) { + return NET_WM_WINDOW_TYPE_NORMAL; + } else if (strcasecmp(type, "dialog") == 0) { + return NET_WM_WINDOW_TYPE_DIALOG; + } else if (strcasecmp(type, "utility") == 0) { + return NET_WM_WINDOW_TYPE_UTILITY; + } else if (strcasecmp(type, "toolbar") == 0) { + return NET_WM_WINDOW_TYPE_TOOLBAR; + } else if (strcasecmp(type, "splash") == 0) { + return NET_WM_WINDOW_TYPE_SPLASH; + } + return ATOM_LAST; // ie. invalid +} + enum criteria_token { T_APP_ID, T_CLASS, @@ -434,7 +466,7 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { generate_regex(&criteria->window_role, effective_value); break; case T_WINDOW_TYPE: - // TODO: This is a string but will be stored as an enum or integer + criteria->window_type = parse_window_type(effective_value); break; #ifdef HAVE_XWAYLAND case T_ID: @@ -526,7 +558,8 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { } ++head; - struct criteria *criteria = calloc(sizeof(struct criteria), 1); + struct criteria *criteria = calloc(1, sizeof(struct criteria)); + criteria->window_type = ATOM_LAST; // default value char *name = NULL, *value = NULL; bool in_quotes = false; diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 1fae5db2..a4f7f928 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -176,7 +176,7 @@ void arrange_layers(struct sway_output *output) { sizeof(struct wlr_box)) != 0) { wlr_log(WLR_DEBUG, "Usable area changed, rearranging output"); memcpy(&output->usable_area, &usable_area, sizeof(struct wlr_box)); - container_set_dirty(output->swayc); + arrange_output(output->swayc); } // Arrange non-exlusive surfaces from top->bottom @@ -247,7 +247,6 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { layer_surface->surface, false); } - arrange_windows(output->swayc); transaction_commit_dirty(); } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index c228979d..c30e52a1 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -559,12 +559,6 @@ void handle_new_output(struct wl_listener *listener, void *data) { wl_list_insert(&root_container.sway_root->all_outputs, &output->link); - if (!wl_list_empty(&wlr_output->modes)) { - struct wlr_output_mode *mode = - wl_container_of(wlr_output->modes.prev, mode, link); - wlr_output_set_mode(wlr_output, mode); - } - output_enable(output); } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 4e401008..10faf91d 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -17,6 +17,7 @@ #include "sway/tree/view.h" static const char *atom_map[ATOM_LAST] = { + "_NET_WM_WINDOW_TYPE_NORMAL", "_NET_WM_WINDOW_TYPE_DIALOG", "_NET_WM_WINDOW_TYPE_UTILITY", "_NET_WM_WINDOW_TYPE_TOOLBAR", @@ -217,7 +218,9 @@ static bool wants_floating(struct sway_view *view) { 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 + if (surface->modal) { + return true; + } for (size_t i = 0; i < surface->window_type_len; ++i) { xcb_atom_t type = surface->window_type[i]; @@ -334,6 +337,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xwayland_view->request_fullscreen.link); wl_list_remove(&xwayland_view->request_move.link); wl_list_remove(&xwayland_view->request_resize.link); + wl_list_remove(&xwayland_view->request_activate.link); wl_list_remove(&xwayland_view->set_title.link); wl_list_remove(&xwayland_view->set_class.link); wl_list_remove(&xwayland_view->set_window_type.link); @@ -460,6 +464,19 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { seat_begin_resize_floating(seat, view->swayc, seat->last_button, e->edges); } +static void handle_request_activate(struct wl_listener *listener, void *data) { + struct sway_xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, request_activate); + struct sway_view *view = &xwayland_view->view; + struct wlr_xwayland_surface *xsurface = view->wlr_xwayland_surface; + if (!xsurface->mapped) { + return; + } + view_request_activate(view); + + transaction_commit_dirty(); +} + static void handle_set_title(struct wl_listener *listener, void *data) { struct sway_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, set_title); @@ -552,6 +569,10 @@ void handle_xwayland_surface(struct wl_listener *listener, void *data) { &xwayland_view->request_fullscreen); xwayland_view->request_fullscreen.notify = handle_request_fullscreen; + wl_signal_add(&xsurface->events.request_activate, + &xwayland_view->request_activate); + xwayland_view->request_activate.notify = handle_request_activate; + wl_signal_add(&xsurface->events.request_move, &xwayland_view->request_move); xwayland_view->request_move.notify = handle_request_move; diff --git a/sway/input/seat.c b/sway/input/seat.c index 36e1d232..4b7c7893 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -635,7 +635,7 @@ void seat_set_focus_warp(struct sway_seat *seat, // find new output's old workspace, which might have to be removed if empty struct sway_container *new_output_last_ws = NULL; - if (last_output != new_output) { + if (new_output && last_output != new_output) { new_output_last_ws = seat_get_active_child(seat, new_output); } @@ -779,7 +779,7 @@ void seat_set_focus_layer(struct sway_seat *seat, wlr_log(WLR_DEBUG, "Returning focus to %p %s '%s'", previous, container_type_to_str(previous->type), previous->name); // Hack to get seat to re-focus the return value of get_focus - seat_set_focus(seat, previous->parent); + seat_set_focus(seat, NULL); seat_set_focus(seat, previous); } return; diff --git a/sway/meson.build b/sway/meson.build index bcb44e8b..c14e58dd 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -46,6 +46,7 @@ sway_sources = files( 'commands/floating_modifier.c', 'commands/focus.c', 'commands/focus_follows_mouse.c', + 'commands/focus_on_window_activation.c', 'commands/focus_wrapping.c', 'commands/font.c', 'commands/for_window.c', diff --git a/sway/server.c b/sway/server.c index 92ed5595..749365cb 100644 --- a/sway/server.c +++ b/sway/server.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -117,7 +116,6 @@ bool server_init(struct sway_server *server) { server->server_decoration.notify = handle_server_decoration; wl_list_init(&server->decorations); - wlr_linux_dmabuf_v1_create(server->wl_display, renderer); wlr_export_dmabuf_manager_v1_create(server->wl_display); wlr_screencopy_manager_v1_create(server->wl_display); diff --git a/sway/tree/view.c b/sway/tree/view.c index 1a98c5f2..6bd0ef67 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -280,6 +280,29 @@ void view_set_activated(struct sway_view *view, bool activated) { } } +void view_request_activate(struct sway_view *view) { + struct sway_container *ws = container_parent(view->swayc, C_WORKSPACE); + struct sway_seat *seat = input_manager_current_seat(input_manager); + + switch (config->focus_on_window_activation) { + case FOWA_SMART: + if (workspace_is_visible(ws)) { + seat_set_focus(seat, view->swayc); + } else { + view_set_urgent(view, true); + } + break; + case FOWA_URGENT: + view_set_urgent(view, true); + break; + case FOWA_FOCUS: + seat_set_focus(seat, view->swayc); + break; + case FOWA_NONE: + break; + } +} + void view_set_tiled(struct sway_view *view, bool tiled) { if (!tiled) { view->using_csd = true;