From a3332c70bf722bdae9fb357d909419c4a211a38e Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 3 Feb 2019 12:12:40 -0500 Subject: [PATCH 0001/2054] Focus ws inactive node with focus_follows_mouse --- sway/input/cursor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index c38d8d3a1..250100cc7 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -362,7 +362,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, struct sway_output *focused_output = node_get_output(focus); struct sway_output *output = node_get_output(node); if (output != focused_output) { - seat_set_focus(seat, node); + seat_set_focus(seat, seat_get_focus_inactive(seat, node)); } } else if (node->type == N_CONTAINER && node->sway_container->view) { // Focus node if the following are true: From da59ffa8a059f6504868f4d93c5d62f099ab77b6 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 3 Feb 2019 13:41:35 -0500 Subject: [PATCH 0002/2054] Skip wildcard seat config in destroy_removed_seats --- sway/config.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sway/config.c b/sway/config.c index 7cb27d955..54d29fc90 100644 --- a/sway/config.c +++ b/sway/config.c @@ -141,6 +141,11 @@ static void destroy_removed_seats(struct sway_config *old_config, int i; for (i = 0; i < old_config->seat_configs->length; i++) { seat_config = old_config->seat_configs->items[i]; + // Skip the wildcard seat config, it won't have a matching real seat. + if (strcmp(seat_config->name, "*") == 0) { + continue; + } + /* Also destroy seats that aren't present in new config */ if (new_config && list_seq_find(new_config->seat_configs, seat_name_cmp, seat_config->name) < 0) { From 907595b10db44dee9f366ff36947c63bf6433fee Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 3 Feb 2019 13:44:37 -0500 Subject: [PATCH 0003/2054] Skip constraining cursor if no seat config --- sway/input/cursor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index c38d8d3a1..5e1cdaeda 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -1459,7 +1459,7 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data) { void sway_cursor_constrain(struct sway_cursor *cursor, struct wlr_pointer_constraint_v1 *constraint) { struct seat_config *config = seat_get_config(cursor->seat); - if (config->allow_constrain == CONSTRAIN_DISABLE) { + if (!config || config->allow_constrain == CONSTRAIN_DISABLE) { return; } From f8a91171491c1e8a3591461aa540511ec4fe4875 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 3 Feb 2019 13:46:44 -0500 Subject: [PATCH 0004/2054] Fall back to wildcard in sway_cursor_constrain --- sway/input/cursor.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 5e1cdaeda..01aae79d4 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -1459,6 +1459,10 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data) { void sway_cursor_constrain(struct sway_cursor *cursor, struct wlr_pointer_constraint_v1 *constraint) { struct seat_config *config = seat_get_config(cursor->seat); + if (!config) { + config = seat_get_config_by_name("*"); + } + if (!config || config->allow_constrain == CONSTRAIN_DISABLE) { return; } From 7299b9a6ca3658852f2ff41b05f6aaa86ff90d81 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 3 Feb 2019 13:56:05 -0500 Subject: [PATCH 0005/2054] seat_cmd_cursor: do not create non-existing seat If a seat does not exist in seat_cmd_cursor, do not create it. A seat without any attachments is useless since it will have no capabilities. This changes `input_manager_get_seat` to have an additional argument that dictates whether or not to create the seat if it does not exist. --- include/sway/input/input-manager.h | 2 +- sway/commands/seat/cursor.c | 5 +++-- sway/config.c | 6 ++++-- sway/input/input-manager.c | 9 +++++---- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index 8e8bf1f20..e166a2377 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h @@ -45,7 +45,7 @@ void input_manager_apply_seat_config(struct seat_config *seat_config); struct sway_seat *input_manager_get_default_seat(void); -struct sway_seat *input_manager_get_seat(const char *seat_name); +struct sway_seat *input_manager_get_seat(const char *seat_name, bool create); /** * If none of the seat configs have a fallback setting (either true or false), diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c index 4f805b227..0c7609eaf 100644 --- a/sway/commands/seat/cursor.c +++ b/sway/commands/seat/cursor.c @@ -61,9 +61,10 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) { } if (strcmp(sc->name, "*") != 0) { - struct sway_seat *seat = input_manager_get_seat(sc->name); + struct sway_seat *seat = input_manager_get_seat(sc->name, false); if (!seat) { - return cmd_results_new(CMD_FAILURE, "Failed to get seat"); + return cmd_results_new(CMD_FAILURE, + "Seat %s does not exist", sc->name); } error = handle_command(seat->cursor, argc, argv); } else { diff --git a/sway/config.c b/sway/config.c index 54d29fc90..ee1c42df3 100644 --- a/sway/config.c +++ b/sway/config.c @@ -149,8 +149,10 @@ static void destroy_removed_seats(struct sway_config *old_config, /* Also destroy seats that aren't present in new config */ if (new_config && list_seq_find(new_config->seat_configs, seat_name_cmp, seat_config->name) < 0) { - seat = input_manager_get_seat(seat_config->name); - seat_destroy(seat); + seat = input_manager_get_seat(seat_config->name, false); + if (seat) { + seat_destroy(seat); + } } } } diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 8d263e06b..f99fc395b 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -31,10 +31,10 @@ struct sway_seat *input_manager_current_seat(void) { } struct sway_seat *input_manager_get_default_seat(void) { - return input_manager_get_seat(DEFAULT_SEAT); + return input_manager_get_seat(DEFAULT_SEAT, true); } -struct sway_seat *input_manager_get_seat(const char *seat_name) { +struct sway_seat *input_manager_get_seat(const char *seat_name, bool create) { struct sway_seat *seat = NULL; wl_list_for_each(seat, &server.input->seats, link) { if (strcmp(seat->wlr_seat->name, seat_name) == 0) { @@ -42,7 +42,7 @@ struct sway_seat *input_manager_get_seat(const char *seat_name) { } } - return seat_create(seat_name); + return create ? seat_create(seat_name) : NULL; } char *input_device_get_identifier(struct wlr_input_device *device) { @@ -674,7 +674,8 @@ void input_manager_apply_seat_config(struct seat_config *seat_config) { seat_apply_config(seat, sc); } } else { - struct sway_seat *seat = input_manager_get_seat(seat_config->name); + struct sway_seat *seat = + input_manager_get_seat(seat_config->name, true); if (!seat) { return; } From 920c64f569183cfa27923b928c8d19ea56a41332 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 5 Feb 2019 01:59:40 -0500 Subject: [PATCH 0006/2054] load_include_configs: fix wordexp fail condition This fixes the failure condition for the wordexp call in load_include_configs. The only success value is zero. Since the error codes are positive, having the check be less than zero was causing segfaults on failure when accessing the words. --- sway/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/config.c b/sway/config.c index ee1c42df3..0c23fad84 100644 --- a/sway/config.c +++ b/sway/config.c @@ -557,7 +557,7 @@ bool load_include_configs(const char *path, struct sway_config *config, wordexp_t p; - if (wordexp(path, &p, 0) < 0) { + if (wordexp(path, &p, 0) != 0) { free(parent_path); free(wd); return false; From 665381d30546b2a397190cdae5fd7125b3d442f8 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 5 Feb 2019 00:31:06 -0500 Subject: [PATCH 0007/2054] swaynag: remove trailing newlines in config Now that swaynag uses getline (instead of the old readline), the trailing newline characters have to be removed when reading the config --- swaynag/config.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/swaynag/config.c b/swaynag/config.c index 40f3f65e8..200611f4b 100644 --- a/swaynag/config.c +++ b/swaynag/config.c @@ -348,6 +348,10 @@ int swaynag_load_config(char *path, struct swaynag *swaynag, list_t *types) { continue; } + if (line[nread - 1] == '\n') { + line[nread - 1] = '\0'; + } + if (line[0] == '[') { char *close = strchr(line, ']'); if (!close) { From 1e65cc1ea6587717160ce413904399f0431e97ba Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 4 Feb 2019 23:39:37 -0500 Subject: [PATCH 0008/2054] execute_command: dont strip quotes for exec_always This removes quote stripping for `exec_always` in `execute_command`. Since `exec_always` commands will be deferred in the config and processed by `execute_command`, the quotes need to be left intact like they are for `exec`. --- sway/commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/commands.c b/sway/commands.c index dd994fa15..82f41545d 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -254,7 +254,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, //TODO better handling of argv int argc; char **argv = split_args(cmd, &argc); - if (strcmp(argv[0], "exec") != 0) { + if (!strcmp(argv[0], "exec") && !strcmp(argv[0], "exec_always")) { int i; for (i = 1; i < argc; ++i) { if (*argv[i] == '\"' || *argv[i] == '\'') { From 478b128c26057dcd7c97aaf5f91aa08dfce7d38f Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 5 Feb 2019 12:52:02 +0100 Subject: [PATCH 0009/2054] Fix quote stripping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's not use !strcmp(…) anymore. --- sway/commands.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/commands.c b/sway/commands.c index 82f41545d..3fc4f86e4 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -254,7 +254,8 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, //TODO better handling of argv int argc; char **argv = split_args(cmd, &argc); - if (!strcmp(argv[0], "exec") && !strcmp(argv[0], "exec_always")) { + if (strcmp(argv[0], "exec") != 0 && + strcmp(argv[0], "exec_always") != 0) { int i; for (i = 1; i < argc; ++i) { if (*argv[i] == '\"' || *argv[i] == '\'') { From 288e35f99eb7ee6a6ca5ba4c14fa666a409855e3 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 5 Feb 2019 07:39:21 -0500 Subject: [PATCH 0010/2054] IPC_COMMAND: split on newline This splits commands given in IPC_COMMAND on newline to match i3's behavior. --- sway/ipc-server.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sway/ipc-server.c b/sway/ipc-server.c index d1920cfd4..eb6f159d6 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -595,6 +595,16 @@ void ipc_client_handle_command(struct ipc_client *client) { switch (client->current_command) { case IPC_COMMAND: { + char *line = strtok(buf, "\n"); + while (line) { + size_t line_length = strlen(line); + if (line + line_length >= buf + client->payload_length) { + break; + } + line[line_length] = ';'; + line = strtok(NULL, "\n"); + } + list_t *res_list = execute_command(buf, NULL, NULL); transaction_commit_dirty(); char *json = cmd_results_to_json(res_list); From 77587ee632db2f047e5e0b7979b0319ed2257405 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Tue, 5 Feb 2019 14:32:05 +0100 Subject: [PATCH 0011/2054] Set version in project file Let's set the version in the meson file instead of declaring it outside. In case git is installed we use the git hash as version. Instead it isn't (like on a clean build system), let's use the version defined in the project. --- meson.build | 12 ++++-------- meson_options.txt | 1 - 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/meson.build b/meson.build index 766bf012f..c50fab176 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,7 @@ project( 'sway', 'c', + version: '1.0', license: 'MIT', meson_version: '>=0.48.0', default_options: [ @@ -127,17 +128,12 @@ endif add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir)), language : 'c') -version = get_option('sway-version') -if version != '' - version = '"@0@"'.format(version) -else - if not git.found() - error('git is required to make the version string') - endif - +if git.found() git_commit_hash = run_command([git.path(), 'describe', '--always', '--tags']).stdout().strip() git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']).stdout().strip() version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash, git_branch) +else + version = '"@0@"'.format(meson.project_version()) endif add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') diff --git a/meson_options.txt b/meson_options.txt index 04b29e17a..d3667acfd 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,4 +1,3 @@ -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.') From f2abe0570ff2febe8a25de122c8dc95c49ef8282 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 5 Feb 2019 08:35:00 -0500 Subject: [PATCH 0012/2054] cmd_workspace_gaps: fix double free on bad amount This fixes a double free in cmd_workspace_gaps when the amount given is invalid. The end pointer from strtol is part of the argument and should not be freed. Freeing the end pointer could result in a double free or bad free depending on whether or not the end pointer was at the start of the argument --- sway/commands/workspace.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index c5d6435a4..65a3f4075 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -76,7 +76,6 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv, char *end; int amount = strtol(argv[gaps_location + 2], &end, 10); if (strlen(end)) { - free(end); return cmd_results_new(CMD_FAILURE, expected); } From 886789c1973ff1eb3cdcc20713c39c366fb38afe Mon Sep 17 00:00:00 2001 From: Connor E <38229097+c-edw@users.noreply.github.com> Date: Tue, 5 Feb 2019 14:37:22 +0000 Subject: [PATCH 0013/2054] If validating the config, do it as early as possible. --- sway/main.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/sway/main.c b/sway/main.c index a3198af10..b3ffdd833 100644 --- a/sway/main.c +++ b/sway/main.c @@ -312,6 +312,18 @@ int main(int argc, char **argv) { wlr_log_init(WLR_ERROR, NULL); } + log_kernel(); + log_distro(); + log_env(); + detect_proprietary(allow_unsupported_gpu); + detect_raspi(); + + if (validate) { + bool valid = load_main_config(config_path, false, true); + free(config_path); + return valid ? 0 : 1; + } + if (optind < argc) { // Behave as IPC client if (optind != 1) { sway_log(SWAY_ERROR, "Don't use options with the IPC client"); @@ -334,11 +346,6 @@ int main(int argc, char **argv) { return 1; } - log_kernel(); - log_distro(); - detect_proprietary(allow_unsupported_gpu); - detect_raspi(); - if (!drop_permissions()) { server_fini(&server); exit(EXIT_FAILURE); @@ -359,12 +366,6 @@ int main(int argc, char **argv) { } ipc_init(&server); - log_env(); - - if (validate) { - bool valid = load_main_config(config_path, false, true); - return valid ? 0 : 1; - } setenv("WAYLAND_DISPLAY", server.socket, true); if (!load_main_config(config_path, false, false)) { From e1b8190d2cce524ee460e4e32ea254bc697d36e9 Mon Sep 17 00:00:00 2001 From: Connor E <38229097+c-edw@users.noreply.github.com> Date: Tue, 5 Feb 2019 15:39:22 +0000 Subject: [PATCH 0014/2054] Initialize server so input manager is available. --- sway/main.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sway/main.c b/sway/main.c index b3ffdd833..67d0f7992 100644 --- a/sway/main.c +++ b/sway/main.c @@ -318,12 +318,6 @@ int main(int argc, char **argv) { detect_proprietary(allow_unsupported_gpu); detect_raspi(); - if (validate) { - bool valid = load_main_config(config_path, false, true); - free(config_path); - return valid ? 0 : 1; - } - if (optind < argc) { // Behave as IPC client if (optind != 1) { sway_log(SWAY_ERROR, "Don't use options with the IPC client"); @@ -365,6 +359,12 @@ int main(int argc, char **argv) { return 1; } + if (validate) { + bool valid = load_main_config(config_path, false, true); + free(config_path); + return valid ? 0 : 1; + } + ipc_init(&server); setenv("WAYLAND_DISPLAY", server.socket, true); From 89afb761ba21926b710b9e3d12361c3922d2baec Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 5 Feb 2019 14:13:27 -0500 Subject: [PATCH 0015/2054] output_cmd_background: fix no file + valid mode If output_cmd_background is given a valid mode as the first argument, then there is no file given and an error should be returned. join_args should not be called with an argc of zero since it sets the last character to the null terminator. With an argc of zero, the length is zero causing a heap buffer overflow when setting the byte before the start of argv to '\0'. This probably will not ever generate a segfault, but may cause data corruption to whatever is directly before it in memory. To make other such cases easier to detect, this also adds a sway_assert in join_args when argc is zero. --- common/stringop.c | 3 +++ sway/commands/output/background.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/common/stringop.c b/common/stringop.c index 8af0d60fa..709be6842 100644 --- a/common/stringop.c +++ b/common/stringop.c @@ -258,6 +258,9 @@ int unescape_string(char *string) { } char *join_args(char **argv, int argc) { + if (!sway_assert(argc > 0, "argc should be positive")) { + return NULL; + } int len = 0, i; for (i = 0; i < argc; ++i) { len += strlen(argv[i]) + 1; diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index f65904bb8..5a15ed0fc 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -61,6 +61,9 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Missing background scaling mode."); } + if (j == 0) { + return cmd_results_new(CMD_INVALID, "Missing background file"); + } wordexp_t p = {0}; char *src = join_args(argv, j); From 9b1905acf9b4de7bacbb57f5cc20d6a356b0d92d Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 5 Feb 2019 19:07:01 +0100 Subject: [PATCH 0016/2054] Fix close_popups for xdg-shell wlr_xdg_popup_destroy will destroy popups, so we need to walk the tree carefully. It's enough to just destroy all direct children, since destroying the parent will also destroy all children. --- sway/desktop/xdg_shell.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 152bd26f0..ce6fe41a1 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -236,19 +236,11 @@ static void _close(struct sway_view *view) { } } -static void close_popups_iterator(struct wlr_surface *surface, - int sx, int sy, void *data) { - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_wlr_surface(surface); - if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP - && xdg_surface->popup) { - wlr_xdg_popup_destroy(xdg_surface); - } -} - static void close_popups(struct sway_view *view) { - wlr_xdg_surface_for_each_popup(view->wlr_xdg_surface, - close_popups_iterator, NULL); + struct wlr_xdg_popup *popup, *tmp; + wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_surface->popups, link) { + wlr_xdg_popup_destroy(popup->base); + } } static void destroy(struct sway_view *view) { From c65057ef8b03ce11d4c002ed8d1615926486b7a4 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 5 Feb 2019 22:06:42 -0500 Subject: [PATCH 0017/2054] load_main_config: add realpath to config_chain Since `load_include_config` compares against the realpath of a config file when checking if a config has already been added, the main config's realpath has to be added to the config_chain. --- sway/config.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/sway/config.c b/sway/config.c index 0c23fad84..18fb69d97 100644 --- a/sway/config.c +++ b/sway/config.c @@ -379,6 +379,14 @@ bool load_main_config(const char *file, bool is_active, bool validating) { path = get_config_path(); } + char *real_path = realpath(path, NULL); + if (real_path == NULL) { + sway_log(SWAY_DEBUG, "%s not found.", path); + free(path); + return false; + } + free(path); + struct sway_config *old_config = config; config = calloc(1, sizeof(struct sway_config)); if (!config) { @@ -401,8 +409,8 @@ bool load_main_config(const char *file, bool is_active, bool validating) { input_manager_reset_all_inputs(); } - config->current_config_path = path; - list_add(config->config_chain, path); + config->current_config_path = real_path; + list_add(config->config_chain, real_path); config->reading = true; @@ -454,7 +462,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { } */ - success = success && load_config(path, config, + success = success && load_config(real_path, config, &config->swaynag_config_errors); if (validating) { From d3b3eb019a82a1c02eae66e130c7b861494c1a6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 6 Feb 2019 15:16:30 +0100 Subject: [PATCH 0018/2054] ipc_has_event_listeners: fix inverted check of subscribed_events subscribed_events is a bit mask, with each *set* bit representing an event the client has subscribed to. --- sway/ipc-server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/ipc-server.c b/sway/ipc-server.c index eb6f159d6..df57cba51 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -264,7 +264,7 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { static bool ipc_has_event_listeners(enum ipc_command_type event) { for (int i = 0; i < ipc_client_list->length; i++) { struct ipc_client *client = ipc_client_list->items[i]; - if ((client->subscribed_events & event_mask(event)) == 0) { + if ((client->subscribed_events & event_mask(event)) != 0) { return true; } } From 855368b67e8b7d0b1dd035bde7f9119d37b35e5d Mon Sep 17 00:00:00 2001 From: Connor E <38229097+c-edw@users.noreply.github.com> Date: Wed, 6 Feb 2019 14:01:40 +0000 Subject: [PATCH 0019/2054] Return false if config could not be loaded. --- sway/config.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sway/config.c b/sway/config.c index 18fb69d97..455d20cde 100644 --- a/sway/config.c +++ b/sway/config.c @@ -574,7 +574,13 @@ bool load_include_configs(const char *path, struct sway_config *config, char **w = p.we_wordv; size_t i; for (i = 0; i < p.we_wordc; ++i) { - load_include_config(w[i], parent_dir, config, swaynag); + bool found = load_include_config(w[i], parent_dir, config, swaynag); + if (!found) { + wordfree(&p); + free(parent_path); + free(wd); + return false; + } } free(parent_path); wordfree(&p); From 921e42c6c06212a61d899d6335d95eb4c781e2e8 Mon Sep 17 00:00:00 2001 From: Connor E <38229097+c-edw@users.noreply.github.com> Date: Wed, 6 Feb 2019 15:42:32 +0000 Subject: [PATCH 0020/2054] Restore CWD if returning early. --- sway/config.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/sway/config.c b/sway/config.c index 455d20cde..9072f7d7e 100644 --- a/sway/config.c +++ b/sway/config.c @@ -558,17 +558,13 @@ bool load_include_configs(const char *path, struct sway_config *config, const char *parent_dir = dirname(parent_path); if (chdir(parent_dir) < 0) { - free(parent_path); - free(wd); - return false; + goto error_chdir; } wordexp_t p; if (wordexp(path, &p, 0) != 0) { - free(parent_path); - free(wd); - return false; + goto error_wordexp; } char **w = p.we_wordv; @@ -576,10 +572,7 @@ bool load_include_configs(const char *path, struct sway_config *config, for (i = 0; i < p.we_wordc; ++i) { bool found = load_include_config(w[i], parent_dir, config, swaynag); if (!found) { - wordfree(&p); - free(parent_path); - free(wd); - return false; + goto error_not_found; } } free(parent_path); @@ -594,6 +587,16 @@ bool load_include_configs(const char *path, struct sway_config *config, free(wd); return true; +error_not_found: + wordfree(&p); +error_wordexp: + if (chdir(wd) < 0) { + sway_log(SWAY_ERROR, "failed to restore working directory"); + } +error_chdir: + free(parent_path); + free(wd); + return false; } void run_deferred_commands(void) { From ee56428b642d8f75350ae2698706c1b88898acf5 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 6 Feb 2019 13:18:46 -0500 Subject: [PATCH 0021/2054] Clarify error for options and positional args When both options and positional arguments are given, sway would print the error `Don't use options with the IPC client`. Over the past several months, it seems like users are including this error message in issues instead of a debug log due to not understanding that the error message means there is an issue with their command. This makes the error message more verbose and will hopefully make it so more users understand that the message is not a bug in sway, but with the command used. --- sway/main.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sway/main.c b/sway/main.c index 67d0f7992..12f92bd80 100644 --- a/sway/main.c +++ b/sway/main.c @@ -320,7 +320,13 @@ int main(int argc, char **argv) { if (optind < argc) { // Behave as IPC client if (optind != 1) { - sway_log(SWAY_ERROR, "Don't use options with the IPC client"); + sway_log(SWAY_ERROR, + "Detected both options and positional arguments. If you " + "are trying to use the IPC client, options are not " + "supported. Otherwise, check the provided arguments for " + "issues. See `man 1 sway` or `sway -h` for usage. If you " + "are trying to generate a debug log, use " + "`sway -d 2>sway.log`."); exit(EXIT_FAILURE); } if (!drop_permissions()) { From 5a32a74b2d0e4685527bd78d5052aafe2df4b8f2 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Thu, 7 Feb 2019 02:16:38 -0500 Subject: [PATCH 0022/2054] load_main_config: use given path, store realpath Since `load_include_config` compares against the realpath of a config file when checking if a config has already been added, the main config's realpath has to be added to the config_chain. However, includes from the main config should be processed relative to the path given to allow for symbolic links. This stores the realpath in `config->config_chain`, but uses the given path for all other operations. --- sway/config.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sway/config.c b/sway/config.c index 9072f7d7e..0c791a35d 100644 --- a/sway/config.c +++ b/sway/config.c @@ -385,7 +385,6 @@ bool load_main_config(const char *file, bool is_active, bool validating) { free(path); return false; } - free(path); struct sway_config *old_config = config; config = calloc(1, sizeof(struct sway_config)); @@ -409,7 +408,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { input_manager_reset_all_inputs(); } - config->current_config_path = real_path; + config->current_config_path = path; list_add(config->config_chain, real_path); config->reading = true; @@ -462,7 +461,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { } */ - success = success && load_config(real_path, config, + success = success && load_config(path, config, &config->swaynag_config_errors); if (validating) { From ec5da0ca5bad6a433f727499d68ac1352397f5aa Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Thu, 7 Feb 2019 01:52:58 -0500 Subject: [PATCH 0023/2054] seat_configure_tablet_tool: configure xcursor Since a tablet tool provides the WL_SEAT_CAPABILITY_POINTER capability, sway will attempt to use the xcursor manager to set a cursor image. If the tablet tool was the first (and possibly only) device to provide the capability for the seat, the xcursor manager was not being configured before attempting to set a cursor image. This was due to `seat_configure_xcursor` only being called in `seat_configure_pointer`. Since the xcursor manager was NULL in this case, it would cause a segfault when attempting to set a cursor image. This adds a call to `seat_configure_xcursor` in `seat_configure_tablet_tool` to ensure that the seat has a xcursor manager. --- sway/input/seat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/input/seat.c b/sway/input/seat.c index d159da224..18664d7cb 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -525,6 +525,7 @@ static void seat_configure_touch(struct sway_seat *seat, static void seat_configure_tablet_tool(struct sway_seat *seat, struct sway_seat_device *sway_device) { + seat_configure_xcursor(seat); wlr_cursor_attach_input_device(seat->cursor->cursor, sway_device->input_device->wlr_device); seat_apply_input_config(seat, sway_device); From 385cf330aebe9d9996076912e8b826efeb365627 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 7 Feb 2019 21:22:53 -0500 Subject: [PATCH 0024/2054] Revert "Restore CWD if returning early." This reverts commit 921e42c6c06212a61d899d6335d95eb4c781e2e8. --- sway/config.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/sway/config.c b/sway/config.c index 0c791a35d..18df2add2 100644 --- a/sway/config.c +++ b/sway/config.c @@ -557,13 +557,17 @@ bool load_include_configs(const char *path, struct sway_config *config, const char *parent_dir = dirname(parent_path); if (chdir(parent_dir) < 0) { - goto error_chdir; + free(parent_path); + free(wd); + return false; } wordexp_t p; if (wordexp(path, &p, 0) != 0) { - goto error_wordexp; + free(parent_path); + free(wd); + return false; } char **w = p.we_wordv; @@ -571,7 +575,10 @@ bool load_include_configs(const char *path, struct sway_config *config, for (i = 0; i < p.we_wordc; ++i) { bool found = load_include_config(w[i], parent_dir, config, swaynag); if (!found) { - goto error_not_found; + wordfree(&p); + free(parent_path); + free(wd); + return false; } } free(parent_path); @@ -586,16 +593,6 @@ bool load_include_configs(const char *path, struct sway_config *config, free(wd); return true; -error_not_found: - wordfree(&p); -error_wordexp: - if (chdir(wd) < 0) { - sway_log(SWAY_ERROR, "failed to restore working directory"); - } -error_chdir: - free(parent_path); - free(wd); - return false; } void run_deferred_commands(void) { From 82d7a70b03b43e8e417aeb84803ef04f452bce13 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Thu, 7 Feb 2019 21:22:53 -0500 Subject: [PATCH 0025/2054] Revert "Return false if config could not be loaded." This reverts commit 855368b67e8b7d0b1dd035bde7f9119d37b35e5d. --- sway/config.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/sway/config.c b/sway/config.c index 18df2add2..ae8d11e35 100644 --- a/sway/config.c +++ b/sway/config.c @@ -573,13 +573,7 @@ bool load_include_configs(const char *path, struct sway_config *config, char **w = p.we_wordv; size_t i; for (i = 0; i < p.we_wordc; ++i) { - bool found = load_include_config(w[i], parent_dir, config, swaynag); - if (!found) { - wordfree(&p); - free(parent_path); - free(wd); - return false; - } + load_include_config(w[i], parent_dir, config, swaynag); } free(parent_path); wordfree(&p); From f5190d1f797f5a9d0596ab6d31240a0e179ac457 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 6 Feb 2019 22:00:29 -0500 Subject: [PATCH 0026/2054] bar_cmd_modifier: add support for none sway-bar(5) documents `modifier none`, which comes from i3. This implements the functionality for `modifier none` since it was not previously implemented. The bar modifier toggles visibility of the bar when the bar mode is set to hide. When the bar modifier is set to `none`, the ability to toggle visibility of the bar will be disabled. --- sway/commands/bar/modifier.c | 29 ++++++++++++++++++----------- sway/input/keyboard.c | 3 ++- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/sway/commands/bar/modifier.c b/sway/commands/bar/modifier.c index c95250d11..d25d01d4b 100644 --- a/sway/commands/bar/modifier.c +++ b/sway/commands/bar/modifier.c @@ -15,19 +15,26 @@ struct cmd_results *bar_cmd_modifier(int argc, char **argv) { } uint32_t mod = 0; - list_t *split = split_string(argv[0], "+"); - for (int i = 0; i < split->length; ++i) { - uint32_t tmp_mod; - if ((tmp_mod = get_modifier_mask_by_name(split->items[i])) > 0) { - mod |= tmp_mod; - } else { - error = cmd_results_new(CMD_INVALID, - "Unknown modifier '%s'", (char *)split->items[i]); - list_free_items_and_destroy(split); - return error; + if (strcmp(argv[0], "none") != 0) { + list_t *split = split_string(argv[0], "+"); + for (int i = 0; i < split->length; ++i) { + uint32_t tmp_mod; + if ((tmp_mod = get_modifier_mask_by_name(split->items[i])) > 0) { + mod |= tmp_mod; + } else if (strcmp(split->items[i], "none") == 0) { + error = cmd_results_new(CMD_INVALID, + "none cannot be used along with other modifiers"); + list_free_items_and_destroy(split); + return error; + } else { + error = cmd_results_new(CMD_INVALID, + "Unknown modifier '%s'", (char *)split->items[i]); + list_free_items_and_destroy(split); + return error; + } } + list_free_items_and_destroy(split); } - list_free_items_and_destroy(split); config->current_bar->modifier = mod; sway_log(SWAY_DEBUG, "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]); diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 12c57366c..efd27f70d 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -425,7 +425,8 @@ static void determine_bar_visibility(uint32_t modifiers) { for (int i = 0; i < config->bars->length; ++i) { struct bar_config *bar = config->bars->items[i]; if (strcmp(bar->mode, bar->hidden_state) == 0) { // both are "hide" - bool should_be_visible = (~modifiers & bar->modifier) == 0; + bool should_be_visible = + bar->modifier != 0 && (~modifiers & bar->modifier) == 0; if (bar->visible_by_modifier != should_be_visible) { bar->visible_by_modifier = should_be_visible; ipc_event_bar_state_update(bar); From 995b6edab6fab33668a9bd320631b6ab65dc10c6 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 8 Feb 2019 22:29:35 -0500 Subject: [PATCH 0027/2054] container_at_stacked: skip titles when zero pixels It is possible to make the title bars have a zero pixel height while stacked, by using a blank font and no padding. This causes a division by zero when attempting to calculate the child index in container_at_stacked, which then results in a segfault when attempting to access the child at that bad index (INT_MIN). This just skips the check to see if the cursor is over a title bar of a child of a stacked container when the title bar height is zero since there will be no title bars. --- sway/tree/container.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 0ebdc51de..9358dad76 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -251,10 +251,12 @@ static struct sway_container *container_at_stacked(struct sway_node *parent, // Title bars int title_height = container_titlebar_height(); - int child_index = (ly - box.y) / title_height; - if (child_index < children->length) { - struct sway_container *child = children->items[child_index]; - return child; + if (title_height > 0) { + int child_index = (ly - box.y) / title_height; + if (child_index < children->length) { + struct sway_container *child = children->items[child_index]; + return child; + } } // Surfaces From 605e515a9324c3e314939d21f1ea13b29881e100 Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Sun, 10 Feb 2019 17:04:12 +0100 Subject: [PATCH 0028/2054] fix double free for mode toggle if bar was invisible If the bar was set to "invisible" and subsequently "toggle" was send twice, the new mode was never set and the bar->mode was double freed. Fix this by not requiring the bar->mode to be "hide" and instead show it unconditionally, because it was either hidden or invisible. Fixes #3637 --- sway/commands/bar/mode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c index d89ddf24e..68a80abf6 100644 --- a/sway/commands/bar/mode.c +++ b/sway/commands/bar/mode.c @@ -11,7 +11,7 @@ static struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode if (strcasecmp("toggle", mode) == 0 && !config->reading) { if (strcasecmp("dock", bar->mode) == 0) { bar->mode = strdup("hide"); - } else if (strcasecmp("hide", bar->mode) == 0) { + } else{ bar->mode = strdup("dock"); } } else if (strcasecmp("dock", mode) == 0) { From 3952d4f4f3b76290101e0b82e7359d11e79058bb Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 10 Feb 2019 12:36:30 -0500 Subject: [PATCH 0029/2054] input/keyboard: respect solo repeat_{rate,delay} If `repeat_rate` or `repeat_delay` is set without the other being set, the default was being used for both. This changes the logic to respect the value given and use the default for the other when only one is set. --- sway/input/keyboard.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index efd27f70d..00fc6a13f 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -549,13 +549,17 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { wlr_keyboard_led_update(wlr_device->keyboard, leds); } - if (input_config && input_config->repeat_delay != INT_MIN - && input_config->repeat_rate != INT_MIN) { - wlr_keyboard_set_repeat_info(wlr_device->keyboard, - input_config->repeat_rate, input_config->repeat_delay); - } else { - wlr_keyboard_set_repeat_info(wlr_device->keyboard, 25, 600); + int repeat_rate = 25; + if (input_config && input_config->repeat_rate != INT_MIN) { + repeat_rate = input_config->repeat_rate; } + int repeat_delay = 600; + if (input_config && input_config->repeat_delay != INT_MIN) { + repeat_delay = input_config->repeat_delay; + } + wlr_keyboard_set_repeat_info(wlr_device->keyboard, repeat_rate, + repeat_delay); + xkb_context_unref(context); struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; wlr_seat_set_keyboard(seat, wlr_device); From 418c19fe528190918d41aefed46abe02c8b6834f Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 10 Feb 2019 15:23:50 -0500 Subject: [PATCH 0030/2054] view: remove pointer constraints on unmap If the view has any pointer constraints, ensure they are removed before the view is unmapped and the surface is no longer tied to the view. --- sway/tree/view.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sway/tree/view.c b/sway/tree/view.c index 612cf96ae..943734dc7 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -664,6 +664,13 @@ void view_unmap(struct sway_view *view) { struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { seat->cursor->image_surface = NULL; + if (seat->cursor->active_constraint) { + struct wlr_surface *constrain_surface = + seat->cursor->active_constraint->surface; + if (view_from_wlr_surface(constrain_surface) == view) { + sway_cursor_constrain(seat->cursor, NULL); + } + } seat_consider_warp_to_focus(seat); } From a64a3ee6bbd3b880c32bbd9c9a38420d04748dd2 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 10 Feb 2019 15:37:24 -0500 Subject: [PATCH 0031/2054] ipc: handle unnamed xkb_active_layout_name If the active xkb_layout does not have a name, use `NULL` instead of `json_object_new_string(NULL)`. This also makes it so swaymsg will pretty print this as `(unnamed)`. --- sway/ipc-json.c | 2 +- swaymsg/main.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index e10989420..23016dbd5 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -597,7 +597,7 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { const char *layout = xkb_keymap_layout_get_name(keymap, layout_idx); json_object_object_add(object, "xkb_active_layout_name", - json_object_new_string(layout)); + layout ? json_object_new_string(layout) : NULL); break; } } diff --git a/swaymsg/main.c b/swaymsg/main.c index c84f5671f..716d2d2ed 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -135,8 +135,8 @@ static void pretty_print_input(json_object *i) { json_object_get_int(vendor)); if (json_object_object_get_ex(i, "xkb_active_layout_name", &kbdlayout)) { - printf(" Active Keyboard Layout: %s\n", - json_object_get_string(kbdlayout)); + const char *layout = json_object_get_string(kbdlayout); + printf(" Active Keyboard Layout: %s\n", layout ? layout : "(unnamed)"); } if (json_object_object_get_ex(i, "libinput_send_events", &events)) { From d2c896ed84534f5c4dc7e8c30c3e92be4081fab1 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 11 Feb 2019 03:26:12 -0500 Subject: [PATCH 0032/2054] fix misc memory leaks This fixes a few misc memory leaks reported by asan: - Items of `config->config_chain` are now freed instead of just the list itself - `bar->swaybar_command` is now freed - The result returned by a seat subcommand is now returned instead of leaked --- sway/commands/seat.c | 2 +- sway/config.c | 2 +- sway/config/bar.c | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sway/commands/seat.c b/sway/commands/seat.c index 5b23dcc6a..aa36ba955 100644 --- a/sway/commands/seat.c +++ b/sway/commands/seat.c @@ -50,5 +50,5 @@ struct cmd_results *cmd_seat(int argc, char **argv) { } config->handler_context.seat_config = NULL; - return cmd_results_new(CMD_SUCCESS, NULL); + return res ? res : cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/config.c b/sway/config.c index ae8d11e35..cd2d18a27 100644 --- a/sway/config.c +++ b/sway/config.c @@ -118,7 +118,7 @@ void free_config(struct sway_config *config) { } list_free(config->no_focus); list_free(config->active_bar_modifiers); - list_free(config->config_chain); + list_free_items_and_destroy(config->config_chain); list_free(config->command_policies); list_free(config->feature_policies); list_free(config->ipc_policies); diff --git a/sway/config/bar.c b/sway/config/bar.c index bafef307c..2e28fa1ef 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -46,6 +46,7 @@ void free_bar_config(struct bar_config *bar) { free(bar->position); free(bar->hidden_state); free(bar->status_command); + free(bar->swaybar_command); free(bar->font); free(bar->separator_symbol); for (int i = 0; i < bar->bindings->length; i++) { From 2540e8ea792f7b2a54698aba34b3bf11161fe102 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 11 Feb 2019 03:59:11 -0500 Subject: [PATCH 0033/2054] Abort early when XDG_RUNTIME_DIR is not set This aborts sway and displays an error message about XDG_RUNTIME_DIR not being set without initializing the wl_display or logging any other information. --- sway/main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sway/main.c b/sway/main.c index 12f92bd80..b118a1823 100644 --- a/sway/main.c +++ b/sway/main.c @@ -299,6 +299,14 @@ int main(int argc, char **argv) { } } + // Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the + // clear error message (when not running as an IPC client). + if (!getenv("XDG_RUNTIME_DIR") && optind == argc) { + fprintf(stderr, + "XDG_RUNTIME_DIR is not set in the environment. Aborting.\n"); + exit(EXIT_FAILURE); + } + // As the 'callback' function for wlr_log is equivalent to that for // sway, we do not need to override it. if (debug) { From fb4f29289fd8d1f94975e64fded6657222ed5390 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 10 Feb 2019 20:12:51 -0500 Subject: [PATCH 0034/2054] workspace_get_initial_output: handle focused layer When a layer surface is focused, `seat_get_focused_workspace` will be NULL. This changes `workspace_get_initial_output` to use output of the focus inactive. If the focus inactive is also NULL, then either the first output or the noop output will be used as fallbacks. --- sway/tree/workspace.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 8b3eb2ad2..b97809223 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -42,10 +42,16 @@ struct sway_output *workspace_get_initial_output(const char *name) { } } } - // Otherwise put it on the focused output + // Otherwise try to put it on the focused output struct sway_seat *seat = input_manager_current_seat(); - struct sway_workspace *focus = seat_get_focused_workspace(seat); - return focus->output; + struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); + if (focus && focus->type == N_WORKSPACE) { + return focus->sway_workspace->output; + } else if (focus && focus->type == N_CONTAINER) { + return focus->sway_container->workspace->output; + } + // Fallback to the first output or noop output for headless + return root->outputs->length ? root->outputs->items[0] : root->noop_output; } static void prevent_invalid_outer_gaps(struct sway_workspace *ws) { From 743d34538333e4111a71fad32668335dff9a4822 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 3 Feb 2019 14:00:37 -0500 Subject: [PATCH 0035/2054] Merge pull request #3563 from vilhalmer/fix-wildcard-seat-constrain-crashes-during-reconfig Fix wildcard seat constrain crashes during reconfig --- sway/config.c | 5 +++++ sway/input/cursor.c | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/sway/config.c b/sway/config.c index 7cb27d955..54d29fc90 100644 --- a/sway/config.c +++ b/sway/config.c @@ -141,6 +141,11 @@ static void destroy_removed_seats(struct sway_config *old_config, int i; for (i = 0; i < old_config->seat_configs->length; i++) { seat_config = old_config->seat_configs->items[i]; + // Skip the wildcard seat config, it won't have a matching real seat. + if (strcmp(seat_config->name, "*") == 0) { + continue; + } + /* Also destroy seats that aren't present in new config */ if (new_config && list_seq_find(new_config->seat_configs, seat_name_cmp, seat_config->name) < 0) { diff --git a/sway/input/cursor.c b/sway/input/cursor.c index c38d8d3a1..01aae79d4 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -1459,7 +1459,11 @@ void handle_pointer_constraint(struct wl_listener *listener, void *data) { void sway_cursor_constrain(struct sway_cursor *cursor, struct wlr_pointer_constraint_v1 *constraint) { struct seat_config *config = seat_get_config(cursor->seat); - if (config->allow_constrain == CONSTRAIN_DISABLE) { + if (!config) { + config = seat_get_config_by_name("*"); + } + + if (!config || config->allow_constrain == CONSTRAIN_DISABLE) { return; } From 130626a5db87c43a38b300700d013313f99ef4c6 Mon Sep 17 00:00:00 2001 From: vilhalmer Date: Sun, 3 Feb 2019 12:12:40 -0500 Subject: [PATCH 0036/2054] Focus ws inactive node with focus_follows_mouse --- sway/input/cursor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 01aae79d4..1bf548db7 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -362,7 +362,7 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, struct sway_output *focused_output = node_get_output(focus); struct sway_output *output = node_get_output(node); if (output != focused_output) { - seat_set_focus(seat, node); + seat_set_focus(seat, seat_get_focus_inactive(seat, node)); } } else if (node->type == N_CONTAINER && node->sway_container->view) { // Focus node if the following are true: From 4272bf127439a2d8515e934821a9744537260b9d Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 3 Feb 2019 13:56:05 -0500 Subject: [PATCH 0037/2054] seat_cmd_cursor: do not create non-existing seat If a seat does not exist in seat_cmd_cursor, do not create it. A seat without any attachments is useless since it will have no capabilities. This changes `input_manager_get_seat` to have an additional argument that dictates whether or not to create the seat if it does not exist. --- include/sway/input/input-manager.h | 2 +- sway/commands/seat/cursor.c | 5 +++-- sway/config.c | 6 ++++-- sway/input/input-manager.c | 9 +++++---- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index 8e8bf1f20..e166a2377 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h @@ -45,7 +45,7 @@ void input_manager_apply_seat_config(struct seat_config *seat_config); struct sway_seat *input_manager_get_default_seat(void); -struct sway_seat *input_manager_get_seat(const char *seat_name); +struct sway_seat *input_manager_get_seat(const char *seat_name, bool create); /** * If none of the seat configs have a fallback setting (either true or false), diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c index 4f805b227..0c7609eaf 100644 --- a/sway/commands/seat/cursor.c +++ b/sway/commands/seat/cursor.c @@ -61,9 +61,10 @@ struct cmd_results *seat_cmd_cursor(int argc, char **argv) { } if (strcmp(sc->name, "*") != 0) { - struct sway_seat *seat = input_manager_get_seat(sc->name); + struct sway_seat *seat = input_manager_get_seat(sc->name, false); if (!seat) { - return cmd_results_new(CMD_FAILURE, "Failed to get seat"); + return cmd_results_new(CMD_FAILURE, + "Seat %s does not exist", sc->name); } error = handle_command(seat->cursor, argc, argv); } else { diff --git a/sway/config.c b/sway/config.c index 54d29fc90..ee1c42df3 100644 --- a/sway/config.c +++ b/sway/config.c @@ -149,8 +149,10 @@ static void destroy_removed_seats(struct sway_config *old_config, /* Also destroy seats that aren't present in new config */ if (new_config && list_seq_find(new_config->seat_configs, seat_name_cmp, seat_config->name) < 0) { - seat = input_manager_get_seat(seat_config->name); - seat_destroy(seat); + seat = input_manager_get_seat(seat_config->name, false); + if (seat) { + seat_destroy(seat); + } } } } diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 8d263e06b..f99fc395b 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -31,10 +31,10 @@ struct sway_seat *input_manager_current_seat(void) { } struct sway_seat *input_manager_get_default_seat(void) { - return input_manager_get_seat(DEFAULT_SEAT); + return input_manager_get_seat(DEFAULT_SEAT, true); } -struct sway_seat *input_manager_get_seat(const char *seat_name) { +struct sway_seat *input_manager_get_seat(const char *seat_name, bool create) { struct sway_seat *seat = NULL; wl_list_for_each(seat, &server.input->seats, link) { if (strcmp(seat->wlr_seat->name, seat_name) == 0) { @@ -42,7 +42,7 @@ struct sway_seat *input_manager_get_seat(const char *seat_name) { } } - return seat_create(seat_name); + return create ? seat_create(seat_name) : NULL; } char *input_device_get_identifier(struct wlr_input_device *device) { @@ -674,7 +674,8 @@ void input_manager_apply_seat_config(struct seat_config *seat_config) { seat_apply_config(seat, sc); } } else { - struct sway_seat *seat = input_manager_get_seat(seat_config->name); + struct sway_seat *seat = + input_manager_get_seat(seat_config->name, true); if (!seat) { return; } From df3ea6a55f4a163ce5d1f241060a1308198ff27a Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 5 Feb 2019 01:59:40 -0500 Subject: [PATCH 0038/2054] load_include_configs: fix wordexp fail condition This fixes the failure condition for the wordexp call in load_include_configs. The only success value is zero. Since the error codes are positive, having the check be less than zero was causing segfaults on failure when accessing the words. --- sway/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/config.c b/sway/config.c index ee1c42df3..0c23fad84 100644 --- a/sway/config.c +++ b/sway/config.c @@ -557,7 +557,7 @@ bool load_include_configs(const char *path, struct sway_config *config, wordexp_t p; - if (wordexp(path, &p, 0) < 0) { + if (wordexp(path, &p, 0) != 0) { free(parent_path); free(wd); return false; From 66062f53de5685eaf9ba28de6e08f236372f04b1 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 5 Feb 2019 00:31:06 -0500 Subject: [PATCH 0039/2054] swaynag: remove trailing newlines in config Now that swaynag uses getline (instead of the old readline), the trailing newline characters have to be removed when reading the config --- swaynag/config.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/swaynag/config.c b/swaynag/config.c index 40f3f65e8..200611f4b 100644 --- a/swaynag/config.c +++ b/swaynag/config.c @@ -348,6 +348,10 @@ int swaynag_load_config(char *path, struct swaynag *swaynag, list_t *types) { continue; } + if (line[nread - 1] == '\n') { + line[nread - 1] = '\0'; + } + if (line[0] == '[') { char *close = strchr(line, ']'); if (!close) { From 5d770d028a0fa29861ef07e04d66ae28cbe9c6a4 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 4 Feb 2019 23:39:37 -0500 Subject: [PATCH 0040/2054] execute_command: dont strip quotes for exec_always This removes quote stripping for `exec_always` in `execute_command`. Since `exec_always` commands will be deferred in the config and processed by `execute_command`, the quotes need to be left intact like they are for `exec`. --- sway/commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/commands.c b/sway/commands.c index dd994fa15..82f41545d 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -254,7 +254,7 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, //TODO better handling of argv int argc; char **argv = split_args(cmd, &argc); - if (strcmp(argv[0], "exec") != 0) { + if (!strcmp(argv[0], "exec") && !strcmp(argv[0], "exec_always")) { int i; for (i = 1; i < argc; ++i) { if (*argv[i] == '\"' || *argv[i] == '\'') { From 610794d4e3b2d8434f4ea3ef518bcdfc8a2dc28c Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 5 Feb 2019 12:52:02 +0100 Subject: [PATCH 0041/2054] Fix quote stripping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's not use !strcmp(…) anymore. --- sway/commands.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/commands.c b/sway/commands.c index 82f41545d..3fc4f86e4 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -254,7 +254,8 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, //TODO better handling of argv int argc; char **argv = split_args(cmd, &argc); - if (!strcmp(argv[0], "exec") && !strcmp(argv[0], "exec_always")) { + if (strcmp(argv[0], "exec") != 0 && + strcmp(argv[0], "exec_always") != 0) { int i; for (i = 1; i < argc; ++i) { if (*argv[i] == '\"' || *argv[i] == '\'') { From 41e10db0de243c903e28ae8f0b669ff681974e69 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 5 Feb 2019 07:39:21 -0500 Subject: [PATCH 0042/2054] IPC_COMMAND: split on newline This splits commands given in IPC_COMMAND on newline to match i3's behavior. --- sway/ipc-server.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sway/ipc-server.c b/sway/ipc-server.c index d1920cfd4..eb6f159d6 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -595,6 +595,16 @@ void ipc_client_handle_command(struct ipc_client *client) { switch (client->current_command) { case IPC_COMMAND: { + char *line = strtok(buf, "\n"); + while (line) { + size_t line_length = strlen(line); + if (line + line_length >= buf + client->payload_length) { + break; + } + line[line_length] = ';'; + line = strtok(NULL, "\n"); + } + list_t *res_list = execute_command(buf, NULL, NULL); transaction_commit_dirty(); char *json = cmd_results_to_json(res_list); From 9346ed1805dab798fe9f6edcb47e003081722d79 Mon Sep 17 00:00:00 2001 From: Michael Vetter Date: Tue, 5 Feb 2019 14:32:05 +0100 Subject: [PATCH 0043/2054] Set version in project file Let's set the version in the meson file instead of declaring it outside. In case git is installed we use the git hash as version. Instead it isn't (like on a clean build system), let's use the version defined in the project. --- meson.build | 12 ++++-------- meson_options.txt | 1 - 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/meson.build b/meson.build index 766bf012f..c50fab176 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,7 @@ project( 'sway', 'c', + version: '1.0', license: 'MIT', meson_version: '>=0.48.0', default_options: [ @@ -127,17 +128,12 @@ endif add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir)), language : 'c') -version = get_option('sway-version') -if version != '' - version = '"@0@"'.format(version) -else - if not git.found() - error('git is required to make the version string') - endif - +if git.found() git_commit_hash = run_command([git.path(), 'describe', '--always', '--tags']).stdout().strip() git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']).stdout().strip() version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash, git_branch) +else + version = '"@0@"'.format(meson.project_version()) endif add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') diff --git a/meson_options.txt b/meson_options.txt index 04b29e17a..d3667acfd 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,4 +1,3 @@ -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.') From 0c091bed769b43089191d5f62cdd81ef399cfd98 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 5 Feb 2019 08:35:00 -0500 Subject: [PATCH 0044/2054] cmd_workspace_gaps: fix double free on bad amount This fixes a double free in cmd_workspace_gaps when the amount given is invalid. The end pointer from strtol is part of the argument and should not be freed. Freeing the end pointer could result in a double free or bad free depending on whether or not the end pointer was at the start of the argument --- sway/commands/workspace.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index c5d6435a4..65a3f4075 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -76,7 +76,6 @@ static struct cmd_results *cmd_workspace_gaps(int argc, char **argv, char *end; int amount = strtol(argv[gaps_location + 2], &end, 10); if (strlen(end)) { - free(end); return cmd_results_new(CMD_FAILURE, expected); } From 2112f0aa2fcdf278dd3dccecc1a8261d3c97b44b Mon Sep 17 00:00:00 2001 From: Connor E <38229097+c-edw@users.noreply.github.com> Date: Tue, 5 Feb 2019 14:37:22 +0000 Subject: [PATCH 0045/2054] If validating the config, do it as early as possible. --- sway/main.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/sway/main.c b/sway/main.c index a3198af10..b3ffdd833 100644 --- a/sway/main.c +++ b/sway/main.c @@ -312,6 +312,18 @@ int main(int argc, char **argv) { wlr_log_init(WLR_ERROR, NULL); } + log_kernel(); + log_distro(); + log_env(); + detect_proprietary(allow_unsupported_gpu); + detect_raspi(); + + if (validate) { + bool valid = load_main_config(config_path, false, true); + free(config_path); + return valid ? 0 : 1; + } + if (optind < argc) { // Behave as IPC client if (optind != 1) { sway_log(SWAY_ERROR, "Don't use options with the IPC client"); @@ -334,11 +346,6 @@ int main(int argc, char **argv) { return 1; } - log_kernel(); - log_distro(); - detect_proprietary(allow_unsupported_gpu); - detect_raspi(); - if (!drop_permissions()) { server_fini(&server); exit(EXIT_FAILURE); @@ -359,12 +366,6 @@ int main(int argc, char **argv) { } ipc_init(&server); - log_env(); - - if (validate) { - bool valid = load_main_config(config_path, false, true); - return valid ? 0 : 1; - } setenv("WAYLAND_DISPLAY", server.socket, true); if (!load_main_config(config_path, false, false)) { From cc5139d52ace3bbfa8fd706fe408551c11766f4c Mon Sep 17 00:00:00 2001 From: Connor E <38229097+c-edw@users.noreply.github.com> Date: Tue, 5 Feb 2019 15:39:22 +0000 Subject: [PATCH 0046/2054] Initialize server so input manager is available. --- sway/main.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sway/main.c b/sway/main.c index b3ffdd833..67d0f7992 100644 --- a/sway/main.c +++ b/sway/main.c @@ -318,12 +318,6 @@ int main(int argc, char **argv) { detect_proprietary(allow_unsupported_gpu); detect_raspi(); - if (validate) { - bool valid = load_main_config(config_path, false, true); - free(config_path); - return valid ? 0 : 1; - } - if (optind < argc) { // Behave as IPC client if (optind != 1) { sway_log(SWAY_ERROR, "Don't use options with the IPC client"); @@ -365,6 +359,12 @@ int main(int argc, char **argv) { return 1; } + if (validate) { + bool valid = load_main_config(config_path, false, true); + free(config_path); + return valid ? 0 : 1; + } + ipc_init(&server); setenv("WAYLAND_DISPLAY", server.socket, true); From d4e49a49ebea61b2e078a494eb94f02b03a6a1cf Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 5 Feb 2019 14:13:27 -0500 Subject: [PATCH 0047/2054] output_cmd_background: fix no file + valid mode If output_cmd_background is given a valid mode as the first argument, then there is no file given and an error should be returned. join_args should not be called with an argc of zero since it sets the last character to the null terminator. With an argc of zero, the length is zero causing a heap buffer overflow when setting the byte before the start of argv to '\0'. This probably will not ever generate a segfault, but may cause data corruption to whatever is directly before it in memory. To make other such cases easier to detect, this also adds a sway_assert in join_args when argc is zero. --- common/stringop.c | 3 +++ sway/commands/output/background.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/common/stringop.c b/common/stringop.c index 8af0d60fa..709be6842 100644 --- a/common/stringop.c +++ b/common/stringop.c @@ -258,6 +258,9 @@ int unescape_string(char *string) { } char *join_args(char **argv, int argc) { + if (!sway_assert(argc > 0, "argc should be positive")) { + return NULL; + } int len = 0, i; for (i = 0; i < argc; ++i) { len += strlen(argv[i]) + 1; diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index f65904bb8..5a15ed0fc 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -61,6 +61,9 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Missing background scaling mode."); } + if (j == 0) { + return cmd_results_new(CMD_INVALID, "Missing background file"); + } wordexp_t p = {0}; char *src = join_args(argv, j); From 48511c3b35be8251517ab50ceea6dd2b33c32b86 Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 5 Feb 2019 19:07:01 +0100 Subject: [PATCH 0048/2054] Fix close_popups for xdg-shell wlr_xdg_popup_destroy will destroy popups, so we need to walk the tree carefully. It's enough to just destroy all direct children, since destroying the parent will also destroy all children. --- sway/desktop/xdg_shell.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 152bd26f0..ce6fe41a1 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -236,19 +236,11 @@ static void _close(struct sway_view *view) { } } -static void close_popups_iterator(struct wlr_surface *surface, - int sx, int sy, void *data) { - struct wlr_xdg_surface *xdg_surface = - wlr_xdg_surface_from_wlr_surface(surface); - if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP - && xdg_surface->popup) { - wlr_xdg_popup_destroy(xdg_surface); - } -} - static void close_popups(struct sway_view *view) { - wlr_xdg_surface_for_each_popup(view->wlr_xdg_surface, - close_popups_iterator, NULL); + struct wlr_xdg_popup *popup, *tmp; + wl_list_for_each_safe(popup, tmp, &view->wlr_xdg_surface->popups, link) { + wlr_xdg_popup_destroy(popup->base); + } } static void destroy(struct sway_view *view) { From d2d2fc545ac706683a2da0118ec1c4842ed87d7f Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 5 Feb 2019 22:06:42 -0500 Subject: [PATCH 0049/2054] load_main_config: add realpath to config_chain Since `load_include_config` compares against the realpath of a config file when checking if a config has already been added, the main config's realpath has to be added to the config_chain. --- sway/config.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/sway/config.c b/sway/config.c index 0c23fad84..18fb69d97 100644 --- a/sway/config.c +++ b/sway/config.c @@ -379,6 +379,14 @@ bool load_main_config(const char *file, bool is_active, bool validating) { path = get_config_path(); } + char *real_path = realpath(path, NULL); + if (real_path == NULL) { + sway_log(SWAY_DEBUG, "%s not found.", path); + free(path); + return false; + } + free(path); + struct sway_config *old_config = config; config = calloc(1, sizeof(struct sway_config)); if (!config) { @@ -401,8 +409,8 @@ bool load_main_config(const char *file, bool is_active, bool validating) { input_manager_reset_all_inputs(); } - config->current_config_path = path; - list_add(config->config_chain, path); + config->current_config_path = real_path; + list_add(config->config_chain, real_path); config->reading = true; @@ -454,7 +462,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { } */ - success = success && load_config(path, config, + success = success && load_config(real_path, config, &config->swaynag_config_errors); if (validating) { From 6ade4bd7dd1cc5eed68842b407aa3f690b1f8e21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 6 Feb 2019 15:16:30 +0100 Subject: [PATCH 0050/2054] ipc_has_event_listeners: fix inverted check of subscribed_events subscribed_events is a bit mask, with each *set* bit representing an event the client has subscribed to. --- sway/ipc-server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/ipc-server.c b/sway/ipc-server.c index eb6f159d6..df57cba51 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -264,7 +264,7 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { static bool ipc_has_event_listeners(enum ipc_command_type event) { for (int i = 0; i < ipc_client_list->length; i++) { struct ipc_client *client = ipc_client_list->items[i]; - if ((client->subscribed_events & event_mask(event)) == 0) { + if ((client->subscribed_events & event_mask(event)) != 0) { return true; } } From a2f661dceb5baa20bd5d329fbb6fb2d75a4a09f8 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 6 Feb 2019 13:18:46 -0500 Subject: [PATCH 0051/2054] Clarify error for options and positional args When both options and positional arguments are given, sway would print the error `Don't use options with the IPC client`. Over the past several months, it seems like users are including this error message in issues instead of a debug log due to not understanding that the error message means there is an issue with their command. This makes the error message more verbose and will hopefully make it so more users understand that the message is not a bug in sway, but with the command used. --- sway/main.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sway/main.c b/sway/main.c index 67d0f7992..12f92bd80 100644 --- a/sway/main.c +++ b/sway/main.c @@ -320,7 +320,13 @@ int main(int argc, char **argv) { if (optind < argc) { // Behave as IPC client if (optind != 1) { - sway_log(SWAY_ERROR, "Don't use options with the IPC client"); + sway_log(SWAY_ERROR, + "Detected both options and positional arguments. If you " + "are trying to use the IPC client, options are not " + "supported. Otherwise, check the provided arguments for " + "issues. See `man 1 sway` or `sway -h` for usage. If you " + "are trying to generate a debug log, use " + "`sway -d 2>sway.log`."); exit(EXIT_FAILURE); } if (!drop_permissions()) { From 8229814e7a8122b022e5f92fb4d925b00d0d4b1c Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Thu, 7 Feb 2019 02:16:38 -0500 Subject: [PATCH 0052/2054] load_main_config: use given path, store realpath Since `load_include_config` compares against the realpath of a config file when checking if a config has already been added, the main config's realpath has to be added to the config_chain. However, includes from the main config should be processed relative to the path given to allow for symbolic links. This stores the realpath in `config->config_chain`, but uses the given path for all other operations. --- sway/config.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sway/config.c b/sway/config.c index 18fb69d97..ae8d11e35 100644 --- a/sway/config.c +++ b/sway/config.c @@ -385,7 +385,6 @@ bool load_main_config(const char *file, bool is_active, bool validating) { free(path); return false; } - free(path); struct sway_config *old_config = config; config = calloc(1, sizeof(struct sway_config)); @@ -409,7 +408,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { input_manager_reset_all_inputs(); } - config->current_config_path = real_path; + config->current_config_path = path; list_add(config->config_chain, real_path); config->reading = true; @@ -462,7 +461,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { } */ - success = success && load_config(real_path, config, + success = success && load_config(path, config, &config->swaynag_config_errors); if (validating) { From 32f790bd15e1a96f214ac2783bf66837dc0e9682 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Thu, 7 Feb 2019 01:52:58 -0500 Subject: [PATCH 0053/2054] seat_configure_tablet_tool: configure xcursor Since a tablet tool provides the WL_SEAT_CAPABILITY_POINTER capability, sway will attempt to use the xcursor manager to set a cursor image. If the tablet tool was the first (and possibly only) device to provide the capability for the seat, the xcursor manager was not being configured before attempting to set a cursor image. This was due to `seat_configure_xcursor` only being called in `seat_configure_pointer`. Since the xcursor manager was NULL in this case, it would cause a segfault when attempting to set a cursor image. This adds a call to `seat_configure_xcursor` in `seat_configure_tablet_tool` to ensure that the seat has a xcursor manager. --- sway/input/seat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/input/seat.c b/sway/input/seat.c index d159da224..18664d7cb 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -525,6 +525,7 @@ static void seat_configure_touch(struct sway_seat *seat, static void seat_configure_tablet_tool(struct sway_seat *seat, struct sway_seat_device *sway_device) { + seat_configure_xcursor(seat); wlr_cursor_attach_input_device(seat->cursor->cursor, sway_device->input_device->wlr_device); seat_apply_input_config(seat, sway_device); From 4339ba6424ddb41e16a4a0674ecdd0dabbf68ab4 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 6 Feb 2019 22:00:29 -0500 Subject: [PATCH 0054/2054] bar_cmd_modifier: add support for none sway-bar(5) documents `modifier none`, which comes from i3. This implements the functionality for `modifier none` since it was not previously implemented. The bar modifier toggles visibility of the bar when the bar mode is set to hide. When the bar modifier is set to `none`, the ability to toggle visibility of the bar will be disabled. --- sway/commands/bar/modifier.c | 29 ++++++++++++++++++----------- sway/input/keyboard.c | 3 ++- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/sway/commands/bar/modifier.c b/sway/commands/bar/modifier.c index c95250d11..d25d01d4b 100644 --- a/sway/commands/bar/modifier.c +++ b/sway/commands/bar/modifier.c @@ -15,19 +15,26 @@ struct cmd_results *bar_cmd_modifier(int argc, char **argv) { } uint32_t mod = 0; - list_t *split = split_string(argv[0], "+"); - for (int i = 0; i < split->length; ++i) { - uint32_t tmp_mod; - if ((tmp_mod = get_modifier_mask_by_name(split->items[i])) > 0) { - mod |= tmp_mod; - } else { - error = cmd_results_new(CMD_INVALID, - "Unknown modifier '%s'", (char *)split->items[i]); - list_free_items_and_destroy(split); - return error; + if (strcmp(argv[0], "none") != 0) { + list_t *split = split_string(argv[0], "+"); + for (int i = 0; i < split->length; ++i) { + uint32_t tmp_mod; + if ((tmp_mod = get_modifier_mask_by_name(split->items[i])) > 0) { + mod |= tmp_mod; + } else if (strcmp(split->items[i], "none") == 0) { + error = cmd_results_new(CMD_INVALID, + "none cannot be used along with other modifiers"); + list_free_items_and_destroy(split); + return error; + } else { + error = cmd_results_new(CMD_INVALID, + "Unknown modifier '%s'", (char *)split->items[i]); + list_free_items_and_destroy(split); + return error; + } } + list_free_items_and_destroy(split); } - list_free_items_and_destroy(split); config->current_bar->modifier = mod; sway_log(SWAY_DEBUG, "Show/Hide the bar when pressing '%s' in hide mode.", argv[0]); diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 12c57366c..efd27f70d 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -425,7 +425,8 @@ static void determine_bar_visibility(uint32_t modifiers) { for (int i = 0; i < config->bars->length; ++i) { struct bar_config *bar = config->bars->items[i]; if (strcmp(bar->mode, bar->hidden_state) == 0) { // both are "hide" - bool should_be_visible = (~modifiers & bar->modifier) == 0; + bool should_be_visible = + bar->modifier != 0 && (~modifiers & bar->modifier) == 0; if (bar->visible_by_modifier != should_be_visible) { bar->visible_by_modifier = should_be_visible; ipc_event_bar_state_update(bar); From 243d5a3a435c2786ecfa305e30fbca658e1861ff Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 8 Feb 2019 22:29:35 -0500 Subject: [PATCH 0055/2054] container_at_stacked: skip titles when zero pixels It is possible to make the title bars have a zero pixel height while stacked, by using a blank font and no padding. This causes a division by zero when attempting to calculate the child index in container_at_stacked, which then results in a segfault when attempting to access the child at that bad index (INT_MIN). This just skips the check to see if the cursor is over a title bar of a child of a stacked container when the title bar height is zero since there will be no title bars. --- sway/tree/container.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 0ebdc51de..9358dad76 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -251,10 +251,12 @@ static struct sway_container *container_at_stacked(struct sway_node *parent, // Title bars int title_height = container_titlebar_height(); - int child_index = (ly - box.y) / title_height; - if (child_index < children->length) { - struct sway_container *child = children->items[child_index]; - return child; + if (title_height > 0) { + int child_index = (ly - box.y) / title_height; + if (child_index < children->length) { + struct sway_container *child = children->items[child_index]; + return child; + } } // Surfaces From 95e16bb744679296aaab5c0e9b8f3995fb2b4be4 Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Sun, 10 Feb 2019 17:04:12 +0100 Subject: [PATCH 0056/2054] fix double free for mode toggle if bar was invisible If the bar was set to "invisible" and subsequently "toggle" was send twice, the new mode was never set and the bar->mode was double freed. Fix this by not requiring the bar->mode to be "hide" and instead show it unconditionally, because it was either hidden or invisible. Fixes #3637 --- sway/commands/bar/mode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c index d89ddf24e..68a80abf6 100644 --- a/sway/commands/bar/mode.c +++ b/sway/commands/bar/mode.c @@ -11,7 +11,7 @@ static struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode if (strcasecmp("toggle", mode) == 0 && !config->reading) { if (strcasecmp("dock", bar->mode) == 0) { bar->mode = strdup("hide"); - } else if (strcasecmp("hide", bar->mode) == 0) { + } else{ bar->mode = strdup("dock"); } } else if (strcasecmp("dock", mode) == 0) { From 062da8eae76710e7766dceef7556261429123585 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 10 Feb 2019 12:36:30 -0500 Subject: [PATCH 0057/2054] input/keyboard: respect solo repeat_{rate,delay} If `repeat_rate` or `repeat_delay` is set without the other being set, the default was being used for both. This changes the logic to respect the value given and use the default for the other when only one is set. --- sway/input/keyboard.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index efd27f70d..00fc6a13f 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -549,13 +549,17 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { wlr_keyboard_led_update(wlr_device->keyboard, leds); } - if (input_config && input_config->repeat_delay != INT_MIN - && input_config->repeat_rate != INT_MIN) { - wlr_keyboard_set_repeat_info(wlr_device->keyboard, - input_config->repeat_rate, input_config->repeat_delay); - } else { - wlr_keyboard_set_repeat_info(wlr_device->keyboard, 25, 600); + int repeat_rate = 25; + if (input_config && input_config->repeat_rate != INT_MIN) { + repeat_rate = input_config->repeat_rate; } + int repeat_delay = 600; + if (input_config && input_config->repeat_delay != INT_MIN) { + repeat_delay = input_config->repeat_delay; + } + wlr_keyboard_set_repeat_info(wlr_device->keyboard, repeat_rate, + repeat_delay); + xkb_context_unref(context); struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; wlr_seat_set_keyboard(seat, wlr_device); From 4e27785980d2d4bb342019e5e31c4441076bf603 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 10 Feb 2019 15:23:50 -0500 Subject: [PATCH 0058/2054] view: remove pointer constraints on unmap If the view has any pointer constraints, ensure they are removed before the view is unmapped and the surface is no longer tied to the view. --- sway/tree/view.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sway/tree/view.c b/sway/tree/view.c index 612cf96ae..943734dc7 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -664,6 +664,13 @@ void view_unmap(struct sway_view *view) { struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { seat->cursor->image_surface = NULL; + if (seat->cursor->active_constraint) { + struct wlr_surface *constrain_surface = + seat->cursor->active_constraint->surface; + if (view_from_wlr_surface(constrain_surface) == view) { + sway_cursor_constrain(seat->cursor, NULL); + } + } seat_consider_warp_to_focus(seat); } From a4d7ee1923e5847829367d741bea8c954034c312 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 10 Feb 2019 15:37:24 -0500 Subject: [PATCH 0059/2054] ipc: handle unnamed xkb_active_layout_name If the active xkb_layout does not have a name, use `NULL` instead of `json_object_new_string(NULL)`. This also makes it so swaymsg will pretty print this as `(unnamed)`. --- sway/ipc-json.c | 2 +- swaymsg/main.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index e10989420..23016dbd5 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -597,7 +597,7 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { const char *layout = xkb_keymap_layout_get_name(keymap, layout_idx); json_object_object_add(object, "xkb_active_layout_name", - json_object_new_string(layout)); + layout ? json_object_new_string(layout) : NULL); break; } } diff --git a/swaymsg/main.c b/swaymsg/main.c index c84f5671f..716d2d2ed 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -135,8 +135,8 @@ static void pretty_print_input(json_object *i) { json_object_get_int(vendor)); if (json_object_object_get_ex(i, "xkb_active_layout_name", &kbdlayout)) { - printf(" Active Keyboard Layout: %s\n", - json_object_get_string(kbdlayout)); + const char *layout = json_object_get_string(kbdlayout); + printf(" Active Keyboard Layout: %s\n", layout ? layout : "(unnamed)"); } if (json_object_object_get_ex(i, "libinput_send_events", &events)) { From a5a189cc73941c41f7486ba6bb561ca80dcea601 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 11 Feb 2019 03:26:12 -0500 Subject: [PATCH 0060/2054] fix misc memory leaks This fixes a few misc memory leaks reported by asan: - Items of `config->config_chain` are now freed instead of just the list itself - `bar->swaybar_command` is now freed - The result returned by a seat subcommand is now returned instead of leaked --- sway/commands/seat.c | 2 +- sway/config.c | 2 +- sway/config/bar.c | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sway/commands/seat.c b/sway/commands/seat.c index 5b23dcc6a..aa36ba955 100644 --- a/sway/commands/seat.c +++ b/sway/commands/seat.c @@ -50,5 +50,5 @@ struct cmd_results *cmd_seat(int argc, char **argv) { } config->handler_context.seat_config = NULL; - return cmd_results_new(CMD_SUCCESS, NULL); + return res ? res : cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/config.c b/sway/config.c index ae8d11e35..cd2d18a27 100644 --- a/sway/config.c +++ b/sway/config.c @@ -118,7 +118,7 @@ void free_config(struct sway_config *config) { } list_free(config->no_focus); list_free(config->active_bar_modifiers); - list_free(config->config_chain); + list_free_items_and_destroy(config->config_chain); list_free(config->command_policies); list_free(config->feature_policies); list_free(config->ipc_policies); diff --git a/sway/config/bar.c b/sway/config/bar.c index bafef307c..2e28fa1ef 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -46,6 +46,7 @@ void free_bar_config(struct bar_config *bar) { free(bar->position); free(bar->hidden_state); free(bar->status_command); + free(bar->swaybar_command); free(bar->font); free(bar->separator_symbol); for (int i = 0; i < bar->bindings->length; i++) { From 40023d45a18abb0e177818fa60b8a359680eb3c3 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 11 Feb 2019 03:59:11 -0500 Subject: [PATCH 0061/2054] Abort early when XDG_RUNTIME_DIR is not set This aborts sway and displays an error message about XDG_RUNTIME_DIR not being set without initializing the wl_display or logging any other information. --- sway/main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sway/main.c b/sway/main.c index 12f92bd80..b118a1823 100644 --- a/sway/main.c +++ b/sway/main.c @@ -299,6 +299,14 @@ int main(int argc, char **argv) { } } + // Since wayland requires XDG_RUNTIME_DIR to be set, abort with just the + // clear error message (when not running as an IPC client). + if (!getenv("XDG_RUNTIME_DIR") && optind == argc) { + fprintf(stderr, + "XDG_RUNTIME_DIR is not set in the environment. Aborting.\n"); + exit(EXIT_FAILURE); + } + // As the 'callback' function for wlr_log is equivalent to that for // sway, we do not need to override it. if (debug) { From e4e579ea3687342d06915be0df0093987e0e0314 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 10 Feb 2019 20:12:51 -0500 Subject: [PATCH 0062/2054] workspace_get_initial_output: handle focused layer When a layer surface is focused, `seat_get_focused_workspace` will be NULL. This changes `workspace_get_initial_output` to use output of the focus inactive. If the focus inactive is also NULL, then either the first output or the noop output will be used as fallbacks. --- sway/tree/workspace.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 8b3eb2ad2..b97809223 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -42,10 +42,16 @@ struct sway_output *workspace_get_initial_output(const char *name) { } } } - // Otherwise put it on the focused output + // Otherwise try to put it on the focused output struct sway_seat *seat = input_manager_current_seat(); - struct sway_workspace *focus = seat_get_focused_workspace(seat); - return focus->output; + struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); + if (focus && focus->type == N_WORKSPACE) { + return focus->sway_workspace->output; + } else if (focus && focus->type == N_CONTAINER) { + return focus->sway_container->workspace->output; + } + // Fallback to the first output or noop output for headless + return root->outputs->length ? root->outputs->items[0] : root->noop_output; } static void prevent_invalid_outer_gaps(struct sway_workspace *ws) { From 97c89b24b8e4a8e091f6974333457deb73b7800f Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 12 Feb 2019 22:55:23 +0100 Subject: [PATCH 0063/2054] Rebase cursor when a layer surface maps Also removes an extraneous arrange_outputs call, it's already called if necessary in arrange_layers. Updates https://github.com/swaywm/sway/issues/3080 --- include/sway/input/cursor.h | 1 + sway/desktop/layer_shell.c | 7 +++++-- sway/desktop/transaction.c | 9 +-------- sway/input/cursor.c | 13 +++++++++++++ 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 072a56ca1..98eb4679b 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -72,6 +72,7 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat); * This chooses a cursor icon and sends a motion event to the surface. */ void cursor_rebase(struct sway_cursor *cursor); +void cursor_rebase_all(void); void cursor_handle_activity(struct sway_cursor *cursor); void cursor_unhide(struct sway_cursor *cursor); diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index de8db75df..0767247c4 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -6,7 +6,9 @@ #include #include #include +#include "log.h" #include "sway/desktop/transaction.h" +#include "sway/input/cursor.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/layers.h" @@ -14,7 +16,6 @@ #include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/workspace.h" -#include "log.h" static void apply_exclusive(struct wlr_box *usable_area, uint32_t anchor, int32_t exclusive, @@ -302,6 +303,8 @@ static void unmap(struct sway_layer_surface *sway_layer) { if (seat->focused_layer == sway_layer->layer_surface) { seat_set_focus_layer(seat, NULL); } + + cursor_rebase_all(); } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -321,7 +324,6 @@ static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = sway_layer->layer_surface->output->data; if (output != NULL) { arrange_layers(output); - arrange_output(output); transaction_commit_dirty(); } wl_list_remove(&sway_layer->output_destroy.link); @@ -339,6 +341,7 @@ static void handle_map(struct wl_listener *listener, void *data) { // TODO: send enter to subsurfaces and popups wlr_surface_send_enter(sway_layer->layer_surface->surface, sway_layer->layer_surface->output); + cursor_rebase_all(); } static void handle_unmap(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index e0c3a5d1e..4098ed22c 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -314,14 +314,7 @@ static void transaction_apply(struct sway_transaction *transaction) { node->instruction = NULL; } - if (root->outputs->length) { - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - if (!seat_doing_seatop(seat)) { - cursor_rebase(seat->cursor); - } - } - } + cursor_rebase_all(); } static void transaction_commit(struct sway_transaction *transaction); diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 1bf548db7..263b6758b 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -283,6 +283,19 @@ void cursor_rebase(struct sway_cursor *cursor) { cursor_do_rebase(cursor, time_msec, cursor->previous.node, surface, sx, sy); } +void cursor_rebase_all(void) { + if (!root->outputs->length) { + return; + } + + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + if (!seat_doing_seatop(seat)) { + cursor_rebase(seat->cursor); + } + } +} + static int hide_notify(void *data) { struct sway_cursor *cursor = data; wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); From cfee167b7bd1c67f8dbed96d69de4050d8860b06 Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 12 Feb 2019 23:13:24 +0100 Subject: [PATCH 0064/2054] Fix --version when building from tarball --- meson.build | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index c50fab176..2336a1489 100644 --- a/meson.build +++ b/meson.build @@ -128,12 +128,13 @@ endif add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir)), language : 'c') +version = '"@0@"'.format(meson.project_version()) if git.found() - git_commit_hash = run_command([git.path(), 'describe', '--always', '--tags']).stdout().strip() - git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']).stdout().strip() - version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash, git_branch) -else - version = '"@0@"'.format(meson.project_version()) + git_commit_hash = run_command([git.path(), 'describe', '--always', '--tags']) + git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']) + if git_commit_hash.returncode() == 0 and git_branch.returncode() == 0 + version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash.stdout().strip(), git_branch.stdout().strip()) + endif endif add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') From 98b40d31c1178228dfc476b26d82e99bae338b5f Mon Sep 17 00:00:00 2001 From: athrungithub Date: Mon, 11 Feb 2019 19:10:28 -0300 Subject: [PATCH 0065/2054] Don't remove from scratchpad on move to workspace on move container window to another workspace, not remove from scratchpad. --- sway/commands/move.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sway/commands/move.c b/sway/commands/move.c index 8c3afae97..16f8cdb6f 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -516,7 +516,6 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { // move container if (container->scratchpad) { - root_scratchpad_remove_container(container); root_scratchpad_show(container); } switch (destination->type) { From 1bccde68a4fadd0b4170faa16b97554a864447d4 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 13 Feb 2019 03:01:06 -0500 Subject: [PATCH 0066/2054] cursor: relative-pointer-v1 time is usec In handle_cursor_motion, the timestamp passed to `wlr_relative_pointer_manager_v1_send_relative_motion` should be microseconds (not milliseconds) according to relative-pointer-v1 spec. --- sway/input/cursor.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 263b6758b..5ede6e74f 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -421,8 +421,8 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) { wlr_relative_pointer_manager_v1_send_relative_motion( server.relative_pointer_manager, - cursor->seat->wlr_seat, event->time_msec, dx, dy, - dx_unaccel, dy_unaccel); + cursor->seat->wlr_seat, (uint64_t)event->time_msec * 1000, + dx, dy, dx_unaccel, dy_unaccel); struct wlr_surface *surface = NULL; double sx, sy; From d168d65f2c0297bf5662c0f48f5f53705e54a376 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 12 Feb 2019 23:21:11 -0500 Subject: [PATCH 0067/2054] subsurface_get_root_coords: break on NULL It is possible for `wlr_surface_is_subsurface` to return true, but `wlr_surface_from_wlr_surface` to be NULL. This adds a NULL check to the value returned by `wlr_surface_from_wlr_surface` and breaks out of the while loop in `subsurface_get_root_coords`. --- sway/tree/view.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/tree/view.c b/sway/tree/view.c index 943734dc7..ca13def72 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -708,6 +708,9 @@ static void subsurface_get_root_coords(struct sway_view_child *child, while (surface && wlr_surface_is_subsurface(surface)) { struct wlr_subsurface *subsurface = wlr_subsurface_from_wlr_surface(surface); + if (subsurface == NULL) { + break; + } *root_sx += subsurface->current.x; *root_sy += subsurface->current.y; surface = subsurface->parent; From 9b8249c350a499bb65e50741e8a45e5575ac7d18 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 13 Feb 2019 14:32:47 -0500 Subject: [PATCH 0068/2054] seatop_move_tiling: use tab/stack parent not self When moving a descendant of a tabbed or stacked container, it is possible for the target node to be the node being moved. This causes a segfault in `handle_finish` since the node will be detached and then attempted to be attached to it own parent, which is NULL due to the detach. In this case, the target node should not be set to the node being moved, but the parent of the node. This also allows for a descendant of a tabbed or stacked container to be dragged out of the tabs/stacks and to be a sibling of the tabbbed/stacked container, which was not previously possible. --- sway/input/seatop_move_tiling.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 8b541f80d..422a4aa2c 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -150,6 +150,9 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { } if (edge) { e->target_node = node_get_parent(&con->node); + if (e->target_node == &e->con->node) { + e->target_node = node_get_parent(e->target_node); + } e->target_edge = edge; node_get_box(e->target_node, &e->drop_box); resize_box(&e->drop_box, edge, DROP_LAYOUT_BORDER); From ab42874f71bceeaf0357dbf3020f1cb73fb2c738 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 13 Feb 2019 23:46:20 -0500 Subject: [PATCH 0069/2054] seat: allow tree focus changes while layer focused This allows the focused inactive tree node and visible workspaces to be changed while a surface layer has focus. The layer temporarily loses focus, the tree focus changes, and the layer gets refocused. --- sway/input/seat.c | 4 ++++ sway/tree/workspace.c | 14 ++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/sway/input/seat.c b/sway/input/seat.c index 18664d7cb..df48b7518 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -725,6 +725,10 @@ void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node) { void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { if (seat->focused_layer) { + struct wlr_layer_surface_v1 *layer = seat->focused_layer; + seat_set_focus_layer(seat, NULL); + seat_set_focus(seat, node); + seat_set_focus_layer(seat, layer); return; } diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index b97809223..cda6caf7c 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -451,9 +451,15 @@ struct sway_workspace *workspace_prev(struct sway_workspace *current) { bool workspace_switch(struct sway_workspace *workspace, bool no_auto_back_and_forth) { struct sway_seat *seat = input_manager_current_seat(); - struct sway_workspace *active_ws = seat_get_focused_workspace(seat); + struct sway_workspace *active_ws = NULL; + struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); + if (focus && focus->type == N_WORKSPACE) { + active_ws = focus->sway_workspace; + } else if (focus && focus->type == N_CONTAINER) { + active_ws = focus->sway_container->workspace; + } - if (!no_auto_back_and_forth && config->auto_back_and_forth + if (!no_auto_back_and_forth && config->auto_back_and_forth && active_ws && active_ws == workspace && seat->prev_workspace_name) { struct sway_workspace *new_ws = workspace_by_name(seat->prev_workspace_name); @@ -462,9 +468,9 @@ bool workspace_switch(struct sway_workspace *workspace, workspace_create(NULL, seat->prev_workspace_name); } - if (!seat->prev_workspace_name || + if (active_ws && (!seat->prev_workspace_name || (strcmp(seat->prev_workspace_name, active_ws->name) - && active_ws != workspace)) { + && active_ws != workspace))) { free(seat->prev_workspace_name); seat->prev_workspace_name = malloc(strlen(active_ws->name) + 1); if (!seat->prev_workspace_name) { From e8c472aee95ce1cbb6feef04d970327d1717d7fc Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 13 Feb 2019 22:38:30 -0500 Subject: [PATCH 0070/2054] seatop_move_tiling: do not move to descendant In seatop_move_tiling, it is possible to cause a stack overflow by dragging a container into one of its descendants. This disables the ability to move into a descendant. --- sway/input/seatop_move_tiling.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 422a4aa2c..1e548f5ae 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -164,7 +164,8 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { // Use the hovered view - but we must be over the actual surface con = node->sway_container; - if (!con->view->surface || node == &e->con->node) { + if (!con->view->surface || node == &e->con->node + || node_has_ancestor(node, &e->con->node)) { e->target_node = NULL; e->target_edge = WLR_EDGE_NONE; return; From 96de2b539c00992003664d0de225cc28c2fb9e54 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 15 Feb 2019 03:01:19 -0500 Subject: [PATCH 0071/2054] apply_output_config: dpms on before modeset On the DRM backend, if an output is dpms'd off and a different output is hotplugged, the CRTC for the output is reclaimed. When modesetting an output without a CRTC, a CRTC will not be given to an output that is not desired to be enabled. This splits setting the dpms state in apply_output_config. If the output should be dpms on, the it is enabled before attempting to modeset. Otherwise, it is dpms'd off after setting everything else. This also adds DPMS_ON to the default output configs. --- sway/config/output.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index 970764b03..f1a06379b 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -199,6 +199,11 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { return true; } + if (oc && oc->dpms_state == DPMS_ON) { + sway_log(SWAY_DEBUG, "Turning on screen"); + wlr_output_enable(wlr_output, true); + } + bool modeset_success; if (oc && oc->width > 0 && oc->height > 0) { sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f GHz)", oc->name, oc->width, @@ -263,19 +268,9 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { } } - if (oc) { - switch (oc->dpms_state) { - case DPMS_ON: - sway_log(SWAY_DEBUG, "Turning on screen"); - wlr_output_enable(wlr_output, true); - break; - case DPMS_OFF: - sway_log(SWAY_DEBUG, "Turning off screen"); - wlr_output_enable(wlr_output, false); - break; - case DPMS_IGNORE: - break; - } + if (oc && oc->dpms_state == DPMS_OFF) { + sway_log(SWAY_DEBUG, "Turning off screen"); + wlr_output_enable(wlr_output, false); } return true; @@ -294,6 +289,7 @@ static void default_output_config(struct output_config *oc, oc->x = oc->y = -1; oc->scale = 1; oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; + oc->dpms_state = DPMS_ON; } static struct output_config *get_output_config(char *identifier, From 5484f308b9aa53ab9c13d670d84a7fc35d447c1a Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Thu, 14 Feb 2019 15:43:34 +0000 Subject: [PATCH 0072/2054] swaybar: prevent signal handler from firing during termination This prevents a heap-use-after-free crash when sway terminates. --- include/swaybar/bar.h | 2 ++ swaybar/bar.c | 6 +++--- swaybar/main.c | 14 +++++--------- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 2d9ba0d9b..2518d5aa8 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -46,6 +46,8 @@ struct swaybar { #if HAVE_TRAY struct swaybar_tray *tray; #endif + + bool running; }; struct swaybar_output { diff --git a/swaybar/bar.c b/swaybar/bar.c index a1f7bfdb9..db1c12228 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -403,8 +404,7 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) { static void display_in(int fd, short mask, void *data) { struct swaybar *bar = data; if (wl_display_dispatch(bar->display) == -1) { - bar_teardown(bar); - exit(0); + bar->running = false; } } @@ -439,7 +439,7 @@ void bar_run(struct swaybar *bar) { loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, bar->tray->bus); } #endif - while (1) { + while (bar->running) { errno = 0; if (wl_display_flush(bar->display) == -1 && errno != EAGAIN) { break; diff --git a/swaybar/main.c b/swaybar/main.c index 4ef746290..108b16e91 100644 --- a/swaybar/main.c +++ b/swaybar/main.c @@ -11,13 +11,7 @@ static struct swaybar swaybar; void sig_handler(int signal) { - bar_teardown(&swaybar); - exit(0); -} - -void sway_terminate(int code) { - bar_teardown(&swaybar); - exit(code); + swaybar.running = false; } int main(int argc, char **argv) { @@ -93,8 +87,6 @@ int main(int argc, char **argv) { } } - signal(SIGTERM, sig_handler); - if (!bar_setup(&swaybar, socket_path)) { free(socket_path); return 1; @@ -102,6 +94,10 @@ int main(int argc, char **argv) { free(socket_path); + signal(SIGINT, sig_handler); + signal(SIGTERM, sig_handler); + + swaybar.running = true; bar_run(&swaybar); bar_teardown(&swaybar); return 0; From d3c527220a445c1f88b892c0e77e801d326541b7 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Fri, 15 Feb 2019 15:15:45 +0000 Subject: [PATCH 0073/2054] tray: use correct parameter to set bus slot to floating Counter-intuitively, `sd_bus_slot_set_floating` expects 0 to set it to floating. --- swaybar/tray/host.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c index 215e1e722..451b08967 100644 --- a/swaybar/tray/host.c +++ b/swaybar/tray/host.c @@ -189,9 +189,9 @@ bool init_host(struct swaybar_host *host, char *protocol, goto error; } - sd_bus_slot_set_floating(reg_slot, 1); - sd_bus_slot_set_floating(unreg_slot, 1); - sd_bus_slot_set_floating(watcher_slot, 1); + sd_bus_slot_set_floating(reg_slot, 0); + sd_bus_slot_set_floating(unreg_slot, 0); + sd_bus_slot_set_floating(watcher_slot, 0); sway_log(SWAY_DEBUG, "Registered %s", host->service); return true; From 3c59069cb0fae32097f117497f540ed11e0210d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C3=A1bir=20Benchakhtir?= Date: Sat, 16 Feb 2019 03:57:14 +0100 Subject: [PATCH 0074/2054] Spanish translation of the README --- README.es.md | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 README.es.md diff --git a/README.es.md b/README.es.md new file mode 100644 index 000000000..1bf96d8cc --- /dev/null +++ b/README.es.md @@ -0,0 +1,70 @@ +# sway + +[**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [Українська](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) - +[Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) - [Español](https://github.com/swaywm/sway/blob/master/README.es.md#sway--) + +sway es un compositor de [Wayland](http://wayland.freedesktop.org/) compatible con i3. +Lea el [FAQ](https://github.com/swaywm/sway/wiki). Unase al [canal de IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on +irc.freenode.net). + +Si quiere apoyar el desarrollo de sway, por favor, contribuya en [la página de Patreon de SirCmpwn](https://patreon.com/sircmpwn). + +## Firmas de las versiones + +Las distintas versiones están firmadas con [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) +y publicadas [en GitHub](https://github.com/swaywm/sway/releases). + +## Instalación + +### Usando paquetes + +Sway está disponible en muchas distribuciones. Pruebe instalando el paquete "sway" desde la suya. +Si no está disponible, puede consultar [esta documentación](https://github.com/swaywm/sway/wiki/Unsupported-packages) +y así obtener información acerca de como instalarlo. + +Si está interesado en crear un paquete para su distribución, unase al canal de IRC o +escriba un email a sir@cmpwn.com + +### Compilando el código fuente + +Instale las dependencias: + +* meson \* +* [wlroots](https://github.com/swaywm/wlroots) +* wayland +* wayland-protocols \* +* pcre +* json-c +* pango +* cairo +* gdk-pixbuf2 \*\* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.8.1 (optional: man pages) \* +* git \* + +_\*Compile-time dep_ + +_\*\*opcional: necesario para swaybg_ + +Desde su consola, ejecute las ordenes: + + meson build + ninja -C build + sudo ninja -C build install + +En sistemas sin `logind`, necesitará cambiar los permisos del archivo compilado de sway: + + sudo chmod a+s /usr/local/bin/sway + +Sway abandonará los permisos de super-usuario al poco de arrancar. + +## Configuración + +Si ya utiliza i3, copie su archivo de configuración de i3 a `~/.config/sway/config` y +sway funcionará sin tener que configurar nada más. En otro caso, copie el archivo de +configuración basico a `~/.config/sway/config`, normalmente se encuentra en `/etc/sway/config`. +Ejecute `man 5 sway` para obtener información sobre la configuración. + +## Ejecución + +Ejecute `sway` desde su consola. Algunos gestores de pantalla pueden funcionar sin estar +soportados por `sway` (sabemos que gdm funciona bastante bien). From 9e590c4d01e5a85a684028fc7092b34ad115a9d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C3=A1bir=20Garc=C3=ADa?= Date: Sat, 16 Feb 2019 04:17:18 +0100 Subject: [PATCH 0075/2054] Add spanish link to the home README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 239e7e3ef..f5173adff 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # sway [**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [Українська](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) - -[Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) +[Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) - [Español](https://github.com/swaywm/sway/blob/master/README.es.md#sway--) sway is an i3-compatible [Wayland](http://wayland.freedesktop.org/) compositor. Read the [FAQ](https://github.com/swaywm/sway/wiki). Join the [IRC From db351dc67022bc25a109addcf0acbfa4e2ba0d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C3=A1bir=20Garc=C3=ADa?= Date: Sat, 16 Feb 2019 04:29:36 +0100 Subject: [PATCH 0076/2054] Fix accents --- README.es.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.es.md b/README.es.md index 1bf96d8cc..daa1fc093 100644 --- a/README.es.md +++ b/README.es.md @@ -4,7 +4,7 @@ [Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) - [Español](https://github.com/swaywm/sway/blob/master/README.es.md#sway--) sway es un compositor de [Wayland](http://wayland.freedesktop.org/) compatible con i3. -Lea el [FAQ](https://github.com/swaywm/sway/wiki). Unase al [canal de IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on +Lea el [FAQ](https://github.com/swaywm/sway/wiki). Únase al [canal de IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net). Si quiere apoyar el desarrollo de sway, por favor, contribuya en [la página de Patreon de SirCmpwn](https://patreon.com/sircmpwn). @@ -22,7 +22,7 @@ Sway está disponible en muchas distribuciones. Pruebe instalando el paquete "sw Si no está disponible, puede consultar [esta documentación](https://github.com/swaywm/sway/wiki/Unsupported-packages) y así obtener información acerca de como instalarlo. -Si está interesado en crear un paquete para su distribución, unase al canal de IRC o +Si está interesado en crear un paquete para su distribución, únase al canal de IRC o escriba un email a sir@cmpwn.com ### Compilando el código fuente @@ -45,7 +45,7 @@ _\*Compile-time dep_ _\*\*opcional: necesario para swaybg_ -Desde su consola, ejecute las ordenes: +Desde su consola, ejecute las órdenes: meson build ninja -C build @@ -61,7 +61,7 @@ Sway abandonará los permisos de super-usuario al poco de arrancar. Si ya utiliza i3, copie su archivo de configuración de i3 a `~/.config/sway/config` y sway funcionará sin tener que configurar nada más. En otro caso, copie el archivo de -configuración basico a `~/.config/sway/config`, normalmente se encuentra en `/etc/sway/config`. +configuración básico a `~/.config/sway/config`, normalmente se encuentra en `/etc/sway/config`. Ejecute `man 5 sway` para obtener información sobre la configuración. ## Ejecución From 859a884e82601044aa4c825cfbfdd53e2b4ef79c Mon Sep 17 00:00:00 2001 From: emersion Date: Sat, 16 Feb 2019 11:57:41 +0100 Subject: [PATCH 0077/2054] Disable unneeded wlroots subproject features --- meson.build | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 2336a1489..cfd77ce7e 100644 --- a/meson.build +++ b/meson.build @@ -60,7 +60,11 @@ rt = cc.find_library('rt') git = find_program('git', required: false) # Try first to find wlroots as a subproject, then as a system dependency -wlroots_proj = subproject('wlroots', required: false) +wlroots_proj = subproject( + 'wlroots', + default_options: ['rootston=disabled', 'examples=disabled'], + required: false, +) if wlroots_proj.found() wlroots = wlroots_proj.get_variable('wlroots') wlroots_conf = wlroots_proj.get_variable('conf_data') From cfacf85755849997ae3b25d2fa241378e0ebf0b9 Mon Sep 17 00:00:00 2001 From: Vincent Vanlaer Date: Fri, 15 Feb 2019 18:29:47 +0100 Subject: [PATCH 0078/2054] Check layout before getting pointer surface coords This fixes issues of clients at the edge of the screen, like swaybar, ignoring buttons. --- sway/input/cursor.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 5ede6e74f..170532bea 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -425,11 +425,12 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) { dx, dy, dx_unaccel, dy_unaccel); struct wlr_surface *surface = NULL; + struct sway_node *node = NULL; double sx, sy; - struct sway_node *node = node_at_coords(cursor->seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - if (cursor->active_constraint) { + node = node_at_coords(cursor->seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + if (cursor->active_constraint->surface != surface) { return; } @@ -445,8 +446,13 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) { } wlr_cursor_move(cursor->cursor, event->device, dx, dy); + + // Recalculate pointer location after layout checks + node = node_at_coords(cursor->seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + cursor_send_pointer_motion(cursor, event->time_msec, node, surface, - sx + dx, sy + dy); + sx, sy); transaction_commit_dirty(); } From 26d6360febeb364a9c7e20844ad03c796e4f08c3 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Sat, 16 Feb 2019 11:01:15 +0000 Subject: [PATCH 0079/2054] tray: when a service is lost, remove all matching items Before, only the first matching item would be removed, which could leave stale items. --- swaybar/tray/watcher.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c index 381510717..432837d07 100644 --- a/swaybar/tray/watcher.c +++ b/swaybar/tray/watcher.c @@ -18,10 +18,6 @@ static int cmp_id(const void *item, const void *cmp_to) { return strcmp(item, cmp_to); } -static int cmp_service(const void *item, const void *cmp_to) { - return strncmp(item, cmp_to, strlen(cmp_to)); -} - static int handle_lost_service(sd_bus_message *msg, void *data, sd_bus_error *error) { char *service, *old_owner, *new_owner; @@ -33,18 +29,23 @@ static int handle_lost_service(sd_bus_message *msg, if (!*new_owner) { struct swaybar_watcher *watcher = data; - int idx = list_seq_find(watcher->items, - using_standard_protocol(watcher) ? cmp_id : cmp_service, service); - if (idx != -1) { + for (int idx = 0; idx < watcher->items->length; ++idx) { char *id = watcher->items->items[idx]; - sway_log(SWAY_DEBUG, "Unregistering Status Notifier Item '%s'", id); - list_del(watcher->items, idx); - sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, - "StatusNotifierItemUnregistered", "s", id); - free(id); + int cmp_res = using_standard_protocol(watcher) ? + cmp_id(id, service) : strncmp(id, service, strlen(service)); + if (cmp_res == 0) { + sway_log(SWAY_DEBUG, "Unregistering Status Notifier Item '%s'", id); + list_del(watcher->items, idx--); + sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, + "StatusNotifierItemUnregistered", "s", id); + free(id); + if (using_standard_protocol(watcher)) { + break; + } + } } - idx = list_seq_find(watcher->hosts, cmp_id, service); + int idx = list_seq_find(watcher->hosts, cmp_id, service); if (idx != -1) { sway_log(SWAY_DEBUG, "Unregistering Status Notifier Host '%s'", service); free(watcher->hosts->items[idx]); From f54077dbca2faf3f0d614377a01a215c82ef075e Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Sat, 16 Feb 2019 11:02:15 +0000 Subject: [PATCH 0080/2054] tray: fix memory leaks --- swaybar/tray/item.c | 6 +++++- swaybar/tray/watcher.c | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index 4262d6877..4fa6c97b5 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -299,6 +299,8 @@ void destroy_sni(struct swaybar_sni *sni) { return; } + cairo_surface_destroy(sni->icon); + sd_bus_slot_unref(sni->new_icon_slot); sd_bus_slot_unref(sni->new_attention_icon_slot); sd_bus_slot_unref(sni->new_status_slot); @@ -308,9 +310,11 @@ void destroy_sni(struct swaybar_sni *sni) { free(sni->path); free(sni->status); free(sni->icon_name); - free(sni->icon_pixmap); + list_free_items_and_destroy(sni->icon_pixmap); free(sni->attention_icon_name); + list_free_items_and_destroy(sni->attention_icon_pixmap); free(sni->menu); + free(sni->icon_theme_path); free(sni); } diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c index 432837d07..951a05890 100644 --- a/swaybar/tray/watcher.c +++ b/swaybar/tray/watcher.c @@ -186,8 +186,8 @@ struct swaybar_watcher *create_watcher(char *protocol, sd_bus *bus) { goto error; } - sd_bus_slot_set_floating(signal_slot, 1); - sd_bus_slot_set_floating(vtable_slot, 1); + sd_bus_slot_set_floating(signal_slot, 0); + sd_bus_slot_set_floating(vtable_slot, 0); watcher->bus = bus; watcher->hosts = create_list(); From 272ca061714a77597feae1075cdd2d44ebdf5ce3 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 16 Feb 2019 16:14:27 -0500 Subject: [PATCH 0081/2054] Fix reload freeze when not modsetting current mode This fixes the issue of the display freezing on reload with wlroots#1545. On master, all output configs are applied on reload. This may cause an output to have its config applied up to three times, instead of just once. The three cases are: output name, output identifier, and wildcard. Not only is this inefficient, but it can cause swaybg to be spawned and immediately killed. However, swaybg requires two roundtrips of wl_display (to obtain needed globals) before it enters its normal event loop. Modesetting will roundtrip the wl_display. Without modesetting, waitpid for killing swaybg could block infinitely due to swaybg being blocked by wl_display_roundtrip. This only configured an output once. It either uses the wildcard config or creates an empty wildcard config and applies that. This also fixes a bug where an output would not be reset when there is no output config to apply to it. --- include/sway/config.h | 2 ++ sway/config.c | 5 ++--- sway/config/output.c | 11 +++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 43ea77788..54cdcc908 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -586,6 +586,8 @@ struct output_config *store_output_config(struct output_config *oc); void apply_output_config_to_outputs(struct output_config *oc); +void reset_outputs(void); + void free_output_config(struct output_config *oc); int workspace_output_cmp_workspace(const void *a, const void *b); diff --git a/sway/config.c b/sway/config.c index cd2d18a27..206ca95cb 100644 --- a/sway/config.c +++ b/sway/config.c @@ -471,9 +471,8 @@ bool load_main_config(const char *file, bool is_active, bool validating) { } if (is_active) { - for (int i = 0; i < config->output_configs->length; i++) { - apply_output_config_to_outputs(config->output_configs->items[i]); - } + reset_outputs(); + config->reloading = false; if (config->swaynag_config_errors.pid > 0) { swaynag_show(&config->swaynag_config_errors); diff --git a/sway/config/output.c b/sway/config/output.c index f1a06379b..0f238715f 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -391,6 +391,17 @@ void apply_output_config_to_outputs(struct output_config *oc) { } } +void reset_outputs(void) { + struct output_config *oc = NULL; + int i = list_seq_find(config->output_configs, output_name_cmp, "*"); + if (i >= 0) { + oc = config->output_configs->items[i]; + } else { + oc = store_output_config(new_output_config("*")); + } + apply_output_config_to_outputs(oc); +} + void free_output_config(struct output_config *oc) { if (!oc) { return; From 8ab64d3fcc98090fd1a49d0696dff1c9d39aad41 Mon Sep 17 00:00:00 2001 From: emersion Date: Sat, 16 Feb 2019 23:09:26 +0100 Subject: [PATCH 0082/2054] Remove unused header include/sway/tree/output.h --- include/sway/tree/output.h | 0 sway/tree/output.c | 1 - 2 files changed, 1 deletion(-) delete mode 100644 include/sway/tree/output.h diff --git a/include/sway/tree/output.h b/include/sway/tree/output.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/sway/tree/output.c b/sway/tree/output.c index 5a992f2db..138144a77 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -8,7 +8,6 @@ #include "sway/layers.h" #include "sway/output.h" #include "sway/tree/arrange.h" -#include "sway/tree/output.h" #include "sway/tree/workspace.h" #include "log.h" #include "util.h" From f7f5b172a02db8209cd7cd315e397e52b67bfed3 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 16 Feb 2019 17:42:56 -0500 Subject: [PATCH 0083/2054] sway-input.5: document wildcard and identifier troubleshooting This documents the wildcard character for both inputs and seats. There is also a tip added on trying the wildcard to verify a setting if the identifier does not appear to be working. --- sway/sway-input.5.scd | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 376e18338..8c3a8225f 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -8,6 +8,13 @@ sway-input - input configuration file and commands Sway allows for configuration of devices within the sway configuration file. To obtain a list of available device identifiers, run *swaymsg -t get_inputs*. +Settings can also be applied to all input devices by using the wildcard, _\*_, +in place of _\_ in the commands below. + +Tip: If the configuration settings do not appear to be taking effect, you could +try using _\*_ instead of _\_. If it works with the wildcard, try +using a different identifier from *swaymsg -t get_inputs* until you find the +correct input device. # INPUT COMMANDS @@ -144,7 +151,13 @@ configured. While sway is running, _-_ (hyphen) can be used as an alias for the current seat. Each seat has an independent keyboard focus and a separate cursor that is controlled by the pointer devices of the seat. This is useful for multiple people using the desktop at the same time with their own devices (each -sitting in their own "seat"). +sitting in their own "seat"). The wildcard character, _\*_, can also be used in +place of _\_ to change settings for all seats. + +Tip: If the configuration settings do not appear to be taking effect, you could +try using _\*_ instead of _\_. If it works with the wildcard, try +using a different identifier from *swaymsg -t get_seats* until you find the +correct seat. *seat* attach Attach an input device to this seat by its input identifier. A special From 806129d3ccef1075e178791c56159e08e868bcaf Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 17 Feb 2019 00:50:53 +0100 Subject: [PATCH 0084/2054] Fix Meson subproject boolean default options --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index cfd77ce7e..d3172bdd8 100644 --- a/meson.build +++ b/meson.build @@ -62,7 +62,7 @@ git = find_program('git', required: false) # Try first to find wlroots as a subproject, then as a system dependency wlroots_proj = subproject( 'wlroots', - default_options: ['rootston=disabled', 'examples=disabled'], + default_options: ['rootston=false', 'examples=false'], required: false, ) if wlroots_proj.found() From 7baaa3a0f80a28f58a3f95f7c3c832cb109b2aab Mon Sep 17 00:00:00 2001 From: emersion Date: Sat, 16 Feb 2019 23:30:19 +0100 Subject: [PATCH 0085/2054] Disconnect swaybg instead of killing it This is much more reliable. This also fixes race conditions when killing swaybg while it's doing a wl_display_roundtrip. --- include/sway/output.h | 8 ++-- sway/config/output.c | 91 ++++++++++++++++++++++++++++++++++--------- sway/desktop/output.c | 2 + sway/tree/output.c | 5 +-- 4 files changed, 80 insertions(+), 26 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 479897ef2..ea7a21741 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -21,6 +21,7 @@ struct sway_output { struct sway_node node; struct wlr_output *wlr_output; struct sway_server *server; + struct wl_list link; struct wl_list layers[4]; // sway_layer_surface::link struct wlr_box usable_area; @@ -36,6 +37,8 @@ struct sway_output { struct sway_output_state current; + struct wl_client *swaybg_client; + struct wl_listener destroy; struct wl_listener mode; struct wl_listener transform; @@ -43,10 +46,7 @@ struct sway_output { struct wl_listener present; struct wl_listener damage_destroy; struct wl_listener damage_frame; - - struct wl_list link; - - pid_t bg_pid; + struct wl_listener swaybg_client_destroy; struct { struct wl_signal destroy; diff --git a/sway/config/output.c b/sway/config/output.c index 0f238715f..513d03e0f 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1,16 +1,17 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include -#include +#include #include #include -#include #include +#include +#include "log.h" #include "sway/config.h" #include "sway/output.h" #include "sway/tree/root.h" -#include "log.h" int output_name_cmp(const void *item, const void *data) { const struct output_config *output = item; @@ -165,14 +166,71 @@ static bool set_mode(struct wlr_output *output, int width, int height, return wlr_output_set_mode(output, best); } -void terminate_swaybg(pid_t pid) { - int ret = kill(pid, SIGTERM); - if (ret != 0) { - sway_log(SWAY_ERROR, "Unable to terminate swaybg [pid: %d]", pid); - } else { - int status; - waitpid(pid, &status, 0); +static void handle_swaybg_client_destroy(struct wl_listener *listener, + void *data) { + struct sway_output *output = + wl_container_of(listener, output, swaybg_client_destroy); + wl_list_remove(&output->swaybg_client_destroy.link); + wl_list_init(&output->swaybg_client_destroy.link); + output->swaybg_client = NULL; +} + +static bool spawn_swaybg(struct sway_output *output, char *const cmd[]) { + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets) != 0) { + sway_log_errno(SWAY_ERROR, "socketpair failed"); + return false; } + + output->swaybg_client = wl_client_create(server.wl_display, sockets[0]); + if (output->swaybg_client == NULL) { + sway_log_errno(SWAY_ERROR, "wl_client_create failed"); + return false; + } + + output->swaybg_client_destroy.notify = handle_swaybg_client_destroy; + wl_client_add_destroy_listener(output->swaybg_client, + &output->swaybg_client_destroy); + + pid_t pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_ERROR, "fork failed"); + return false; + } else if (pid == 0) { + pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_ERROR, "fork failed"); + exit(EXIT_FAILURE); + } else if (pid == 0) { + // Remove the CLOEXEC flag + int flags = fcntl(sockets[1], F_GETFD); + if (flags == -1) { + sway_log_errno(SWAY_ERROR, "fcntl() failed"); + exit(EXIT_FAILURE); + } + if (fcntl(sockets[1], F_SETFD, flags & ~FD_CLOEXEC) == -1) { + sway_log_errno(SWAY_ERROR, "fcntl() failed"); + exit(EXIT_FAILURE); + } + + char wayland_socket_str[16]; + snprintf(wayland_socket_str, sizeof(wayland_socket_str), + "%d", sockets[1]); + setenv("WAYLAND_SOCKET", wayland_socket_str, true); + + execvp(cmd[0], cmd); + sway_log_errno(SWAY_ERROR, "execvp failed"); + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); + } + + if (waitpid(pid, NULL, 0) < 0) { + sway_log_errno(SWAY_ERROR, "waitpid failed"); + return false; + } + + return true; } bool apply_output_config(struct output_config *oc, struct sway_output *output) { @@ -243,8 +301,8 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { wlr_output_layout_add_auto(root->output_layout, wlr_output); } - if (output->bg_pid != 0) { - terminate_swaybg(output->bg_pid); + if (output->swaybg_client != NULL) { + wl_client_destroy(output->swaybg_client); } if (oc && oc->background && config->swaybg_command) { sway_log(SWAY_DEBUG, "Setting background for output %s to %s", @@ -258,13 +316,8 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { oc->background_fallback ? oc->background_fallback : NULL, NULL, }; - - output->bg_pid = fork(); - if (output->bg_pid < 0) { - sway_log_errno(SWAY_ERROR, "fork failed"); - } else if (output->bg_pid == 0) { - execvp(cmd[0], cmd); - sway_log_errno(SWAY_ERROR, "Failed to execute swaybg"); + if (!spawn_swaybg(output, cmd)) { + return false; } } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index ad75bb35a..c5461ee66 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -506,6 +506,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&output->present.link); wl_list_remove(&output->damage_destroy.link); wl_list_remove(&output->damage_frame.link); + wl_list_remove(&output->swaybg_client_destroy.link); transaction_commit_dirty(); } @@ -612,6 +613,7 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->damage_frame.notify = damage_handle_frame; wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); output->damage_destroy.notify = damage_handle_destroy; + wl_list_init(&output->swaybg_client_destroy.link); struct output_config *oc = output_find_config(output); if (!oc || oc->enabled) { diff --git a/sway/tree/output.c b/sway/tree/output.c index 138144a77..60e0af9f4 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -233,9 +233,8 @@ void output_disable(struct sway_output *output) { root_for_each_container(untrack_output, output); - if (output->bg_pid) { - terminate_swaybg(output->bg_pid); - output->bg_pid = 0; + if (output->swaybg_client != NULL) { + wl_client_destroy(output->swaybg_client); } int index = list_find(root->outputs, output); From cd10e755c1b820b15eac448451d6b9dabd339fc0 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sun, 17 Feb 2019 09:51:56 -0500 Subject: [PATCH 0086/2054] Remove refs to unimplemented debuglog command Closes #3695 --- include/sway/commands.h | 1 - sway/sway.5.scd | 4 ---- 2 files changed, 5 deletions(-) diff --git a/include/sway/commands.h b/include/sway/commands.h index 3ed007635..764821a0e 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -112,7 +112,6 @@ sway_cmd cmd_client_placeholder; sway_cmd cmd_client_background; sway_cmd cmd_commands; sway_cmd cmd_create_output; -sway_cmd cmd_debuglog; sway_cmd cmd_default_border; sway_cmd cmd_default_floating_border; sway_cmd cmd_default_orientation; diff --git a/sway/sway.5.scd b/sway/sway.5.scd index fd0a22dc7..b6955acd5 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -413,10 +413,6 @@ The default colors are: : #0c0c0c -*debuglog* on|off|toggle - Enables, disables or toggles debug logging. _toggle_ cannot be used in the - configuration file. - *default_border* normal|none|pixel [] Set default border style for new tiled windows. From 487c83f0de9ca2a7650ad636eed6fd694ddcb82e Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Fri, 15 Feb 2019 21:01:54 -0500 Subject: [PATCH 0087/2054] Add workspace {prev,next}_on_output --create This creates the next workspace if you hit the end. --- include/sway/tree/workspace.h | 6 ++++-- sway/commands/workspace.c | 13 ++++++++----- sway/tree/workspace.c | 29 ++++++++++++++++++++--------- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index 7abfbff13..41b597963 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -67,11 +67,13 @@ struct sway_workspace *workspace_by_number(const char* name); struct sway_workspace *workspace_by_name(const char*); -struct sway_workspace *workspace_output_next(struct sway_workspace *current); +struct sway_workspace *workspace_output_next( + struct sway_workspace *current, bool create); struct sway_workspace *workspace_next(struct sway_workspace *current); -struct sway_workspace *workspace_output_prev(struct sway_workspace *current); +struct sway_workspace *workspace_output_prev( + struct sway_workspace *current, bool create); struct sway_workspace *workspace_prev(struct sway_workspace *current); diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index 65a3f4075..5fde81299 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -181,7 +181,9 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { ++argv; } - + bool create = argc > 1 && strcasecmp(argv[1], "--create") == 0; + struct sway_seat *seat = config->handler_context.seat; + struct sway_workspace *current = seat_get_focused_workspace(seat); struct sway_workspace *ws = NULL; if (strcasecmp(argv[0], "number") == 0) { if (argc < 2) { @@ -199,12 +201,13 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { } } else if (strcasecmp(argv[0], "next") == 0 || strcasecmp(argv[0], "prev") == 0 || - strcasecmp(argv[0], "next_on_output") == 0 || - strcasecmp(argv[0], "prev_on_output") == 0 || strcasecmp(argv[0], "current") == 0) { ws = workspace_by_name(argv[0]); + } else if (strcasecmp(argv[0], "next_on_output") == 0) { + ws = workspace_output_next(current, create); + } else if (strcasecmp(argv[0], "prev_on_output") == 0) { + ws = workspace_output_prev(current, create); } else if (strcasecmp(argv[0], "back_and_forth") == 0) { - struct sway_seat *seat = config->handler_context.seat; if (!seat->prev_workspace_name) { return cmd_results_new(CMD_INVALID, "There is no previous workspace"); @@ -220,7 +223,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { free(name); } workspace_switch(ws, no_auto_back_and_forth); - seat_consider_warp_to_focus(config->handler_context.seat); + seat_consider_warp_to_focus(seat); } return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index cda6caf7c..68f1de502 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -230,8 +230,10 @@ static void workspace_name_from_binding(const struct sway_binding * binding, // not a command about workspaces if (strcmp(_target, "next") == 0 || strcmp(_target, "prev") == 0 || - strcmp(_target, "next_on_output") == 0 || - strcmp(_target, "prev_on_output") == 0 || + strncmp(_target, "next_on_output", + strlen("next_on_output")) == 0 || + strncmp(_target, "prev_on_output", + strlen("next_on_output")) == 0 || strcmp(_target, "number") == 0 || strcmp(_target, "back_and_forth") == 0 || strcmp(_target, "current") == 0) { @@ -372,11 +374,11 @@ struct sway_workspace *workspace_by_name(const char *name) { if (strcmp(name, "prev") == 0) { return workspace_prev(current); } else if (strcmp(name, "prev_on_output") == 0) { - return workspace_output_prev(current); + return workspace_output_prev(current, false); } else if (strcmp(name, "next") == 0) { return workspace_next(current); } else if (strcmp(name, "next_on_output") == 0) { - return workspace_output_next(current); + return workspace_output_next(current, false); } else if (strcmp(name, "current") == 0) { return current; } else if (strcasecmp(name, "back_and_forth") == 0) { @@ -397,11 +399,18 @@ struct sway_workspace *workspace_by_name(const char *name) { * otherwise the next one is returned. */ static struct sway_workspace *workspace_output_prev_next_impl( - struct sway_output *output, int dir) { + struct sway_output *output, int dir, bool create) { struct sway_seat *seat = input_manager_current_seat(); struct sway_workspace *workspace = seat_get_focused_workspace(seat); int index = list_find(output->workspaces, workspace); + if (!workspace_is_empty(workspace) && create && + (index + dir < 0 || index + dir == output->workspaces->length)) { + struct sway_output *output = workspace->output; + char *next = workspace_next_name(output->wlr_output->name); + workspace_create(output, next); + free(next); + } size_t new_index = wrap(index + dir, output->workspaces->length); return output->workspaces->items[new_index]; } @@ -432,16 +441,18 @@ static struct sway_workspace *workspace_prev_next_impl( } } -struct sway_workspace *workspace_output_next(struct sway_workspace *current) { - return workspace_output_prev_next_impl(current->output, 1); +struct sway_workspace *workspace_output_next( + struct sway_workspace *current, bool create) { + return workspace_output_prev_next_impl(current->output, 1, create); } struct sway_workspace *workspace_next(struct sway_workspace *current) { return workspace_prev_next_impl(current, 1); } -struct sway_workspace *workspace_output_prev(struct sway_workspace *current) { - return workspace_output_prev_next_impl(current->output, -1); +struct sway_workspace *workspace_output_prev( + struct sway_workspace *current, bool create) { + return workspace_output_prev_next_impl(current->output, -1, create); } struct sway_workspace *workspace_prev(struct sway_workspace *current) { From faf15ee733681f14797d8b47078854a00f03f8fc Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 17 Feb 2019 13:07:04 -0500 Subject: [PATCH 0088/2054] workspace_next_name: fallback to next available number This changes `workspace_next_name` to use the next available number as the workspace name instead of the number of outputs. This fixes the case where a number that is already in use could be returned. The workspace numbers in use have no relation to the number of outputs so it makes more sense to use the lowest available number --- sway/tree/workspace.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 68f1de502..73322491b 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -336,16 +336,13 @@ char *workspace_next_name(const char *output_name) { if (target != NULL) { return target; } - // As a fall back, get the current number of active workspaces - // and return that + 1 for the next workspace's name - int ws_num = root->outputs->length; - int l = snprintf(NULL, 0, "%d", ws_num); - char *name = malloc(l + 1); - if (!sway_assert(name, "Could not allocate workspace name")) { - return NULL; - } - sprintf(name, "%d", ws_num++); - return name; + // As a fall back, use the next available number + char name[12] = ""; + unsigned int ws_num = 1; + do { + snprintf(name, sizeof(name), "%u", ws_num++); + } while (workspace_by_number(name)); + return strdup(name); } static bool _workspace_by_number(struct sway_workspace *ws, void *data) { From 73c8e63438efff8f43271812505f32c829bd4a98 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 17 Feb 2019 20:16:23 -0500 Subject: [PATCH 0089/2054] seat_cmd_cursor: fix typo in expected syntax This just fixes a typo in the expected syntax for seat_cmd_cursor --- sway/commands/seat/cursor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c index 0c7609eaf..085e6a33f 100644 --- a/sway/commands/seat/cursor.c +++ b/sway/commands/seat/cursor.c @@ -12,7 +12,7 @@ static struct cmd_results *press_or_release(struct sway_cursor *cursor, static const char expected_syntax[] = "Expected 'cursor ' or " "'cursor ' or " - "'curor '"; + "'cursor '"; static struct cmd_results *handle_command(struct sway_cursor *cursor, int argc, char **argv) { From 041594d2ab4fb6f8de2055d3704da5f54146c44e Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 17 Feb 2019 20:33:37 -0500 Subject: [PATCH 0090/2054] Use container under cursor for mouse bindings This matches i3's behavior of executing mouse bindings in regards to the container under the cursor instead of what is focused. --- sway/commands/bind.c | 20 +++++++++++++++++--- sway/sway.5.scd | 13 +++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 59116d67f..172e6b8ae 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -1,10 +1,11 @@ #define _POSIX_C_SOURCE 200809L #include #include -#include -#include #include #include +#include +#include +#include #include "sway/commands.h" #include "sway/config.h" #include "sway/input/cursor.h" @@ -330,7 +331,20 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) { void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) { sway_log(SWAY_DEBUG, "running command for binding: %s", binding->command); - list_t *res_list = execute_command(binding->command, seat, NULL); + struct sway_container *con = NULL; + if (binding->type == BINDING_MOUSESYM + || binding->type == BINDING_MOUSECODE) { + struct wlr_surface *surface = NULL; + double sx, sy; + struct sway_node *node = node_at_coords(seat, + seat->cursor->cursor->x, seat->cursor->cursor->y, + &surface, &sx, &sy); + if (node && node->type == N_CONTAINER) { + con = node->sway_container; + } + } + + list_t *res_list = execute_command(binding->command, seat, con); bool success = true; for (int i = 0; i < res_list->length; ++i) { struct cmd_results *results = res_list->items[i]; diff --git a/sway/sway.5.scd b/sway/sway.5.scd index b6955acd5..76467d23a 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -293,12 +293,13 @@ runtime. overwrite a binding, swaynag will give you a warning. To silence this, use the _--no-warn_ flag. - Mouse buttons can either be specified in the form _button[1-9]_ or by using - the name of the event code (ex _BTN\_LEFT_ or _BTN\_RIGHT_). For the former - option, the buttons will be mapped to their values in X11 (1=left, 2=middle, - 3=right, 4=scroll up, 5=scroll down, 6=scroll left, 7=scroll right, 8=back, - 9=forward). For the latter option, you can find the event names using - _libinput debug-events_. + Mouse bindings operate on the container under the cursor instead of the + container that has focus. Mouse buttons can either be specified in the form + _button[1-9]_ or by using the name of the event code (ex _BTN\_LEFT_ or + _BTN\_RIGHT_). For the former option, the buttons will be mapped to their + values in X11 (1=left, 2=middle, 3=right, 4=scroll up, 5=scroll down, + 6=scroll left, 7=scroll right, 8=back, 9=forward). For the latter option, + you can find the event names using _libinput debug-events_. _--whole-window_, _--border_, and _--exclude-titlebar_ are mouse-only options which affect the region in which the mouse bindings can be triggered. By From 59f20560223a87c5d23dfa80edcad41fb4eb8d22 Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 18 Feb 2019 13:19:58 +0100 Subject: [PATCH 0091/2054] Fix drag icon map listener not removed --- sway/input/seat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/input/seat.c b/sway/input/seat.c index df48b7518..69b048430 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -287,6 +287,7 @@ static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&icon->link); wl_list_remove(&icon->surface_commit.link); wl_list_remove(&icon->unmap.link); + wl_list_remove(&icon->map.link); wl_list_remove(&icon->destroy.link); free(icon); } From fc397f52bbbeca3ef078ae290257329404207071 Mon Sep 17 00:00:00 2001 From: Yorick van Pelt Date: Mon, 18 Feb 2019 13:57:16 +0100 Subject: [PATCH 0092/2054] Add 'visible' key to view json response --- sway/ipc-json.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 23016dbd5..6d36c7274 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -344,6 +344,9 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(object, "app_id", app_id ? json_object_new_string(app_id) : NULL); + bool visible = view_is_visible(c->view); + json_object_object_add(object, "visible", json_object_new_boolean(visible)); + json_object *marks = json_object_new_array(); list_t *con_marks = c->marks; for (int i = 0; i < con_marks->length; ++i) { From 06e03ed8784fe2048e48909af47c80a55d201a81 Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 12 Feb 2019 22:55:23 +0100 Subject: [PATCH 0093/2054] Rebase cursor when a layer surface maps Also removes an extraneous arrange_outputs call, it's already called if necessary in arrange_layers. Updates https://github.com/swaywm/sway/issues/3080 --- include/sway/input/cursor.h | 1 + sway/desktop/layer_shell.c | 7 +++++-- sway/desktop/transaction.c | 9 +-------- sway/input/cursor.c | 13 +++++++++++++ 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 072a56ca1..98eb4679b 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -72,6 +72,7 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat); * This chooses a cursor icon and sends a motion event to the surface. */ void cursor_rebase(struct sway_cursor *cursor); +void cursor_rebase_all(void); void cursor_handle_activity(struct sway_cursor *cursor); void cursor_unhide(struct sway_cursor *cursor); diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index de8db75df..0767247c4 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -6,7 +6,9 @@ #include #include #include +#include "log.h" #include "sway/desktop/transaction.h" +#include "sway/input/cursor.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/layers.h" @@ -14,7 +16,6 @@ #include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/workspace.h" -#include "log.h" static void apply_exclusive(struct wlr_box *usable_area, uint32_t anchor, int32_t exclusive, @@ -302,6 +303,8 @@ static void unmap(struct sway_layer_surface *sway_layer) { if (seat->focused_layer == sway_layer->layer_surface) { seat_set_focus_layer(seat, NULL); } + + cursor_rebase_all(); } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -321,7 +324,6 @@ static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = sway_layer->layer_surface->output->data; if (output != NULL) { arrange_layers(output); - arrange_output(output); transaction_commit_dirty(); } wl_list_remove(&sway_layer->output_destroy.link); @@ -339,6 +341,7 @@ static void handle_map(struct wl_listener *listener, void *data) { // TODO: send enter to subsurfaces and popups wlr_surface_send_enter(sway_layer->layer_surface->surface, sway_layer->layer_surface->output); + cursor_rebase_all(); } static void handle_unmap(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index e0c3a5d1e..4098ed22c 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -314,14 +314,7 @@ static void transaction_apply(struct sway_transaction *transaction) { node->instruction = NULL; } - if (root->outputs->length) { - struct sway_seat *seat; - wl_list_for_each(seat, &server.input->seats, link) { - if (!seat_doing_seatop(seat)) { - cursor_rebase(seat->cursor); - } - } - } + cursor_rebase_all(); } static void transaction_commit(struct sway_transaction *transaction); diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 1bf548db7..263b6758b 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -283,6 +283,19 @@ void cursor_rebase(struct sway_cursor *cursor) { cursor_do_rebase(cursor, time_msec, cursor->previous.node, surface, sx, sy); } +void cursor_rebase_all(void) { + if (!root->outputs->length) { + return; + } + + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + if (!seat_doing_seatop(seat)) { + cursor_rebase(seat->cursor); + } + } +} + static int hide_notify(void *data) { struct sway_cursor *cursor = data; wlr_cursor_set_image(cursor->cursor, NULL, 0, 0, 0, 0, 0, 0); From e50e1fe058f51ac14d4e9130ca1270e3b569cc47 Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 12 Feb 2019 23:13:24 +0100 Subject: [PATCH 0094/2054] Fix --version when building from tarball --- meson.build | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index c50fab176..2336a1489 100644 --- a/meson.build +++ b/meson.build @@ -128,12 +128,13 @@ endif add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir)), language : 'c') +version = '"@0@"'.format(meson.project_version()) if git.found() - git_commit_hash = run_command([git.path(), 'describe', '--always', '--tags']).stdout().strip() - git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']).stdout().strip() - version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash, git_branch) -else - version = '"@0@"'.format(meson.project_version()) + git_commit_hash = run_command([git.path(), 'describe', '--always', '--tags']) + git_branch = run_command([git.path(), 'rev-parse', '--abbrev-ref', 'HEAD']) + if git_commit_hash.returncode() == 0 and git_branch.returncode() == 0 + version = '"@0@ (" __DATE__ ", branch \'@1@\')"'.format(git_commit_hash.stdout().strip(), git_branch.stdout().strip()) + endif endif add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') From 59b3c4d06e2941f08ca60e25a223c0e52b83a00e Mon Sep 17 00:00:00 2001 From: athrungithub Date: Mon, 11 Feb 2019 19:10:28 -0300 Subject: [PATCH 0095/2054] Don't remove from scratchpad on move to workspace on move container window to another workspace, not remove from scratchpad. --- sway/commands/move.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sway/commands/move.c b/sway/commands/move.c index 8c3afae97..16f8cdb6f 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -516,7 +516,6 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { // move container if (container->scratchpad) { - root_scratchpad_remove_container(container); root_scratchpad_show(container); } switch (destination->type) { From d126410de18aaacf83ea72753e5eb066fc338997 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 13 Feb 2019 03:01:06 -0500 Subject: [PATCH 0096/2054] cursor: relative-pointer-v1 time is usec In handle_cursor_motion, the timestamp passed to `wlr_relative_pointer_manager_v1_send_relative_motion` should be microseconds (not milliseconds) according to relative-pointer-v1 spec. --- sway/input/cursor.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 263b6758b..5ede6e74f 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -421,8 +421,8 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) { wlr_relative_pointer_manager_v1_send_relative_motion( server.relative_pointer_manager, - cursor->seat->wlr_seat, event->time_msec, dx, dy, - dx_unaccel, dy_unaccel); + cursor->seat->wlr_seat, (uint64_t)event->time_msec * 1000, + dx, dy, dx_unaccel, dy_unaccel); struct wlr_surface *surface = NULL; double sx, sy; From ad34837ddaab418fc4d5aed61370227d634912cc Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 12 Feb 2019 23:21:11 -0500 Subject: [PATCH 0097/2054] subsurface_get_root_coords: break on NULL It is possible for `wlr_surface_is_subsurface` to return true, but `wlr_surface_from_wlr_surface` to be NULL. This adds a NULL check to the value returned by `wlr_surface_from_wlr_surface` and breaks out of the while loop in `subsurface_get_root_coords`. --- sway/tree/view.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/tree/view.c b/sway/tree/view.c index 943734dc7..ca13def72 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -708,6 +708,9 @@ static void subsurface_get_root_coords(struct sway_view_child *child, while (surface && wlr_surface_is_subsurface(surface)) { struct wlr_subsurface *subsurface = wlr_subsurface_from_wlr_surface(surface); + if (subsurface == NULL) { + break; + } *root_sx += subsurface->current.x; *root_sy += subsurface->current.y; surface = subsurface->parent; From 1d4b995c0f290c392c8a0d3e1e293c76bf82c3d9 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 13 Feb 2019 14:32:47 -0500 Subject: [PATCH 0098/2054] seatop_move_tiling: use tab/stack parent not self When moving a descendant of a tabbed or stacked container, it is possible for the target node to be the node being moved. This causes a segfault in `handle_finish` since the node will be detached and then attempted to be attached to it own parent, which is NULL due to the detach. In this case, the target node should not be set to the node being moved, but the parent of the node. This also allows for a descendant of a tabbed or stacked container to be dragged out of the tabs/stacks and to be a sibling of the tabbbed/stacked container, which was not previously possible. --- sway/input/seatop_move_tiling.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 8b541f80d..422a4aa2c 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -150,6 +150,9 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { } if (edge) { e->target_node = node_get_parent(&con->node); + if (e->target_node == &e->con->node) { + e->target_node = node_get_parent(e->target_node); + } e->target_edge = edge; node_get_box(e->target_node, &e->drop_box); resize_box(&e->drop_box, edge, DROP_LAYOUT_BORDER); From ee236afb43c8da4a65b31edad0b470d4099b3cd6 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 13 Feb 2019 23:46:20 -0500 Subject: [PATCH 0099/2054] seat: allow tree focus changes while layer focused This allows the focused inactive tree node and visible workspaces to be changed while a surface layer has focus. The layer temporarily loses focus, the tree focus changes, and the layer gets refocused. --- sway/input/seat.c | 4 ++++ sway/tree/workspace.c | 14 ++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/sway/input/seat.c b/sway/input/seat.c index 18664d7cb..df48b7518 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -725,6 +725,10 @@ void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node) { void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { if (seat->focused_layer) { + struct wlr_layer_surface_v1 *layer = seat->focused_layer; + seat_set_focus_layer(seat, NULL); + seat_set_focus(seat, node); + seat_set_focus_layer(seat, layer); return; } diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index b97809223..cda6caf7c 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -451,9 +451,15 @@ struct sway_workspace *workspace_prev(struct sway_workspace *current) { bool workspace_switch(struct sway_workspace *workspace, bool no_auto_back_and_forth) { struct sway_seat *seat = input_manager_current_seat(); - struct sway_workspace *active_ws = seat_get_focused_workspace(seat); + struct sway_workspace *active_ws = NULL; + struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); + if (focus && focus->type == N_WORKSPACE) { + active_ws = focus->sway_workspace; + } else if (focus && focus->type == N_CONTAINER) { + active_ws = focus->sway_container->workspace; + } - if (!no_auto_back_and_forth && config->auto_back_and_forth + if (!no_auto_back_and_forth && config->auto_back_and_forth && active_ws && active_ws == workspace && seat->prev_workspace_name) { struct sway_workspace *new_ws = workspace_by_name(seat->prev_workspace_name); @@ -462,9 +468,9 @@ bool workspace_switch(struct sway_workspace *workspace, workspace_create(NULL, seat->prev_workspace_name); } - if (!seat->prev_workspace_name || + if (active_ws && (!seat->prev_workspace_name || (strcmp(seat->prev_workspace_name, active_ws->name) - && active_ws != workspace)) { + && active_ws != workspace))) { free(seat->prev_workspace_name); seat->prev_workspace_name = malloc(strlen(active_ws->name) + 1); if (!seat->prev_workspace_name) { From f0e8c68ca77698f741d2117f6391946b5266c1ff Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 13 Feb 2019 22:38:30 -0500 Subject: [PATCH 0100/2054] seatop_move_tiling: do not move to descendant In seatop_move_tiling, it is possible to cause a stack overflow by dragging a container into one of its descendants. This disables the ability to move into a descendant. --- sway/input/seatop_move_tiling.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 422a4aa2c..1e548f5ae 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -164,7 +164,8 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { // Use the hovered view - but we must be over the actual surface con = node->sway_container; - if (!con->view->surface || node == &e->con->node) { + if (!con->view->surface || node == &e->con->node + || node_has_ancestor(node, &e->con->node)) { e->target_node = NULL; e->target_edge = WLR_EDGE_NONE; return; From 8d708b8e1d804198a2c6a53342def6d1505845db Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 15 Feb 2019 03:01:19 -0500 Subject: [PATCH 0101/2054] apply_output_config: dpms on before modeset On the DRM backend, if an output is dpms'd off and a different output is hotplugged, the CRTC for the output is reclaimed. When modesetting an output without a CRTC, a CRTC will not be given to an output that is not desired to be enabled. This splits setting the dpms state in apply_output_config. If the output should be dpms on, the it is enabled before attempting to modeset. Otherwise, it is dpms'd off after setting everything else. This also adds DPMS_ON to the default output configs. --- sway/config/output.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index 970764b03..f1a06379b 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -199,6 +199,11 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { return true; } + if (oc && oc->dpms_state == DPMS_ON) { + sway_log(SWAY_DEBUG, "Turning on screen"); + wlr_output_enable(wlr_output, true); + } + bool modeset_success; if (oc && oc->width > 0 && oc->height > 0) { sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f GHz)", oc->name, oc->width, @@ -263,19 +268,9 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { } } - if (oc) { - switch (oc->dpms_state) { - case DPMS_ON: - sway_log(SWAY_DEBUG, "Turning on screen"); - wlr_output_enable(wlr_output, true); - break; - case DPMS_OFF: - sway_log(SWAY_DEBUG, "Turning off screen"); - wlr_output_enable(wlr_output, false); - break; - case DPMS_IGNORE: - break; - } + if (oc && oc->dpms_state == DPMS_OFF) { + sway_log(SWAY_DEBUG, "Turning off screen"); + wlr_output_enable(wlr_output, false); } return true; @@ -294,6 +289,7 @@ static void default_output_config(struct output_config *oc, oc->x = oc->y = -1; oc->scale = 1; oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; + oc->dpms_state = DPMS_ON; } static struct output_config *get_output_config(char *identifier, From 47abc45e867043b9144cc7a7181d08203d7ec4f2 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Thu, 14 Feb 2019 15:43:34 +0000 Subject: [PATCH 0102/2054] swaybar: prevent signal handler from firing during termination This prevents a heap-use-after-free crash when sway terminates. --- include/swaybar/bar.h | 2 ++ swaybar/bar.c | 6 +++--- swaybar/main.c | 14 +++++--------- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 2d9ba0d9b..2518d5aa8 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -46,6 +46,8 @@ struct swaybar { #if HAVE_TRAY struct swaybar_tray *tray; #endif + + bool running; }; struct swaybar_output { diff --git a/swaybar/bar.c b/swaybar/bar.c index a1f7bfdb9..db1c12228 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -403,8 +404,7 @@ bool bar_setup(struct swaybar *bar, const char *socket_path) { static void display_in(int fd, short mask, void *data) { struct swaybar *bar = data; if (wl_display_dispatch(bar->display) == -1) { - bar_teardown(bar); - exit(0); + bar->running = false; } } @@ -439,7 +439,7 @@ void bar_run(struct swaybar *bar) { loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, bar->tray->bus); } #endif - while (1) { + while (bar->running) { errno = 0; if (wl_display_flush(bar->display) == -1 && errno != EAGAIN) { break; diff --git a/swaybar/main.c b/swaybar/main.c index 4ef746290..108b16e91 100644 --- a/swaybar/main.c +++ b/swaybar/main.c @@ -11,13 +11,7 @@ static struct swaybar swaybar; void sig_handler(int signal) { - bar_teardown(&swaybar); - exit(0); -} - -void sway_terminate(int code) { - bar_teardown(&swaybar); - exit(code); + swaybar.running = false; } int main(int argc, char **argv) { @@ -93,8 +87,6 @@ int main(int argc, char **argv) { } } - signal(SIGTERM, sig_handler); - if (!bar_setup(&swaybar, socket_path)) { free(socket_path); return 1; @@ -102,6 +94,10 @@ int main(int argc, char **argv) { free(socket_path); + signal(SIGINT, sig_handler); + signal(SIGTERM, sig_handler); + + swaybar.running = true; bar_run(&swaybar); bar_teardown(&swaybar); return 0; From 1a46da7fcfd4c6022a71a4466a9637d0a338aa62 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Fri, 15 Feb 2019 15:15:45 +0000 Subject: [PATCH 0103/2054] tray: use correct parameter to set bus slot to floating Counter-intuitively, `sd_bus_slot_set_floating` expects 0 to set it to floating. --- swaybar/tray/host.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/swaybar/tray/host.c b/swaybar/tray/host.c index 215e1e722..451b08967 100644 --- a/swaybar/tray/host.c +++ b/swaybar/tray/host.c @@ -189,9 +189,9 @@ bool init_host(struct swaybar_host *host, char *protocol, goto error; } - sd_bus_slot_set_floating(reg_slot, 1); - sd_bus_slot_set_floating(unreg_slot, 1); - sd_bus_slot_set_floating(watcher_slot, 1); + sd_bus_slot_set_floating(reg_slot, 0); + sd_bus_slot_set_floating(unreg_slot, 0); + sd_bus_slot_set_floating(watcher_slot, 0); sway_log(SWAY_DEBUG, "Registered %s", host->service); return true; From b02198a0d14d8d58a85d357d94c801bad4c56764 Mon Sep 17 00:00:00 2001 From: emersion Date: Sat, 16 Feb 2019 11:57:41 +0100 Subject: [PATCH 0104/2054] Disable unneeded wlroots subproject features --- meson.build | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 2336a1489..cfd77ce7e 100644 --- a/meson.build +++ b/meson.build @@ -60,7 +60,11 @@ rt = cc.find_library('rt') git = find_program('git', required: false) # Try first to find wlroots as a subproject, then as a system dependency -wlroots_proj = subproject('wlroots', required: false) +wlroots_proj = subproject( + 'wlroots', + default_options: ['rootston=disabled', 'examples=disabled'], + required: false, +) if wlroots_proj.found() wlroots = wlroots_proj.get_variable('wlroots') wlroots_conf = wlroots_proj.get_variable('conf_data') From c07d91ca96d9362f2dfe2757ddba31f3c812b63c Mon Sep 17 00:00:00 2001 From: Vincent Vanlaer Date: Fri, 15 Feb 2019 18:29:47 +0100 Subject: [PATCH 0105/2054] Check layout before getting pointer surface coords This fixes issues of clients at the edge of the screen, like swaybar, ignoring buttons. --- sway/input/cursor.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 5ede6e74f..170532bea 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -425,11 +425,12 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) { dx, dy, dx_unaccel, dy_unaccel); struct wlr_surface *surface = NULL; + struct sway_node *node = NULL; double sx, sy; - struct sway_node *node = node_at_coords(cursor->seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - if (cursor->active_constraint) { + node = node_at_coords(cursor->seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + if (cursor->active_constraint->surface != surface) { return; } @@ -445,8 +446,13 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) { } wlr_cursor_move(cursor->cursor, event->device, dx, dy); + + // Recalculate pointer location after layout checks + node = node_at_coords(cursor->seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + cursor_send_pointer_motion(cursor, event->time_msec, node, surface, - sx + dx, sy + dy); + sx, sy); transaction_commit_dirty(); } From 8dbd4b98f7b0c8c1c15fc7045b49a2957b993165 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Sat, 16 Feb 2019 11:01:15 +0000 Subject: [PATCH 0106/2054] tray: when a service is lost, remove all matching items Before, only the first matching item would be removed, which could leave stale items. --- swaybar/tray/watcher.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c index 381510717..432837d07 100644 --- a/swaybar/tray/watcher.c +++ b/swaybar/tray/watcher.c @@ -18,10 +18,6 @@ static int cmp_id(const void *item, const void *cmp_to) { return strcmp(item, cmp_to); } -static int cmp_service(const void *item, const void *cmp_to) { - return strncmp(item, cmp_to, strlen(cmp_to)); -} - static int handle_lost_service(sd_bus_message *msg, void *data, sd_bus_error *error) { char *service, *old_owner, *new_owner; @@ -33,18 +29,23 @@ static int handle_lost_service(sd_bus_message *msg, if (!*new_owner) { struct swaybar_watcher *watcher = data; - int idx = list_seq_find(watcher->items, - using_standard_protocol(watcher) ? cmp_id : cmp_service, service); - if (idx != -1) { + for (int idx = 0; idx < watcher->items->length; ++idx) { char *id = watcher->items->items[idx]; - sway_log(SWAY_DEBUG, "Unregistering Status Notifier Item '%s'", id); - list_del(watcher->items, idx); - sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, - "StatusNotifierItemUnregistered", "s", id); - free(id); + int cmp_res = using_standard_protocol(watcher) ? + cmp_id(id, service) : strncmp(id, service, strlen(service)); + if (cmp_res == 0) { + sway_log(SWAY_DEBUG, "Unregistering Status Notifier Item '%s'", id); + list_del(watcher->items, idx--); + sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, + "StatusNotifierItemUnregistered", "s", id); + free(id); + if (using_standard_protocol(watcher)) { + break; + } + } } - idx = list_seq_find(watcher->hosts, cmp_id, service); + int idx = list_seq_find(watcher->hosts, cmp_id, service); if (idx != -1) { sway_log(SWAY_DEBUG, "Unregistering Status Notifier Host '%s'", service); free(watcher->hosts->items[idx]); From 9ff59b7c6bbd46dcbaf5f516705f349a9138261c Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Sat, 16 Feb 2019 11:02:15 +0000 Subject: [PATCH 0107/2054] tray: fix memory leaks --- swaybar/tray/item.c | 6 +++++- swaybar/tray/watcher.c | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index 4262d6877..4fa6c97b5 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -299,6 +299,8 @@ void destroy_sni(struct swaybar_sni *sni) { return; } + cairo_surface_destroy(sni->icon); + sd_bus_slot_unref(sni->new_icon_slot); sd_bus_slot_unref(sni->new_attention_icon_slot); sd_bus_slot_unref(sni->new_status_slot); @@ -308,9 +310,11 @@ void destroy_sni(struct swaybar_sni *sni) { free(sni->path); free(sni->status); free(sni->icon_name); - free(sni->icon_pixmap); + list_free_items_and_destroy(sni->icon_pixmap); free(sni->attention_icon_name); + list_free_items_and_destroy(sni->attention_icon_pixmap); free(sni->menu); + free(sni->icon_theme_path); free(sni); } diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c index 432837d07..951a05890 100644 --- a/swaybar/tray/watcher.c +++ b/swaybar/tray/watcher.c @@ -186,8 +186,8 @@ struct swaybar_watcher *create_watcher(char *protocol, sd_bus *bus) { goto error; } - sd_bus_slot_set_floating(signal_slot, 1); - sd_bus_slot_set_floating(vtable_slot, 1); + sd_bus_slot_set_floating(signal_slot, 0); + sd_bus_slot_set_floating(vtable_slot, 0); watcher->bus = bus; watcher->hosts = create_list(); From 19586ab768ad2a79cb8342d34e16b25105bc36bd Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 16 Feb 2019 16:14:27 -0500 Subject: [PATCH 0108/2054] Fix reload freeze when not modsetting current mode This fixes the issue of the display freezing on reload with wlroots#1545. On master, all output configs are applied on reload. This may cause an output to have its config applied up to three times, instead of just once. The three cases are: output name, output identifier, and wildcard. Not only is this inefficient, but it can cause swaybg to be spawned and immediately killed. However, swaybg requires two roundtrips of wl_display (to obtain needed globals) before it enters its normal event loop. Modesetting will roundtrip the wl_display. Without modesetting, waitpid for killing swaybg could block infinitely due to swaybg being blocked by wl_display_roundtrip. This only configured an output once. It either uses the wildcard config or creates an empty wildcard config and applies that. This also fixes a bug where an output would not be reset when there is no output config to apply to it. --- include/sway/config.h | 2 ++ sway/config.c | 5 ++--- sway/config/output.c | 11 +++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 43ea77788..54cdcc908 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -586,6 +586,8 @@ struct output_config *store_output_config(struct output_config *oc); void apply_output_config_to_outputs(struct output_config *oc); +void reset_outputs(void); + void free_output_config(struct output_config *oc); int workspace_output_cmp_workspace(const void *a, const void *b); diff --git a/sway/config.c b/sway/config.c index cd2d18a27..206ca95cb 100644 --- a/sway/config.c +++ b/sway/config.c @@ -471,9 +471,8 @@ bool load_main_config(const char *file, bool is_active, bool validating) { } if (is_active) { - for (int i = 0; i < config->output_configs->length; i++) { - apply_output_config_to_outputs(config->output_configs->items[i]); - } + reset_outputs(); + config->reloading = false; if (config->swaynag_config_errors.pid > 0) { swaynag_show(&config->swaynag_config_errors); diff --git a/sway/config/output.c b/sway/config/output.c index f1a06379b..0f238715f 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -391,6 +391,17 @@ void apply_output_config_to_outputs(struct output_config *oc) { } } +void reset_outputs(void) { + struct output_config *oc = NULL; + int i = list_seq_find(config->output_configs, output_name_cmp, "*"); + if (i >= 0) { + oc = config->output_configs->items[i]; + } else { + oc = store_output_config(new_output_config("*")); + } + apply_output_config_to_outputs(oc); +} + void free_output_config(struct output_config *oc) { if (!oc) { return; From d2456c3b370a4b9206b3b05cdadc087265506f8a Mon Sep 17 00:00:00 2001 From: emersion Date: Sat, 16 Feb 2019 23:09:26 +0100 Subject: [PATCH 0109/2054] Remove unused header include/sway/tree/output.h --- include/sway/tree/output.h | 0 sway/tree/output.c | 1 - 2 files changed, 1 deletion(-) delete mode 100644 include/sway/tree/output.h diff --git a/include/sway/tree/output.h b/include/sway/tree/output.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/sway/tree/output.c b/sway/tree/output.c index 5a992f2db..138144a77 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -8,7 +8,6 @@ #include "sway/layers.h" #include "sway/output.h" #include "sway/tree/arrange.h" -#include "sway/tree/output.h" #include "sway/tree/workspace.h" #include "log.h" #include "util.h" From f120f70f5a1137358a5baf111fd77c0164849a47 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 16 Feb 2019 17:42:56 -0500 Subject: [PATCH 0110/2054] sway-input.5: document wildcard and identifier troubleshooting This documents the wildcard character for both inputs and seats. There is also a tip added on trying the wildcard to verify a setting if the identifier does not appear to be working. --- sway/sway-input.5.scd | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 376e18338..8c3a8225f 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -8,6 +8,13 @@ sway-input - input configuration file and commands Sway allows for configuration of devices within the sway configuration file. To obtain a list of available device identifiers, run *swaymsg -t get_inputs*. +Settings can also be applied to all input devices by using the wildcard, _\*_, +in place of _\_ in the commands below. + +Tip: If the configuration settings do not appear to be taking effect, you could +try using _\*_ instead of _\_. If it works with the wildcard, try +using a different identifier from *swaymsg -t get_inputs* until you find the +correct input device. # INPUT COMMANDS @@ -144,7 +151,13 @@ configured. While sway is running, _-_ (hyphen) can be used as an alias for the current seat. Each seat has an independent keyboard focus and a separate cursor that is controlled by the pointer devices of the seat. This is useful for multiple people using the desktop at the same time with their own devices (each -sitting in their own "seat"). +sitting in their own "seat"). The wildcard character, _\*_, can also be used in +place of _\_ to change settings for all seats. + +Tip: If the configuration settings do not appear to be taking effect, you could +try using _\*_ instead of _\_. If it works with the wildcard, try +using a different identifier from *swaymsg -t get_seats* until you find the +correct seat. *seat* attach Attach an input device to this seat by its input identifier. A special From c6b43359e774ce1422e9d469c5081d6a7193dedb Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 17 Feb 2019 00:50:53 +0100 Subject: [PATCH 0111/2054] Fix Meson subproject boolean default options --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index cfd77ce7e..d3172bdd8 100644 --- a/meson.build +++ b/meson.build @@ -62,7 +62,7 @@ git = find_program('git', required: false) # Try first to find wlroots as a subproject, then as a system dependency wlroots_proj = subproject( 'wlroots', - default_options: ['rootston=disabled', 'examples=disabled'], + default_options: ['rootston=false', 'examples=false'], required: false, ) if wlroots_proj.found() From eaec82abd20ebe847bdf17f28b0667b449a3f0bc Mon Sep 17 00:00:00 2001 From: emersion Date: Sat, 16 Feb 2019 23:30:19 +0100 Subject: [PATCH 0112/2054] Disconnect swaybg instead of killing it This is much more reliable. This also fixes race conditions when killing swaybg while it's doing a wl_display_roundtrip. --- include/sway/output.h | 8 ++-- sway/config/output.c | 91 ++++++++++++++++++++++++++++++++++--------- sway/desktop/output.c | 2 + sway/tree/output.c | 5 +-- 4 files changed, 80 insertions(+), 26 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 479897ef2..ea7a21741 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -21,6 +21,7 @@ struct sway_output { struct sway_node node; struct wlr_output *wlr_output; struct sway_server *server; + struct wl_list link; struct wl_list layers[4]; // sway_layer_surface::link struct wlr_box usable_area; @@ -36,6 +37,8 @@ struct sway_output { struct sway_output_state current; + struct wl_client *swaybg_client; + struct wl_listener destroy; struct wl_listener mode; struct wl_listener transform; @@ -43,10 +46,7 @@ struct sway_output { struct wl_listener present; struct wl_listener damage_destroy; struct wl_listener damage_frame; - - struct wl_list link; - - pid_t bg_pid; + struct wl_listener swaybg_client_destroy; struct { struct wl_signal destroy; diff --git a/sway/config/output.c b/sway/config/output.c index 0f238715f..513d03e0f 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1,16 +1,17 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include -#include +#include #include #include -#include #include +#include +#include "log.h" #include "sway/config.h" #include "sway/output.h" #include "sway/tree/root.h" -#include "log.h" int output_name_cmp(const void *item, const void *data) { const struct output_config *output = item; @@ -165,14 +166,71 @@ static bool set_mode(struct wlr_output *output, int width, int height, return wlr_output_set_mode(output, best); } -void terminate_swaybg(pid_t pid) { - int ret = kill(pid, SIGTERM); - if (ret != 0) { - sway_log(SWAY_ERROR, "Unable to terminate swaybg [pid: %d]", pid); - } else { - int status; - waitpid(pid, &status, 0); +static void handle_swaybg_client_destroy(struct wl_listener *listener, + void *data) { + struct sway_output *output = + wl_container_of(listener, output, swaybg_client_destroy); + wl_list_remove(&output->swaybg_client_destroy.link); + wl_list_init(&output->swaybg_client_destroy.link); + output->swaybg_client = NULL; +} + +static bool spawn_swaybg(struct sway_output *output, char *const cmd[]) { + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets) != 0) { + sway_log_errno(SWAY_ERROR, "socketpair failed"); + return false; } + + output->swaybg_client = wl_client_create(server.wl_display, sockets[0]); + if (output->swaybg_client == NULL) { + sway_log_errno(SWAY_ERROR, "wl_client_create failed"); + return false; + } + + output->swaybg_client_destroy.notify = handle_swaybg_client_destroy; + wl_client_add_destroy_listener(output->swaybg_client, + &output->swaybg_client_destroy); + + pid_t pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_ERROR, "fork failed"); + return false; + } else if (pid == 0) { + pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_ERROR, "fork failed"); + exit(EXIT_FAILURE); + } else if (pid == 0) { + // Remove the CLOEXEC flag + int flags = fcntl(sockets[1], F_GETFD); + if (flags == -1) { + sway_log_errno(SWAY_ERROR, "fcntl() failed"); + exit(EXIT_FAILURE); + } + if (fcntl(sockets[1], F_SETFD, flags & ~FD_CLOEXEC) == -1) { + sway_log_errno(SWAY_ERROR, "fcntl() failed"); + exit(EXIT_FAILURE); + } + + char wayland_socket_str[16]; + snprintf(wayland_socket_str, sizeof(wayland_socket_str), + "%d", sockets[1]); + setenv("WAYLAND_SOCKET", wayland_socket_str, true); + + execvp(cmd[0], cmd); + sway_log_errno(SWAY_ERROR, "execvp failed"); + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); + } + + if (waitpid(pid, NULL, 0) < 0) { + sway_log_errno(SWAY_ERROR, "waitpid failed"); + return false; + } + + return true; } bool apply_output_config(struct output_config *oc, struct sway_output *output) { @@ -243,8 +301,8 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { wlr_output_layout_add_auto(root->output_layout, wlr_output); } - if (output->bg_pid != 0) { - terminate_swaybg(output->bg_pid); + if (output->swaybg_client != NULL) { + wl_client_destroy(output->swaybg_client); } if (oc && oc->background && config->swaybg_command) { sway_log(SWAY_DEBUG, "Setting background for output %s to %s", @@ -258,13 +316,8 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { oc->background_fallback ? oc->background_fallback : NULL, NULL, }; - - output->bg_pid = fork(); - if (output->bg_pid < 0) { - sway_log_errno(SWAY_ERROR, "fork failed"); - } else if (output->bg_pid == 0) { - execvp(cmd[0], cmd); - sway_log_errno(SWAY_ERROR, "Failed to execute swaybg"); + if (!spawn_swaybg(output, cmd)) { + return false; } } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index ad75bb35a..c5461ee66 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -506,6 +506,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&output->present.link); wl_list_remove(&output->damage_destroy.link); wl_list_remove(&output->damage_frame.link); + wl_list_remove(&output->swaybg_client_destroy.link); transaction_commit_dirty(); } @@ -612,6 +613,7 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->damage_frame.notify = damage_handle_frame; wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); output->damage_destroy.notify = damage_handle_destroy; + wl_list_init(&output->swaybg_client_destroy.link); struct output_config *oc = output_find_config(output); if (!oc || oc->enabled) { diff --git a/sway/tree/output.c b/sway/tree/output.c index 138144a77..60e0af9f4 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -233,9 +233,8 @@ void output_disable(struct sway_output *output) { root_for_each_container(untrack_output, output); - if (output->bg_pid) { - terminate_swaybg(output->bg_pid); - output->bg_pid = 0; + if (output->swaybg_client != NULL) { + wl_client_destroy(output->swaybg_client); } int index = list_find(root->outputs, output); From f22aef018ba1f2ff50d35676ec6aa2e18e2a4ad5 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sun, 17 Feb 2019 09:51:56 -0500 Subject: [PATCH 0113/2054] Remove refs to unimplemented debuglog command Closes #3695 --- include/sway/commands.h | 1 - sway/sway.5.scd | 4 ---- 2 files changed, 5 deletions(-) diff --git a/include/sway/commands.h b/include/sway/commands.h index 3ed007635..764821a0e 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -112,7 +112,6 @@ sway_cmd cmd_client_placeholder; sway_cmd cmd_client_background; sway_cmd cmd_commands; sway_cmd cmd_create_output; -sway_cmd cmd_debuglog; sway_cmd cmd_default_border; sway_cmd cmd_default_floating_border; sway_cmd cmd_default_orientation; diff --git a/sway/sway.5.scd b/sway/sway.5.scd index fd0a22dc7..b6955acd5 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -413,10 +413,6 @@ The default colors are: : #0c0c0c -*debuglog* on|off|toggle - Enables, disables or toggles debug logging. _toggle_ cannot be used in the - configuration file. - *default_border* normal|none|pixel [] Set default border style for new tiled windows. From 34f5c1e7c40d479a00b384ac0849c0cf0a6c3f5e Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 17 Feb 2019 13:07:04 -0500 Subject: [PATCH 0114/2054] workspace_next_name: fallback to next available number This changes `workspace_next_name` to use the next available number as the workspace name instead of the number of outputs. This fixes the case where a number that is already in use could be returned. The workspace numbers in use have no relation to the number of outputs so it makes more sense to use the lowest available number --- sway/tree/workspace.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index cda6caf7c..9b7c5112d 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -334,16 +334,13 @@ char *workspace_next_name(const char *output_name) { if (target != NULL) { return target; } - // As a fall back, get the current number of active workspaces - // and return that + 1 for the next workspace's name - int ws_num = root->outputs->length; - int l = snprintf(NULL, 0, "%d", ws_num); - char *name = malloc(l + 1); - if (!sway_assert(name, "Could not allocate workspace name")) { - return NULL; - } - sprintf(name, "%d", ws_num++); - return name; + // As a fall back, use the next available number + char name[12] = ""; + unsigned int ws_num = 1; + do { + snprintf(name, sizeof(name), "%u", ws_num++); + } while (workspace_by_number(name)); + return strdup(name); } static bool _workspace_by_number(struct sway_workspace *ws, void *data) { From 0940a46c114cef62e1d666a3038bf5e18d81ba95 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 17 Feb 2019 20:16:23 -0500 Subject: [PATCH 0115/2054] seat_cmd_cursor: fix typo in expected syntax This just fixes a typo in the expected syntax for seat_cmd_cursor --- sway/commands/seat/cursor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c index 0c7609eaf..085e6a33f 100644 --- a/sway/commands/seat/cursor.c +++ b/sway/commands/seat/cursor.c @@ -12,7 +12,7 @@ static struct cmd_results *press_or_release(struct sway_cursor *cursor, static const char expected_syntax[] = "Expected 'cursor ' or " "'cursor ' or " - "'curor '"; + "'cursor '"; static struct cmd_results *handle_command(struct sway_cursor *cursor, int argc, char **argv) { From a1b9aa11d12a5b0a2a521d6b9b89d487be2eedd3 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 17 Feb 2019 20:33:37 -0500 Subject: [PATCH 0116/2054] Use container under cursor for mouse bindings This matches i3's behavior of executing mouse bindings in regards to the container under the cursor instead of what is focused. --- sway/commands/bind.c | 20 +++++++++++++++++--- sway/sway.5.scd | 13 +++++++------ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 59116d67f..172e6b8ae 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -1,10 +1,11 @@ #define _POSIX_C_SOURCE 200809L #include #include -#include -#include #include #include +#include +#include +#include #include "sway/commands.h" #include "sway/config.h" #include "sway/input/cursor.h" @@ -330,7 +331,20 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) { void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding) { sway_log(SWAY_DEBUG, "running command for binding: %s", binding->command); - list_t *res_list = execute_command(binding->command, seat, NULL); + struct sway_container *con = NULL; + if (binding->type == BINDING_MOUSESYM + || binding->type == BINDING_MOUSECODE) { + struct wlr_surface *surface = NULL; + double sx, sy; + struct sway_node *node = node_at_coords(seat, + seat->cursor->cursor->x, seat->cursor->cursor->y, + &surface, &sx, &sy); + if (node && node->type == N_CONTAINER) { + con = node->sway_container; + } + } + + list_t *res_list = execute_command(binding->command, seat, con); bool success = true; for (int i = 0; i < res_list->length; ++i) { struct cmd_results *results = res_list->items[i]; diff --git a/sway/sway.5.scd b/sway/sway.5.scd index b6955acd5..76467d23a 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -293,12 +293,13 @@ runtime. overwrite a binding, swaynag will give you a warning. To silence this, use the _--no-warn_ flag. - Mouse buttons can either be specified in the form _button[1-9]_ or by using - the name of the event code (ex _BTN\_LEFT_ or _BTN\_RIGHT_). For the former - option, the buttons will be mapped to their values in X11 (1=left, 2=middle, - 3=right, 4=scroll up, 5=scroll down, 6=scroll left, 7=scroll right, 8=back, - 9=forward). For the latter option, you can find the event names using - _libinput debug-events_. + Mouse bindings operate on the container under the cursor instead of the + container that has focus. Mouse buttons can either be specified in the form + _button[1-9]_ or by using the name of the event code (ex _BTN\_LEFT_ or + _BTN\_RIGHT_). For the former option, the buttons will be mapped to their + values in X11 (1=left, 2=middle, 3=right, 4=scroll up, 5=scroll down, + 6=scroll left, 7=scroll right, 8=back, 9=forward). For the latter option, + you can find the event names using _libinput debug-events_. _--whole-window_, _--border_, and _--exclude-titlebar_ are mouse-only options which affect the region in which the mouse bindings can be triggered. By From b4836ecbcc67cecf42aa2cbc80e5116a78176328 Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 19 Feb 2019 12:11:16 +0100 Subject: [PATCH 0117/2054] ci: add Alpine CI --- .builds/alpine.yml | 38 +++++++++++++++++++++++++++++ .build.yml => .builds/archlinux.yml | 22 ++++++++--------- 2 files changed, 49 insertions(+), 11 deletions(-) create mode 100644 .builds/alpine.yml rename .build.yml => .builds/archlinux.yml (85%) diff --git a/.builds/alpine.yml b/.builds/alpine.yml new file mode 100644 index 000000000..328625a5d --- /dev/null +++ b/.builds/alpine.yml @@ -0,0 +1,38 @@ +image: alpine/edge +packages: + - cairo-dev + - eudev-dev + - gdk-pixbuf-dev + - json-c-dev + - libevdev-dev + - libinput-dev + - libxcb-dev + - libxkbcommon-dev + - mesa-dev + - meson + - pango-dev + - pixman-dev + - scdoc + - wayland-dev + - wayland-protocols + - xcb-util-image-dev + - xorg-server-xwayland +sources: + - https://github.com/swaywm/sway + - https://github.com/swaywm/wlroots +tasks: + - wlroots: | + cd wlroots + meson --prefix=/usr build -Drootston=false -Dexamples=false + ninja -C build + sudo ninja -C build install + - setup: | + cd sway + meson build + - build: | + cd sway + ninja -C build + - build-no-xwayland: | + cd sway + meson configure build -Dxwayland=disabled + ninja -C build diff --git a/.build.yml b/.builds/archlinux.yml similarity index 85% rename from .build.yml rename to .builds/archlinux.yml index 144333118..c8f116e00 100644 --- a/.build.yml +++ b/.builds/archlinux.yml @@ -1,25 +1,25 @@ -# vim: ft=yaml ts=2 sw=2 et : image: archlinux packages: - - meson - - xorg-server-xwayland - - xcb-util-image - - json-c - - pango - cairo + - gdk-pixbuf2 + - json-c + - libinput + - libxcb + - libxkbcommon + - meson + - pango + - scdoc - wayland - wayland-protocols - - gdk-pixbuf2 - - libinput - - libxkbcommon - - scdoc + - xcb-util-image + - xorg-server-xwayland sources: - https://github.com/swaywm/sway - https://github.com/swaywm/wlroots tasks: - wlroots: | cd wlroots - meson --prefix=/usr build + meson --prefix=/usr build -Drootston=false -Dexamples=false ninja -C build sudo ninja -C build install - setup: | From 11bc369301aa6a91ab03666abad8cb358f525027 Mon Sep 17 00:00:00 2001 From: ljedrz Date: Tue, 19 Feb 2019 14:02:36 +0100 Subject: [PATCH 0118/2054] Add Polish README --- README.md | 3 ++- README.pl.md | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 README.pl.md diff --git a/README.md b/README.md index f5173adff..e11a0dc13 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # sway [**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [Українська](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) - -[Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) - [Español](https://github.com/swaywm/sway/blob/master/README.es.md#sway--) +[Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) - [Español](https://github.com/swaywm/sway/blob/master/README.es.md#sway--) - +[Polski](https://github.com/swaywm/sway/blob/master/README.pl.md#sway--) sway is an i3-compatible [Wayland](http://wayland.freedesktop.org/) compositor. Read the [FAQ](https://github.com/swaywm/sway/wiki). Join the [IRC diff --git a/README.pl.md b/README.pl.md new file mode 100644 index 000000000..843fae605 --- /dev/null +++ b/README.pl.md @@ -0,0 +1,71 @@ +# sway + +[**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [Українська](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) - +[Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) - [Español](https://github.com/swaywm/sway/blob/master/README.es.md#sway--) - +[Polski](https://github.com/swaywm/sway/blob/master/README.pl.md#sway--) + +sway jest kompozytorem [Wayland](http://wayland.freedesktop.org/) kompatybilnym z i3. +Przeczytaj [FAQ](https://github.com/swaywm/sway/wiki). Dołącz do [kanału IRC](http://webchat.freenode.net/?channels=sway&uio=d4) +(#sway na irc.freenode.net). + +Jeśli chcesz wesprzeć rozwój sway, rozważ wsparcie SirCmpwn na jego [stronie Patreon](https://patreon.com/sircmpwn). + +## Podpisy cyfrowe wydań + +Wydania są podpisywane przy pomocy klucza [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) +i publikowane [na GitHubie](https://github.com/swaywm/sway/releases). + +## Instalacja + +### Z pakietów + +Sway jest dostępny w wielu dystybucjach. Spróbuj zainstalować pakiet "sway" w swoim +menedżerze pakietów. Jeśli nie jest dostępny, sprawdź [tę stronę wiki](https://github.com/swaywm/sway/wiki/Unsupported-packages) +aby uzyskać informacje dotyczące instalacji w swojej dystrybucji. + +Jeśli chciałbyś stworzyć pakiet sway dla swojej dystrybucji, odwiedź kanał IRC lub wyślij email na +adres sir@cmpwn.com w celu uzyskania wskazówek. + +### Kompilacja ze Źródła + +Zainstaluj zależności: + +* meson \* +* [wlroots](https://github.com/swaywm/wlroots) +* wayland +* wayland-protocols \* +* pcre +* json-c +* pango +* cairo +* gdk-pixbuf2 \*\* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.8.1 (opcjonalnie: strony pomocy man) \* +* git \* + +_\*zależności kompilacji_ + +_\*\*opcjonalnie: wymagane dla swaybg_ + +Wykonaj następujące polecenia: + + meson build + ninja -C build + sudo ninja -C build install + +Na systemach bez logind należy wykonać polecenie suid na pliku wykonywalnym sway: + + sudo chmod a+s /usr/local/bin/sway + +Sway pozbędzie się uprawnień roota tuż po wystartowaniu. + +## Konfiguracja + +Jeśli już korzystasz z i3, skopiuj swoją konfigurację i3 do katalogu `~/.config/sway/config` i +zadziała od ręki. W przeciwnym razie skopiuj przykładowy plik konfiguracyjny do folderu +`~/.config/sway/config`; zazwyczaj znajduje się w `/etc/sway/config`. +Wykonaj polecenie `man 5 sway` aby uzyskać informacje dotyczące konfiguracji. + +## Uruchamianie + +Wykonaj polecenie `sway` z poziomu TTY. Niektóre menedżery wyświetlania mogą umożliwiać rozruch z ich +poziomu, ale nie jest to wspierane przez sway (w gdm podobno działa to całkiem nieźle). From a42b5d079a6fa184724caa05e4d577a308c7888e Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 19 Feb 2019 16:09:29 +0100 Subject: [PATCH 0119/2054] ci: add FreeBSD --- .builds/freebsd.yml | 53 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .builds/freebsd.yml diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml new file mode 100644 index 000000000..8fa760257 --- /dev/null +++ b/.builds/freebsd.yml @@ -0,0 +1,53 @@ +image: freebsd/latest +packages: +- devel/json-c +- devel/libevdev +- devel/meson +- devel/pkgconf +- graphics/cairo +- graphics/gdk-pixbuf2 +- graphics/wayland +- graphics/wayland-protocols +- textproc/scdoc +- x11-toolkits/pango +- x11/libxcb +- x11/libxkbcommon +# wlroots dependencies +- devel/evdev-proto +- devel/libepoll-shim +- devel/libudev-devd +- graphics/libdrm +- graphics/mesa-libs +- x11/libinput +- x11/libX11 +- x11/pixman +- x11/xcb-util-wm +sources: +- https://github.com/swaywm/sway +- https://github.com/swaywm/wlroots +tasks: +- fixup_epoll: | + cat << 'EOF' | sudo tee /usr/local/libdata/pkgconfig/epoll-shim.pc + prefix=/usr/local + exec_prefix=\$\{\$prefix\} + libdir=${prefix}/lib + sharedlibdir=${prefix}/lib + includedir=${prefix}/include/libepoll-shim + Name: epoll-shim + Description: epoll shim implemented using kevent + Version: 0 + Requires: + Libs: -L${libdir} -L${sharedlibdir} -lepoll-shim + Libs.private: -pthread -lrt + Cflags: -I${includedir} + EOF +- setup: | + cd sway + mkdir subprojects + cd subprojects + ln -s ../../wlroots wlroots + cd .. + meson build +- build: | + cd sway + ninja -C build From b799a30962ff1221dc4a32e76a22fd9ddb9e7ece Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 19 Feb 2019 16:18:31 +0100 Subject: [PATCH 0120/2054] Don't use SOCK_CLOEXEC Manually set the CLOEXEC flag instead, since SOCK_CLOEXEC isn't POSIX. --- sway/config/output.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index 513d03e0f..e7fbad833 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -175,12 +175,33 @@ static void handle_swaybg_client_destroy(struct wl_listener *listener, output->swaybg_client = NULL; } +static bool set_cloexec(int fd, bool cloexec) { + int flags = fcntl(fd, F_GETFD); + if (flags == -1) { + sway_log_errno(SWAY_ERROR, "fcntl failed"); + return false; + } + if (cloexec) { + flags = flags | FD_CLOEXEC; + } else { + flags = flags & ~FD_CLOEXEC; + } + if (fcntl(fd, F_SETFD, flags) == -1) { + sway_log_errno(SWAY_ERROR, "fcntl failed"); + return false; + } + return true; +} + static bool spawn_swaybg(struct sway_output *output, char *const cmd[]) { int sockets[2]; - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockets) != 0) { + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) { sway_log_errno(SWAY_ERROR, "socketpair failed"); return false; } + if (!set_cloexec(sockets[0], true) || !set_cloexec(sockets[1], true)) { + return false; + } output->swaybg_client = wl_client_create(server.wl_display, sockets[0]); if (output->swaybg_client == NULL) { @@ -202,14 +223,7 @@ static bool spawn_swaybg(struct sway_output *output, char *const cmd[]) { sway_log_errno(SWAY_ERROR, "fork failed"); exit(EXIT_FAILURE); } else if (pid == 0) { - // Remove the CLOEXEC flag - int flags = fcntl(sockets[1], F_GETFD); - if (flags == -1) { - sway_log_errno(SWAY_ERROR, "fcntl() failed"); - exit(EXIT_FAILURE); - } - if (fcntl(sockets[1], F_SETFD, flags & ~FD_CLOEXEC) == -1) { - sway_log_errno(SWAY_ERROR, "fcntl() failed"); + if (!set_cloexec(sockets[1], false)) { exit(EXIT_FAILURE); } @@ -225,6 +239,10 @@ static bool spawn_swaybg(struct sway_output *output, char *const cmd[]) { exit(EXIT_SUCCESS); } + if (close(sockets[1]) != 0) { + sway_log_errno(SWAY_ERROR, "close failed"); + return false; + } if (waitpid(pid, NULL, 0) < 0) { sway_log_errno(SWAY_ERROR, "waitpid failed"); return false; From 9bbf10bdfcec1ed1fecb8c8033211dc8b8b66a66 Mon Sep 17 00:00:00 2001 From: Eric Drechsel Date: Mon, 18 Feb 2019 23:56:12 -0800 Subject: [PATCH 0121/2054] layout cmd: always operate on parent container, like i3Fixes #3724 --- sway/commands/layout.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sway/commands/layout.c b/sway/commands/layout.c index 2aca31dc5..7d61c3be3 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -111,10 +111,8 @@ struct cmd_results *cmd_layout(int argc, char **argv) { "Unable to change layout of floating windows"); } - // Typically we change the layout of the current container, but if the - // current container is a view (it usually is) then we'll change the layout - // of the parent instead, as it doesn't make sense for views to have layout. - if (container && container->view) { + // Operate on parent container, like i3. + if (container) { container = container->parent; } From 444e00662cc8e87d4334617904bf3578cb022911 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 20 Feb 2019 11:16:05 +0100 Subject: [PATCH 0122/2054] Add wp-primary-selection-unstable-v1 --- sway/server.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sway/server.c b/sway/server.c index 712d8022b..0fcd58003 100644 --- a/sway/server.c +++ b/sway/server.c @@ -10,13 +10,14 @@ #include #include #include -#include #include +#include #include #include #include -#include #include +#include +#include #include #include #include @@ -122,6 +123,7 @@ bool server_init(struct sway_server *server) { wlr_export_dmabuf_manager_v1_create(server->wl_display); wlr_screencopy_manager_v1_create(server->wl_display); wlr_data_control_manager_v1_create(server->wl_display); + wlr_primary_selection_v1_device_manager_create(server->wl_display); server->socket = wl_display_add_socket_auto(server->wl_display); if (!server->socket) { From 546b976baa9989676c2fb2aff8c36f42b3d19810 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 20 Feb 2019 14:08:20 -0500 Subject: [PATCH 0123/2054] output_evacuate: call workspace_consider_destroy This calls `workspace_consider_destroy` on the workspace that was visible on an output that a workspace was just evacuated to. This prevents having hidden empty workspaces. --- sway/tree/output.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sway/tree/output.c b/sway/tree/output.c index 60e0af9f4..146bc4231 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -189,10 +189,19 @@ static void output_evacuate(struct sway_output *output) { continue; } + struct sway_workspace *new_output_ws = + output_get_active_workspace(new_output); + workspace_output_add_priority(workspace, new_output); output_add_workspace(new_output, workspace); output_sort_workspaces(new_output); ipc_event_workspace(NULL, workspace, "move"); + + // If there is an old workspace (the noop output may not have one), + // check to see if it is empty and should be destroyed. + if (new_output_ws) { + workspace_consider_destroy(new_output_ws); + } } } From a968a650e26b9f6bba134ee8dfa9865e958c29da Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Thu, 21 Feb 2019 08:36:58 +0100 Subject: [PATCH 0124/2054] ipc-client: free payload after sending it over the socket Fixes memory leaks in the form of: Direct leak of 20 byte(s) in 1 object(s) allocated from: #0 0x7f5f7c2f4f30 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0xedf30) #1 0x563c7995b36a in join_args ../common/stringop.c:268 #2 0x563c798a6a1a in main ../sway/main.c:348 #3 0x7f5f7b4d609a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2409a) --- sway/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/main.c b/sway/main.c index b118a1823..5a65e50cf 100644 --- a/sway/main.c +++ b/sway/main.c @@ -347,6 +347,7 @@ int main(int argc, char **argv) { } char *command = join_args(argv + optind, argc - optind); run_as_ipc_client(command, socket_path); + free(command); return 0; } From aa4ccd845cf93ac2117b12083fed748f4c01e744 Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Thu, 21 Feb 2019 08:37:23 +0100 Subject: [PATCH 0125/2054] run_as_ipc_client: free response after running the IPC command Fixes memory leaks in the form of: Direct leak of 24 byte(s) in 1 object(s) allocated from: #0 0x7f5f7c2f4f30 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0xedf30) #1 0x563c799569f2 in ipc_recv_response ../common/ipc-client.c:94 #2 0x563c79957062 in ipc_single_command ../common/ipc-client.c:138 #3 0x563c798a56cc in run_as_ipc_client ../sway/main.c:127 #4 0x563c798a6a3a in main ../sway/main.c:349 #5 0x7f5f7b4d609a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2409a) --- sway/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/main.c b/sway/main.c index 5a65e50cf..6754190f9 100644 --- a/sway/main.c +++ b/sway/main.c @@ -126,6 +126,7 @@ void run_as_ipc_client(char *command, char *socket_path) { uint32_t len = strlen(command); char *resp = ipc_single_command(socketfd, IPC_COMMAND, command, &len); printf("%s\n", resp); + free(resp); close(socketfd); } From 03dfdb1edbfc445fe30b6456827ae93ef6c18426 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Thu, 21 Feb 2019 08:06:35 -0500 Subject: [PATCH 0126/2054] output_get_active_workspace: check workspaces length If an output's node was dirty and the transaction was committed before a workspace was moved to or created for the output, the instruction would have a bad value for `state->active_workspace` due to a missing length check in `output_get_active_workspace`. If there was no focus on the output, the first workspace was being returned. If the workspace list was currently empty, the value was either garbage, or in the case of an output being disabled and re-enabled, a workspace that may have been previously freed. This just adds the length check to avoid returning out of bounds value. --- sway/desktop/output.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index c5461ee66..61beb7af3 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -320,6 +320,9 @@ struct sway_workspace *output_get_active_workspace(struct sway_output *output) { struct sway_seat *seat = input_manager_current_seat(); struct sway_node *focus = seat_get_active_tiling_child(seat, &output->node); if (!focus) { + if (!output->workspaces->length) { + return NULL; + } return output->workspaces->items[0]; } return focus->sway_workspace; From d3d7956576341bbbfb60d045175b0e8a44752e0b Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Thu, 21 Feb 2019 13:24:13 -0500 Subject: [PATCH 0127/2054] Handle NULL from output_get_active_workspace This modifies the places where output_get_active_workspace is called to handle a NULL result. Some places already handled it and did not need a change, some just have guard off code blocks, others return errors, and some have sway_asserts since the case should never happen. A lot of this is probably just safety precautions since they probably will never be called when `output_get_active_workspace` is not fully configured with a workspace. --- sway/commands/focus.c | 3 +++ sway/commands/move.c | 19 ++++++++++++++++++- sway/commands/sticky.c | 6 ++++++ sway/commands/swap.c | 4 ++++ sway/commands/titlebar_border_thickness.c | 7 ++++++- sway/desktop/output.c | 3 +++ sway/input/cursor.c | 3 +++ sway/ipc-json.c | 3 +++ sway/tree/container.c | 2 +- sway/tree/output.c | 3 +++ 10 files changed, 50 insertions(+), 3 deletions(-) diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 79b2f551f..25df51304 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -37,6 +37,9 @@ static struct sway_node *get_node_in_output_direction( struct sway_output *output, enum wlr_direction dir) { struct sway_seat *seat = config->handler_context.seat; struct sway_workspace *ws = output_get_active_workspace(output); + if (!sway_assert(ws, "Expected output to have a workspace")) { + return NULL; + } if (ws->fullscreen) { return seat_get_focus_inactive(seat, &ws->fullscreen->node); } diff --git a/sway/commands/move.c b/sway/commands/move.c index 16f8cdb6f..d4fb90226 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -283,6 +283,9 @@ static bool container_move_in_direction(struct sway_container *container, return false; } struct sway_workspace *ws = output_get_active_workspace(new_output); + if (!sway_assert(ws, "Expected output to have a workspace")) { + return false; + } container_move_to_workspace(container, ws); return true; } @@ -360,6 +363,9 @@ static bool container_move_in_direction(struct sway_container *container, output_get_in_direction(container->workspace->output, move_dir); if (output) { struct sway_workspace *ws = output_get_active_workspace(output); + if (!sway_assert(ws, "Expected output to have a workspace")) { + return false; + } container_move_to_workspace_from_direction(container, ws, move_dir); return true; } @@ -525,6 +531,10 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { case N_OUTPUT: { struct sway_output *output = destination->sway_output; struct sway_workspace *ws = output_get_active_workspace(output); + if (!sway_assert(ws, "Expected output to have a workspace")) { + return cmd_results_new(CMD_FAILURE, + "Expected output to have a workspace"); + } container_move_to_workspace(container, ws); } break; @@ -538,7 +548,11 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { // restore focus on destination output back to its last active workspace struct sway_workspace *new_workspace = output_get_active_workspace(new_output); - if (new_output_last_ws && new_output_last_ws != new_workspace) { + if (!sway_assert(new_workspace, "Expected output to have a workspace")) { + return cmd_results_new(CMD_FAILURE, + "Expected output to have a workspace"); + } + if (new_output_last_ws != new_workspace) { struct sway_node *new_output_last_focus = seat_get_focus_inactive(seat, &new_output_last_ws->node); seat_set_raw_focus(seat, new_output_last_focus); @@ -585,6 +599,9 @@ static void workspace_move_to_output(struct sway_workspace *workspace, workspace_detach(workspace); struct sway_workspace *new_output_old_ws = output_get_active_workspace(output); + if (!sway_assert(new_output_old_ws, "Expected output to have a workspace")) { + return; + } output_add_workspace(output, workspace); diff --git a/sway/commands/sticky.c b/sway/commands/sticky.c index 5b70199cb..9df1fe09d 100644 --- a/sway/commands/sticky.c +++ b/sway/commands/sticky.c @@ -9,6 +9,7 @@ #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "list.h" +#include "log.h" #include "util.h" struct cmd_results *cmd_sticky(int argc, char **argv) { @@ -29,6 +30,11 @@ struct cmd_results *cmd_sticky(int argc, char **argv) { // move container to active workspace struct sway_workspace *active_workspace = output_get_active_workspace(container->workspace->output); + if (!sway_assert(active_workspace, + "Expected output to have a workspace")) { + return cmd_results_new(CMD_FAILURE, + "Expected output to have a workspace"); + } if (container->workspace != active_workspace) { struct sway_workspace *old_workspace = container->workspace; container_detach(container); diff --git a/sway/commands/swap.c b/sway/commands/swap.c index 0e2c2d105..b978af162 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -118,6 +118,10 @@ static void container_swap(struct sway_container *con1, output_get_active_workspace(con1->workspace->output); struct sway_workspace *vis2 = output_get_active_workspace(con2->workspace->output); + if (!sway_assert(vis1 && vis2, "con1 or con2 are on an output without a" + "workspace. This should not happen")) { + return; + } char *stored_prev_name = NULL; if (seat->prev_workspace_name) { diff --git a/sway/commands/titlebar_border_thickness.c b/sway/commands/titlebar_border_thickness.c index 3c5e9ba12..7c27c163a 100644 --- a/sway/commands/titlebar_border_thickness.c +++ b/sway/commands/titlebar_border_thickness.c @@ -21,7 +21,12 @@ struct cmd_results *cmd_titlebar_border_thickness(int argc, char **argv) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; - arrange_workspace(output_get_active_workspace(output)); + struct sway_workspace *ws = output_get_active_workspace(output); + if (!sway_assert(ws, "Expected output to have a workspace")) { + return cmd_results_new(CMD_FAILURE, + "Expected output to have a workspace"); + } + arrange_workspace(ws); output_damage_whole(output); } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 61beb7af3..d7d3fc07e 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -251,6 +251,9 @@ static void output_for_each_surface(struct sway_output *output, }; struct sway_workspace *workspace = output_get_active_workspace(output); + if (!workspace) { + return; + } struct sway_container *fullscreen_con = root->fullscreen_global; if (fullscreen_con && container_is_scratchpad_hidden(fullscreen_con)) { fullscreen_con = NULL; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 170532bea..3236c74a1 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -104,6 +104,9 @@ struct sway_node *node_at_coords( // find the focused workspace on the output for this seat struct sway_workspace *ws = output_get_active_workspace(output); + if (!ws) { + return NULL; + } if ((*surface = layer_surface_at(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 6d36c7274..a2ab2bba8 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -198,6 +198,9 @@ static void ipc_json_describe_output(struct sway_output *output, ipc_json_output_transform_description(wlr_output->transform))); struct sway_workspace *ws = output_get_active_workspace(output); + if (!sway_assert(ws, "Expected output to have a workspace")) { + return; + } json_object_object_add(object, "current_workspace", json_object_new_string(ws->name)); diff --git a/sway/tree/container.c b/sway/tree/container.c index 9358dad76..933907f4e 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -831,7 +831,7 @@ void container_floating_move_to(struct sway_container *con, } struct sway_workspace *new_workspace = output_get_active_workspace(new_output); - if (old_workspace != new_workspace) { + if (new_workspace && old_workspace != new_workspace) { container_detach(con); workspace_add_floating(new_workspace, con); arrange_workspace(old_workspace); diff --git a/sway/tree/output.c b/sway/tree/output.c index 146bc4231..e0a66e0bd 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -143,6 +143,9 @@ void output_enable(struct sway_output *output, struct output_config *oc) { static void evacuate_sticky(struct sway_workspace *old_ws, struct sway_output *new_output) { struct sway_workspace *new_ws = output_get_active_workspace(new_output); + if (!sway_assert(new_ws, "New output does not have a workspace")) { + return; + } while (old_ws->floating->length) { struct sway_container *sticky = old_ws->floating->items[0]; container_detach(sticky); From 16ddd00102c7713b7d1c404393768eb06c829751 Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Fri, 22 Feb 2019 06:42:40 +0100 Subject: [PATCH 0128/2054] commands/move: reintroduce wrongly removed NULL check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit d3d7956576341bbbfb60d045175b0e8a44752e0b removed this NULL check, which leads to the following backtrace: #0 0x0000557bd201df46 in node_is_view (node=0x0) at ../sway/sway/tree/node.c:41 #1 0x0000557bd1ff5d4e in seat_get_focus_inactive (seat=0x557bd3fc7580, node=0x0) at ../sway/sway/input/seat.c:968 current = 0x557bd2033485 #2 0x0000557bd2009f24 in cmd_move_container (argc=3, argv=0x557bd46b19c0) at ../sway/sway/commands/move.c:557 new_output_last_focus = 0x0 error = 0x0 node = 0x557bd469f360 workspace = 0x557bd4572ee0 container = 0x557bd469f360 no_auto_back_and_forth = false seat = 0x557bd3fc7580 old_parent = 0x0 old_ws = 0x557bd4572ee0 old_output = 0x557bd411f740 destination = 0x557bd46a0cc0 new_output = 0x557bd411f740 new_output_last_ws = 0x0 focus = 0x557bd469f360 __PRETTY_FUNCTION__ = "cmd_move_container" new_workspace = 0x557bd4572ee0 […] Reintroduce the NULL check to fix the bug. Fixes #3746 --- sway/commands/move.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/commands/move.c b/sway/commands/move.c index d4fb90226..b593138af 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -552,7 +552,7 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "Expected output to have a workspace"); } - if (new_output_last_ws != new_workspace) { + if (new_output_last_ws && new_output_last_ws != new_workspace) { struct sway_node *new_output_last_focus = seat_get_focus_inactive(seat, &new_output_last_ws->node); seat_set_raw_focus(seat, new_output_last_focus); From 7252ca09a7ccd09673a2903509cde6dcb077bfb7 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 22 Feb 2019 00:10:02 -0500 Subject: [PATCH 0129/2054] move scratchpad: hide visible scratchpad container This makes it so running `move [to] scratchpad` on a container already in the scratchpad does not return an error. To match i3's behavior, a visible scratchpad container will be hidden and a hidden scratchpad container will be treated as a noop. --- sway/commands/move.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sway/commands/move.c b/sway/commands/move.c index b593138af..4306aac86 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -854,11 +854,11 @@ static struct cmd_results *cmd_move_to_scratchpad(void) { } } - if (con->scratchpad) { - return cmd_results_new(CMD_INVALID, - "Container is already in the scratchpad"); + if (!con->scratchpad) { + root_scratchpad_add_container(con); + } else if (con->workspace) { + root_scratchpad_hide(con); } - root_scratchpad_add_container(con); return cmd_results_new(CMD_SUCCESS, NULL); } From f078a1ccb899e04d24ba76418bdf574aa45c9c7e Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 22 Feb 2019 02:48:57 -0500 Subject: [PATCH 0130/2054] input/cursor: allow whole-window bindings on ws To match i3's behavior, this allows mouse bindings to be triggered over a workspace when `--whole-window` is given. --- sway/input/cursor.c | 23 +++++++++++++++-------- sway/sway.5.scd | 6 +++--- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 3236c74a1..fb1edd0b0 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -552,10 +552,11 @@ static void state_add_button(struct sway_cursor *cursor, uint32_t button) { static struct sway_binding* get_active_mouse_binding( const struct sway_cursor *cursor, list_t *bindings, uint32_t modifiers, bool release, bool on_titlebar, bool on_border, bool on_content, - const char *identifier) { - uint32_t click_region = (on_titlebar ? BINDING_TITLEBAR : 0) | - (on_border ? BINDING_BORDER : 0) | - (on_content ? BINDING_CONTENTS : 0); + bool on_workspace, const char *identifier) { + uint32_t click_region = + ((on_titlebar || on_workspace) ? BINDING_TITLEBAR : 0) | + ((on_border || on_workspace) ? BINDING_BORDER : 0) | + ((on_content || on_workspace) ? BINDING_CONTENTS : 0); struct sway_binding *current = NULL; for (int i = 0; i < bindings->length; ++i) { @@ -564,6 +565,8 @@ static struct sway_binding* get_active_mouse_binding( cursor->pressed_button_count != (size_t)binding->keys->length || release != (binding->flags & BINDING_RELEASE) || !(click_region & binding->flags) || + (on_workspace && + (click_region & binding->flags) != click_region) || (strcmp(binding->input, identifier) != 0 && strcmp(binding->input, "*") != 0)) { continue; @@ -629,6 +632,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, find_resize_edge(cont, cursor) : WLR_EDGE_NONE; bool on_border = edge != WLR_EDGE_NONE; bool on_contents = cont && !on_border && surface; + bool on_workspace = node && node->type == N_WORKSPACE; bool on_titlebar = cont && !on_border && !surface; // Handle mouse bindings @@ -642,11 +646,13 @@ void dispatch_cursor_button(struct sway_cursor *cursor, state_add_button(cursor, button); binding = get_active_mouse_binding(cursor, config->current_mode->mouse_bindings, modifiers, false, - on_titlebar, on_border, on_contents, device_identifier); + on_titlebar, on_border, on_contents, on_workspace, + device_identifier); } else { binding = get_active_mouse_binding(cursor, config->current_mode->mouse_bindings, modifiers, true, - on_titlebar, on_border, on_contents, device_identifier); + on_titlebar, on_border, on_contents, on_workspace, + device_identifier); state_erase_button(cursor, button); } free(device_identifier); @@ -836,6 +842,7 @@ void dispatch_cursor_axis(struct sway_cursor *cursor, bool on_titlebar_border = cont && on_border && cursor->cursor->y < cont->content_y; bool on_contents = cont && !on_border && surface; + bool on_workspace = node && node->type == N_WORKSPACE; float scroll_factor = (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor; @@ -854,7 +861,7 @@ void dispatch_cursor_axis(struct sway_cursor *cursor, state_add_button(cursor, button); binding = get_active_mouse_binding(cursor, config->current_mode->mouse_bindings, modifiers, false, - on_titlebar, on_border, on_contents, dev_id); + on_titlebar, on_border, on_contents, on_workspace, dev_id); if (binding) { seat_execute_command(seat, binding); handled = true; @@ -895,7 +902,7 @@ void dispatch_cursor_axis(struct sway_cursor *cursor, // Handle mouse bindings - x11 mouse buttons 4-7 - release event binding = get_active_mouse_binding(cursor, config->current_mode->mouse_bindings, modifiers, true, - on_titlebar, on_border, on_contents, dev_id); + on_titlebar, on_border, on_contents, on_workspace, dev_id); state_erase_button(cursor, button); if (binding) { seat_execute_command(seat, binding); diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 76467d23a..1affca5fe 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -310,9 +310,9 @@ runtime. conjunction with any other option to specify that the titlebar should be excluded from the region of consideration. - There is currently, however, no way to execute a mouse binding over a layer - surface (which includes the background of an empty workspace). This behaviour - is carried over from i3. + If _--whole-window_ is given, the command can be triggered when the cursor + is over an empty workspace. Using a mouse binding over a layer surface's + exclusive region is not currently possible. Example: ``` From 3ead287064e8176520e7f15bf84fb179cbf1f1b1 Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Sat, 23 Feb 2019 08:28:49 +0100 Subject: [PATCH 0131/2054] view: set xdg_decoration->view to NULL, check decoration destroy Fixes heap-use-after-free: ==32046==ERROR: AddressSanitizer: heap-use-after-free on address 0x615000064d20 at pc 0x55571ce4d303 bp 0x7fff545c64c0 sp 0x7fff545c64b0 WRITE of size 8 at 0x615000064d20 thread T0 #0 0x55571ce4d302 in xdg_decoration_handle_destroy ../sway/xdg_decoration.c:13 #1 0x7f64009d6f36 in wlr_signal_emit_safe ../util/signal.c:29 #2 0x7f64009d3c46 in toplevel_decoration_handle_resource_destroy ../types/wlr_xdg_decoration_v1.c:65 #3 0x7f6400a19f8d (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0x7f8d) #4 0x7f6400a19fed in wl_resource_destroy (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0x7fed) #5 0x7f64009d3d1f in toplevel_decoration_handle_surface_destroy ../types/wlr_xdg_decoration_v1.c:82 #6 0x7f64009d6f36 in wlr_signal_emit_safe ../util/signal.c:29 #7 0x7f64009b059c in reset_xdg_surface ../types/xdg_shell/wlr_xdg_surface.c:453 #8 0x7f64009b0688 in destroy_xdg_surface ../types/xdg_shell/wlr_xdg_surface.c:483 #9 0x7f64009af08c in xdg_client_handle_resource_destroy ../types/xdg_shell/wlr_xdg_shell.c:71 #10 0x7f6400a19f8d (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0x7f8d) #11 0x7f6400a1e211 (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0xc211) #12 0x7f6400a1e6fe (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0xc6fe) #13 0x7f6400a1a0ec in wl_client_destroy (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0x80ec) #14 0x7f6400a1a1c4 (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0x81c4) #15 0x7f6400a1b941 in wl_event_loop_dispatch (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0x9941) #16 0x7f6400a1a569 in wl_display_run (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0x8569) #17 0x55571ce4c7fd in server_run ../sway/server.c:214 #18 0x55571ce4ad59 in main ../sway/main.c:405 #19 0x7f640071109a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2409a) #20 0x55571ce2cfa9 in _start (/usr/local/bin/sway+0x35fa9) 0x615000064d20 is located 32 bytes inside of 504-byte region [0x615000064d00,0x615000064ef8) freed by thread T0 here: #0 0x7f6401531b70 in free (/usr/lib/x86_64-linux-gnu/libasan.so.5+0xedb70) #1 0x55571ce6c72b in destroy ../sway/desktop/xdg_shell.c:252 #2 0x55571cee3f7b in view_destroy ../sway/tree/view.c:60 #3 0x55571cee4090 in view_begin_destroy ../sway/tree/view.c:73 #4 0x55571ce6dd95 in handle_destroy ../sway/desktop/xdg_shell.c:464 #5 0x7f64009d6f36 in wlr_signal_emit_safe ../util/signal.c:29 #6 0x7f64009b059c in reset_xdg_surface ../types/xdg_shell/wlr_xdg_surface.c:453 #7 0x7f64009b0688 in destroy_xdg_surface ../types/xdg_shell/wlr_xdg_surface.c:483 #8 0x7f64009af08c in xdg_client_handle_resource_destroy ../types/xdg_shell/wlr_xdg_shell.c:71 #9 0x7f6400a19f8d (/usr/lib/x86_64-linux-gnu/libwayland-server.so.0+0x7f8d) previously allocated by thread T0 here: #0 0x7f6401532138 in calloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0xee138) #1 0x55571ce6df39 in handle_xdg_shell_surface ../sway/desktop/xdg_shell.c:485 #2 0x7f64009d6f36 in wlr_signal_emit_safe ../util/signal.c:29 #3 0x7f64009b0167 in handle_xdg_surface_commit ../types/xdg_shell/wlr_xdg_surface.c:350 #4 0x7f64009ce2a5 in surface_commit_pending ../types/wlr_surface.c:372 #5 0x7f64009ce523 in surface_commit ../types/wlr_surface.c:444 #6 0x7f63ff63ddad in ffi_call_unix64 (/usr/lib/x86_64-linux-gnu/libffi.so.6+0x5dad) Fixes #3759 --- sway/desktop/xdg_shell.c | 4 ++++ sway/xdg_decoration.c | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index ce6fe41a1..c84ca1115 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -17,6 +17,7 @@ #include "sway/tree/container.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" +#include "sway/xdg_decoration.h" static const struct sway_view_child_impl popup_impl; @@ -461,6 +462,9 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xdg_shell_view->map.link); wl_list_remove(&xdg_shell_view->unmap.link); view->wlr_xdg_surface = NULL; + if (view->xdg_decoration) { + view->xdg_decoration->view = NULL; + } view_begin_destroy(view); } diff --git a/sway/xdg_decoration.c b/sway/xdg_decoration.c index 39e0df564..9ac87191d 100644 --- a/sway/xdg_decoration.c +++ b/sway/xdg_decoration.c @@ -10,7 +10,9 @@ static void xdg_decoration_handle_destroy(struct wl_listener *listener, void *data) { struct sway_xdg_decoration *deco = wl_container_of(listener, deco, destroy); - deco->view->xdg_decoration = NULL; + if(deco->view) { + deco->view->xdg_decoration = NULL; + } wl_list_remove(&deco->destroy.link); wl_list_remove(&deco->request_mode.link); wl_list_remove(&deco->link); From bcec866c6b383cbba1e7294f011be64425a05ff7 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 22 Feb 2019 23:34:26 -0500 Subject: [PATCH 0132/2054] handle_layer_shell_surface: do not use noop output If the noop output is focused (all other outputs disabled/disconnected), do not auto assign a layer surface to it. The noop output is not enabled and does not have the `output->layers` list initialized. It also does not make sense to map the layer surfaces to something that is not visible. --- sway/desktop/layer_shell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 0767247c4..b2fea880f 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -378,7 +378,7 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { output = ws->output; } } - if (!output) { + if (!output || output == root->noop_output) { if (!root->outputs->length) { sway_log(SWAY_ERROR, "no output to auto-assign layer surface '%s' to", From cdf30033afdb4c1ead47ed208cee0c63a82b7ea4 Mon Sep 17 00:00:00 2001 From: Tim Sampson Date: Sat, 23 Feb 2019 08:27:03 +0200 Subject: [PATCH 0133/2054] fish: improve completions a little bit --- completions/fish/sway.fish | 1 + completions/fish/swaymsg.fish | 1 + completions/fish/swaynag.fish | 45 ++++++++++++++++++----------------- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/completions/fish/sway.fish b/completions/fish/sway.fish index 31165ef4f..d488d49ab 100644 --- a/completions/fish/sway.fish +++ b/completions/fish/sway.fish @@ -1,5 +1,6 @@ # sway(1) completion +complete -f -c sway complete -c sway -s h -l help --description "Show help message and quit." complete -c sway -s c -l config --description "Specifies a config file." complete -c sway -s C -l validate --description "Check the validity of the config file, then exit." diff --git a/completions/fish/swaymsg.fish b/completions/fish/swaymsg.fish index fd577413f..636e35006 100644 --- a/completions/fish/swaymsg.fish +++ b/completions/fish/swaymsg.fish @@ -1,5 +1,6 @@ # swaymsg(1) completion +complete -f -c swaymsg complete -c swaymsg -s h -l help --description "Show help message and quit." complete -c swaymsg -s q -l quiet --description "Sends the IPC message but does not print the response from sway." complete -c swaymsg -s v -l version --description "Print the version (of swaymsg) and quit." diff --git a/completions/fish/swaynag.fish b/completions/fish/swaynag.fish index 784d7fade..7015d5e34 100644 --- a/completions/fish/swaynag.fish +++ b/completions/fish/swaynag.fish @@ -1,29 +1,30 @@ # swaynag -complete -c swaynag -s C -l config --description 'The config file to use. Default: $HOME/.swaylock/config, $XDG_CONFIG_HOME/swaylock/config, and SYSCONFDIR/swaylock/config.' +complete -f -c swaynag +complete -c swaynag -s C -l config -r --description 'The config file to use. Default: $HOME/.swaynag/config, $XDG_CONFIG_HOME/swaynag/config, and SYSCONFDIR/swaynag/config.' complete -c swaynag -s d -l debug --description 'Enable debugging.' -complete -c swaynag -s e -l edge --description 'Set the edge to use: top or bottom' -complete -c swaynag -s f -l font --description 'Set the font to use.' +complete -c swaynag -s e -l edge -fr --description 'Set the edge to use: top or bottom' +complete -c swaynag -s f -l font -r --description 'Set the font to use.' complete -c swaynag -s h -l help --description 'Show help message and quit.' -complete -c swaynag -s b -l button --description 'Create a button with a text and an action which is executed when pressed. Multiple buttons can be defined by providing the flag multiple times.' +complete -c swaynag -s b -l button -fr --description 'Create a button with a text and an action which is executed when pressed. Multiple buttons can be defined by providing the flag multiple times.' complete -c swaynag -s l -l detailed-message --description 'Read a detailed message from stdin. A button to toggle details will be added. Details are shown in a scrollable multi-line text area.' -complete -c swaynag -s L -l detailed-button --description 'Set the text for the button that toggles details. This has no effect if there is not a detailed message. The default is "Toggle details".' -complete -c swaynag -s m -l message --description 'Set the message text.' -complete -c swaynag -s o -l output --description 'Set the output to use.' -complete -c swaynag -s s -l dismiss-button --description 'Sets the text for the dismiss nagbar button. The default is "X".' -complete -c swaynag -s t -l type --description 'Set the message type. Two types are created by default "error" and "warning". Custom types can be defined in the config file.' +complete -c swaynag -s L -l detailed-button -fr --description 'Set the text for the button that toggles details. This has no effect if there is not a detailed message. The default is "Toggle details".' +complete -c swaynag -s m -l message -fr --description 'Set the message text.' +complete -c swaynag -s o -l output -fr --description 'Set the output to use.' +complete -c swaynag -s s -l dismiss-button -fr --description 'Sets the text for the dismiss nagbar button. The default is "X".' +complete -c swaynag -s t -l type -fr --description 'Set the message type. Two types are created by default "error" and "warning". Custom types can be defined in the config file.' complete -c swaynag -s v -l version --description 'Show the version number and quit.' # Appearance -complete -c swaynag -l background --description 'Set the color of the background.' -complete -c swaynag -l border --description 'Set the color of the border.' -complete -c swaynag -l border-bottom --description 'Set the color of the bottom border.' -complete -c swaynag -l button-background --description 'Set the color for the background for buttons.' -complete -c swaynag -l text --description 'Set the text color.' -complete -c swaynag -l border-bottom-size --description 'Set the thickness of the bottom border.' -complete -c swaynag -l message-padding --description 'Set the padding for the message.' -complete -c swaynag -l details-border-size --description 'Set the thickness for the details border.' -complete -c swaynag -l button-border-size --description 'Set the thickness for the button border.' -complete -c swaynag -l button-gap --description 'Set the size of the gap between buttons.' -complete -c swaynag -l button-dismiss-gap --description 'Set the size of the gap between the dismiss button and another button.' -complete -c swaynag -l button-margin-right --description 'Set the margin from the right of the dismiss button to edge.' -complete -c swaynag -l button-padding --description 'Set the padding for the button text.' +complete -c swaynag -l background -fr --description 'Set the color of the background.' +complete -c swaynag -l border -fr --description 'Set the color of the border.' +complete -c swaynag -l border-bottom -fr --description 'Set the color of the bottom border.' +complete -c swaynag -l button-background -fr --description 'Set the color for the background for buttons.' +complete -c swaynag -l text -fr --description 'Set the text color.' +complete -c swaynag -l border-bottom-size -fr --description 'Set the thickness of the bottom border.' +complete -c swaynag -l message-padding -fr --description 'Set the padding for the message.' +complete -c swaynag -l details-border-size -fr --description 'Set the thickness for the details border.' +complete -c swaynag -l button-border-size -fr --description 'Set the thickness for the button border.' +complete -c swaynag -l button-gap -fr --description 'Set the size of the gap between buttons.' +complete -c swaynag -l button-dismiss-gap -fr --description 'Set the size of the gap between the dismiss button and another button.' +complete -c swaynag -l button-margin-right -fr --description 'Set the margin from the right of the dismiss button to edge.' +complete -c swaynag -l button-padding -fr --description 'Set the padding for the button text.' From 9350a52c0e8b502f9dc7f876da0527607a44bfec Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 22 Feb 2019 23:09:39 -0500 Subject: [PATCH 0134/2054] handle_seat_node_destroy: update seat->workspace If an unmanaged or layer surface is focused when an output gets disabled and an empty workspace on the output was focused by the seat, the seat needs to refocus it's focus inactive to update the value of `seat->workspace`. --- sway/input/seat.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sway/input/seat.c b/sway/input/seat.c index 69b048430..6482a47cb 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -146,6 +146,19 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { struct sway_node *focus = seat_get_focus(seat); if (node->type == N_WORKSPACE) { + // If an unmanaged or layer surface is focused when an output gets + // disabled and an empty workspace on the output was focused by the + // seat, the seat needs to refocus it's focus inactive to update the + // value of seat->workspace. + if (seat->workspace == node->sway_workspace) { + struct sway_node *node = seat_get_focus_inactive(seat, &root->node); + seat_set_focus(seat, NULL); + if (node) { + seat_set_focus(seat, node); + } else { + seat->workspace = NULL; + } + } seat_node_destroy(seat_node); return; } From 656541bcc41cf495e87e0be17c8e61d14f586dac Mon Sep 17 00:00:00 2001 From: emersion Date: Sat, 16 Feb 2019 11:55:44 +0100 Subject: [PATCH 0135/2054] Update for swaywm/wlroots#1517 --- include/sway/input/seat.h | 3 ++- sway/input/cursor.c | 19 ++++++------- sway/input/seat.c | 57 ++++++++++++++++++++++++++++++++------- 3 files changed, 60 insertions(+), 19 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 1c9354df4..8fedf7972 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -75,7 +75,8 @@ struct sway_seat { struct wl_listener focus_destroy; struct wl_listener new_node; - struct wl_listener new_drag_icon; + struct wl_listener request_start_drag; + struct wl_listener start_drag; struct wl_listener request_set_selection; struct wl_listener request_set_primary_selection; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index fb1edd0b0..fb4728b42 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -353,7 +353,6 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, } struct sway_seat *seat = cursor->seat; - struct wlr_seat *wlr_seat = seat->wlr_seat; if (seat_doing_seatop(seat)) { seatop_motion(seat, time_msec); @@ -404,10 +403,11 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, cursor_do_rebase(cursor, time_msec, node, surface, sx, sy); - struct wlr_drag_icon *wlr_drag_icon; - wl_list_for_each(wlr_drag_icon, &wlr_seat->drag_icons, link) { - struct sway_drag_icon *drag_icon = wlr_drag_icon->data; - drag_icon_update_position(drag_icon); + struct sway_drag_icon *drag_icon; + wl_list_for_each(drag_icon, &root->drag_icons, link) { + if (drag_icon->seat == seat) { + drag_icon_update_position(drag_icon); + } } } @@ -991,10 +991,11 @@ static void handle_touch_motion(struct wl_listener *listener, void *data) { seat->touch_x = lx; seat->touch_y = ly; - struct wlr_drag_icon *wlr_drag_icon; - wl_list_for_each(wlr_drag_icon, &wlr_seat->drag_icons, link) { - struct sway_drag_icon *drag_icon = wlr_drag_icon->data; - drag_icon_update_position(drag_icon); + struct sway_drag_icon *drag_icon; + wl_list_for_each(drag_icon, &root->drag_icons, link) { + if (drag_icon->seat == seat) { + drag_icon_update_position(drag_icon); + } } } diff --git a/sway/input/seat.c b/sway/input/seat.c index 6482a47cb..a16d3f276 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -44,7 +44,8 @@ void seat_destroy(struct sway_seat *seat) { } sway_cursor_destroy(seat->cursor); wl_list_remove(&seat->new_node.link); - wl_list_remove(&seat->new_drag_icon.link); + wl_list_remove(&seat->request_start_drag.link); + wl_list_remove(&seat->start_drag.link); wl_list_remove(&seat->request_set_selection.link); wl_list_remove(&seat->request_set_primary_selection.link); wl_list_remove(&seat->link); @@ -261,12 +262,16 @@ void drag_icon_update_position(struct sway_drag_icon *icon) { struct wlr_drag_icon *wlr_icon = icon->wlr_drag_icon; struct sway_seat *seat = icon->seat; struct wlr_cursor *cursor = seat->cursor->cursor; - if (wlr_icon->is_pointer) { + switch (wlr_icon->drag->grab_type) { + case WLR_DRAG_GRAB_KEYBOARD: + return; + case WLR_DRAG_GRAB_KEYBOARD_POINTER: icon->x = cursor->x; icon->y = cursor->y; - } else { + break; + case WLR_DRAG_GRAB_KEYBOARD_TOUCH:; struct wlr_touch_point *point = - wlr_seat_touch_get_point(seat->wlr_seat, wlr_icon->touch_id); + wlr_seat_touch_get_point(seat->wlr_seat, wlr_icon->drag->touch_id); if (point == NULL) { return; } @@ -305,9 +310,39 @@ static void drag_icon_handle_destroy(struct wl_listener *listener, void *data) { free(icon); } -static void handle_new_drag_icon(struct wl_listener *listener, void *data) { - struct sway_seat *seat = wl_container_of(listener, seat, new_drag_icon); - struct wlr_drag_icon *wlr_drag_icon = data; +static void handle_request_start_drag(struct wl_listener *listener, + void *data) { + struct sway_seat *seat = wl_container_of(listener, seat, request_start_drag); + struct wlr_seat_request_start_drag_event *event = data; + + if (wlr_seat_validate_pointer_grab_serial(seat->wlr_seat, + event->origin, event->serial)) { + wlr_seat_start_pointer_drag(seat->wlr_seat, event->drag, event->serial); + return; + } + + struct wlr_touch_point *point; + if (wlr_seat_validate_touch_grab_serial(seat->wlr_seat, + event->origin, event->serial, &point)) { + wlr_seat_start_touch_drag(seat->wlr_seat, + event->drag, event->serial, point); + return; + } + + // TODO: tablet grabs + + sway_log(SWAY_DEBUG, "Ignoring start_drag request: " + "could not validate pointer or touch serial %" PRIu32, event->serial); + wlr_data_source_destroy(event->drag->source); +} + +static void handle_start_drag(struct wl_listener *listener, void *data) { + struct sway_seat *seat = wl_container_of(listener, seat, start_drag); + struct wlr_drag *wlr_drag = data; + struct wlr_drag_icon *wlr_drag_icon = wlr_drag->icon; + if (wlr_drag_icon == NULL) { + return; + } struct sway_drag_icon *icon = calloc(1, sizeof(struct sway_drag_icon)); if (icon == NULL) { @@ -406,8 +441,12 @@ struct sway_seat *seat_create(const char *seat_name) { wl_signal_add(&root->events.new_node, &seat->new_node); seat->new_node.notify = handle_new_node; - wl_signal_add(&seat->wlr_seat->events.new_drag_icon, &seat->new_drag_icon); - seat->new_drag_icon.notify = handle_new_drag_icon; + wl_signal_add(&seat->wlr_seat->events.request_start_drag, + &seat->request_start_drag); + seat->request_start_drag.notify = handle_request_start_drag; + + wl_signal_add(&seat->wlr_seat->events.start_drag, &seat->start_drag); + seat->start_drag.notify = handle_start_drag; wl_signal_add(&seat->wlr_seat->events.request_set_selection, &seat->request_set_selection); From 4599907de7af4f5b6335399644b47f4be91bd949 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sun, 17 Feb 2019 10:13:11 -0500 Subject: [PATCH 0136/2054] swaybar: process hotspots on touch tap --- include/swaybar/bar.h | 1 + include/swaybar/input.h | 13 ++++ swaybar/input.c | 139 +++++++++++++++++++++++++++++++++++----- 3 files changed, 138 insertions(+), 15 deletions(-) diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index 2518d5aa8..dfadc200a 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -34,6 +34,7 @@ struct swaybar { struct swaybar_config *config; struct swaybar_pointer pointer; + struct swaybar_touch touch; struct status_line *status; struct loop *eventloop; diff --git a/include/swaybar/input.h b/include/swaybar/input.h index d76cd551f..88e5abc5e 100644 --- a/include/swaybar/input.h +++ b/include/swaybar/input.h @@ -22,6 +22,19 @@ struct swaybar_pointer { uint32_t serial; }; +struct touch_slot { + int32_t id; + uint32_t time; + struct swaybar_output *output; + double start_x, start_y; + double x, y; +}; + +struct swaybar_touch { + struct wl_touch *touch; + struct touch_slot slots[16]; +}; + enum hotspot_event_handling { HOTSPOT_IGNORE, HOTSPOT_PROCESS, diff --git a/swaybar/input.c b/swaybar/input.c index e416f6e73..d443c7772 100644 --- a/swaybar/input.c +++ b/swaybar/input.c @@ -123,6 +123,23 @@ static bool check_bindings(struct swaybar *bar, uint32_t button, return false; } +static void process_hotspots(struct swaybar_output *output, + double x, double y, uint32_t button) { + x *= output->scale; + y *= output->scale; + struct swaybar_hotspot *hotspot; + wl_list_for_each(hotspot, &output->hotspots, link) { + if (x >= hotspot->x && y >= hotspot->y + && x < hotspot->x + hotspot->width + && y < hotspot->y + hotspot->height) { + if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, + x / output->scale, y / output->scale, button, hotspot->data)) { + return; + } + } + } +} + static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) { struct swaybar *bar = data; @@ -139,20 +156,7 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, if (state != WL_POINTER_BUTTON_STATE_PRESSED) { return; } - struct swaybar_hotspot *hotspot; - wl_list_for_each(hotspot, &output->hotspots, link) { - double x = pointer->x * output->scale; - double y = pointer->y * output->scale; - if (x >= hotspot->x - && y >= hotspot->y - && x < hotspot->x + hotspot->width - && y < hotspot->y + hotspot->height) { - if (HOTSPOT_IGNORE == hotspot->callback(output, hotspot, - pointer->x, pointer->y, button, hotspot->data)) { - return; - } - } - } + process_hotspots(output, pointer->x, pointer->y, button); } static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, @@ -255,7 +259,7 @@ static void wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer, // Who cares } -struct wl_pointer_listener pointer_listener = { +static struct wl_pointer_listener pointer_listener = { .enter = wl_pointer_enter, .leave = wl_pointer_leave, .motion = wl_pointer_motion, @@ -267,6 +271,107 @@ struct wl_pointer_listener pointer_listener = { .axis_discrete = wl_pointer_axis_discrete, }; +static struct touch_slot *get_touch_slot(struct swaybar_touch *touch, int32_t id) { + int next = -1; + for (size_t i = 0; i < sizeof(touch->slots) / sizeof(*touch->slots); ++i) { + if (touch->slots[i].id == id) { + return &touch->slots[i]; + } + if (next == -1 && !touch->slots[i].output) { + next = i; + } + } + if (next == -1) { + sway_log(SWAY_ERROR, "Ran out of touch slots"); + return NULL; + } + return &touch->slots[next]; +} + +static void wl_touch_down(void *data, struct wl_touch *wl_touch, + uint32_t serial, uint32_t time, struct wl_surface *surface, + int32_t id, wl_fixed_t _x, wl_fixed_t _y) { + struct swaybar *bar = data; + struct swaybar_output *_output, *output; + wl_list_for_each(_output, &bar->outputs, link) { + if (_output->surface == surface) { + output = _output; + break; + } + } + if (!output) { + sway_log(SWAY_DEBUG, "Got touch event for unknown surface"); + return; + } + struct touch_slot *slot = get_touch_slot(&bar->touch, id); + if (!slot) { + return; + } + slot->id = id; + slot->output = output; + slot->x = slot->start_x = wl_fixed_to_double(_x); + slot->y = slot->start_y = wl_fixed_to_double(_y); + slot->time = time; +} + +static void wl_touch_up(void *data, struct wl_touch *wl_touch, + uint32_t serial, uint32_t time, int32_t id) { + struct swaybar *bar = data; + struct touch_slot *slot = get_touch_slot(&bar->touch, id); + if (!slot) { + return; + } + if (time - slot->time < 500) { + // Tap, treat it like a pointer click + process_hotspots(slot->output, slot->x, slot->y, BTN_LEFT); + } + slot->output = NULL; +} + +static void wl_touch_motion(void *data, struct wl_touch *wl_touch, + uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) { + struct swaybar *bar = data; + struct touch_slot *slot = get_touch_slot(&bar->touch, id); + if (!slot) { + return; + } + slot->x = wl_fixed_to_double(x); + slot->y = wl_fixed_to_double(y); + // TODO: Slide gestures +} + +static void wl_touch_frame(void *data, struct wl_touch *wl_touch) { + // Who cares +} + +static void wl_touch_cancel(void *data, struct wl_touch *wl_touch) { + struct swaybar *bar = data; + struct swaybar_touch *touch = &bar->touch; + for (size_t i = 0; i < sizeof(touch->slots) / sizeof(*touch->slots); ++i) { + touch->slots[i].output = NULL; + } +} + +static void wl_touch_shape(void *data, struct wl_touch *wl_touch, int32_t id, + wl_fixed_t major, wl_fixed_t minor) { + // Who cares +} + +static void wl_touch_orientation(void *data, struct wl_touch *wl_touch, + int32_t id, wl_fixed_t orientation) { + // Who cares +} + +static struct wl_touch_listener touch_listener = { + .down = wl_touch_down, + .up = wl_touch_up, + .motion = wl_touch_motion, + .frame = wl_touch_frame, + .cancel = wl_touch_cancel, + .shape = wl_touch_shape, + .orientation = wl_touch_orientation, +}; + static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) { struct swaybar *bar = data; @@ -278,6 +383,10 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, bar->pointer.pointer = wl_seat_get_pointer(wl_seat); wl_pointer_add_listener(bar->pointer.pointer, &pointer_listener, bar); } + if ((caps & WL_SEAT_CAPABILITY_TOUCH)) { + bar->touch.touch = wl_seat_get_touch(wl_seat); + wl_touch_add_listener(bar->touch.touch, &touch_listener, bar); + } } static void seat_handle_name(void *data, struct wl_seat *wl_seat, From d1588e37390279c0e99304cda93da8c416292097 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sun, 17 Feb 2019 11:00:13 -0500 Subject: [PATCH 0137/2054] swaybar: cycle workspaces on touch drag --- swaybar/input.c | 93 +++++++++++++++++++++++++++++-------------------- 1 file changed, 56 insertions(+), 37 deletions(-) diff --git a/swaybar/input.c b/swaybar/input.c index d443c7772..c83d8c33c 100644 --- a/swaybar/input.c +++ b/swaybar/input.c @@ -159,6 +159,44 @@ static void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, process_hotspots(output, pointer->x, pointer->y, button); } +static void workspace_next(struct swaybar *bar, struct swaybar_output *output, + bool left) { + struct swaybar_config *config = bar->config; + struct swaybar_workspace *first = + wl_container_of(output->workspaces.next, first, link); + struct swaybar_workspace *last = + wl_container_of(output->workspaces.prev, last, link); + + struct swaybar_workspace *active; + wl_list_for_each(active, &output->workspaces, link) { + if (active->visible) { + break; + } + } + if (!sway_assert(active->visible, "axis with null workspace")) { + return; + } + + struct swaybar_workspace *new; + if (left) { + if (active == first) { + new = config->wrap_scroll ? last : NULL; + } else { + new = wl_container_of(active->link.prev, new, link); + } + } else { + if (active == last) { + new = config->wrap_scroll ? first : NULL; + } else { + new = wl_container_of(active->link.next, new, link); + } + } + + if (new) { + ipc_send_workspace_command(bar, new->name); + } +} + static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { struct swaybar *bar = data; @@ -202,39 +240,7 @@ static void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, return; } - struct swaybar_workspace *first = - wl_container_of(output->workspaces.next, first, link); - struct swaybar_workspace *last = - wl_container_of(output->workspaces.prev, last, link); - - struct swaybar_workspace *active; - wl_list_for_each(active, &output->workspaces, link) { - if (active->visible) { - break; - } - } - if (!sway_assert(active->visible, "axis with null workspace")) { - return; - } - - struct swaybar_workspace *new; - if (amt < 0.0) { - if (active == first) { - new = config->wrap_scroll ? last : NULL; - } else { - new = wl_container_of(active->link.prev, new, link); - } - } else { - if (active == last) { - new = config->wrap_scroll ? first : NULL; - } else { - new = wl_container_of(active->link.next, new, link); - } - } - - if (new) { - ipc_send_workspace_command(bar, new->name); - } + workspace_next(bar, output, amt < 0.0); // Check button release bindings check_bindings(bar, button, WL_POINTER_BUTTON_STATE_RELEASED); @@ -272,7 +278,7 @@ static struct wl_pointer_listener pointer_listener = { }; static struct touch_slot *get_touch_slot(struct swaybar_touch *touch, int32_t id) { - int next = -1; + ssize_t next = -1; for (size_t i = 0; i < sizeof(touch->slots) / sizeof(*touch->slots); ++i) { if (touch->slots[i].id == id) { return &touch->slots[i]; @@ -292,7 +298,7 @@ static void wl_touch_down(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, struct wl_surface *surface, int32_t id, wl_fixed_t _x, wl_fixed_t _y) { struct swaybar *bar = data; - struct swaybar_output *_output, *output; + struct swaybar_output *_output = NULL, *output = NULL; wl_list_for_each(_output, &bar->outputs, link) { if (_output->surface == surface) { output = _output; @@ -335,9 +341,18 @@ static void wl_touch_motion(void *data, struct wl_touch *wl_touch, if (!slot) { return; } + int prev_progress = (int)((slot->x - slot->start_x) + / slot->output->width * 100); slot->x = wl_fixed_to_double(x); slot->y = wl_fixed_to_double(y); - // TODO: Slide gestures + // "progress" is a measure from 0..100 representing the fraction of the + // output the touch gesture has travelled, positive when moving to the right + // and negative when moving to the left. + int progress = (int)((slot->x - slot->start_x) + / slot->output->width * 100); + if (abs(progress) / 20 != abs(prev_progress) / 20) { + workspace_next(bar, slot->output, progress - prev_progress < 0); + } } static void wl_touch_frame(void *data, struct wl_touch *wl_touch) { @@ -362,7 +377,7 @@ static void wl_touch_orientation(void *data, struct wl_touch *wl_touch, // Who cares } -static struct wl_touch_listener touch_listener = { +static const struct wl_touch_listener touch_listener = { .down = wl_touch_down, .up = wl_touch_up, .motion = wl_touch_motion, @@ -379,6 +394,10 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, wl_pointer_release(bar->pointer.pointer); bar->pointer.pointer = NULL; } + if (bar->touch.touch != NULL) { + wl_touch_release(bar->touch.touch); + bar->touch.touch = NULL; + } if ((caps & WL_SEAT_CAPABILITY_POINTER)) { bar->pointer.pointer = wl_seat_get_pointer(wl_seat); wl_pointer_add_listener(bar->pointer.pointer, &pointer_listener, bar); From f84ac3f1148e2e25c68ed15b75ca69a694050328 Mon Sep 17 00:00:00 2001 From: Caleb Bassi Date: Sat, 23 Feb 2019 07:49:40 -0800 Subject: [PATCH 0138/2054] ipc: add missing fields to disabled outputs i3 requires all outputs to have certain fields, including 'primary', 'current_workspace', and 'rect' which were missing on disabled outputs. https://i3wm.org/docs/ipc.html#_outputs_reply --- sway/ipc-json.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index a2ab2bba8..125df3877 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -251,6 +251,7 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { json_object_object_add(object, "name", json_object_new_string(wlr_output->name)); json_object_object_add(object, "active", json_object_new_boolean(false)); + json_object_object_add(object, "primary", json_object_new_boolean(false)); json_object_object_add(object, "make", json_object_new_string(wlr_output->make)); json_object_object_add(object, "model", @@ -259,6 +260,15 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { json_object_new_string(wlr_output->serial)); json_object_object_add(object, "modes", json_object_new_array()); + json_object_object_add(object, "current_workspace", NULL); + + json_object *rect_object = json_object_new_object(); + json_object_object_add(rect_object, "x", json_object_new_int(0)); + json_object_object_add(rect_object, "y", json_object_new_int(0)); + json_object_object_add(rect_object, "width", json_object_new_int(0)); + json_object_object_add(rect_object, "height", json_object_new_int(0)); + json_object_object_add(object, "rect", rect_object); + json_object_object_add(object, "percent", NULL); return object; From 713883f04cf1f2a2bafb389562a79d98b87909e9 Mon Sep 17 00:00:00 2001 From: minus Date: Fri, 22 Feb 2019 22:26:40 +0100 Subject: [PATCH 0139/2054] Fix crash exiting fullscreened floating container container_floating_move_to_center and container_fullscreen_disable were calling recursively when the container spawned as a fullscreen floating container (via for_window). Such a window now doesn't crash sway anymore but is still configured with a wrong, zero size, making it not directly usable. --- sway/tree/container.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 933907f4e..d448df22a 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -847,16 +847,9 @@ void container_floating_move_to_center(struct sway_container *con) { return; } struct sway_workspace *ws = con->workspace; - enum sway_fullscreen_mode fullscreen_mode = con->fullscreen_mode; - if (fullscreen_mode) { - container_fullscreen_disable(con); - } double new_lx = ws->x + (ws->width - con->width) / 2; double new_ly = ws->y + (ws->height - con->height) / 2; container_floating_translate(con, new_lx - con->x, new_ly - con->y); - if (fullscreen_mode) { - container_set_fullscreen(con, fullscreen_mode); - } } static bool find_urgent_iterator(struct sway_container *con, void *data) { From d4b1e71b919a52fbad0ba8191259eb4f14dfd1ab Mon Sep 17 00:00:00 2001 From: Connor E <38229097+c-edw@users.noreply.github.com> Date: Sun, 24 Feb 2019 04:39:08 +0000 Subject: [PATCH 0140/2054] Make load_include_configs void. Fix some cases where WD would not be restored. --- include/sway/config.h | 2 +- sway/commands/include.c | 7 ++----- sway/config.c | 37 ++++++++++++++----------------------- 3 files changed, 17 insertions(+), 29 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 54cdcc908..ab494e784 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -513,7 +513,7 @@ bool load_main_config(const char *path, bool is_active, bool validating); /** * Loads an included config. Can only be used after load_main_config. */ -bool load_include_configs(const char *path, struct sway_config *config, +void load_include_configs(const char *path, struct sway_config *config, struct swaynag_instance *swaynag); /** diff --git a/sway/commands/include.c b/sway/commands/include.c index d18098564..d4c14c35f 100644 --- a/sway/commands/include.c +++ b/sway/commands/include.c @@ -7,11 +7,8 @@ struct cmd_results *cmd_include(int argc, char **argv) { return error; } - if (!load_include_configs(argv[0], config, - &config->swaynag_config_errors)) { - return cmd_results_new(CMD_INVALID, - "Failed to include sub configuration file: %s", argv[0]); - } + // We don't care if the included config(s) fails to load. + load_include_configs(argv[0], config, &config->swaynag_config_errors); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/config.c b/sway/config.c index 206ca95cb..4cd21bbc4 100644 --- a/sway/config.c +++ b/sway/config.c @@ -549,43 +549,34 @@ static bool load_include_config(const char *path, const char *parent_dir, return true; } -bool load_include_configs(const char *path, struct sway_config *config, +void load_include_configs(const char *path, struct sway_config *config, struct swaynag_instance *swaynag) { char *wd = getcwd(NULL, 0); char *parent_path = strdup(config->current_config_path); const char *parent_dir = dirname(parent_path); if (chdir(parent_dir) < 0) { - free(parent_path); - free(wd); - return false; + sway_log(SWAY_ERROR, "failed to change working directory"); + goto cleanup; } wordexp_t p; - - if (wordexp(path, &p, 0) != 0) { - free(parent_path); - free(wd); - return false; + if (wordexp(path, &p, 0) == 0) { + char **w = p.we_wordv; + size_t i; + for (i = 0; i < p.we_wordc; ++i) { + load_include_config(w[i], parent_dir, config, swaynag); + } + wordfree(&p); } - char **w = p.we_wordv; - size_t i; - for (i = 0; i < p.we_wordc; ++i) { - load_include_config(w[i], parent_dir, config, swaynag); - } - free(parent_path); - wordfree(&p); - - // restore wd + // Attempt to restore working directory before returning. if (chdir(wd) < 0) { - free(wd); - sway_log(SWAY_ERROR, "failed to restore working directory"); - return false; + sway_log(SWAY_ERROR, "failed to change working directory"); } - +cleanup: + free(parent_path); free(wd); - return true; } void run_deferred_commands(void) { From 2510e3df384d9ab7b27e66c27371ec44606f5d8e Mon Sep 17 00:00:00 2001 From: db Date: Sun, 24 Feb 2019 10:00:15 +0100 Subject: [PATCH 0141/2054] add --i3 flag to hide_edge_borders Enables i3-compatible behavior regarding hiding the title bar on tabbed and stacked containers with one child. Related issues and merge requests: #3031, #3002, #2912, #2987. --- include/sway/config.h | 1 + sway/commands/hide_edge_borders.c | 10 ++++++++-- sway/config.c | 1 + sway/desktop/render.c | 8 ++++++++ sway/sway.5.scd | 6 ++++-- sway/tree/view.c | 19 +++++++++++-------- 6 files changed, 33 insertions(+), 12 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index ab494e784..46ca7cee4 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -465,6 +465,7 @@ struct sway_config { int floating_border_thickness; enum edge_border_types hide_edge_borders; enum edge_border_types saved_edge_borders; + bool hide_lone_tab; // border colors struct { diff --git a/sway/commands/hide_edge_borders.c b/sway/commands/hide_edge_borders.c index 84a217b89..6120a17fa 100644 --- a/sway/commands/hide_edge_borders.c +++ b/sway/commands/hide_edge_borders.c @@ -5,10 +5,16 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, "hide_edge_borders", EXPECTED_EQUAL_TO, 1))) { + if ((error = checkarg(argc, "hide_edge_borders", EXPECTED_AT_LEAST, 1))) { return error; } + if (strcmp(*argv, "--i3") == 0) { + config->hide_lone_tab = true; + ++argv; + --argc; + } + if (strcmp(argv[0], "none") == 0) { config->hide_edge_borders = E_NONE; } else if (strcmp(argv[0], "vertical") == 0) { @@ -23,7 +29,7 @@ struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { config->hide_edge_borders = E_SMART_NO_GAPS; } else { return cmd_results_new(CMD_INVALID, "Expected 'hide_edge_borders " - "'"); + "[--i3] '"); } config->saved_edge_borders = config->hide_edge_borders; diff --git a/sway/config.c b/sway/config.c index 4cd21bbc4..48bbd1ea8 100644 --- a/sway/config.c +++ b/sway/config.c @@ -261,6 +261,7 @@ static void config_defaults(struct sway_config *config) { config->floating_border_thickness = 2; config->hide_edge_borders = E_NONE; config->saved_edge_borders = E_NONE; + config->hide_lone_tab = false; // border colors set_color(config->border_colors.focused.border, 0x4C7899); diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 92e623ef4..5df160757 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -841,6 +841,14 @@ static void render_containers_stacked(struct sway_output *output, static void render_containers(struct sway_output *output, pixman_region32_t *damage, struct parent_data *parent) { + if (config->hide_lone_tab && parent->children->length == 1) { + struct sway_container *child = parent->children->items[0]; + if (child->view) { + render_containers_linear(output,damage, parent); + return; + } + } + switch (parent->layout) { case L_NONE: case L_HORIZ: diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 1affca5fe..c0ec85cd7 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -493,8 +493,10 @@ The default colors are: This affects new workspaces only, and is used when the workspace doesn't have its own gaps settings (see: workspace gaps ...). -*hide_edge_borders* none|vertical|horizontal|both|smart|smart_no_gaps - Hides window borders adjacent to the screen edges. Default is _none_. +*hide_edge_borders* [--i3] none|vertical|horizontal|both|smart|smart_no_gaps + Hides window borders adjacent to the screen edges. Default is _none_. The + _--i3_ option enables i3-compatible behavior to hide the title bar on tabbed + and stacked containers with one child. *input* For details on input subcommands, see *sway-input*(5). diff --git a/sway/tree/view.c b/sway/tree/view.c index ca13def72..14cc07d9f 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -246,14 +246,17 @@ void view_autoconfigure(struct sway_view *view) { // In a tabbed or stacked container, the container's y is the top of the // title area. We have to offset the surface y by the height of the title, // bar, and disable any top border because we'll always have the title bar. - enum sway_container_layout layout = container_parent_layout(con); - if (layout == L_TABBED && !container_is_floating(con)) { - y_offset = container_titlebar_height(); - con->border_top = false; - } else if (layout == L_STACKED && !container_is_floating(con)) { - list_t *siblings = container_get_siblings(con); - y_offset = container_titlebar_height() * siblings->length; - con->border_top = false; + list_t *siblings = container_get_siblings(con); + bool show_titlebar = siblings->length > 1 || !config->hide_lone_tab; + if (show_titlebar && !container_is_floating(con)) { + enum sway_container_layout layout = container_parent_layout(con); + if (layout == L_TABBED) { + y_offset = container_titlebar_height(); + con->border_top = false; + } else if (layout == L_STACKED) { + y_offset = container_titlebar_height() * siblings->length; + con->border_top = false; + } } double x, y, width, height; From 2f7247e08a16610228067c9ec34d2b6d897e15fa Mon Sep 17 00:00:00 2001 From: Milkey Mouse Date: Sat, 23 Feb 2019 21:59:36 -0800 Subject: [PATCH 0142/2054] swaybar: add overlay mode (fix #1620) Overlay mode puts the bar above normal windows and passes through/ignores any touch/mouse/keyboard events that would be sent to it. --- include/swaybar/bar.h | 1 + sway/commands/bar/mode.c | 2 ++ sway/sway-bar.5.scd | 6 ++++-- swaybar/bar.c | 16 ++++++++++++++-- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/include/swaybar/bar.h b/include/swaybar/bar.h index dfadc200a..031993b57 100644 --- a/include/swaybar/bar.h +++ b/include/swaybar/bar.h @@ -58,6 +58,7 @@ struct swaybar_output { struct zxdg_output_v1 *xdg_output; struct wl_surface *surface; struct zwlr_layer_surface_v1 *layer_surface; + struct wl_region *input_region; uint32_t wl_name; struct wl_list workspaces; // swaybar_workspace::link diff --git a/sway/commands/bar/mode.c b/sway/commands/bar/mode.c index 68a80abf6..1081ad4b8 100644 --- a/sway/commands/bar/mode.c +++ b/sway/commands/bar/mode.c @@ -20,6 +20,8 @@ static struct cmd_results *bar_set_mode(struct bar_config *bar, const char *mode bar->mode = strdup("hide"); } else if (strcasecmp("invisible", mode) == 0) { bar->mode = strdup("invisible"); + } else if (strcasecmp("overlay", mode) == 0) { + bar->mode = strdup("overlay"); } else { return cmd_results_new(CMD_INVALID, "Invalid value %s", mode); } diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd index 13827e5ee..1f4ceaf2f 100644 --- a/sway/sway-bar.5.scd +++ b/sway/sway-bar.5.scd @@ -84,11 +84,13 @@ Sway allows configuring swaybar in the sway configuration file. debug-events`. To disable the default behavior for a button, use the command _nop_. -*mode* dock|hide|invisible +*mode* dock|hide|invisible|overlay Specifies the visibility of the bar. In _dock_ mode, it is permanently visible at one edge of the screen. In _hide_ mode, it is hidden unless the modifier key is pressed, though this behaviour depends on the hidden state. - In _invisible_ mode, it is permanently hidden. Default is _dock_. + In _invisible_ mode, it is permanently hidden. In _overlay_ mode, it is + permanently visible on top of other windows. (In _overlay_ mode the bar is + transparent to input events.) Default is _dock_. *hidden_state* hide|show Specifies the behaviour of the bar when it is in _hide_ mode. When the diff --git a/swaybar/bar.c b/swaybar/bar.c index db1c12228..ca7cd88c4 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -51,6 +51,9 @@ static void swaybar_output_free(struct swaybar_output *output) { if (output->surface != NULL) { wl_surface_destroy(output->surface); } + if (output->input_region != NULL) { + wl_region_destroy(output->input_region); + } zxdg_output_v1_destroy(output->xdg_output); wl_output_destroy(output->output); destroy_buffer(&output->buffers[0]); @@ -100,16 +103,25 @@ static void add_layer_surface(struct swaybar_output *output) { struct swaybar_config *config = bar->config; bool hidden = strcmp(config->mode, "hide") == 0; + bool overlay = !hidden && strcmp(config->mode, "overlay") == 0; output->layer_surface = zwlr_layer_shell_v1_get_layer_surface( bar->layer_shell, output->surface, output->output, - hidden ? ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY : + hidden || overlay ? ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY : ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "panel"); assert(output->layer_surface); zwlr_layer_surface_v1_add_listener(output->layer_surface, &layer_surface_listener, output); + if (overlay) { + // Empty input region + output->input_region = wl_compositor_create_region(bar->compositor); + assert(output->input_region); + + wl_surface_set_input_region(output->surface, output->input_region); + } + zwlr_layer_surface_v1_set_anchor(output->layer_surface, config->position); - if (hidden) { + if (hidden || overlay) { zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, -1); } } From 9032be4f08a0b58da383d5d3b965719ad0ef78f1 Mon Sep 17 00:00:00 2001 From: Connor E <38229097+c-edw@users.noreply.github.com> Date: Mon, 25 Feb 2019 09:40:47 +0000 Subject: [PATCH 0143/2054] Allow 0 degree transform (normal transform). --- sway/commands/output/transform.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/commands/output/transform.c b/sway/commands/output/transform.c index 8613a8e78..8e5324adb 100644 --- a/sway/commands/output/transform.c +++ b/sway/commands/output/transform.c @@ -12,7 +12,8 @@ struct cmd_results *output_cmd_transform(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Missing transform argument."); } enum wl_output_transform transform; - if (strcmp(*argv, "normal") == 0) { + if (strcmp(*argv, "normal") == 0 || + strcmp(*argv, "0") == 0) { transform = WL_OUTPUT_TRANSFORM_NORMAL; } else if (strcmp(*argv, "90") == 0) { transform = WL_OUTPUT_TRANSFORM_90; From b00b0fb9d342ea42a0d15d258206c196b5ef9140 Mon Sep 17 00:00:00 2001 From: Carlo Abelli Date: Mon, 25 Feb 2019 14:24:02 -0500 Subject: [PATCH 0144/2054] fix smart_borders description in manual The wording for smart_borders was opposite the actual behavior. --- sway/sway.5.scd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index c0ec85cd7..25cb9f4d0 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -513,9 +513,9 @@ The default colors are: *smart_borders* on|no_gaps|off If smart_borders are _on_, borders will only be enabled if the workspace - only has one visible child (identical to _hide_edge_borders_ smart). If - smart_borders is set to _no_gaps_, borders will only be enabled if the - workspace only has one visible child and gaps greater than zero. + has more than one visible child (identical to _hide_edge_borders_ smart). + If smart_borders is set to _no_gaps_, borders will only be enabled if the + workspace has more than one visible child and gaps equal to zero. *smart_gaps* on|off If smart_gaps are _on_ gaps will only be enabled if a workspace has more From efe370ba1220b11d5c736b23cb109d47483878d2 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 26 Feb 2019 00:24:39 -0500 Subject: [PATCH 0145/2054] execute_command: do not strip qoutes for cmd_mode `cmd_mode` performs its own quote stripping for the mode string to avoid double stripping quotes for `cmd_bindcode` and `cmd_bindsym` in `config_command` and `execute_command`. Stripping quotes in `execute_command` for `cmd_mode` will also result in double stripping, which will cause issues for any mode string with spaces, such as pango markup. --- sway/commands.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/commands.c b/sway/commands.c index 3fc4f86e4..522a5fd47 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -255,7 +255,8 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, int argc; char **argv = split_args(cmd, &argc); if (strcmp(argv[0], "exec") != 0 && - strcmp(argv[0], "exec_always") != 0) { + strcmp(argv[0], "exec_always") != 0 && + strcmp(argv[0], "mode") != 0) { int i; for (i = 1; i < argc; ++i) { if (*argv[i] == '\"' || *argv[i] == '\'') { From 35d610a43c01bd9a610a37564cc2de35dc88ca45 Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Tue, 26 Feb 2019 20:21:33 +0100 Subject: [PATCH 0146/2054] cursor: remove unused node assignement The node variable is not used before its reassigned later in the function, remove the assignement. --- sway/input/cursor.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index fb4728b42..210e6144d 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -431,7 +431,7 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) { struct sway_node *node = NULL; double sx, sy; if (cursor->active_constraint) { - node = node_at_coords(cursor->seat, + node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); if (cursor->active_constraint->surface != surface) { From 4e028dba47f924b7a9133920fe8ebad4668ce9b7 Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Tue, 26 Feb 2019 20:46:25 +0100 Subject: [PATCH 0147/2054] cursor: intitialize sx and sy to zero If node_at_coords does an early return without setting these values, they can be used uninitialized later. Initialize both to zero. --- sway/input/cursor.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 210e6144d..87811550a 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -280,7 +280,7 @@ static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec, void cursor_rebase(struct sway_cursor *cursor) { uint32_t time_msec = get_current_time_msec(); struct wlr_surface *surface = NULL; - double sx, sy; + double sx = 0.0, sy = 0.0; cursor->previous.node = node_at_coords(cursor->seat, cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); cursor_do_rebase(cursor, time_msec, cursor->previous.node, surface, sx, sy); @@ -476,7 +476,7 @@ static void cursor_motion_absolute(struct sway_cursor *cursor, dx, dy, dx, dy); struct wlr_surface *surface = NULL; - double sx, sy; + double sx = 0.0, sy = 0.0; struct sway_node *node = node_at_coords(cursor->seat, lx, ly, &surface, &sx, &sy); From d016848bcee829a9db09ed640d075bcff914f06b Mon Sep 17 00:00:00 2001 From: Rouven Czerwinski Date: Tue, 26 Feb 2019 20:49:42 +0100 Subject: [PATCH 0148/2054] config: remove double assignement to result in get_output_config --- sway/config/output.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/config/output.c b/sway/config/output.c index e7fbad833..cb889b3e8 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -378,7 +378,7 @@ static struct output_config *get_output_config(char *identifier, oc_id = config->output_configs->items[i]; } - struct output_config *result = result = new_output_config("temp"); + struct output_config *result = new_output_config("temp"); if (config->reloading) { default_output_config(result, sway_output->wlr_output); } From f876009c7f5c332af5d60b19ccbf8d991b972ffa Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 27 Feb 2019 13:23:10 -0500 Subject: [PATCH 0149/2054] Add sway-ipc.7.scd to document IPC protocol This add `sway-ipc.7.scd` that documents the IPC protocol. This also increased the minimum scdoc version from 1.8.1 to 1.9.0 to allow for table cells to be continued on the following line --- README.es.md | 2 +- README.md | 2 +- README.pl.md | 2 +- meson.build | 1 + sway/sway-ipc.7.scd | 1578 +++++++++++++++++++++++++++++++++++++++++ sway/sway.1.scd | 1 + sway/sway.5.scd | 2 +- swaymsg/swaymsg.1.scd | 4 + 8 files changed, 1588 insertions(+), 4 deletions(-) create mode 100644 sway/sway-ipc.7.scd diff --git a/README.es.md b/README.es.md index daa1fc093..076d89005 100644 --- a/README.es.md +++ b/README.es.md @@ -38,7 +38,7 @@ Instale las dependencias: * pango * cairo * gdk-pixbuf2 \*\* -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.8.1 (optional: man pages) \* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.9.0 (optional: man pages) \* * git \* _\*Compile-time dep_ diff --git a/README.md b/README.md index e11a0dc13..aa7cd846d 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Install dependencies: * pango * cairo * gdk-pixbuf2 \*\* -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.8.1 (optional: man pages) \* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.9.0 (optional: man pages) \* * git \* _\*Compile-time dep_ diff --git a/README.pl.md b/README.pl.md index 843fae605..ebfd9214a 100644 --- a/README.pl.md +++ b/README.pl.md @@ -39,7 +39,7 @@ Zainstaluj zależności: * pango * cairo * gdk-pixbuf2 \*\* -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.8.1 (opcjonalnie: strony pomocy man) \* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.9.0 (opcjonalnie: strony pomocy man) \* * git \* _\*zależności kompilacji_ diff --git a/meson.build b/meson.build index d3172bdd8..45df87998 100644 --- a/meson.build +++ b/meson.build @@ -107,6 +107,7 @@ if scdoc.found() 'sway/sway.5.scd', 'sway/sway-bar.5.scd', 'sway/sway-input.5.scd', + 'sway/sway-ipc.7.scd', 'sway/sway-output.5.scd', 'swaymsg/swaymsg.1.scd', 'swaynag/swaynag.1.scd', diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd new file mode 100644 index 000000000..63fbacc75 --- /dev/null +++ b/sway/sway-ipc.7.scd @@ -0,0 +1,1578 @@ +sway-ipc(7) + +# NAME + +sway-ipc - IPC protocol for sway + +# DESCRIPTION + +This details the interprocess communication (IPC) protocol for *sway*(1). This +IPC protocol can be used to control or obtain information from a sway process. + +The IPC protocol uses a UNIX socket as the method of communication. The path +to the socket is stored in the environment variable _SWAYSOCK_ and, for +backwards compatibility with i3, _I3SOCK_. You can also retrieve the socket +path by calling _sway --get-socketpath_. + +# MESSAGE AND REPLY FORMAT + +The format for messages and replies is: + +Where++ + is _i3-ipc_, for compatibility with i3++ + is a 32-bit integer in native byte order++ + is a 32-bit integer in native byte order + +For example, sending the _exit_ command would look like the following hexdump: +``` +00000000 | 69 33 2d 69 70 63 04 00 00 00 00 00 00 00 65 78 |i3-ipc........ex| +00000010 | 69 74 |it | +``` + +The payload for replies will be a valid serialized JSON data structure. + +# MESSAGES AND REPLIES + +The following message types and their corresponding reply types are currently +supported. *For all replies, any properties not listed are subject to removal.* + +[- *TYPE NUMBER* +:- *MESSAGE NAME* +:- *PURPOSE* +|- 0 +: RUN_COMMAND +:[ Runs the payload as sway commands +|- 1 +: GET_WORKSPACES +: Get the list of current workspaces +|- 2 +: SUBSCRIBE +: Subscribe the IPC connection to the events listed in the payload +|- 3 +: GET_OUTPUTS +: Get the list of current outputs +|- 4 +: GET_TREE +: Get the node layout tree +|- 5 +: GET_MARKS +: Get the names of all the marks currently set +|- 6 +: GET_BAR_CONFIG +: Get the specified bar config or a list of bar config names +|- 7 +: GET_VERSION +: Get the version of sway that owns the IPC socket +|- 8 +: GET_BINDING_MODES +: Get the list of binding mode names +|- 9 +: GET_CONFIG +: Returns the config that was last loaded +|- 10 +: SEND_TICK +: Sends a tick event with the specified payload +|- 11 +: SYNC +: Replies failure object for i3 compatibility +|- 100 +: GET_INPUTS +: Get the list of input devices +|- 101 +: GET_SEATS +: Get the list of seats + +## 0. RUN_COMMAND + +*MESSAGE*++ +Parses and runs the payload as sway commands + +*REPLY*++ +An array of objects corresponding to each command that was parsed. Each object +has the property _success_, which is a boolean indicating whether the command +was successful. The object may also contain the property _error_, which is a +human readable error message. + +*Example Reply:* +``` +[ + { + "success": true + }, + { + "success": false, + "error": "Invalid/unknown command" + } +] +``` + +## 1. GET_WORKSPACES + +*MESSAGE*++ +Retrieves the list of workspaces. + +*REPLY*++ +The reply is an array of objects corresponding to each workspace. Each object +has the following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- num +: integer +:[ The workspace number or -1 for workspaces that do not start with a number +|- name +: string +: The name of the workspace +|- visible +: boolean +: Whether the workspace is currently visible on any output +|- focused +: boolean +: Whether the workspace is currently focused by the default seat (_seat0_) +|- urgent +: boolean +: Whether a view on the workspace has the urgent flag set +|- rect +: object +: The bounds of the workspace. It consists of _x_, _y_, _width_, and _height_ +|- output +: string +: The name of the output that the workspace is on + + +*Example Reply:* +``` +[ + { + "num": 1, + "name": "1", + "visible": true, + "focused": true, + "rect": { + "x": 0, + "y": 23, + "width": 1920, + "height": 1057 + }, + "output": "eDP-1" + }, +] +``` + +## 2. SUBSCRIBE + +*MESSAGE*++ +Subscribe this IPC connection to the event types specified in the message +payload. The payload should be a valid JSON array of events. See the _EVENTS_ +section for the list of supported events. + +*REPLY*++ +A single object that contains the property _success_, which is a boolean value +indicating whether the subscription was successful or not. + +*Example Reply:* +``` +{ + "success": true +} +``` + +## 3. GET_OUTPUTS + +*MESSAGE*++ +Retrieve the list of outputs + +*REPLY*++ +An array of objects corresponding to each output. Each object has the +following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- name +: string +:[ The name of the output. On DRM, this is the connector +|- make +: string +: The make of the output +|- model +: string +: The model of the output +|- serial +: string +: The output's serial number as a hexadecimal string +|- active +: boolean +: Whether this output is active/enabled +|- primary +: boolean +: For i3 compatibility, this will be false. It does not make sense in Wayland +|- scale +: float +: The scale currently in use on the output or _-1_ for disabled outputs +|- transform +: string +: The transform currently in use for the output. This can be _normal_, _90_, + _180_, _270_, _flipped-90_, _flipped-180_, or _flipped-270_ +|- current_workspace +: string +: The workspace currently visible on the output or _null_ for disabled outputs +|- modes +: array +: An array of supported mode objects. Each object contains _width_, _height_, + and _refresh_ +|- current_mode +: object +: An object representing the current mode containing _width_, _height_, and + _refresh_ +|- rect +: object +: The bounds for the output consisting of _x_, _y_, _width_, and _height_ + + +*Example Reply:* +``` +[ + { + "name": "HDMI-A-2", + "make": "Unknown", + "model": "NS-19E310A13", + "serial": "0x00000001", + "active": true, + "primary": false, + "scale": 1.0, + "transform": "normal", + "current_workspace": "1", + "modes": [ + { + "width": 640, + "height": 480, + "refresh": 59940 + }, + { + "width": 800, + "height": 600, + "refresh": 60317 + }, + { + "width": 1024, + "height": 768, + "refresh": 60004 + }, + { + "width": 1920, + "height": 1080, + "refresh": 60000 + } + ], + "current_mode": { + "width": 1920, + "height": 1080, + "refresh": 60000 + } + } +] +``` + +## 4. GET_TREE + +*MESSAGE*++ +Retrieve a JSON representation of the tree + +*REPLY*++ +An array of object the represent the current tree. Each object represents one +node and will have the following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- id +: integer +:[ The internal unique ID for this node +|- name +: string +: The name of the node such as the output name or window title. For the + scratchpad, this will be _\_\_i3\_scratch_ for compatibility with i3. +|- type +: string +: The node type. It can be _root_, _output_, _workspace_, _con_, or + _floating\_con_ +|- border +: string +: The border style for the node. It can be _normal_, _none_, _pixel_, or _csd_ +|- current_border_width +: integer +: Number of pixels used for the border width +|- layout +: string +: The node's layout. It can either be _splith_, _splitv_, _stacked_, + _tabbed_, or _output_ +|- percent +: float +: The percentage of the node's parent that it takes up or _null_ for the root + and other special nodes such as the scratchpad +|- rect +: object +: The absolute geometry of the node +|- window_rect +: object +: The geometry of the contents inside the node +|- deco_rect +: object +: The geometry of the decorations inside the node +|- geometry +: object +: The natural geometry of the contents if it were to size itself +|- urgent +: boolean +: Whether the node or any of its descendants has the urgent hint set. Note: + This may not exist when compiled without _xwayland_ support +|- focused +: boolean +: Whether the node is currently focused by the default seat (_seat0_) +|- focus +: array +: Array of child node IDs in the current focus order +|- node +: array +: The tiling children nodes for the node +|- floating_nodes +: array +: The floating children nodes for the node +|- representation +: string +: (Only workspaces) A string representation of the layout of the workspace + that can be used as an aid in submitting reproduction steps for bug reports +|- app_id +: string +: (Only views) For an xdg-shell view, the name of the application, if set. + Otherwise, _null_ +|- pid +: integer +: (Only views) The PID of the application that owns the view +|- window +: integer +: (Only xwayland views) The X11 window ID for the xwayland view +|- window_properties +: object +: (Only xwayland views) An object containing the _title_, _class_, _instance_, + _window\_role_, and _transient\_for_ for the view + + +*Example Reply:* +``` +{ + "id": 1, + "name": "root", + "rect": { + "x": 0, + "y": 0, + "width": 1920, + "height": 1080 + }, + "focused": false, + "focus": [ + 3 + ], + "border": "none", + "current_border_width": 0, + "layout": "splith", + "percent": null, + "window_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "deco_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "geometry": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "window": null, + "urgent": false, + "floating_nodes": [ + ], + "type": "root", + "nodes": [ + { + "id": 2147483647, + "name": "__i3", + "rect": { + "x": 0, + "y": 0, + "width": 1920, + "height": 1080 + }, + "focused": false, + "focus": [ + 2147483646 + ], + "border": "none", + "current_border_width": 0, + "layout": "output", + "percent": null, + "window_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "deco_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "geometry": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "window": null, + "urgent": false, + "floating_nodes": [ + ], + "type": "output", + "nodes": [ + { + "id": 2147483646, + "name": "__i3_scratch", + "rect": { + "x": 0, + "y": 0, + "width": 1920, + "height": 1080 + }, + "focused": false, + "focus": [ + ], + "border": "none", + "current_border_width": 0, + "layout": "splith", + "percent": null, + "window_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "deco_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "geometry": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "window": null, + "urgent": false, + "floating_nodes": [ + ], + "type": "workspace" + } + ] + }, + { + "id": 3, + "name": "eDP-1", + "rect": { + "x": 0, + "y": 0, + "width": 1920, + "height": 1080 + }, + "focused": false, + "focus": [ + 4 + ], + "border": "none", + "current_border_width": 0, + "layout": "output", + "percent": 1.0, + "window_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "deco_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "geometry": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "window": null, + "urgent": false, + "floating_nodes": [ + ], + "type": "output", + "active": true, + "primary": false, + "make": "Unknown", + "model": "0x38ED", + "serial": "0x00000000", + "scale": 1.0, + "transform": "normal", + "current_workspace": "1", + "modes": [ + { + "width": 1920, + "height": 1080, + "refresh": 60052 + } + ], + "current_mode": { + "width": 1920, + "height": 1080, + "refresh": 60052 + }, + "nodes": [ + { + "id": 4, + "name": "1", + "rect": { + "x": 0, + "y": 23, + "width": 1920, + "height": 1057 + }, + "focused": false, + "focus": [ + 6, + 5 + ], + "border": "none", + "current_border_width": 0, + "layout": "splith", + "percent": null, + "window_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "deco_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "geometry": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "window": null, + "urgent": false, + "floating_nodes": [ + ], + "num": 1, + "output": "eDP-1", + "type": "workspace", + "representation": "H[URxvt termite]", + "nodes": [ + { + "id": 5, + "name": "urxvt", + "rect": { + "x": 0, + "y": 23, + "width": 960, + "height": 1057 + }, + "focused": false, + "focus": [ + ], + "border": "normal", + "current_border_width": 2, + "layout": "none", + "percent": 0.5, + "window_rect": { + "x": 2, + "y": 0, + "width": 956, + "height": 1030 + }, + "deco_rect": { + "x": 0, + "y": 0, + "width": 960, + "height": 25 + }, + "geometry": { + "x": 0, + "y": 0, + "width": 1124, + "height": 422 + }, + "window": 4194313, + "urgent": false, + "floating_nodes": [ + ], + "type": "con", + "pid": 23959, + "app_id": null, + "window_properties": { + "class": "URxvt", + "instance": "urxvt", + "title": "urxvt", + "transient_for": null + }, + "nodes": [ + ] + }, + { + "id": 6, + "name": "", + "rect": { + "x": 960, + "y": 23, + "width": 960, + "height": 1057 + }, + "focused": true, + "focus": [ + ], + "border": "normal", + "current_border_width": 2, + "layout": "none", + "percent": 0.5, + "window_rect": { + "x": 2, + "y": 0, + "width": 956, + "height": 1030 + }, + "deco_rect": { + "x": 0, + "y": 0, + "width": 960, + "height": 25 + }, + "geometry": { + "x": 0, + "y": 0, + "width": 817, + "height": 458 + }, + "window": null, + "urgent": false, + "floating_nodes": [ + ], + "type": "con", + "pid": 25370, + "app_id": "termite", + "nodes": [ + ] + } + ] + } + ] + } + ] +} +``` + +## 5. GET_MARKS + +*MESSAGE*++ +Retrieve the currently set marks + +*REPLY*++ +An array of marks current set. Since each mark can only be set for one +container, this is a set so each value is unique and the order is undefined. + +*Example Reply:* +``` +[ + "one", + "test" +] +``` + +## 6. GET_BAR_CONFIG (WITHOUT A PAYLOAD) + +*MESSAGE*++ +When sending without a payload, this retrieves the list of configured bar IDs + +*REPLY*++ +An array of bar IDs, which are strings + +*Example Reply:* +``` +[ + "bar-0", + "bar-1" +] +``` + +## 6. GET_BAR_CONFIG (WITH A PAYLOAD) + +*MESSAGE*++ +When sent with a bar ID as the payload, this retrieves the config associated +with the specified by the bar ID in the payload. This is used by swaybar, but +could also be used for third party bars + +*REPLY*++ +An object that represents the configuration for the bar with the bar ID sent as +the payload. The following properties exists and more information about what +their value mean can be found in *sway-bar*(5): + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- id +: string +:[ The bar ID +|- mode +: string +: The mode for the bar. It can be _dock_, _hide_, or _invisible_ +|- position +: string +: The bar's position. It can currently either be _bottom_ or _top_ +|- status_command +: string +: The command which should be run to generate the status line +|- font +: string +: The font to use for the text on the bar +|- workspace_buttons +: boolean +: Whether to display the workspace buttons on the bar +|- binding_mode_indicator +: boolean +: Whether to display the current binding mode on the bar +|- verbose +: boolean +: For i3 compatibility, this will be the boolean value _false_. +|- colors +: object +: An object containing the _#RRGGBBAA_ colors to use for the bar. See below + for more information +|- gaps +: object +: An object representing the gaps for the bar consisting of _top_, _right_, + _bottom_, and _left_. +|- bar_height +: integer +: The absolute height to use for the bar or _0_ to automatically size based + on the font +|- status_padding +: integer +: The vertical padding to use for the status line +|- status_edge_padding +: integer +: The horizontal padding to use for the status line when at the end of an + output + + +The colors object contains the following properties, which are all strings +containing the _#RRGGBBAA_ representation of the color: +[- *PROPERTY* +:- *DESCRIPTION* +|- background +:[ The color to use for the bar background on unfocused outputs +|- statusline +: The color to use for the status line text on unfocused outputs +|- separator +: The color to use for the separator text on unfocused outputs +|- focused_background +: The color to use for the background of the bar on the focused output +|- focused_statusline +: The color to use for the status line text on the focused output +|- focused_separator +: The color to use for the separator text on the focused output +|- focused_workspace_text +: The color to use for the text of the focused workspace button +|- focused_workspace_bg +: The color to use for the background of the focused workspace button +|- focused_workspace_border +: The color to use for the border of the focused workspace button +|- active_workspace_text +: The color to use for the text of the workspace buttons for the visible + workspaces on unfocused outputs +|- active_workspace_bg +: The color to use for the background of the workspace buttons for the visible + workspaces on unfocused outputs +|- active_workspace_border +: The color to use for the border of the workspace buttons for the visible + workspaces on unfocused outputs +|- inactive_workspace_text +: The color to use for the text of the workspace buttons for workspaces that + are not visible +|- inactive_workspace_bg +: The color to use for the background of the workspace buttons for workspaces + that are not visible +|- inactive_workspace_border +: The color to use for the border of the workspace buttons for workspaces + that are not visible +|- urgent_workspace_text +: The color to use for the text of the workspace buttons for workspaces that + contain an urgent view +|- urgent_workspace_bg +: The color to use for the background of the workspace buttons for workspaces + that contain an urgent view +|- urgent_workspace_border +: The color to use for the border of the workspace buttons for workspaces that + contain an urgent view +|- binding_mode_text +: The color to use for the text of the binding mode indicator +|- binding_mode_bg +: The color to use for the background of the binding mode indicator +|- binding_mode_border +: The color to use for the border of the binding mode indicator + + +*Example Reply:* +``` +{ + "id": "bar-0", + "mode": "dock", + "position": "top", + "status_command": "while date +'%Y-%m-%d %l:%M:%S %p'; do sleep 1; done", + "font": "monospace 10", + "gaps": { + "top": 0, + "right": 0, + "bottom": 0, + "left": 0 + }, + "bar_height": 0, + "status_padding": 1, + "status_edge_padding": 3, + "workspace_buttons": true, + "binding_mode_indicator": true, + "verbose": false, + "pango_markup": false, + "colors": { + "background": "#323232ff", + "statusline": "#ffffffff", + "separator": "#666666ff", + "focused_background": "#323232ff", + "focused_statusline": "#ffffffff", + "focused_separator": "#666666ff", + "focused_workspace_border": "#4c7899ff", + "focused_workspace_bg": "#285577ff", + "focused_workspace_text": "#ffffffff", + "inactive_workspace_border": "#32323200", + "inactive_workspace_bg": "#32323200", + "inactive_workspace_text": "#5c5c5cff", + "active_workspace_border": "#333333ff", + "active_workspace_bg": "#5f676aff", + "active_workspace_text": "#ffffffff", + "urgent_workspace_border": "#2f343aff", + "urgent_workspace_bg": "#900000ff", + "urgent_workspace_text": "#ffffffff", + "binding_mode_border": "#2f343aff", + "binding_mode_bg": "#900000ff", + "binding_mode_text": "#ffffffff" + }, +} +``` + +## 7. GET_VERSION + +*MESSAGE*++ +Retrieve version information about the sway process + +*REPLY*++ +An object containing the following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- major +: integer +:[ The major version of the sway process +|- minor +: integer +: The minor version of the sway process +|- patch +: integer +: The patch version of the sway process +|- human_readable +: string +: A human readable version string that will likely contain more useful + information such as the git commit short hash and git branch +|- loaded_config_file_name +: string +: The path to the loaded config file + + +*Example Reply:* +``` +{ + "human_readable": "1.0-rc1-117-g2f7247e0 (Feb 24 2019, branch 'master')", + "major": 1, + "minor": 0, + "patch": 0, + "loaded_config_file_name": "/home/redsoxfan/.config/sway/config" +} +``` + +## 8. GET_BINDING_MODES + +*MESSAGE*++ +Retrieve the list of binding modes that currently configured + +*REPLY*++ +An array of strings, with each string being the name of a binding mode. This +will always contain at least one mode (currently _default_), which is the +default binding mode + +*Example Reply:* +``` +[ + "default", + "resize", +] +``` + +## 9. GET_CONFIG + +*MESSAGE*++ +Retrieve the contents of the config that was last loaded + +*REPLY*++ +An object with a single string property containing the contents of the config + +*Example Reply:* +``` +{ + "config": "set $mod Mod4\nbindsym $mod+q exit\n" +} +``` + +## 10. SEND_TICK + +*MESSAGE*++ +Issues a _TICK_ event to all clients subscribing to the event to ensure that +all events prior to the tick were received. If a payload is given, it will be +included in the _TICK_ event + +*REPLY*++ +A single object contains the property _success_, which is a boolean value +indicating whether the _TICK_ event was sent. + +*Example Reply:* +``` +{ + "success": true +} +``` + +## 11. SYNC + +*MESSAGE*++ +For i3 compatibility, this command will just return a failure object since it +does not make sense to implement in sway due to the X11 nature of the command. +If you are curious about what this IPC command does in i3, refer to the i3 +documentation. + +*REPLY*++ +A single object that contains the property _success_, which is set to the +boolean value _false_. + +*Exact Reply:* +``` +{ + "success": false +} +``` + +## 100. GET_INPUTS + +*MESSAGE*++ +Retrieve a list of the input devices currently available + +*REPLY*++ +An array of objects corresponding to each input device. Each object has the +following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- identifier +: string +:[ The identifier for the input device +|- name +: string +: The human readable name for the device +|- vendor +: integer +: The vendor code for the input device +|- product +: integer +: The product code for the input device +|- type +: string +: The device type. Currently this can be _keyboard_, _pointer_, _touch_, + _tablet\_tool_, _tablet\_pad_, or _switch_ +|- xkb_active_layout_name +: string +: (Only keyboards) The active keyboard layout in use +|- libinput_send_events +: string +: (Only libinput devices) The send events value in use by libinput for this + device. It can be _enabled_, _disabled_, or _disabled\_on\_external\_mouse_ + + +*Example Reply:* +``` +[ + { + "identifier": "1:1:AT_Translated_Set_2_keyboard", + "name": "AT Translated Set 2 keyboard", + "vendor": 1, + "product": 1, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + }, + { + "identifier": "1267:5:Elan_Touchpad", + "name": "Elan Touchpad", + "vendor": 1267, + "product": 5, + "type": "pointer", + "libinput_send_events": "enabled" + }, + { + "identifier": "3034:22494:USB2.0_VGA_UVC_WebCam:_USB2.0_V", + "name": "USB2.0 VGA UVC WebCam: USB2.0 V", + "vendor": 3034, + "product": 22494, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + }, + { + "identifier": "0:3:Sleep_Button", + "name": "Sleep Button", + "vendor": 0, + "product": 3, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + }, + { + "identifier": "0:5:Lid_Switch", + "name": "Lid Switch", + "vendor": 0, + "product": 5, + "type": "switch", + "libinput_send_events": "enabled" + { + "identifier": "0:6:Video_Bus", + "name": "Video Bus", + "vendor": 0, + "product": 6, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + }, + { + "identifier": "0:1:Power_Button", + "name": "Power Button", + "vendor": 0, + "product": 1, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + } +] +``` + +## 101. GET_SEATS + +*MESSAGE*++ +Retrieve a list of the seats currently configured + +*REPLY*++ +An array of objects corresponding to each seat. There will always be at least +one seat. Each object has the following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- name +: string +:] The unique name for the seat +|- capabilities +: integer +: The number of capabilities that the seat has +|- focus +: integer +: The id of the node currently focused by the seat or _0_ when the seat is + not currently focused by a node (i.e. a surface layer or xwayland unmanaged + has focus) +|- devices +: array +: An array of input devices that are attached to the seat. Currently, this + is an array of objects that are identical to those returned by _GET\_INPUTS_ + + +*Example Reply:* +``` +[ + { + "name": "seat0", + "capabilities": 3, + "focus": 7, + "devices": [ + { + "identifier": "1:1:AT_Translated_Set_2_keyboard", + "name": "AT Translated Set 2 keyboard", + "vendor": 1, + "product": 1, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + }, + { + "identifier": "1267:5:Elan_Touchpad", + "name": "Elan Touchpad", + "vendor": 1267, + "product": 5, + "type": "pointer", + "libinput_send_events": "enabled" + }, + { + "identifier": "3034:22494:USB2.0_VGA_UVC_WebCam:_USB2.0_V", + "name": "USB2.0 VGA UVC WebCam: USB2.0 V", + "vendor": 3034, + "product": 22494, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + }, + { + "identifier": "0:3:Sleep_Button", + "name": "Sleep Button", + "vendor": 0, + "product": 3, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + }, + { + "identifier": "0:5:Lid_Switch", + "name": "Lid Switch", + "vendor": 0, + "product": 5, + "type": "switch", + "libinput_send_events": "enabled" + { + "identifier": "0:6:Video_Bus", + "name": "Video Bus", + "vendor": 0, + "product": 6, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + }, + { + "identifier": "0:1:Power_Button", + "name": "Power Button", + "vendor": 0, + "product": 1, + "type": "keyboard", + "xkb_active_layout_name": "English (US)", + "libinput_send_events": "enabled" + } + ] + } +] +``` + +# EVENTS + +Events are a way for client to get notified of changes to sway. A client can +subscribe to any events it wants to be notified of changes for. The event is +sent in the same format as a reply. The following events are currently +available: + +[- *EVENT TYPE* +:- *NAME* +:- *DESCRIPTION* +|- 0x80000000 +: workspace +:[ Sent whenever an event involving a workspace occurs such as initialization + of a new workspace or a different workspace gains focus +|- 0x80000002 +: mode +: Sent whenever the binding mode changes +|- 0x80000003 +: window +: Sent whenever an event involving a view occurs such as being reparented, + focused, or closed +|- 0x80000004 +: barconfig_update +: Sent whenever a bar config changes +|- 0x80000005 +: binding +: Sent when a configured binding is executed +|- 0x80000006 +: shutdown +: Sent when the ipc shuts down because sway is exiting +|- 0x80000007 +: tick +: Sent when an ipc client sends a _SEND\_TICK_ message +|- 0x80000014 +: bar_status_update +: Send when the visibility of a bar should change due to a modifier + + +## 0x80000000. WORKSPACE + +Sent whenever a change involving a workspace occurs. The event consists of a +single object with the following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- change +: string +:[ The type of change that occurred. See below for more information +|- current +: object +: An object representing the workspace effected or _null_ for _reload_ changes +|- old +: object +: For a _focus_ change, this is will be an object representing the workspace + being switched from. Otherwise, it is _null_ + + +The following change types are currently available: +[- *TYPE* +:- *DESCRIPTION* +|- init +:[ The workspace was created +|- empty +: The workspace is empty and is being destroyed since it is not visible +|- focus +: The workspace was focused. See the _old_ property for the previous focus +|- move +: The workspace was moved to a different output +|- rename +: The workspace was renamed +|- urgent +: A view on the workspace has had their urgency hint set or all urgency hints + for views on the workspace have been cleared +|- reload +: The configuration file has been reloaded + + +*Example Event:* +``` +{ + "change": "init", + "old": null, + "current": { + "id": 10, + "name": "2", + "rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "focused": false, + "focus": [ + ], + "border": "none", + "current_border_width": 0, + "layout": "splith", + "percent": null, + "window_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "deco_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "geometry": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "window": null, + "urgent": false, + "floating_nodes": [ + ], + "num": 2, + "output": "eDP-1", + "type": "workspace", + "representation": null, + "nodes": [ + ] + } +} +``` + +## 0x80000002. MODE + +Sent whenever the binding mode changes. The event consists of a single object +with the following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- change +: string +:[ The binding mode that became active +|- pango_markup +: boolean +: Whether the mode should be parsed as pango markup + + +*Example Event:* +``` +{ + "change": "default", + "pango_markup": false +} +``` + +## 0x80000003. WINDOW + +Sent whenever a change involving a view occurs. The event consists of a single +object with the following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- change +: string +:[ The type of change that occurred. See below for more information +|- container +: object +: An object representing the view effected + + +The following change types are currently available: +[- *TYPE* +:- *DESCRIPTION* +|- new +:[ The view was created +|- close +: The view was closed +|- focus +: The view was focused +|- title +: The view's title has changed +|- fullscreen_mode +: The view's fullscreen mode has changed +|- move +: The view has been reparented in the tree +|- floating +: The view has become floating or is no longer floating +|- urgent +: The view's urgency hint has changed status +|- mark +: A mark has been added or removed from the view + + +*Example Event:* +``` +{ + "change": "new", + "container": { + "id": 12, + "name": null, + "rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "focused": false, + "focus": [ + ], + "border": "none", + "current_border_width": 0, + "layout": "none", + "percent": 0.0, + "window_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "deco_rect": { + "x": 0, + "y": 0, + "width": 0, + "height": 0 + }, + "geometry": { + "x": 0, + "y": 0, + "width": 1124, + "height": 422 + }, + "window": 4194313, + "urgent": false, + "floating_nodes": [ + ], + "type": "con", + "pid": 19787, + "app_id": null, + "window_properties": { + "class": "URxvt", + "instance": "urxvt", + "transient_for": null + }, + "nodes": [ + ] + } +} +``` + +## 0x80000004. BARCONFIG_UPDATE + +Sent whenever a config for a bar changes. The event is identical to that of +_GET_BAR_CONFIG_ when a bar ID is given as a payload. See _6. GET\_BAR\_CONFIG +(WITH A PAYLOAD)_ above for more information. + +## 0x80000005. BINDING + +Sent whenever a binding is executed. The event is a single object with the +following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- change +: string +:[ The change that occurred for the binding. Currently this will only be _run_ +|- command +: string +: The command associated with the binding +|- event_state_mask +: array +: An array of strings that correspond to each modifier key for the binding +|- input_code +: integer +: For keyboard bindcodes, this is the key code for the binding. For mouse + bindings, this is the X11 button number, if there is an equivalent. In all + other cases, this will be _0_. +|- symbol +: string +: For keyboard bindsyms, this is the bindsym for the binding. Otherwise, this + will be _null_ +|- input_type +: string +: The input type that triggered the binding. This is either _keyboard_ or + _mouse_ + + +*Example Event:* +``` +{ + "change": "run", + "binding": { + "command": "workspace 2", + "event_state_mask": [ + "Mod4" + ], + "input_code": 0, + "symbol": "2", + "input_type": "keyboard" + } +} +``` + +## 0x80000006. SHUTDOWN + +Sent whenever the IPC is shutting down. The event is a single object with the +property _change_, which is a string containing the reason for the shutdown. +Currently, the only value for _change_ is _exit_, which is issued when sway is +exiting. + +*Example Event:* +``` +{ + "change": "exit" +} +``` + +## 0x80000007. TICK + +Sent when first subscribing to tick events or by a _SEND\_TICK_ message. The +event is a single object with the following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- first +: boolean +: Whether this event was triggered by subscribing to the tick events +|- payload +: string +: The payload given with a _SEND\_TICK_ message, if any. Otherwise, an empty + string + + +*Example Event:* +``` +{ + "first": true + "payload": "" +} +``` + +## 0x80000014. BAR_STATUS_UPDATE + +Sent when the visibility of a bar changes due to a modifier being pressed. The +event is a single object with the following properties: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- id +: string +:[ The bar ID effected +|- visible_by_modifier +: boolean +: Whether the bar should be made visible due to a modifier being pressed + + +*Example Event:* +``` +{ + "id": "bar-0", + "visible_by_modifier": true +} +``` + +# SEE ALSO + +*sway*(1) *sway*(5) *sway-bar*(5) *swaymsg*(1) *sway-input*(5) *sway-output*(5) diff --git a/sway/sway.1.scd b/sway/sway.1.scd index bce635270..cfb4817a1 100644 --- a/sway/sway.1.scd +++ b/sway/sway.1.scd @@ -89,3 +89,4 @@ source contributors. For more information about sway development, see # SEE ALSO *sway*(5) *swaymsg*(1) *sway-input*(5) *sway-output*(5) *sway-bar*(5) +*sway-ipc*(7) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 1affca5fe..1b85a75bc 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -748,4 +748,4 @@ The following attributes may be matched with: # SEE ALSO -*sway*(1) *sway-input*(5) *sway-output*(5) *sway-bar*(5) +*sway*(1) *sway-input*(5) *sway-output*(5) *sway-bar*(5) *sway-ipc*(7) diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd index f55f86a9d..523db6cc0 100644 --- a/swaymsg/swaymsg.1.scd +++ b/swaymsg/swaymsg.1.scd @@ -82,3 +82,7 @@ _swaymsg_ [options...] [message] Subscribe to a list of event types. The argument for this type should be provided in the form of a valid JSON array. If any of the types are invalid or if an valid JSON array is not provided, this will result in an failure. + +# SEE ALSO + +*sway*(5) *sway-bar*(5) *sway-input*(5) *sway-output*(5) *sway-ipc*(7) From 8228fa60d4e03b4b9f541cdc411d28c106cef2d3 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 27 Feb 2019 21:13:16 +0100 Subject: [PATCH 0150/2054] Set minimum wlroots version --- meson.build | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index d3172bdd8..aaffc41fa 100644 --- a/meson.build +++ b/meson.build @@ -60,17 +60,19 @@ rt = cc.find_library('rt') git = find_program('git', required: false) # Try first to find wlroots as a subproject, then as a system dependency +wlroots_version = '>=0.4.1' wlroots_proj = subproject( 'wlroots', default_options: ['rootston=false', 'examples=false'], required: false, + version: wlroots_version, ) if wlroots_proj.found() wlroots = wlroots_proj.get_variable('wlroots') wlroots_conf = wlroots_proj.get_variable('conf_data') wlroots_has_xwayland = wlroots_conf.get('WLR_HAS_XWAYLAND') == 1 else - wlroots = dependency('wlroots') + wlroots = dependency('wlroots', version: wlroots_version) wlroots_has_xwayland = cc.get_define('WLR_HAS_XWAYLAND', prefix: '#include ', dependencies: wlroots) == '1' endif From 6658d69271daa932eeeec9f35bf7b81f84992ba5 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 27 Feb 2019 21:26:50 +0100 Subject: [PATCH 0151/2054] Print Meson features --- meson.build | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index d3172bdd8..3c3c1f41f 100644 --- a/meson.build +++ b/meson.build @@ -93,11 +93,6 @@ conf_data.set10('HAVE_SYSTEMD', systemd.found()) conf_data.set10('HAVE_ELOGIND', elogind.found()) conf_data.set10('HAVE_TRAY', have_tray) -if not systemd.found() and not elogind.found() - warning('The sway binary must be setuid when compiled without (e)logind') - warning('You must do this manually post-install: chmod a+s /path/to/sway') -endif - scdoc = find_program('scdoc', required: get_option('man-pages')) if scdoc.found() sh = find_program('sh') @@ -235,3 +230,21 @@ if get_option('fish-completions') install_data(fish_files, install_dir: fish_install_dir) endif + +status = [ + '', + 'Features:', + 'xwayland: @0@'.format(have_xwayland), + 'gdk-pixbuf: @0@'.format(gdk_pixbuf.found()), + 'systemd: @0@'.format(systemd.found()), + 'elogind: @0@'.format(elogind.found()), + 'tray: @0@'.format(have_tray), + 'man-pages: @0@'.format(scdoc.found()), + '', +] +message('\n'.join(status)) + +if not systemd.found() and not elogind.found() + warning('The sway binary must be setuid when compiled without (e)logind') + warning('You must do this manually post-install: chmod a+s /path/to/sway') +endif From 4431ae68fcfcc12bd41f2b2daf4cb2b34a6f5547 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 27 Feb 2019 11:48:30 +0100 Subject: [PATCH 0152/2054] Add output dpms to manpage --- sway/sway-output.5.scd | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index 517f69465..bb3745f36 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -51,7 +51,7 @@ must be separated by one space. For example: output eDP1 pos 1600 0 res 1920x1080 - Note that the left x-pos of eDP1 is 1600 = 3200/2 and the bottom y-pos is + Note that the left x-pos of eDP1 is 1600 = 3200/2 and the bottom y-pos is 1020 + (1800 / 2) = 1920 = 0 + 1920 *output* scale @@ -69,8 +69,7 @@ must be separated by one space. For example: given scaling mode (one of "stretch", "fill", "fit", "center", "tile"). If the specified file cannot be accessed or if the image does fill the entire output, a fallback color may be provided to cover the rest of the output. - __fallback_color__ should be specified as _#RRGGBB_. Alpha is not - supported. + __fallback_color__ should be specified as _#RRGGBB_. Alpha is not supported. *output* background|bg solid_color Sets the background of the given output to the specified color. _color_ @@ -80,14 +79,18 @@ must be separated by one space. For example: Sets the background transform to the given value. Can be one of "90", "180", "270" for rotation; or "flipped", "flipped-90", "flipped-180", "flipped-270" to apply a rotation and flip, or "normal" to apply no transform. If a single - output is chosen and a rotation direction is specified - (_clockwise_ or _anticlockwise_) then the transform is added or - subtracted from the current transform. + output is chosen and a rotation direction is specified (_clockwise_ or + _anticlockwise_) then the transform is added or subtracted from the current + transform. *output* disable|enable Enables or disables the specified output (all outputs are enabled by default). +*output* dpms on|off + Enables or disables the specified output via DPMS. To turn an output off + (ie. blank the screen but keep workspaces as-is), one can set DPMS to off. + # SEE ALSO *sway*(5) *sway-input*(5) From 56217bfbc01ca8c244ccbcd3f5b60227f41f5e36 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 27 Feb 2019 22:11:58 -0500 Subject: [PATCH 0153/2054] sway-ipc.7: clarify window_rect omits decorations According to the i3 ipc documentation, `window_rect` excludes the window decorations from the calculation. This just clarifies that in `sway-ipc.7.scd` --- sway/sway-ipc.7.scd | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index 63fbacc75..d598a54fd 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -317,7 +317,8 @@ node and will have the following properties: : The absolute geometry of the node |- window_rect : object -: The geometry of the contents inside the node +: The geometry of the contents inside the node. The window decorations are + excluded from this calculation, but borders are included. |- deco_rect : object : The geometry of the decorations inside the node From 416c6ecb99f90a7c84cce0b106401652692a4681 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Thu, 28 Feb 2019 12:02:14 +0000 Subject: [PATCH 0154/2054] tray: fix pixmap colors by converting from network byte order to host byte order --- swaybar/tray/item.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index 4fa6c97b5..027b3001f 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -1,4 +1,5 @@ #define _POSIX_C_SOURCE 200809L +#include #include #include #include @@ -76,7 +77,12 @@ static int read_pixmap(sd_bus_message *msg, struct swaybar_sni *sni, struct swaybar_pixmap *pixmap = malloc(sizeof(struct swaybar_pixmap) + npixels); pixmap->size = height; - memcpy(pixmap->pixels, pixels, npixels); + + // convert from network byte order to host byte order + for (int i = 0; i < height * width; ++i) { + ((uint32_t *)pixmap->pixels)[i] = ntohl(((uint32_t *)pixels)[i]); + } + list_add(pixmaps, pixmap); } else { sway_log(SWAY_DEBUG, "%s %s: discard invalid icon w:%d h:%d", sni->watcher_id, prop, width, height); From 88b283c55713c45968e2df5f8b89a40a0b32f720 Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 28 Feb 2019 19:22:47 +0100 Subject: [PATCH 0155/2054] seat: don't send button release when not pressed All seat operations except "down" eat the button pressed event and don't send it to clients. Thus, when ending such seat operations we shouldn't send the button released event. This commit moves the logic used to send pressed/released into the "down" operation. --- include/sway/input/seat.h | 9 ++++----- sway/input/cursor.c | 6 ++---- sway/input/seat.c | 4 ++-- sway/input/seatop_down.c | 9 ++++++--- sway/input/seatop_move_floating.c | 2 +- sway/input/seatop_move_tiling.c | 2 +- sway/input/seatop_resize_floating.c | 2 +- sway/input/seatop_resize_tiling.c | 2 +- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 8fedf7972..0f5dab983 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -10,7 +10,7 @@ struct sway_seat; struct sway_seatop_impl { void (*motion)(struct sway_seat *seat, uint32_t time_msec); - void (*finish)(struct sway_seat *seat); + void (*finish)(struct sway_seat *seat, uint32_t time_msec); void (*abort)(struct sway_seat *seat); void (*unref)(struct sway_seat *seat, struct sway_container *con); void (*render)(struct sway_seat *seat, struct sway_output *output, @@ -185,8 +185,8 @@ bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); void drag_icon_update_position(struct sway_drag_icon *icon); -void seatop_begin_down(struct sway_seat *seat, - struct sway_container *con, uint32_t button, int sx, int sy); +void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, + uint32_t time_msec, uint32_t button, int sx, int sy); void seatop_begin_move_floating(struct sway_seat *seat, struct sway_container *con, uint32_t button); @@ -218,7 +218,7 @@ void seatop_motion(struct sway_seat *seat, uint32_t time_msec); /** * End a seatop and apply the affects. */ -void seatop_finish(struct sway_seat *seat); +void seatop_finish(struct sway_seat *seat, uint32_t time_msec); /** * End a seatop without applying the affects. @@ -239,5 +239,4 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con); void seatop_render(struct sway_seat *seat, struct sway_output *output, pixman_region32_t *damage); - #endif diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 87811550a..44b5ff147 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -606,8 +606,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, // Handle existing seat operation if (seat_doing_seatop(seat)) { if (button == seat->seatop_button && state == WLR_BUTTON_RELEASED) { - seatop_finish(seat); - seat_pointer_notify_button(seat, time_msec, button, state); + seatop_finish(seat, time_msec); } if (state == WLR_BUTTON_PRESSED) { state_add_button(cursor, button); @@ -784,8 +783,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, // Handle mousedown on a container surface if (surface && cont && state == WLR_BUTTON_PRESSED) { seat_set_focus_container(seat, cont); - seat_pointer_notify_button(seat, time_msec, button, state); - seatop_begin_down(seat, cont, button, sx, sy); + seatop_begin_down(seat, cont, time_msec, button, sx, sy); return; } diff --git a/sway/input/seat.c b/sway/input/seat.c index a16d3f276..9888ddfcb 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1211,9 +1211,9 @@ void seatop_motion(struct sway_seat *seat, uint32_t time_msec) { } } -void seatop_finish(struct sway_seat *seat) { +void seatop_finish(struct sway_seat *seat, uint32_t time_msec) { if (seat->seatop_impl && seat->seatop_impl->finish) { - seat->seatop_impl->finish(seat); + seat->seatop_impl->finish(seat, time_msec); } free(seat->seatop_data); seat->seatop_data = NULL; diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index c2256c9a1..33f9b31a7 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -24,7 +24,7 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { e->moved = true; } -static void handle_finish(struct sway_seat *seat) { +static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { struct seatop_down_event *e = seat->seatop_data; struct sway_cursor *cursor = seat->cursor; // Set the cursor's previous coords to the x/y at the start of the @@ -40,6 +40,8 @@ static void handle_finish(struct sway_seat *seat) { cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); cursor_send_pointer_motion(cursor, 0, node, surface, sx, sy); } + seat_pointer_notify_button(seat, time_msec, + seat->seatop_button, WLR_BUTTON_RELEASED); } static void handle_abort(struct sway_seat *seat) { @@ -60,8 +62,8 @@ static const struct sway_seatop_impl seatop_impl = { .unref = handle_unref, }; -void seatop_begin_down(struct sway_seat *seat, - struct sway_container *con, uint32_t button, int sx, int sy) { +void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, + uint32_t time_msec, uint32_t button, int sx, int sy) { seatop_abort(seat); struct seatop_down_event *e = @@ -80,5 +82,6 @@ void seatop_begin_down(struct sway_seat *seat, seat->seatop_data = e; seat->seatop_button = button; + seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); container_raise_floating(con); } diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c index 08e3a5a4e..8a48a9681 100644 --- a/sway/input/seatop_move_floating.c +++ b/sway/input/seatop_move_floating.c @@ -17,7 +17,7 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { desktop_damage_whole_container(e->con); } -static void handle_finish(struct sway_seat *seat) { +static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { struct seatop_move_floating_event *e = seat->seatop_data; // We "move" the container to its own location diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 1e548f5ae..4b5aa81ed 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -226,7 +226,7 @@ static bool is_parallel(enum sway_container_layout layout, return layout_is_horiz == edge_is_horiz; } -static void handle_finish(struct sway_seat *seat) { +static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { struct seatop_move_tiling_event *e = seat->seatop_data; if (!e->target_node) { diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c index 12851b406..bf6c7ab42 100644 --- a/sway/input/seatop_resize_floating.c +++ b/sway/input/seatop_resize_floating.c @@ -142,7 +142,7 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { arrange_container(con); } -static void handle_finish(struct sway_seat *seat) { +static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { cursor_set_image(seat->cursor, "left_ptr", NULL); } diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c index cb0f723d0..db32065c7 100644 --- a/sway/input/seatop_resize_tiling.c +++ b/sway/input/seatop_resize_tiling.c @@ -49,7 +49,7 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { } } -static void handle_finish(struct sway_seat *seat) { +static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { cursor_set_image(seat->cursor, "left_ptr", NULL); } From f98410c090c995d491ba538850b792c763407583 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 27 Feb 2019 22:19:53 +0100 Subject: [PATCH 0156/2054] meson: check scdoc version --- meson.build | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 53454cdd7..270dc6b79 100644 --- a/meson.build +++ b/meson.build @@ -95,8 +95,9 @@ conf_data.set10('HAVE_SYSTEMD', systemd.found()) conf_data.set10('HAVE_ELOGIND', elogind.found()) conf_data.set10('HAVE_TRAY', have_tray) -scdoc = find_program('scdoc', required: get_option('man-pages')) +scdoc = dependency('scdoc', version: '>=1.9', native: true, required: get_option('man-pages')) if scdoc.found() + scdoc_prog = find_program('scdoc') sh = find_program('sh') mandir = get_option('mandir') man_files = [ @@ -120,7 +121,7 @@ if scdoc.found() input: filename, output: output, command: [ - sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc.path(), output) + sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output) ], install: true, install_dir: '@0@/man@1@'.format(mandir, section) From 77b16a5124efb8ede9c4219ddea7832503ebfddf Mon Sep 17 00:00:00 2001 From: emersion Date: Fri, 1 Mar 2019 19:42:40 +0100 Subject: [PATCH 0157/2054] ci: use scdoc-git because 1.9.1 is broken --- .builds/alpine.yml | 6 +++++- .builds/archlinux.yml | 6 +++++- .builds/freebsd.yml | 8 +++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.builds/alpine.yml b/.builds/alpine.yml index 328625a5d..07a10db07 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml @@ -12,7 +12,6 @@ packages: - meson - pango-dev - pixman-dev - - scdoc - wayland-dev - wayland-protocols - xcb-util-image-dev @@ -20,7 +19,12 @@ packages: sources: - https://github.com/swaywm/sway - https://github.com/swaywm/wlroots + - https://git.sr.ht/~sircmpwn/scdoc tasks: + - scdoc: | + cd scdoc + make PREFIX=/usr + sudo make install PREFIX=/usr - wlroots: | cd wlroots meson --prefix=/usr build -Drootston=false -Dexamples=false diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index c8f116e00..a9e3a9ae5 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -8,7 +8,6 @@ packages: - libxkbcommon - meson - pango - - scdoc - wayland - wayland-protocols - xcb-util-image @@ -16,7 +15,12 @@ packages: sources: - https://github.com/swaywm/sway - https://github.com/swaywm/wlroots + - https://git.sr.ht/~sircmpwn/scdoc tasks: + - scdoc: | + cd scdoc + make PREFIX=/usr + sudo make install PREFIX=/usr - wlroots: | cd wlroots meson --prefix=/usr build -Drootston=false -Dexamples=false diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 8fa760257..91a3817d1 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -8,7 +8,6 @@ packages: - graphics/gdk-pixbuf2 - graphics/wayland - graphics/wayland-protocols -- textproc/scdoc - x11-toolkits/pango - x11/libxcb - x11/libxkbcommon @@ -22,10 +21,17 @@ packages: - x11/libX11 - x11/pixman - x11/xcb-util-wm +# scdoc dependencies +- devel/gmake sources: - https://github.com/swaywm/sway - https://github.com/swaywm/wlroots +- https://git.sr.ht/~sircmpwn/scdoc tasks: +- scdoc: | + cd scdoc + gmake PREFIX=/usr/local + sudo gmake install PREFIX=/usr/local - fixup_epoll: | cat << 'EOF' | sudo tee /usr/local/libdata/pkgconfig/epoll-shim.pc prefix=/usr/local From 37f0e1f1a2cc8e072c8e01e0769c62c536b8295b Mon Sep 17 00:00:00 2001 From: hugbubby Date: Fri, 1 Mar 2019 12:14:14 -0600 Subject: [PATCH 0158/2054] Minor fix of code duplication. Removes 3~ lines of code that didn't need to be restated. --- sway/main.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sway/main.c b/sway/main.c index 6754190f9..22b728131 100644 --- a/sway/main.c +++ b/sway/main.c @@ -186,11 +186,7 @@ static void log_kernel(void) { static bool drop_permissions(void) { if (getuid() != geteuid() || getgid() != getegid()) { - if (setgid(getgid()) != 0) { - sway_log(SWAY_ERROR, "Unable to drop root, refusing to start"); - return false; - } - if (setuid(getuid()) != 0) { + if (setuid(getuid()) != 0 || setgid(getgid()) != 0) { sway_log(SWAY_ERROR, "Unable to drop root, refusing to start"); return false; } From 430359519c1cb9583020fc8da04f5ecc31b0e914 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 2 Mar 2019 02:29:28 -0500 Subject: [PATCH 0159/2054] floating_maximum_size: change default behavior This changes the way zero (which is the default) is interpreted for both the width and height of `floating_maximum_size`. It now refers to the width and height of the entire output layout, which matches i3's behavior. This also removes duplicated code to calculate the floating constraints in three files. Before this, `container_init_floating` used two-thirds of the workspace width/height as the max and the entire workspace width/height was used everywhere else. Now, all callers use a single function `floating_calculate_constraints`. --- include/sway/tree/container.h | 3 ++ sway/commands/resize.c | 45 ++------------------ sway/input/seatop_resize_floating.c | 37 +---------------- sway/sway.5.scd | 3 +- sway/tree/container.c | 64 +++++++++++++++++++---------- 5 files changed, 53 insertions(+), 99 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index f7a4ac37e..d25089949 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -212,6 +212,9 @@ void container_update_representation(struct sway_container *container); */ size_t container_titlebar_height(void); +void floating_calculate_constraints(int *min_width, int *max_width, + int *min_height, int *max_height); + /** * Resize and center the container in its workspace. */ diff --git a/sway/commands/resize.c b/sway/commands/resize.c index c9261535a..440937f0d 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -66,45 +66,6 @@ static int parse_resize_amount(int argc, char **argv, return 2; } -static void calculate_constraints(int *min_width, int *max_width, - int *min_height, int *max_height) { - struct sway_container *con = config->handler_context.container; - - if (config->floating_minimum_width == -1) { // no minimum - *min_width = 0; - } else if (config->floating_minimum_width == 0) { // automatic - *min_width = 75; - } else { - *min_width = config->floating_minimum_width; - } - - if (config->floating_minimum_height == -1) { // no minimum - *min_height = 0; - } else if (config->floating_minimum_height == 0) { // automatic - *min_height = 50; - } else { - *min_height = config->floating_minimum_height; - } - - if (config->floating_maximum_width == -1 || - container_is_scratchpad_hidden(con)) { // no max - *max_width = INT_MAX; - } else if (config->floating_maximum_width == 0) { // automatic - *max_width = con->workspace->width; - } else { - *max_width = config->floating_maximum_width; - } - - if (config->floating_maximum_height == -1 || - container_is_scratchpad_hidden(con)) { // no max - *max_height = INT_MAX; - } else if (config->floating_maximum_height == 0) { // automatic - *max_height = con->workspace->height; - } else { - *max_height = config->floating_maximum_height; - } -} - static uint32_t parse_resize_axis(const char *axis) { if (strcasecmp(axis, "width") == 0 || strcasecmp(axis, "horizontal") == 0) { return AXIS_HORIZONTAL; @@ -258,7 +219,8 @@ static struct cmd_results *resize_adjust_floating(uint32_t axis, // Make sure we're not adjusting beyond floating min/max size int min_width, max_width, min_height, max_height; - calculate_constraints(&min_width, &max_width, &min_height, &max_height); + floating_calculate_constraints(&min_width, &max_width, + &min_height, &max_height); if (con->width + grow_width < min_width) { grow_width = min_width - con->width; } else if (con->width + grow_width > max_width) { @@ -383,7 +345,8 @@ static struct cmd_results *resize_set_tiled(struct sway_container *con, static struct cmd_results *resize_set_floating(struct sway_container *con, struct resize_amount *width, struct resize_amount *height) { int min_width, max_width, min_height, max_height, grow_width = 0, grow_height = 0; - calculate_constraints(&min_width, &max_width, &min_height, &max_height); + floating_calculate_constraints(&min_width, &max_width, + &min_height, &max_height); if (width->amount) { switch (width->unit) { diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c index bf6c7ab42..18c6db73b 100644 --- a/sway/input/seatop_resize_floating.c +++ b/sway/input/seatop_resize_floating.c @@ -17,41 +17,6 @@ struct seatop_resize_floating_event { double ref_con_lx, ref_con_ly; // container's x/y at start of op }; -static void calculate_floating_constraints(struct sway_container *con, - int *min_width, int *max_width, int *min_height, int *max_height) { - if (config->floating_minimum_width == -1) { // no minimum - *min_width = 0; - } else if (config->floating_minimum_width == 0) { // automatic - *min_width = 75; - } else { - *min_width = config->floating_minimum_width; - } - - if (config->floating_minimum_height == -1) { // no minimum - *min_height = 0; - } else if (config->floating_minimum_height == 0) { // automatic - *min_height = 50; - } else { - *min_height = config->floating_minimum_height; - } - - if (config->floating_maximum_width == -1) { // no maximum - *max_width = INT_MAX; - } else if (config->floating_maximum_width == 0) { // automatic - *max_width = con->workspace->width; - } else { - *max_width = config->floating_maximum_width; - } - - if (config->floating_maximum_height == -1) { // no maximum - *max_height = INT_MAX; - } else if (config->floating_maximum_height == 0) { // automatic - *max_height = con->workspace->height; - } else { - *max_height = config->floating_maximum_height; - } -} - static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { struct seatop_resize_floating_event *e = seat->seatop_data; struct sway_container *con = e->con; @@ -85,7 +50,7 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { double width = e->ref_width + grow_width; double height = e->ref_height + grow_height; int min_width, max_width, min_height, max_height; - calculate_floating_constraints(con, &min_width, &max_width, + floating_calculate_constraints(&min_width, &max_width, &min_height, &max_height); width = fmax(min_width, fmin(width, max_width)); height = fmax(min_height, fmin(height, max_height)); diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 8f8b7e3d2..4f4522fb5 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -430,7 +430,8 @@ The default colors are: *floating_maximum_size* x Specifies the maximum size of floating windows. -1 x -1 removes the upper - limit. + limit. The default is 0 x 0, which will use the width and height of the + entire output layout as the maximums *floating_minimum_size* x Specifies the minimum size of floating windows. The default is 75 x 50. diff --git a/sway/tree/container.c b/sway/tree/container.c index d448df22a..330439411 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -611,56 +611,78 @@ size_t container_titlebar_height(void) { return config->font_height + config->titlebar_v_padding * 2; } -void container_init_floating(struct sway_container *con) { - struct sway_workspace *ws = con->workspace; - int min_width, min_height; - int max_width, max_height; - +void floating_calculate_constraints(int *min_width, int *max_width, + int *min_height, int *max_height) { if (config->floating_minimum_width == -1) { // no minimum - min_width = 0; + *min_width = 0; } else if (config->floating_minimum_width == 0) { // automatic - min_width = 75; + *min_width = 75; } else { - min_width = config->floating_minimum_width; + *min_width = config->floating_minimum_width; } if (config->floating_minimum_height == -1) { // no minimum - min_height = 0; + *min_height = 0; } else if (config->floating_minimum_height == 0) { // automatic - min_height = 50; + *min_height = 50; } else { - min_height = config->floating_minimum_height; + *min_height = config->floating_minimum_height; } + struct wlr_box *box = wlr_output_layout_get_box(root->output_layout, NULL); + if (config->floating_maximum_width == -1) { // no maximum - max_width = INT_MAX; + *max_width = INT_MAX; } else if (config->floating_maximum_width == 0) { // automatic - max_width = ws->width * 0.6666; + *max_width = box->width; } else { - max_width = config->floating_maximum_width; + *max_width = config->floating_maximum_width; } if (config->floating_maximum_height == -1) { // no maximum - max_height = INT_MAX; + *max_height = INT_MAX; } else if (config->floating_maximum_height == 0) { // automatic - max_height = ws->height * 0.6666; + *max_height = box->height; } else { - max_height = config->floating_maximum_height; + *max_height = config->floating_maximum_height; } +} + +void container_init_floating(struct sway_container *con) { + struct sway_workspace *ws = con->workspace; + int min_width, max_width, min_height, max_height; + floating_calculate_constraints(&min_width, &max_width, + &min_height, &max_height); + if (!con->view) { con->width = max_width; con->height = max_height; - con->x = ws->x + (ws->width - con->width) / 2; - con->y = ws->y + (ws->height - con->height) / 2; + if (con->width > ws->width || con->height > ws->height) { + struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, + ws->output->wlr_output); + con->x = ob->x + (ob->width - con->width) / 2; + con->y = ob->y + (ob->height - con->height) / 2; + } else { + con->x = ws->x + (ws->width - con->width) / 2; + con->y = ws->y + (ws->height - con->height) / 2; + } } else { struct sway_view *view = con->view; con->content_width = fmax(min_width, fmin(view->natural_width, max_width)); con->content_height = fmax(min_height, fmin(view->natural_height, max_height)); - con->content_x = ws->x + (ws->width - con->content_width) / 2; - con->content_y = ws->y + (ws->height - con->content_height) / 2; + if (con->content_width > ws->width + || con->content_height > ws->height) { + struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, + ws->output->wlr_output); + con->content_x = ob->x + (ob->width - con->content_width) / 2; + con->content_y = ob->y + (ob->height - con->content_height) / 2; + } else { + con->content_x = ws->x + (ws->width - con->content_width) / 2; + con->content_y = ws->y + (ws->height - con->content_height) / 2; + } // If the view's border is B_NONE then these properties are ignored. con->border_top = con->border_bottom = true; From 1c329f2fe6ae344b3201447a586f6c9a571d44ff Mon Sep 17 00:00:00 2001 From: Noam Preil Date: Sat, 2 Mar 2019 16:09:14 -0500 Subject: [PATCH 0160/2054] Fixes crash in spawn_swaybg (closes #3733) --- sway/config/output.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index cb889b3e8..3a36ed180 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -221,10 +221,10 @@ static bool spawn_swaybg(struct sway_output *output, char *const cmd[]) { pid = fork(); if (pid < 0) { sway_log_errno(SWAY_ERROR, "fork failed"); - exit(EXIT_FAILURE); + _exit(EXIT_FAILURE); } else if (pid == 0) { if (!set_cloexec(sockets[1], false)) { - exit(EXIT_FAILURE); + _exit(EXIT_FAILURE); } char wayland_socket_str[16]; @@ -234,9 +234,9 @@ static bool spawn_swaybg(struct sway_output *output, char *const cmd[]) { execvp(cmd[0], cmd); sway_log_errno(SWAY_ERROR, "execvp failed"); - exit(EXIT_FAILURE); + _exit(EXIT_FAILURE); } - exit(EXIT_SUCCESS); + _exit(EXIT_SUCCESS); } if (close(sockets[1]) != 0) { From 23f075e71d985754effde5372f4242ddb09cbbc0 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 2 Mar 2019 23:17:46 -0500 Subject: [PATCH 0161/2054] render_floating: skip fullscreen floaters If a floater is fullscreen either on a workspace or globally, it should not be rendered on any output is is not fullscreened on. When rendering it on an output it should not be rendered on, there will be an extraneous border along the adjacent side of the output. This adds a check in render_floating to skip all fullscreened floaters --- sway/desktop/render.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 5df160757..4b36a9c24 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -944,6 +944,9 @@ static void render_floating(struct sway_output *soutput, } for (int k = 0; k < ws->current.floating->length; ++k) { struct sway_container *floater = ws->current.floating->items[k]; + if (floater->fullscreen_mode != FULLSCREEN_NONE) { + continue; + } render_floating_container(soutput, damage, floater); } } From a053823f39c0ea72f3d3763e7c1e0f18ab9c4d61 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 3 Mar 2019 12:38:05 -0500 Subject: [PATCH 0162/2054] sway.1.scd: document environment vars set by sway This just documents the few environment variables set by sway in sway.1.scd --- sway/sway.1.scd | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sway/sway.1.scd b/sway/sway.1.scd index cfb4817a1..f8820c4e0 100644 --- a/sway/sway.1.scd +++ b/sway/sway.1.scd @@ -80,6 +80,27 @@ _XKB\_DEFAULT\_VARIANT_, _XKB\_DEFAULT\_OPTIONS_ preferred way to configure the keyboard is via the configuration file, see *sway-input*(5). +The following environment variables are set by sway: + +_DISPLAY_ + If compiled with Xwayland support and Xwayland is not disabled by the + config, this will be set to the name of the X display used for Xwayland + +_I3SOCK_ + For compatibility with i3, specifies the path to the sway IPC socket + +_SWAYSOCK_ + Specifies the path to the sway IPC socket + +_WAYLAND_DISPLAY_ + Specifies the name of the Wayland display that sway is running on + +_XCURSOR_SIZE_ + Specifies the configured cursor size + +_XCURSOR_THEME_ + Specifies the configured cursor theme + # AUTHORS Maintained by Drew DeVault , who is assisted by other open From bdac0df4f8fd1a816f0cfff8223d54d09823233b Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 4 Mar 2019 21:04:16 +1000 Subject: [PATCH 0163/2054] Allow concurrent clicks If two cursor buttons are pressed at the same time, the client will now be notified of the second button press. The main reason for not sending the concurrent presses was due to an early return in dispatch_cursor_button if a seatop is in progress. This patch makes it call seat_pointer_notify_button prior to returning. But it also has to make sure there's not a mismatch in events such as a release without a press. Prior to this patch, the down seatop would send press and release events in its begin and finish functions. No other seatops did this. A press event would be sent prior to starting tiling drag, but never an associated release. After this patch, no seatops send their own press or release events. We send them prior to calling the seatop begin functions, then the first part of dispatch_cursor_button handles all presses during seatops and when releasing the seatop. --- sway/input/cursor.c | 7 +++++++ sway/input/seatop_down.c | 3 --- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 44b5ff147..b96fde889 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -613,6 +613,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, } else { state_erase_button(cursor, button); } + seat_pointer_notify_button(seat, time_msec, button, state); return; } @@ -681,6 +682,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (cont && resize_edge && button == BTN_LEFT && state == WLR_BUTTON_PRESSED && !is_floating) { seat_set_focus_container(seat, cont); + seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_resize_tiling(seat, cont, button, edge); return; } @@ -711,6 +713,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, } cursor_set_image(seat->cursor, image, NULL); seat_set_focus_container(seat, cont); + seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_resize_tiling(seat, cont, button, edge); return; } @@ -726,6 +729,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, cont = cont->parent; } seat_set_focus_container(seat, cont); + seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_move_floating(seat, cont, button); return; } @@ -736,6 +740,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, state == WLR_BUTTON_PRESSED) { // Via border if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { + seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_resize_floating(seat, cont, button, resize_edge); return; } @@ -753,6 +758,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, WLR_EDGE_RIGHT : WLR_EDGE_LEFT; edge |= cursor->cursor->y > floater->y + floater->height / 2 ? WLR_EDGE_BOTTOM : WLR_EDGE_TOP; + seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_resize_floating(seat, floater, button, edge); return; } @@ -784,6 +790,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (surface && cont && state == WLR_BUTTON_PRESSED) { seat_set_focus_container(seat, cont); seatop_begin_down(seat, cont, time_msec, button, sx, sy); + seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); return; } diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index 33f9b31a7..7f3940957 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -40,8 +40,6 @@ static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); cursor_send_pointer_motion(cursor, 0, node, surface, sx, sy); } - seat_pointer_notify_button(seat, time_msec, - seat->seatop_button, WLR_BUTTON_RELEASED); } static void handle_abort(struct sway_seat *seat) { @@ -82,6 +80,5 @@ void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, seat->seatop_data = e; seat->seatop_button = button; - seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); container_raise_floating(con); } From 3f184cf188234083498e0b03435853bbadd01a99 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 4 Mar 2019 10:25:34 -0500 Subject: [PATCH 0164/2054] meson: update scdoc requirement to >= 1.9.2 Since scdoc 1.9.1 is bugged, this updates the meson version check to >= 1.9.2 and drops the version requirement from the README. This should make it more obvious to users who have 1.9.1 that they need to update scdoc to be able to compile man pages and hopefully cut down on the duplicate issues --- README.es.md | 2 +- README.md | 2 +- README.pl.md | 2 +- meson.build | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.es.md b/README.es.md index 076d89005..79953848b 100644 --- a/README.es.md +++ b/README.es.md @@ -38,7 +38,7 @@ Instale las dependencias: * pango * cairo * gdk-pixbuf2 \*\* -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.9.0 (optional: man pages) \* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (optional: man pages) \* * git \* _\*Compile-time dep_ diff --git a/README.md b/README.md index aa7cd846d..99835d6e5 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Install dependencies: * pango * cairo * gdk-pixbuf2 \*\* -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.9.0 (optional: man pages) \* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (optional: man pages) \* * git \* _\*Compile-time dep_ diff --git a/README.pl.md b/README.pl.md index ebfd9214a..0574093ab 100644 --- a/README.pl.md +++ b/README.pl.md @@ -39,7 +39,7 @@ Zainstaluj zależności: * pango * cairo * gdk-pixbuf2 \*\* -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) >= 1.9.0 (opcjonalnie: strony pomocy man) \* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (opcjonalnie: strony pomocy man) \* * git \* _\*zależności kompilacji_ diff --git a/meson.build b/meson.build index 270dc6b79..a08b2603d 100644 --- a/meson.build +++ b/meson.build @@ -95,7 +95,7 @@ conf_data.set10('HAVE_SYSTEMD', systemd.found()) conf_data.set10('HAVE_ELOGIND', elogind.found()) conf_data.set10('HAVE_TRAY', have_tray) -scdoc = dependency('scdoc', version: '>=1.9', native: true, required: get_option('man-pages')) +scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) if scdoc.found() scdoc_prog = find_program('scdoc') sh = find_program('sh') From a6711740bcd311e1ee551c83a5dfc46d9344d17e Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 4 Mar 2019 20:51:48 +0100 Subject: [PATCH 0165/2054] Set DISPLAY after initializing Xwayland This is necessary after https://github.com/swaywm/wlroots/pull/1596 --- sway/server.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/server.c b/sway/server.c index 0fcd58003..b24cc1dc4 100644 --- a/sway/server.c +++ b/sway/server.c @@ -184,6 +184,8 @@ bool server_start(struct sway_server *server) { &server->xwayland_ready); server->xwayland_ready.notify = handle_xwayland_ready; + setenv("DISPLAY", server->xwayland.wlr_xwayland->display_name, true); + server->xwayland.xcursor_manager = wlr_xcursor_manager_create(cursor_theme, cursor_size); wlr_xcursor_manager_load(server->xwayland.xcursor_manager, 1); From 19df2e590602abe0b5e1b53bc11debdb37be3fbe Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 6 Mar 2019 00:33:10 -0500 Subject: [PATCH 0166/2054] ipc: change {,deco_}rect to match i3 This fixes the `deco_rect` and `rect` properties in the IPC responses to match i3's behavior. `deco_rect` should be relative to the parent node, not the current node. This also takes tabbed and stacked decorations into account and will calculate `deco_rect` for all containers since tabbed and stacked child containers will have decorations. `rect` should exclude the window decorations. --- sway/ipc-json.c | 51 +++++++++++++++++++++++++++++++++++++-------- sway/sway-ipc.7.scd | 5 +++-- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 125df3877..c008f241f 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -350,6 +350,38 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace, json_object_object_add(object, "floating_nodes", floating_array); } +static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { + enum sway_container_layout parent_layout = container_parent_layout(c); + if (parent_layout != L_TABBED && parent_layout != L_STACKED && + c->current.border != B_NORMAL) { + deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; + return; + } + + if (c->parent) { + deco_rect->x = c->x - c->parent->x; + deco_rect->y = c->y - c->parent->y; + } else { + deco_rect->x = c->x - c->workspace->x; + deco_rect->y = c->y - c->workspace->y; + } + deco_rect->width = c->width; + deco_rect->height = container_titlebar_height(); + + if (parent_layout == L_TABBED) { + deco_rect->width = c->parent + ? c->parent->width / c->parent->children->length + : c->workspace->width / c->workspace->tiling->length; + deco_rect->x += deco_rect->width * container_sibling_index(c); + } else if (container_parent_layout(c) == L_STACKED) { + if (!c->view) { + size_t siblings = container_get_siblings(c)->length; + deco_rect->y -= deco_rect->height * siblings; + } + deco_rect->y += deco_rect->height * container_sibling_index(c); + } +} + static void ipc_json_describe_view(struct sway_container *c, json_object *object) { json_object_object_add(object, "pid", json_object_new_int(c->view->pid)); @@ -377,15 +409,6 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(object, "window_rect", ipc_json_create_rect(&window_box)); - struct wlr_box deco_box = {0, 0, 0, 0}; - - if (c->current.border == B_NORMAL) { - deco_box.width = c->width; - deco_box.height = c->content_y - c->y; - } - - json_object_object_add(object, "deco_rect", ipc_json_create_rect(&deco_box)); - struct wlr_box geometry = {0, 0, c->view->natural_width, c->view->natural_height}; json_object_object_add(object, "geometry", ipc_json_create_rect(&geometry)); @@ -465,6 +488,10 @@ static void ipc_json_describe_container(struct sway_container *c, json_object *o json_object_new_int(c->current.border_thickness)); json_object_object_add(object, "floating_nodes", json_object_new_array()); + struct wlr_box deco_box = {0, 0, 0, 0}; + get_deco_rect(c, &deco_box); + json_object_object_add(object, "deco_rect", ipc_json_create_rect(&deco_box)); + if (c->view) { ipc_json_describe_view(c, object); } @@ -505,6 +532,12 @@ json_object *ipc_json_describe_node(struct sway_node *node) { struct wlr_box box; node_get_box(node, &box); + if (node->type == N_CONTAINER) { + struct wlr_box deco_rect = {0, 0, 0, 0}; + get_deco_rect(node->sway_container, &deco_rect); + box.y += deco_rect.height; + box.height -= deco_rect.height; + } json_object *focus = json_object_new_array(); struct focus_inactive_data data = { diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index d598a54fd..6b4004536 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -314,14 +314,15 @@ node and will have the following properties: and other special nodes such as the scratchpad |- rect : object -: The absolute geometry of the node +: The absolute geometry of the node. The window decorations are excluded from + this, but borders are included. |- window_rect : object : The geometry of the contents inside the node. The window decorations are excluded from this calculation, but borders are included. |- deco_rect : object -: The geometry of the decorations inside the node +: The geometry of the decorations for the node relative to the parent node |- geometry : object : The natural geometry of the contents if it were to size itself From 8b6bd106aa92192e9e2aa6781e9b2e51f1e22fe2 Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 6 Mar 2019 10:47:50 +0100 Subject: [PATCH 0167/2054] Fix container_parent_layout for scratchpad windows --- sway/tree/container.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 330439411..a6142193f 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -1163,7 +1163,10 @@ enum sway_container_layout container_parent_layout(struct sway_container *con) { if (con->parent) { return con->parent->layout; } - return con->workspace->layout; + if (con->workspace) { + return con->workspace->layout; + } + return L_NONE; } enum sway_container_layout container_current_parent_layout( From 8ada2daba5f58fe2bba77127bedd4e9af7c8844f Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 6 Mar 2019 12:16:28 -0500 Subject: [PATCH 0168/2054] ipc: fix rect for stacked children This now takes all titlebars for stacked children into account for the ipc property `rect` --- sway/ipc-json.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index c008f241f..bcc525ad1 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -535,8 +535,12 @@ json_object *ipc_json_describe_node(struct sway_node *node) { if (node->type == N_CONTAINER) { struct wlr_box deco_rect = {0, 0, 0, 0}; get_deco_rect(node->sway_container, &deco_rect); - box.y += deco_rect.height; - box.height -= deco_rect.height; + size_t count = 1; + if (container_parent_layout(node->sway_container) == L_STACKED) { + count = container_get_siblings(node->sway_container)->length; + } + box.y += deco_rect.height * count; + box.height -= deco_rect.height * count; } json_object *focus = json_object_new_array(); From 0df76ed96afa75ff8dd8ab6cf7748026e1caf5e7 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 6 Mar 2019 12:02:19 -0500 Subject: [PATCH 0169/2054] ipc: fix fullscreen deco_rect This fixes the deco_rect reported by the ipc for fullscreen containers to be all zeroes. Children of the fullscreen container should still have their decorations reported correctly --- sway/ipc-json.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index bcc525ad1..20dcafb1e 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -352,8 +352,9 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace, static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { enum sway_container_layout parent_layout = container_parent_layout(c); - if (parent_layout != L_TABBED && parent_layout != L_STACKED && - c->current.border != B_NORMAL) { + if ((parent_layout != L_TABBED && parent_layout != L_STACKED && + c->current.border != B_NORMAL) || + c->fullscreen_mode != FULLSCREEN_NONE) { deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; return; } From 7f700e08ac8ac95c395b54c86b40d2fc79242310 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Thu, 7 Mar 2019 03:37:49 -0500 Subject: [PATCH 0170/2054] ipc: describe libinput device configuration This adds the device configurations to the ipc response for libinput devices. Only supported configuration options for the device will be added. This also moves `libinput_send_events` inside a new `libinput` object that contains the rest of the configuration options. sway-ipc(7) has been updated to reflect the changes and document the new additions. --- sway/ipc-json.c | 198 ++++++++++++++++++++++++++++++++++++++++---- sway/sway-ipc.7.scd | 144 ++++++++++++++++++++++++++++---- swaymsg/main.c | 10 ++- 3 files changed, 317 insertions(+), 35 deletions(-) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 20dcafb1e..e9564b049 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -617,6 +617,187 @@ json_object *ipc_json_describe_node_recursive(struct sway_node *node) { return object; } +static json_object *describe_libinput_device(struct libinput_device *device) { + json_object *object = json_object_new_object(); + + const char *events = "unknown"; + switch (libinput_device_config_send_events_get_mode(device)) { + case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED: + events = "enabled"; + break; + case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE: + events = "disabled_on_external_mouse"; + break; + case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED: + events = "disabled"; + break; + } + json_object_object_add(object, "send_events", + json_object_new_string(events)); + + if (libinput_device_config_tap_get_finger_count(device) > 0) { + const char *tap = "unknown"; + switch (libinput_device_config_tap_get_enabled(device)) { + case LIBINPUT_CONFIG_TAP_ENABLED: + tap = "enabled"; + break; + case LIBINPUT_CONFIG_TAP_DISABLED: + tap = "disabled"; + break; + } + json_object_object_add(object, "tap", json_object_new_string(tap)); + + const char *button_map = "unknown"; + switch (libinput_device_config_tap_get_button_map(device)) { + case LIBINPUT_CONFIG_TAP_MAP_LRM: + button_map = "lrm"; + break; + case LIBINPUT_CONFIG_TAP_MAP_LMR: + button_map = "lmr"; + break; + } + json_object_object_add(object, "tap_button_map", + json_object_new_string(button_map)); + + const char* drag = "unknown"; + switch (libinput_device_config_tap_get_drag_enabled(device)) { + case LIBINPUT_CONFIG_DRAG_ENABLED: + drag = "enabled"; + break; + case LIBINPUT_CONFIG_DRAG_DISABLED: + drag = "disabled"; + break; + } + json_object_object_add(object, "tap_drag", + json_object_new_string(drag)); + + const char *drag_lock = "unknown"; + switch (libinput_device_config_tap_get_drag_lock_enabled(device)) { + case LIBINPUT_CONFIG_DRAG_LOCK_ENABLED: + drag_lock = "enabled"; + break; + case LIBINPUT_CONFIG_DRAG_LOCK_DISABLED: + drag_lock = "disabled"; + break; + } + json_object_object_add(object, "tap_drag_lock", + json_object_new_string(drag_lock)); + } + + if (libinput_device_config_accel_is_available(device)) { + double accel = libinput_device_config_accel_get_speed(device); + json_object_object_add(object, "accel_speed", + json_object_new_double(accel)); + + const char *accel_profile = "unknown"; + switch (libinput_device_config_accel_get_profile(device)) { + case LIBINPUT_CONFIG_ACCEL_PROFILE_NONE: + accel_profile = "none"; + break; + case LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT: + accel_profile = "flat"; + break; + case LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE: + accel_profile = "adaptive"; + break; + } + json_object_object_add(object, "accel_profile", + json_object_new_string(accel_profile)); + } + + if (libinput_device_config_scroll_has_natural_scroll(device)) { + const char *natural_scroll = "disabled"; + if (libinput_device_config_scroll_get_natural_scroll_enabled(device)) { + natural_scroll = "enabled"; + } + json_object_object_add(object, "natural_scroll", + json_object_new_string(natural_scroll)); + } + + if (libinput_device_config_left_handed_is_available(device)) { + const char *left_handed = "disabled"; + if (libinput_device_config_left_handed_get(device) != 0) { + left_handed = "enabled"; + } + json_object_object_add(object, "left_handed", + json_object_new_string(left_handed)); + } + + uint32_t click_methods = libinput_device_config_click_get_methods(device); + if ((click_methods & ~LIBINPUT_CONFIG_CLICK_METHOD_NONE) != 0) { + const char *click_method = "unknown"; + switch (libinput_device_config_click_get_method(device)) { + case LIBINPUT_CONFIG_CLICK_METHOD_NONE: + click_method = "none"; + break; + case LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS: + click_method = "button_areas"; + break; + case LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER: + click_method = "clickfinger"; + break; + } + json_object_object_add(object, "click_method", + json_object_new_string(click_method)); + } + + if (libinput_device_config_middle_emulation_is_available(device)) { + const char *middle_emulation = "unknown"; + switch (libinput_device_config_middle_emulation_get_enabled(device)) { + case LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED: + middle_emulation = "enabled"; + break; + case LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED: + middle_emulation = "disabled"; + break; + } + json_object_object_add(object, "middle_emulation", + json_object_new_string(middle_emulation)); + } + + uint32_t scroll_methods = libinput_device_config_scroll_get_methods(device); + if ((scroll_methods & ~LIBINPUT_CONFIG_SCROLL_NO_SCROLL) != 0) { + const char *scroll_method = "unknown"; + switch (libinput_device_config_scroll_get_method(device)) { + case LIBINPUT_CONFIG_SCROLL_NO_SCROLL: + scroll_method = "none"; + break; + case LIBINPUT_CONFIG_SCROLL_2FG: + scroll_method = "two_finger"; + break; + case LIBINPUT_CONFIG_SCROLL_EDGE: + scroll_method = "edge"; + break; + case LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN: + scroll_method = "on_button_down"; + break; + } + json_object_object_add(object, "scroll_method", + json_object_new_string(scroll_method)); + + if ((scroll_methods & LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN) != 0) { + uint32_t button = libinput_device_config_scroll_get_button(device); + json_object_object_add(object, "scroll_button", + json_object_new_int(button)); + } + } + + if (libinput_device_config_dwt_is_available(device)) { + const char *dwt = "unknown"; + switch (libinput_device_config_dwt_get_enabled(device)) { + case LIBINPUT_CONFIG_DWT_ENABLED: + dwt = "enabled"; + break; + case LIBINPUT_CONFIG_DWT_DISABLED: + dwt = "disabled"; + break; + } + json_object_object_add(object, "dwt", json_object_new_string(dwt)); + } + + return object; +} + json_object *ipc_json_describe_input(struct sway_input_device *device) { if (!(sway_assert(device, "Device must not be null"))) { return NULL; @@ -660,21 +841,8 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { if (wlr_input_device_is_libinput(device->wlr_device)) { struct libinput_device *libinput_dev; libinput_dev = wlr_libinput_get_device_handle(device->wlr_device); - - const char *events = "unknown"; - switch (libinput_device_config_send_events_get_mode(libinput_dev)) { - case LIBINPUT_CONFIG_SEND_EVENTS_ENABLED: - events = "enabled"; - break; - case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE: - events = "disabled_on_external_mouse"; - break; - case LIBINPUT_CONFIG_SEND_EVENTS_DISABLED: - events = "disabled"; - break; - } - json_object_object_add(object, "libinput_send_events", - json_object_new_string(events)); + json_object_object_add(object, "libinput", + describe_libinput_device(libinput_dev)); } return object; diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index 6b4004536..b43b30302 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -1034,10 +1034,66 @@ following properties: |- xkb_active_layout_name : string : (Only keyboards) The active keyboard layout in use -|- libinput_send_events +|- libinput +: object +: (Only libinput devices) An object describing the current device settings. + See below for more information + +The _libinput_ object describes the device configuration for libinput devices. +Only properties that are supported for the device will be added to the object. +In addition to the possible options listed, all string properties may also be +_unknown_, in the case that a new option is added to libinput. See +*sway-input*(5) for information on the meaning of the possible values. The +following properties will be included for devices that support them: + +[- *PROPERTY* +:- *DATA TYPE* +:- *DESCRIPTION* +|- send_events : string -: (Only libinput devices) The send events value in use by libinput for this - device. It can be _enabled_, _disabled_, or _disabled\_on\_external\_mouse_ +:[ Whether events are being sent by the device. It can be _enabled_, + _disabled_, or _disabled\_on\_external\_mouse_ +|- tap +: string +: Whether tap to click is enabled. It can be _enabled_ or _disabled_ +|- tap_button_map +: string +: The finger to button mapping in use. It can be _lmr_ or _lrm_ +|- tap_drag +: string +: Whether tap-and-drag is enabled. It can be _enabled_ or _disabled_ +|- tap_drag_lock +: string +: Whether drag-lock is enabled. It can be _enabled_ or _disabled_ +|- accel_speed +: double +: The pointer-acceleration in use +|- accel_profile +: string +: The acceleration profile in use. It can be _none_, _flat_, or _adaptive_ +|- natural_scroll +: string +: Whether natural scrolling is enabled. It can be _enabled_ or _disabled_ +|- left_handed +: string +: Whether left-handed mode is enabled. It can be _enabled_ or _disabled_ +|- click_method +: string +: The click method in use. It can be _none_, _button_areas_, or _clickfinger_ +|- middle_emulation +: string +: Whether middle emulation is enabled. It can be _enabled_ or _disabled_ +|- scroll_method +: string +: The scroll method in use. It can be _none_, _two_finger_, _edge_, or + _on_button_down_ +|- scroll_button +: int +: The scroll button to use when _scroll_method_ is _on_button_down_. This + will be given as an input event code +|- dwt +: string +: Whether disable-while-typing is enabled. It can be _enabled_ or _disabled_ *Example Reply:* @@ -1050,7 +1106,9 @@ following properties: "product": 1, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } }, { "identifier": "1267:5:Elan_Touchpad", @@ -1058,7 +1116,21 @@ following properties: "vendor": 1267, "product": 5, "type": "pointer", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled", + "tap": "enabled", + "tap_button_map": "lmr", + "tap_drag": "enabled", + "tap_drag_lock": "disabled", + "accel_speed": 0.0, + "accel_profile": "none", + "natural_scroll", "disabled", + "left_handed": "disabled", + "click_method": "button_areas", + "middle_emulation": "disabled", + "scroll_method": "edge", + "dwt": "enabled" + } }, { "identifier": "3034:22494:USB2.0_VGA_UVC_WebCam:_USB2.0_V", @@ -1067,7 +1139,9 @@ following properties: "product": 22494, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } }, { "identifier": "0:3:Sleep_Button", @@ -1076,7 +1150,9 @@ following properties: "product": 3, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } }, { "identifier": "0:5:Lid_Switch", @@ -1084,7 +1160,10 @@ following properties: "vendor": 0, "product": 5, "type": "switch", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } + }, { "identifier": "0:6:Video_Bus", "name": "Video Bus", @@ -1092,7 +1171,9 @@ following properties: "product": 6, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } }, { "identifier": "0:1:Power_Button", @@ -1101,7 +1182,9 @@ following properties: "product": 1, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } } ] ``` @@ -1150,7 +1233,9 @@ one seat. Each object has the following properties: "product": 1, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } }, { "identifier": "1267:5:Elan_Touchpad", @@ -1158,7 +1243,21 @@ one seat. Each object has the following properties: "vendor": 1267, "product": 5, "type": "pointer", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled", + "tap": "enabled", + "tap_button_map": "lmr", + "tap_drag": "enabled", + "tap_drag_lock": "disabled", + "accel_speed": 0.0, + "accel_profile": "none", + "natural_scroll", "disabled", + "left_handed": "disabled", + "click_method": "button_areas", + "middle_emulation": "disabled", + "scroll_method": "edge", + "dwt": "enabled" + } }, { "identifier": "3034:22494:USB2.0_VGA_UVC_WebCam:_USB2.0_V", @@ -1167,7 +1266,9 @@ one seat. Each object has the following properties: "product": 22494, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } }, { "identifier": "0:3:Sleep_Button", @@ -1176,7 +1277,9 @@ one seat. Each object has the following properties: "product": 3, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } }, { "identifier": "0:5:Lid_Switch", @@ -1184,7 +1287,10 @@ one seat. Each object has the following properties: "vendor": 0, "product": 5, "type": "switch", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } + }, { "identifier": "0:6:Video_Bus", "name": "Video Bus", @@ -1192,7 +1298,9 @@ one seat. Each object has the following properties: "product": 6, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } }, { "identifier": "0:1:Power_Button", @@ -1201,7 +1309,9 @@ one seat. Each object has the following properties: "product": 1, "type": "keyboard", "xkb_active_layout_name": "English (US)", - "libinput_send_events": "enabled" + "libinput": { + "send_events": "enabled" + } } ] } diff --git a/swaymsg/main.c b/swaymsg/main.c index 716d2d2ed..e51c00d92 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -113,7 +113,7 @@ static const char *pretty_type_name(const char *name) { } static void pretty_print_input(json_object *i) { - json_object *id, *name, *type, *product, *vendor, *kbdlayout, *events; + json_object *id, *name, *type, *product, *vendor, *kbdlayout, *libinput; json_object_object_get_ex(i, "identifier", &id); json_object_object_get_ex(i, "name", &name); json_object_object_get_ex(i, "type", &type); @@ -139,8 +139,12 @@ static void pretty_print_input(json_object *i) { printf(" Active Keyboard Layout: %s\n", layout ? layout : "(unnamed)"); } - if (json_object_object_get_ex(i, "libinput_send_events", &events)) { - printf(" Libinput Send Events: %s\n", json_object_get_string(events)); + if (json_object_object_get_ex(i, "libinput", &libinput)) { + json_object *events; + if (json_object_object_get_ex(libinput, "send_events", &events)) { + printf(" Libinput Send Events: %s\n", + json_object_get_string(events)); + } } printf("\n"); From b7fe5097e949b9bfde6fd52f26d2599068833563 Mon Sep 17 00:00:00 2001 From: Peter Grayson Date: Fri, 8 Mar 2019 12:43:04 -0500 Subject: [PATCH 0171/2054] Add -p/--pretty option to swaymsg This new option forces pretty (non-raw/non-JSON) output. By default, when not using a tty, swaymsg outputs using the "raw" format. This makes it impossible to, for example, pipe the pretty output to a pager such as `less` since piping does not use a tty. The new -p/--pretty option gives the user explicit control over the output format while retaining the default tty-dependent behavior. Signed-off-by: Peter Grayson --- completions/bash/swaymsg | 2 ++ completions/fish/swaymsg.fish | 3 ++- completions/zsh/_swaymsg | 7 ++++--- swaymsg/main.c | 7 ++++++- swaymsg/swaymsg.1.scd | 3 +++ 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/completions/bash/swaymsg b/completions/bash/swaymsg index 20092bdc4..0b0119029 100644 --- a/completions/bash/swaymsg +++ b/completions/bash/swaymsg @@ -21,6 +21,7 @@ _swaymsg() short=( -h + -p -q -r -s @@ -30,6 +31,7 @@ _swaymsg() long=( --help + --pretty --quiet --raw --socket diff --git a/completions/fish/swaymsg.fish b/completions/fish/swaymsg.fish index 636e35006..3f1cc50c1 100644 --- a/completions/fish/swaymsg.fish +++ b/completions/fish/swaymsg.fish @@ -2,10 +2,11 @@ complete -f -c swaymsg complete -c swaymsg -s h -l help --description "Show help message and quit." +complete -c swaymsg -s p -l pretty --description "Use pretty output even when not using a tty." complete -c swaymsg -s q -l quiet --description "Sends the IPC message but does not print the response from sway." -complete -c swaymsg -s v -l version --description "Print the version (of swaymsg) and quit." complete -c swaymsg -s r -l raw --description "Use raw output even if using tty." complete -c swaymsg -s s -l socket -r --description "Use the specified socket path. Otherwise, swaymsg will ask where the socket is (which is the value of $SWAYSOCK, then of $I3SOCK)." +complete -c swaymsg -s v -l version --description "Print the version (of swaymsg) and quit." complete -c swaymsg -s t -l type -fr --description "Specify the type of IPC message." complete -c swaymsg -s t -l type -fra 'get_workspaces' --description "Gets a JSON-encoded list of workspaces and their status." diff --git a/completions/zsh/_swaymsg b/completions/zsh/_swaymsg index 0ba45d4a9..c11fa9905 100644 --- a/completions/zsh/_swaymsg +++ b/completions/zsh/_swaymsg @@ -28,10 +28,11 @@ types=( ) _arguments -s \ - '(-v --version)'{-v,--version}'[Show the version number and quit]' \ - '(-m --monitor)'{-m,--monitor}'[Monitor until killed (-t SUBSCRIBE only)]' \ '(-h --help)'{-h,--help}'[Show help message and quit]' \ + '(-m --monitor)'{-m,--monitor}'[Monitor until killed (-t SUBSCRIBE only)]' \ + '(-p --pretty)'{-p,--pretty}'[Use pretty output even when not using a tty]' \ '(-q --quiet)'{-q,--quiet}'[Be quiet]' \ '(-r --raw)'{-r,--raw}'[Use raw output even if using a tty]' \ '(-s --socket)'{-s,--socket}'[Use the specified socket path]:files:_files' \ - '(-t --type)'{-t,--type}'[Specify the message type]:type:{_describe "type" types}' + '(-t --type)'{-t,--type}'[Specify the message type]:type:{_describe "type" types}' \ + '(-v --version)'{-v,--version}'[Show the version number and quit]' diff --git a/swaymsg/main.c b/swaymsg/main.c index e51c00d92..a0ef7e3d9 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -326,6 +326,7 @@ int main(int argc, char **argv) { static struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"monitor", no_argument, NULL, 'm'}, + {"pretty", no_argument, NULL, 'p'}, {"quiet", no_argument, NULL, 'q'}, {"raw", no_argument, NULL, 'r'}, {"socket", required_argument, NULL, 's'}, @@ -339,6 +340,7 @@ int main(int argc, char **argv) { "\n" " -h, --help Show help message and quit.\n" " -m, --monitor Monitor until killed (-t SUBSCRIBE only)\n" + " -p, --pretty Use pretty output even when not using a tty\n" " -q, --quiet Be quiet.\n" " -r, --raw Use raw output even if using a tty\n" " -s, --socket Use the specified socket.\n" @@ -350,7 +352,7 @@ int main(int argc, char **argv) { int c; while (1) { int option_index = 0; - c = getopt_long(argc, argv, "hmqrs:t:v", long_options, &option_index); + c = getopt_long(argc, argv, "hmpqrs:t:v", long_options, &option_index); if (c == -1) { break; } @@ -358,6 +360,9 @@ int main(int argc, char **argv) { case 'm': // Monitor monitor = true; break; + case 'p': // Pretty + raw = false; + break; case 'q': // Quiet quiet = true; break; diff --git a/swaymsg/swaymsg.1.scd b/swaymsg/swaymsg.1.scd index 523db6cc0..1bcf956ad 100644 --- a/swaymsg/swaymsg.1.scd +++ b/swaymsg/swaymsg.1.scd @@ -19,6 +19,9 @@ _swaymsg_ [options...] [message] there is a malformed response or an invalid event type was requested, swaymsg will stop monitoring and exit. +*-p, --pretty* + Use raw output even when not using a tty. + *-q, --quiet* Sends the IPC message but does not print the response from sway. From 1a9145e40983aa84da0e08ec3806b6e6fc8983f8 Mon Sep 17 00:00:00 2001 From: Peter Grayson Date: Fri, 8 Mar 2019 14:39:35 -0500 Subject: [PATCH 0172/2054] Add missing swaymsg completions The `-m/--monitor` option was missing from the bash and fish completions. The `subscribe` IPC message type was missing from the bash, fish, and zsh completions. Signed-off-by: Peter Grayson --- completions/bash/swaymsg | 3 +++ completions/fish/swaymsg.fish | 2 ++ completions/zsh/_swaymsg | 1 + 3 files changed, 6 insertions(+) diff --git a/completions/bash/swaymsg b/completions/bash/swaymsg index 0b0119029..a333c9124 100644 --- a/completions/bash/swaymsg +++ b/completions/bash/swaymsg @@ -17,10 +17,12 @@ _swaymsg() 'get_binding_modes' 'get_config' 'send_tick' + 'subscribe' ) short=( -h + -m -p -q -r @@ -31,6 +33,7 @@ _swaymsg() long=( --help + --monitor --pretty --quiet --raw diff --git a/completions/fish/swaymsg.fish b/completions/fish/swaymsg.fish index 3f1cc50c1..25085f9a7 100644 --- a/completions/fish/swaymsg.fish +++ b/completions/fish/swaymsg.fish @@ -2,6 +2,7 @@ complete -f -c swaymsg complete -c swaymsg -s h -l help --description "Show help message and quit." +complete -c swaymsg -s m -l monitor --description "Monitor subscribed events until killed." complete -c swaymsg -s p -l pretty --description "Use pretty output even when not using a tty." complete -c swaymsg -s q -l quiet --description "Sends the IPC message but does not print the response from sway." complete -c swaymsg -s r -l raw --description "Use raw output even if using tty." @@ -20,3 +21,4 @@ complete -c swaymsg -s t -l type -fra 'get_binding_modes' --description "Gets a complete -c swaymsg -s t -l type -fra 'get_config' --description "Gets a JSON-encoded copy of the current configuration." complete -c swaymsg -s t -l type -fra 'get_seats' --description "Gets a JSON-encoded list of all seats, its properties and all assigned devices." complete -c swaymsg -s t -l type -fra 'send_tick' --description "Sends a tick event to all subscribed clients." +complete -c swaymsg -s t -l type -fra 'subscribe' --description "Subscribe to a list of event types." diff --git a/completions/zsh/_swaymsg b/completions/zsh/_swaymsg index c11fa9905..3f42a77d7 100644 --- a/completions/zsh/_swaymsg +++ b/completions/zsh/_swaymsg @@ -25,6 +25,7 @@ types=( 'get_binding_modes' 'get_config' 'send_tick' +'subscribe' ) _arguments -s \ From 5fab8a2ad4c05fb7bfdbb45915b8abf7ebeab5fe Mon Sep 17 00:00:00 2001 From: minus Date: Sat, 9 Mar 2019 11:06:09 +0100 Subject: [PATCH 0173/2054] Fix crash when moving window to scratchpad --- sway/ipc-json.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index e9564b049..bebe6dd7b 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -354,7 +354,8 @@ static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { enum sway_container_layout parent_layout = container_parent_layout(c); if ((parent_layout != L_TABBED && parent_layout != L_STACKED && c->current.border != B_NORMAL) || - c->fullscreen_mode != FULLSCREEN_NONE) { + c->fullscreen_mode != FULLSCREEN_NONE || + c->workspace == NULL) { deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; return; } From a73e8f1328a247766f4ff78fdc73b38488dec385 Mon Sep 17 00:00:00 2001 From: Jeff Peeler Date: Fri, 8 Mar 2019 22:06:38 -0500 Subject: [PATCH 0174/2054] fix "directive argument is null" errors --- sway/criteria.c | 2 +- sway/input/seat.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/sway/criteria.c b/sway/criteria.c index 48ef39df4..5ad481452 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -624,8 +624,8 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { in_quotes = false; } unescape(value); + sway_log(SWAY_DEBUG, "Found pair: %s=%s", name, value); } - sway_log(SWAY_DEBUG, "Found pair: %s=%s", name, value); if (!parse_token(criteria, name, value)) { *error_arg = error; goto cleanup; diff --git a/sway/input/seat.c b/sway/input/seat.c index 9888ddfcb..245326a1e 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -705,8 +705,7 @@ void seat_configure_xcursor(struct sway_seat *seat) { seat->cursor->xcursor_manager = wlr_xcursor_manager_create(cursor_theme, 24); if (sway_assert(seat->cursor->xcursor_manager, - "Cannot create XCursor manager for theme %s", - cursor_theme)) { + "Cannot create XCursor manager for theme")) { return; } } From 7580718d12ed752ed5565e28ce3d46c3a589ffe3 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 8 Mar 2019 12:44:42 -0500 Subject: [PATCH 0175/2054] output_damage_whole_container: damage subsurfaces This adds an iterative call in `output_damage_whole_container` to damage the subsurfaces for all visible views that are inside of the container. This is needed to damage subsurfaces that extend outside the box of the container. Without this, those subsurfaces will create artifacts when moving or resizing. --- sway/desktop/output.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index d7d3fc07e..54b9f2942 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -473,6 +473,17 @@ void output_damage_box(struct sway_output *output, struct wlr_box *_box) { wlr_output_damage_add_box(output->damage, &box); } +static void damage_child_views_iterator(struct sway_container *con, + void *data) { + if (!con->view || !view_is_visible(con->view)) { + return; + } + struct sway_output *output = data; + bool whole = true; + output_view_for_each_surface(output, con->view, damage_surface_iterator, + &whole); +} + void output_damage_whole_container(struct sway_output *output, struct sway_container *con) { // Pad the box by 1px, because the width is a double and might be a fraction @@ -484,6 +495,12 @@ void output_damage_whole_container(struct sway_output *output, }; scale_box(&box, output->wlr_output->scale); wlr_output_damage_add_box(output->damage, &box); + // Damage subsurfaces as well, which may extend outside the box + if (con->view) { + damage_child_views_iterator(con, output); + } else { + container_for_each_child(con, damage_child_views_iterator, output); + } } static void damage_handle_destroy(struct wl_listener *listener, void *data) { From c02d2a01289581fd3c32a22e1a498ffad736281d Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 8 Mar 2019 12:57:50 -0500 Subject: [PATCH 0176/2054] damage: remove output_damage_view This removes `output_damage_view` since it is unnecessary. The logic has been moved into its only caller `output_damage_from_view`. When damaging the whole view, `output_damage_whole_container` should be used instead --- sway/desktop/output.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 54b9f2942..3ff4d7262 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -450,19 +450,15 @@ void output_damage_surface(struct sway_output *output, double ox, double oy, damage_surface_iterator, &whole); } -static void output_damage_view(struct sway_output *output, - struct sway_view *view, bool whole) { +void output_damage_from_view(struct sway_output *output, + struct sway_view *view) { if (!view_is_visible(view)) { return; } + bool whole = false; output_view_for_each_surface(output, view, damage_surface_iterator, &whole); } -void output_damage_from_view(struct sway_output *output, - struct sway_view *view) { - output_damage_view(output, view, false); -} - // Expecting an unscaled box in layout coordinates void output_damage_box(struct sway_output *output, struct wlr_box *_box) { struct wlr_box box; From 9a75f0010f9bc448dfedb5de6e484719613d6428 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 10 Mar 2019 15:00:15 -0400 Subject: [PATCH 0177/2054] arrange: use int not size_t for title offsets This changes `apply_tabbed_layout` and `apply_stacked_layout` to use `int` instead of `size_t`. This is necessary for tabbed and stacked containers to be positioned correctly when the y-location is negative. The reasoning for this is signed plus unsigned is always an unsigned value. This was causing the y-location of the container to be positioned near `INT_MIN` due to an unsigned integer underflow --- sway/tree/arrange.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index da372aa44..438a21335 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -99,7 +99,7 @@ static void apply_tabbed_layout(list_t *children, struct wlr_box *parent) { } for (int i = 0; i < children->length; ++i) { struct sway_container *child = children->items[i]; - size_t parent_offset = child->view ? 0 : container_titlebar_height(); + int parent_offset = child->view ? 0 : container_titlebar_height(); container_remove_gaps(child); child->x = parent->x; child->y = parent->y + parent_offset; @@ -115,7 +115,7 @@ static void apply_stacked_layout(list_t *children, struct wlr_box *parent) { } for (int i = 0; i < children->length; ++i) { struct sway_container *child = children->items[i]; - size_t parent_offset = child->view ? 0 : + int parent_offset = child->view ? 0 : container_titlebar_height() * children->length; container_remove_gaps(child); child->x = parent->x; From 83b68b21541ac00649c9d100cfec198326152fec Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sun, 10 Mar 2019 15:09:23 -0400 Subject: [PATCH 0178/2054] Update language in sway.desktop & sway(1) --- sway.desktop | 2 +- sway/sway.1.scd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sway.desktop b/sway.desktop index 98c9af293..420db5aa1 100644 --- a/sway.desktop +++ b/sway.desktop @@ -1,5 +1,5 @@ [Desktop Entry] Name=Sway -Comment=SirCmpwn's Wayland window manager +Comment=An i3-compatible Wayland compositor Exec=sway Type=Application diff --git a/sway/sway.1.scd b/sway/sway.1.scd index f8820c4e0..ba31e4a68 100644 --- a/sway/sway.1.scd +++ b/sway/sway.1.scd @@ -2,7 +2,7 @@ sway(1) # NAME -sway - SirCmpwn's Wayland window manager +sway - An i3-compatible Wayland compositor # SYNOPSIS From 191305468c0fe9b1acf8b6ad6c8a49b97cf793aa Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 10 Mar 2019 21:47:30 +0100 Subject: [PATCH 0179/2054] Fix crash in cmd_workspace when layer surface has focus --- sway/commands/workspace.c | 5 +++++ sway/input/seat.c | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index 5fde81299..362dcd1b2 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -184,6 +184,11 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { bool create = argc > 1 && strcasecmp(argv[1], "--create") == 0; struct sway_seat *seat = config->handler_context.seat; struct sway_workspace *current = seat_get_focused_workspace(seat); + if (!current) { + return cmd_results_new(CMD_FAILURE, "workspace", + "No workspace to switch from"); + } + struct sway_workspace *ws = NULL; if (strcasecmp(argv[0], "number") == 0) { if (argc < 2) { diff --git a/sway/input/seat.c b/sway/input/seat.c index 245326a1e..3a68904bc 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1101,7 +1101,7 @@ struct sway_node *seat_get_focus(struct sway_seat *seat) { } struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat) { - struct sway_node *focus = seat_get_focus(seat); + struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); if (!focus) { return NULL; } @@ -1111,7 +1111,7 @@ struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat) { if (focus->type == N_WORKSPACE) { return focus->sway_workspace; } - return NULL; // unreachable + return NULL; // output doesn't have a workspace yet } struct sway_container *seat_get_focused_container(struct sway_seat *seat) { From 30931ad9e77b106956007530821d98bc20bca969 Mon Sep 17 00:00:00 2001 From: Ben Challenor Date: Sun, 10 Mar 2019 20:26:50 +0000 Subject: [PATCH 0180/2054] Make raw keysyms take precedence over translated Allows both BackSpace and Shift+BackSpace to be bound under the US keyboard layout, per #3705. --- sway/input/keyboard.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 00fc6a13f..06ae99c49 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -292,32 +292,32 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { // Identify new keycode, raw keysym(s), and translated keysym(s) xkb_keycode_t keycode = event->keycode + 8; + const xkb_keysym_t *raw_keysyms; + uint32_t raw_modifiers; + size_t raw_keysyms_len = + keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &raw_modifiers); + const xkb_keysym_t *translated_keysyms; uint32_t translated_modifiers; size_t translated_keysyms_len = keyboard_keysyms_translated(keyboard, keycode, &translated_keysyms, &translated_modifiers); - const xkb_keysym_t *raw_keysyms; - uint32_t raw_modifiers; - size_t raw_keysyms_len = - keyboard_keysyms_raw(keyboard, keycode, &raw_keysyms, &raw_modifiers); - uint32_t code_modifiers = wlr_keyboard_get_modifiers(wlr_device->keyboard); // Update shortcut model state update_shortcut_state(&keyboard->state_keycodes, event, (uint32_t)keycode, code_modifiers); - for (size_t i = 0; i < translated_keysyms_len; ++i) { - update_shortcut_state(&keyboard->state_keysyms_translated, - event, (uint32_t)translated_keysyms[i], - code_modifiers); - } for (size_t i = 0; i < raw_keysyms_len; ++i) { update_shortcut_state(&keyboard->state_keysyms_raw, event, (uint32_t)raw_keysyms[i], code_modifiers); } + for (size_t i = 0; i < translated_keysyms_len; ++i) { + update_shortcut_state(&keyboard->state_keysyms_translated, + event, (uint32_t)translated_keysyms[i], + code_modifiers); + } bool handled = false; @@ -326,12 +326,12 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { get_active_binding(&keyboard->state_keycodes, config->current_mode->keycode_bindings, &binding_released, code_modifiers, true, input_inhibited, device_identifier); - get_active_binding(&keyboard->state_keysyms_translated, - config->current_mode->keysym_bindings, &binding_released, - translated_modifiers, true, input_inhibited, device_identifier); get_active_binding(&keyboard->state_keysyms_raw, config->current_mode->keysym_bindings, &binding_released, raw_modifiers, true, input_inhibited, device_identifier); + get_active_binding(&keyboard->state_keysyms_translated, + config->current_mode->keysym_bindings, &binding_released, + translated_modifiers, true, input_inhibited, device_identifier); // Execute stored release binding once no longer active if (keyboard->held_binding && binding_released != keyboard->held_binding && @@ -352,13 +352,13 @@ static void handle_keyboard_key(struct wl_listener *listener, void *data) { get_active_binding(&keyboard->state_keycodes, config->current_mode->keycode_bindings, &binding, code_modifiers, false, input_inhibited, device_identifier); + get_active_binding(&keyboard->state_keysyms_raw, + config->current_mode->keysym_bindings, &binding, + raw_modifiers, false, input_inhibited, device_identifier); get_active_binding(&keyboard->state_keysyms_translated, config->current_mode->keysym_bindings, &binding, translated_modifiers, false, input_inhibited, device_identifier); - get_active_binding(&keyboard->state_keysyms_raw, - config->current_mode->keysym_bindings, &binding, - raw_modifiers, false, input_inhibited, device_identifier); } // Set up (or clear) keyboard repeat for a pressed binding. Since the From a418349c188288ebe1e8aeb9cb002e484d3e1f17 Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 10 Mar 2019 22:59:15 +0100 Subject: [PATCH 0181/2054] meson: use pkg-config var for scdoc path --- meson.build | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index a08b2603d..02b5d606c 100644 --- a/meson.build +++ b/meson.build @@ -57,7 +57,7 @@ elogind = dependency('libelogind', version: '>=239', required: false) xcb = dependency('xcb', required: get_option('xwayland')) math = cc.find_library('m') rt = cc.find_library('rt') -git = find_program('git', required: false) +git = find_program('git', native: true, required: false) # Try first to find wlroots as a subproject, then as a system dependency wlroots_version = '>=0.4.1' @@ -97,8 +97,8 @@ conf_data.set10('HAVE_TRAY', have_tray) scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) if scdoc.found() - scdoc_prog = find_program('scdoc') - sh = find_program('sh') + scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true) + sh = find_program('sh', native: true) mandir = get_option('mandir') man_files = [ 'sway/sway.1.scd', From a729bda17fb25afcf043dd740e304b8c2c1302a1 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 10 Mar 2019 18:43:22 -0400 Subject: [PATCH 0182/2054] fullscreen: init floating on disable without size If a container gets mapped as fullscreen and set to floating by criteria, the size and location are never set for the floating container. This adds a check in container_fullscreen_disable for a width or height of 0 and calls container_init_floating --- sway/tree/container.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sway/tree/container.c b/sway/tree/container.c index a6142193f..93cff7ff5 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -984,6 +984,13 @@ void container_fullscreen_disable(struct sway_container *con) { root->fullscreen_global = NULL; } + // If the container was mapped as fullscreen and set as floating by + // criteria, it needs to be reinitialized as floating to get the proper + // size and location + if (container_is_floating(con) && (con->width == 0 || con->height == 0)) { + container_init_floating(con); + } + con->fullscreen_mode = FULLSCREEN_NONE; container_end_mouse_operation(con); ipc_event_window(con, "fullscreen_mode"); From 20c4d242f64f68fc89d72c364506fa7bd854904e Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 11 Mar 2019 00:10:18 -0400 Subject: [PATCH 0183/2054] detect_proprietary: use strncmp Only the main nvidia module needs to be blocked. Others such as nvidiafb are benign and do not need to be blocked --- sway/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/main.c b/sway/main.c index 22b728131..f3cc1bc8b 100644 --- a/sway/main.c +++ b/sway/main.c @@ -91,7 +91,7 @@ void detect_proprietary(int allow_unsupported_gpu) { char *line = NULL; size_t line_size = 0; while (getline(&line, &line_size, f) != -1) { - if (strstr(line, "nvidia")) { + if (strncmp(line, "nvidia ", 7) == 0) { if (allow_unsupported_gpu) { sway_log(SWAY_ERROR, "!!! Proprietary Nvidia drivers are in use !!!"); From 017a7c4da169ace601f282c87a9aaabc138a34d3 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 9 Mar 2019 02:23:20 -0500 Subject: [PATCH 0184/2054] sway_view_child: add listener for view unmap Since not all child views's have an unmap event, it is possible for it to still be mapped (default state) in the destruction handler. When the destruction handler is called, the corresponding view may have already been freed and the memory location reallocated. This adds a listener for the view unmapping and removes the mapped status. This ensures that the child view is damaged due to destruction while the view still exists and not after. --- include/sway/tree/view.h | 1 + sway/tree/view.c | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 5cc9777b4..ac203ac79 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -202,6 +202,7 @@ struct sway_view_child { struct wl_listener surface_map; struct wl_listener surface_unmap; struct wl_listener surface_destroy; + struct wl_listener view_unmap; }; struct sway_subsurface { diff --git a/sway/tree/view.c b/sway/tree/view.c index 14cc07d9f..4759c9982 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -820,6 +820,14 @@ static void view_child_handle_surface_unmap(struct wl_listener *listener, child->mapped = false; } +static void view_child_handle_view_unmap(struct wl_listener *listener, + void *data) { + struct sway_view_child *child = + wl_container_of(listener, child, view_unmap); + view_child_damage(child, true); + child->mapped = false; +} + void view_child_init(struct sway_view_child *child, const struct sway_view_child_impl *impl, struct sway_view *view, struct wlr_surface *surface) { @@ -840,6 +848,9 @@ void view_child_init(struct sway_view_child *child, child->surface_map.notify = view_child_handle_surface_map; child->surface_unmap.notify = view_child_handle_surface_unmap; + wl_signal_add(&view->events.unmap, &child->view_unmap); + child->view_unmap.notify = view_child_handle_view_unmap; + struct sway_output *output = child->view->container->workspace->output; wlr_surface_send_enter(child->surface, output->wlr_output); @@ -853,6 +864,7 @@ void view_child_destroy(struct sway_view_child *child) { wl_list_remove(&child->surface_commit.link); wl_list_remove(&child->surface_destroy.link); + wl_list_remove(&child->view_unmap.link); if (child->impl && child->impl->destroy) { child->impl->destroy(child); From 5c8424c0743c36b11baabe8b316ee6daecd0cfb8 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Thu, 17 Jan 2019 16:47:44 +0000 Subject: [PATCH 0185/2054] stringop.c: remove unused functions The only use of `join_list` in swaybar/tray/icon.c has been rewritten. --- common/stringop.c | 115 -------------------------------------------- include/stringop.h | 13 ----- swaybar/tray/icon.c | 47 ++++++++++++++---- 3 files changed, 38 insertions(+), 137 deletions(-) diff --git a/common/stringop.c b/common/stringop.c index 709be6842..b9c9a1939 100644 --- a/common/stringop.c +++ b/common/stringop.c @@ -150,29 +150,6 @@ void free_argv(int argc, char **argv) { free(argv); } -char *code_strstr(const char *haystack, const char *needle) { - /* TODO */ - return strstr(haystack, needle); -} - -char *code_strchr(const char *str, char delimiter) { - int in_string = 0, in_character = 0; - int i = 0; - while (str[i] != '\0') { - if (str[i] == '"' && !in_character) { - in_string = !in_string; - } else if (str[i] == '\'' && !in_string) { - in_character = !in_character; - } else if (!in_character && !in_string) { - if (str[i] == delimiter) { - return (char *)str + i; - } - } - ++i; - } - return NULL; -} - int unescape_string(char *string) { /* TODO: More C string escapes */ int len = strlen(string); @@ -276,84 +253,6 @@ char *join_args(char **argv, int argc) { return res; } -static bool has_whitespace(const char *str) { - while (*str) { - if (isspace(*str)) { - return true; - } - ++str; - } - return false; -} - -/** - * Add quotes around any argv with whitespaces. - */ -void add_quotes(char **argv, int argc) { - int i; - for (i = 0; i < argc; ++i) { - if (has_whitespace(argv[i])) { - int len = strlen(argv[i]) + 3; - char *tmp = argv[i]; - argv[i] = malloc(len * sizeof(char)); - snprintf(argv[i], len, "\"%s\"", tmp); - free(tmp); - } - } -} - -/* - * Join a list of strings, adding separator in between. Separator can be NULL. - */ -char *join_list(list_t *list, char *separator) { - if (!sway_assert(list != NULL, "list != NULL") || list->length == 0) { - return NULL; - } - - size_t len = 1; // NULL terminator - size_t sep_len = 0; - if (separator != NULL) { - sep_len = strlen(separator); - len += (list->length - 1) * sep_len; - } - - for (int i = 0; i < list->length; i++) { - len += strlen(list->items[i]); - } - - char *res = malloc(len); - - char *p = res + strlen(list->items[0]); - strcpy(res, list->items[0]); - - for (int i = 1; i < list->length; i++) { - if (sep_len) { - memcpy(p, separator, sep_len); - p += sep_len; - } - strcpy(p, list->items[i]); - p += strlen(list->items[i]); - } - - *p = '\0'; - - return res; -} - -char *cmdsep(char **stringp, const char *delim) { - // skip over leading delims - char *head = *stringp + strspn(*stringp, delim); - // Find end token - char *tail = *stringp += strcspn(*stringp, delim); - // Set stringp to beginning of next token - *stringp += strspn(*stringp, delim); - // Set stringp to null if last token - if (!**stringp) *stringp = NULL; - // Nullify end of first token - *tail = 0; - return head; -} - char *argsep(char **stringp, const char *delim) { char *start = *stringp; char *end = start; @@ -389,17 +288,3 @@ 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 f7ca60a51..6f920999a 100644 --- a/include/stringop.h +++ b/include/stringop.h @@ -4,7 +4,6 @@ #include "list.h" void strip_whitespace(char *str); -char *strip_comments(char *str); void strip_quotes(char *str); // strcat that does nothing if dest or src is NULL @@ -21,22 +20,10 @@ list_t *split_string(const char *str, const char *delims); char **split_args(const char *str, int *argc); void free_argv(int argc, char **argv); -char *code_strchr(const char *string, char delimiter); -char *code_strstr(const char *haystack, const char *needle); int unescape_string(char *string); char *join_args(char **argv, int argc); -char *join_list(list_t *list, char *separator); -/** - * Add quotes around any argv with whitespaces. - */ -void add_quotes(char **argv, int argc); - -// split string into 2 by delim. -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/swaybar/tray/icon.c b/swaybar/tray/icon.c index bf2736c2a..c7ce20b4d 100644 --- a/swaybar/tray/icon.c +++ b/swaybar/tray/icon.c @@ -301,6 +301,43 @@ static list_t *load_themes_in_dir(char *basedir) { return themes; } +static void log_loaded_themes(list_t *themes) { + if (themes->length == 0) { + sway_log(SWAY_INFO, "Warning: no icon themes loaded"); + return; + } + + const char *sep = ", "; + size_t sep_len = strlen(sep); + + size_t len = 1 - sep_len; + for (int i = 0; i < themes->length; ++i) { + struct icon_theme *theme = themes->items[i]; + len += strlen(theme->name) + sep_len; + } + + char *str = malloc(len); + if (!str) { + return; + } + char *p = str; + for (int i = 0; i < themes->length; ++i) { + if (i > 0) { + memcpy(p, sep, sep_len); + p += sep_len; + } + + struct icon_theme *theme = themes->items[i]; + size_t name_len = strlen(theme->name); + memcpy(p, theme->name, name_len); + p += name_len; + } + *p = '\0'; + + sway_log(SWAY_DEBUG, "Loaded icon themes: %s", str); + free(str); +} + void init_themes(list_t **themes, list_t **basedirs) { *basedirs = get_basedirs(); @@ -311,15 +348,7 @@ void init_themes(list_t **themes, list_t **basedirs) { list_free(dir_themes); } - list_t *theme_names = create_list(); - for (int i = 0; i < (*themes)->length; ++i) { - struct icon_theme *theme = (*themes)->items[i]; - list_add(theme_names, theme->name); - } - char *theme_list = join_list(theme_names, ", "); - sway_log(SWAY_DEBUG, "Loaded themes: %s", theme_list); - free(theme_list); - list_free(theme_names); + log_loaded_themes(*themes); } void finish_themes(list_t *themes, list_t *basedirs) { From be1543d301ecd6933b87c90ec527d235af49377c Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Thu, 17 Jan 2019 16:57:34 +0000 Subject: [PATCH 0186/2054] stringop.c: clean up headers --- common/stringop.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/common/stringop.c b/common/stringop.c index b9c9a1939..b42604c17 100644 --- a/common/stringop.c +++ b/common/stringop.c @@ -1,13 +1,13 @@ #define _POSIX_C_SOURCE 200809L -#include +#include +#include #include +#include #include #include -#include -#include "stringop.h" -#include "log.h" -#include "string.h" #include "list.h" +#include "log.h" +#include "stringop.h" static const char whitespace[] = " \f\n\r\t\v"; From 00570c139e037d5d88f1683bb71b42a7f34139b8 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Thu, 17 Jan 2019 16:56:04 +0000 Subject: [PATCH 0187/2054] stringop.c: refactor a few functions --- common/stringop.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/common/stringop.c b/common/stringop.c index b42604c17..dea152cce 100644 --- a/common/stringop.c +++ b/common/stringop.c @@ -78,12 +78,10 @@ int lenient_strcmp(char *a, char *b) { list_t *split_string(const char *str, const char *delims) { list_t *res = create_list(); char *copy = strdup(str); - char *token; - token = strtok(copy, delims); - while(token) { - token = strdup(token); - list_add(res, token); + char *token = strtok(copy, delims); + while (token) { + list_add(res, strdup(token)); token = strtok(NULL, delims); } free(copy); @@ -268,13 +266,13 @@ char *argsep(char **stringp, const char *delim) { escaped = !escaped; } else if (*end == '\0') { *stringp = NULL; - goto found; + break; } else if (!in_string && !in_char && !escaped && strchr(delim, *end)) { if (end - start) { *(end++) = 0; *stringp = end + strspn(end, delim);; if (!**stringp) *stringp = NULL; - goto found; + break; } else { ++start; end = start; @@ -285,6 +283,5 @@ char *argsep(char **stringp, const char *delim) { } ++end; } - found: return start; } From d16845d5f0f7ea0316fb9d5e2b7d20b78ca41991 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 6 Mar 2019 18:54:41 +1000 Subject: [PATCH 0188/2054] Don't send button events to surfaces when dragging or resizing It turns out sending button events during all seat operations is not desirable. This patch introduces a new property `seatop_impl.allows_events` which allows each operation to define whether button events should be passed to the surface or not. The `down` seat operation is the only one that supports this. As all the other seatops don't support it, the calls to seat_pointer_notify_button prior to starting them have been removed. --- include/sway/input/seat.h | 3 +++ sway/input/cursor.c | 11 +++-------- sway/input/seat.c | 4 ++++ sway/input/seatop_down.c | 1 + 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index 0f5dab983..eb674b705 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -15,6 +15,7 @@ struct sway_seatop_impl { void (*unref)(struct sway_seat *seat, struct sway_container *con); void (*render)(struct sway_seat *seat, struct sway_output *output, pixman_region32_t *damage); + bool allows_events; }; struct sway_seat_device { @@ -239,4 +240,6 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con); void seatop_render(struct sway_seat *seat, struct sway_output *output, pixman_region32_t *damage); +bool seatop_allows_events(struct sway_seat *seat); + #endif diff --git a/sway/input/cursor.c b/sway/input/cursor.c index b96fde889..ef03c6aa4 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -613,7 +613,9 @@ void dispatch_cursor_button(struct sway_cursor *cursor, } else { state_erase_button(cursor, button); } - seat_pointer_notify_button(seat, time_msec, button, state); + if (seatop_allows_events(seat)) { + seat_pointer_notify_button(seat, time_msec, button, state); + } return; } @@ -682,7 +684,6 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (cont && resize_edge && button == BTN_LEFT && state == WLR_BUTTON_PRESSED && !is_floating) { seat_set_focus_container(seat, cont); - seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_resize_tiling(seat, cont, button, edge); return; } @@ -713,7 +714,6 @@ void dispatch_cursor_button(struct sway_cursor *cursor, } cursor_set_image(seat->cursor, image, NULL); seat_set_focus_container(seat, cont); - seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_resize_tiling(seat, cont, button, edge); return; } @@ -729,7 +729,6 @@ void dispatch_cursor_button(struct sway_cursor *cursor, cont = cont->parent; } seat_set_focus_container(seat, cont); - seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_move_floating(seat, cont, button); return; } @@ -740,7 +739,6 @@ void dispatch_cursor_button(struct sway_cursor *cursor, state == WLR_BUTTON_PRESSED) { // Via border if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { - seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_resize_floating(seat, cont, button, resize_edge); return; } @@ -758,7 +756,6 @@ void dispatch_cursor_button(struct sway_cursor *cursor, WLR_EDGE_RIGHT : WLR_EDGE_LEFT; edge |= cursor->cursor->y > floater->y + floater->height / 2 ? WLR_EDGE_BOTTOM : WLR_EDGE_TOP; - seat_pointer_notify_button(seat, time_msec, button, state); seatop_begin_resize_floating(seat, floater, button, edge); return; } @@ -775,8 +772,6 @@ void dispatch_cursor_button(struct sway_cursor *cursor, seat_set_focus(seat, node); } - seat_pointer_notify_button(seat, time_msec, button, state); - // If moving a container by it's title bar, use a threshold for the drag if (!mod_pressed && config->tiling_drag_threshold > 0) { seatop_begin_move_tiling_threshold(seat, cont, button); diff --git a/sway/input/seat.c b/sway/input/seat.c index 3a68904bc..be5235390 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1234,3 +1234,7 @@ void seatop_render(struct sway_seat *seat, struct sway_output *output, seat->seatop_impl->render(seat, output, damage); } } + +bool seatop_allows_events(struct sway_seat *seat) { + return seat->seatop_impl && seat->seatop_impl->allows_events; +} diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index 7f3940957..895571b10 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -58,6 +58,7 @@ static const struct sway_seatop_impl seatop_impl = { .finish = handle_finish, .abort = handle_abort, .unref = handle_unref, + .allows_events = true, }; void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, From 783b3d6b378db0bdf2a10bfa8a6787bd4b152a5a Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 11 Mar 2019 18:11:01 +1000 Subject: [PATCH 0189/2054] Fix click behaviour By the time seatop_allows_events was called, seatop_impl was already NULL, causing the function to always return false. This means a press event was sent to clients without a corresponding release event. This patch moves the call to seatop_finish to after the seatop_allows_events check. --- sway/input/cursor.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index ef03c6aa4..d531a20e3 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -605,9 +605,6 @@ void dispatch_cursor_button(struct sway_cursor *cursor, // Handle existing seat operation if (seat_doing_seatop(seat)) { - if (button == seat->seatop_button && state == WLR_BUTTON_RELEASED) { - seatop_finish(seat, time_msec); - } if (state == WLR_BUTTON_PRESSED) { state_add_button(cursor, button); } else { @@ -616,6 +613,9 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (seatop_allows_events(seat)) { seat_pointer_notify_button(seat, time_msec, button, state); } + if (button == seat->seatop_button && state == WLR_BUTTON_RELEASED) { + seatop_finish(seat, time_msec); + } return; } From bcde298a719f60b9913133dbd2a169dedbc8dd7d Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 11 Mar 2019 17:00:06 +0100 Subject: [PATCH 0190/2054] Fix size_t temporary underflow in log_loaded_themes `len` will underflow but will overflow right after, so it's not as bad as it may appear. Still better not to under/overflow at all. Fixes https://github.com/swaywm/sway/issues/3862 --- swaybar/tray/icon.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c index c7ce20b4d..2276e36de 100644 --- a/swaybar/tray/icon.c +++ b/swaybar/tray/icon.c @@ -307,16 +307,16 @@ static void log_loaded_themes(list_t *themes) { return; } - const char *sep = ", "; + const char sep[] = ", "; size_t sep_len = strlen(sep); - size_t len = 1 - sep_len; + size_t len = 0; for (int i = 0; i < themes->length; ++i) { struct icon_theme *theme = themes->items[i]; len += strlen(theme->name) + sep_len; } - char *str = malloc(len); + char *str = malloc(len + 1); if (!str) { return; } From 076257a978ce5f93b9b1613e43a067e602b5b041 Mon Sep 17 00:00:00 2001 From: emersion Date: Mon, 11 Mar 2019 11:45:01 +0100 Subject: [PATCH 0191/2054] Stop using wlr_output->{lx,ly} Also fixes sway_output->{lx,ly,width,height} not being updated. Also fixes output_get_in_direction adding buffer coords to layout coords. --- include/sway/output.h | 6 ++-- sway/config/output.c | 8 +++++ sway/desktop/desktop.c | 6 ++-- sway/desktop/output.c | 24 +++++++-------- sway/desktop/render.c | 53 +++++++++++++++++---------------- sway/input/seatop_move_tiling.c | 2 +- sway/tree/arrange.c | 4 +-- sway/tree/output.c | 11 +++---- 8 files changed, 61 insertions(+), 53 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index ea7a21741..32ed1e28e 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -29,8 +29,8 @@ struct sway_output { struct timespec last_frame; struct wlr_output_damage *damage; - int lx, ly; - int width, height; + int lx, ly; // layout coords + int width, height; // transformed buffer size bool enabled, configured; list_t *workspaces; @@ -144,7 +144,7 @@ void output_get_box(struct sway_output *output, struct wlr_box *box); enum sway_container_layout output_get_default_layout( struct sway_output *output); -void render_rect(struct wlr_output *wlr_output, +void render_rect(struct sway_output *output, pixman_region32_t *output_damage, const struct wlr_box *_box, float color[static 4]); diff --git a/sway/config/output.c b/sway/config/output.c index 3a36ed180..1f55fd6ab 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -319,6 +319,14 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { wlr_output_layout_add_auto(root->output_layout, wlr_output); } + // Update output->{lx, ly, width, height} + struct wlr_box *output_box = + wlr_output_layout_get_box(root->output_layout, wlr_output); + output->lx = output_box->x; + output->ly = output_box->y; + wlr_output_transformed_resolution(wlr_output, + &output->width, &output->height); + if (output->swaybg_client != NULL) { wl_client_destroy(output->swaybg_client); } diff --git a/sway/desktop/desktop.c b/sway/desktop/desktop.c index d8dd02403..ec45d80a1 100644 --- a/sway/desktop/desktop.c +++ b/sway/desktop/desktop.c @@ -6,8 +6,10 @@ void desktop_damage_surface(struct wlr_surface *surface, double lx, double ly, bool whole) { for (int i = 0; i < root->outputs->length; ++i) { struct sway_output *output = root->outputs->items[i]; - output_damage_surface(output, lx - output->wlr_output->lx, - ly - output->wlr_output->ly, surface, whole); + struct wlr_box *output_box = wlr_output_layout_get_box( + root->output_layout, output->wlr_output); + output_damage_surface(output, lx - output_box->x, + ly - output_box->y, surface, whole); } } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 3ff4d7262..1d9abbfd9 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -150,9 +150,9 @@ void output_view_for_each_surface(struct sway_output *output, .user_iterator = iterator, .user_data = user_data, .output = output, - .ox = view->container->current.content_x - output->wlr_output->lx + .ox = view->container->current.content_x - output->lx - view->geometry.x, - .oy = view->container->current.content_y - output->wlr_output->ly + .oy = view->container->current.content_y - output->ly - view->geometry.y, .width = view->container->current.content_width, .height = view->container->current.content_height, @@ -169,9 +169,9 @@ void output_view_for_each_popup(struct sway_output *output, .user_iterator = iterator, .user_data = user_data, .output = output, - .ox = view->container->current.content_x - output->wlr_output->lx + .ox = view->container->current.content_x - output->lx - view->geometry.x, - .oy = view->container->current.content_y - output->wlr_output->ly + .oy = view->container->current.content_y - output->ly - view->geometry.y, .width = view->container->current.content_width, .height = view->container->current.content_height, @@ -202,8 +202,8 @@ void output_unmanaged_for_each_surface(struct sway_output *output, wl_list_for_each(unmanaged_surface, unmanaged, link) { struct wlr_xwayland_surface *xsurface = unmanaged_surface->wlr_xwayland_surface; - double ox = unmanaged_surface->lx - output->wlr_output->lx; - double oy = unmanaged_surface->ly - output->wlr_output->ly; + double ox = unmanaged_surface->lx - output->lx; + double oy = unmanaged_surface->ly - output->ly; output_surface_for_each_surface(output, xsurface->surface, ox, oy, iterator, user_data); @@ -216,8 +216,8 @@ void output_drag_icons_for_each_surface(struct sway_output *output, void *user_data) { struct sway_drag_icon *drag_icon; wl_list_for_each(drag_icon, drag_icons, link) { - double ox = drag_icon->x - output->wlr_output->lx; - double oy = drag_icon->y - output->wlr_output->ly; + double ox = drag_icon->x - output->lx; + double oy = drag_icon->y - output->ly; if (drag_icon->wlr_drag_icon->mapped) { output_surface_for_each_surface(output, @@ -463,8 +463,8 @@ void output_damage_from_view(struct sway_output *output, void output_damage_box(struct sway_output *output, struct wlr_box *_box) { struct wlr_box box; memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= output->wlr_output->lx; - box.y -= output->wlr_output->ly; + box.x -= output->lx; + box.y -= output->ly; scale_box(&box, output->wlr_output->scale); wlr_output_damage_add_box(output->damage, &box); } @@ -484,8 +484,8 @@ void output_damage_whole_container(struct sway_output *output, struct sway_container *con) { // Pad the box by 1px, because the width is a double and might be a fraction struct wlr_box box = { - .x = con->current.x - output->wlr_output->lx - 1, - .y = con->current.y - output->wlr_output->ly - 1, + .x = con->current.x - output->lx - 1, + .y = con->current.y - output->ly - 1, .width = con->current.width + 2, .height = con->current.height + 2, }; diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 4b36a9c24..9e936bb53 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -157,16 +157,17 @@ static void render_drag_icons(struct sway_output *output, // _box.x and .y are expected to be layout-local // _box.width and .height are expected to be output-buffer-local -void render_rect(struct wlr_output *wlr_output, +void render_rect(struct sway_output *output, pixman_region32_t *output_damage, const struct wlr_box *_box, float color[static 4]) { + struct wlr_output *wlr_output = output->wlr_output; struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend); struct wlr_box box; memcpy(&box, _box, sizeof(struct wlr_box)); - box.x -= wlr_output->lx * wlr_output->scale; - box.y -= wlr_output->ly * wlr_output->scale; + box.x -= output->lx * wlr_output->scale; + box.y -= output->ly * wlr_output->scale; pixman_region32_t damage; pixman_region32_init(&damage); @@ -205,9 +206,9 @@ static void render_view_toplevels(struct sway_view *view, }; // Render all toplevels without descending into popups double ox = view->container->surface_x - - output->wlr_output->lx - view->geometry.x; + output->lx - view->geometry.x; double oy = view->container->surface_y - - output->wlr_output->ly - view->geometry.y; + output->ly - view->geometry.y; output_surface_for_each_surface(output, view->surface, ox, oy, render_surface_iterator, &data); } @@ -240,9 +241,9 @@ static void render_saved_view(struct sway_view *view, return; } struct wlr_box box = { - .x = view->container->surface_x - output->wlr_output->lx - + .x = view->container->surface_x - output->lx - view->saved_geometry.x, - .y = view->container->surface_y - output->wlr_output->ly - + .y = view->container->surface_y - output->ly - view->saved_geometry.y, .width = view->saved_buffer_width, .height = view->saved_buffer_height, @@ -298,7 +299,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, box.width = state->border_thickness; box.height = state->content_height; scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); + render_rect(output, damage, &box, color); } list_t *siblings = container_get_current_siblings(con); @@ -317,7 +318,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, box.width = state->border_thickness; box.height = state->content_height; scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); + render_rect(output, damage, &box, color); } if (state->border_bottom) { @@ -332,7 +333,7 @@ static void render_view(struct sway_output *output, pixman_region32_t *damage, box.width = state->width; box.height = state->border_thickness; scale_box(&box, output_scale); - render_rect(output->wlr_output, damage, &box, color); + render_rect(output, damage, &box, color); } } @@ -359,8 +360,8 @@ static void render_titlebar(struct sway_output *output, list_t *children = container_get_current_siblings(con); bool is_last_child = children->length == 0 || children->items[children->length - 1] == con; - double output_x = output->wlr_output->lx; - double output_y = output->wlr_output->ly; + double output_x = output->lx; + double output_y = output->ly; int titlebar_border_thickness = config->titlebar_border_thickness; int titlebar_h_padding = config->titlebar_h_padding; int titlebar_v_padding = config->titlebar_v_padding; @@ -374,7 +375,7 @@ static void render_titlebar(struct sway_output *output, box.width = width; box.height = titlebar_border_thickness; scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); // Single pixel bar below title size_t left_offset = 0, right_offset = 0; @@ -392,7 +393,7 @@ static void render_titlebar(struct sway_output *output, box.width = width - left_offset - right_offset; box.height = titlebar_border_thickness; scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); if (layout == L_TABBED) { // Single pixel left edge @@ -402,7 +403,7 @@ static void render_titlebar(struct sway_output *output, box.height = container_titlebar_height() - titlebar_border_thickness * 2; scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); // Single pixel right edge box.x = x + width - titlebar_border_thickness; @@ -411,7 +412,7 @@ static void render_titlebar(struct sway_output *output, box.height = container_titlebar_height() - titlebar_border_thickness * 2; scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); } int inner_x = x - output_x + titlebar_h_padding; @@ -470,12 +471,12 @@ static void render_titlebar(struct sway_output *output, box.y = round((y + titlebar_border_thickness) * output_scale); box.width = texture_box.width; box.height = ob_padding_above; - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); // Padding below box.y += ob_padding_above + texture_box.height; box.height = ob_padding_below; - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); } // Title text @@ -538,12 +539,12 @@ static void render_titlebar(struct sway_output *output, box.y = round((y + titlebar_border_thickness) * output_scale); box.width = texture_box.width; box.height = ob_padding_above; - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); // Padding below box.y += ob_padding_above + texture_box.height; box.height = ob_padding_below; - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); } // Determine the left + right extends of the textures (output-buffer local) @@ -577,7 +578,7 @@ static void render_titlebar(struct sway_output *output, box.x = ob_left_x + ob_left_width + round(output_x * output_scale); box.y = round(bg_y * output_scale); box.height = ob_bg_height; - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); } // Padding on left side @@ -592,7 +593,7 @@ static void render_titlebar(struct sway_output *output, if (box.x + box.width < left_x) { box.width += left_x - box.x - box.width; } - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); // Padding on right side right_offset = (layout == L_TABBED) * titlebar_border_thickness; @@ -607,7 +608,7 @@ static void render_titlebar(struct sway_output *output, box.width += box.x - right_rx; box.x = right_rx; } - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); if (connects_sides) { // Left pixel in line with bottom bar @@ -616,7 +617,7 @@ static void render_titlebar(struct sway_output *output, box.width = state->border_thickness * state->border_left; box.height = titlebar_border_thickness; scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); // Right pixel in line with bottom bar box.x = x + width - state->border_thickness * state->border_right; @@ -624,7 +625,7 @@ static void render_titlebar(struct sway_output *output, box.width = state->border_thickness * state->border_right; box.height = titlebar_border_thickness; scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); } } @@ -650,7 +651,7 @@ static void render_top_border(struct sway_output *output, box.width = state->width; box.height = state->border_thickness; scale_box(&box, output_scale); - render_rect(output->wlr_output, output_damage, &box, color); + render_rect(output, output_damage, &box, color); } struct parent_data { diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 4b5aa81ed..2904792bd 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -37,7 +37,7 @@ static void handle_render(struct sway_seat *seat, struct wlr_box box; memcpy(&box, &e->drop_box, sizeof(struct wlr_box)); scale_box(&box, output->wlr_output->scale); - render_rect(output->wlr_output, damage, &box, color); + render_rect(output, damage, &box, color); } } diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 438a21335..8583c53e5 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -195,8 +195,8 @@ void arrange_workspace(struct sway_workspace *workspace) { double prev_y = workspace->y; workspace->width = area->width; workspace->height = area->height; - workspace->x = output->wlr_output->lx + area->x; - workspace->y = output->wlr_output->ly + area->y; + workspace->x = output->lx + area->x; + workspace->y = output->ly + area->y; // Adjust any floating containers double diff_x = workspace->x - prev_x; diff --git a/sway/tree/output.c b/sway/tree/output.c index e0a66e0bd..227d487c1 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -101,11 +101,6 @@ void output_enable(struct sway_output *output, struct output_config *oc) { output->configured = true; list_add(root->outputs, output); - output->lx = wlr_output->lx; - output->ly = wlr_output->ly; - wlr_output_transformed_resolution(wlr_output, - &output->width, &output->height); - restore_workspaces(output); struct sway_workspace *ws = NULL; @@ -311,8 +306,10 @@ struct sway_output *output_get_in_direction(struct sway_output *reference, if (!sway_assert(direction, "got invalid direction: %d", direction)) { return NULL; } - int lx = reference->wlr_output->lx + reference->width / 2; - int ly = reference->wlr_output->ly + reference->height / 2; + struct wlr_box *output_box = + wlr_output_layout_get_box(root->output_layout, reference->wlr_output); + int lx = output_box->x + output_box->width / 2; + int ly = output_box->y + output_box->height / 2; struct wlr_output *wlr_adjacent = wlr_output_layout_adjacent_output( root->output_layout, direction, reference->wlr_output, lx, ly); if (!wlr_adjacent) { From 055d662baa3975d71552992772e97d6b489f0771 Mon Sep 17 00:00:00 2001 From: Ian Fan Date: Mon, 11 Mar 2019 16:50:52 +0000 Subject: [PATCH 0192/2054] commands: allow tiled sticky containers to be moved Namely, to a workspace on the same output. However, tiled sticky children of floating containers are still restricted. --- sway/commands/move.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sway/commands/move.c b/sway/commands/move.c index 4306aac86..926b2e8e9 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -473,7 +473,7 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { // We have to create the workspace, but if the container is // sticky and the workspace is going to be created on the same // output, we'll bail out first. - if (container->is_sticky) { + if (container->is_sticky && container_is_floating_or_child(container)) { struct sway_output *new_output = workspace_get_initial_output(ws_name); if (old_output == new_output) { @@ -507,8 +507,8 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { return cmd_results_new(CMD_INVALID, expected_syntax); } - if (container->is_sticky && old_output && - node_has_ancestor(destination, &old_output->node)) { + if (container->is_sticky && container_is_floating_or_child(container) && + old_output && node_has_ancestor(destination, &old_output->node)) { return cmd_results_new(CMD_FAILURE, "Can't move sticky " "container to another workspace on the same output"); } From 79369681ab3d6785aabf39bd8080cd4f30507524 Mon Sep 17 00:00:00 2001 From: Peter Grayson Date: Mon, 11 Mar 2019 22:28:35 -0400 Subject: [PATCH 0193/2054] Repair swaynag crash reading message from stdin When swaynag is run with the -l/--detailed-message option, a crash may occur if the detailed message read from stdin is large enough. E.g.: swaynag -m hello -l < ~/.config/sway/config The root cause is that the read_from_stdin() function under-allocates memory for the destination buffer which causes that buffer to be overflowed when copying line data to it with snprintf(). The repair is to allocate one more byte for the terminating null byte. N.B. although getline() returns the number of bytes read excluding a terminating null byte, the line buffer is terminated with a null byte. Thus we have a guarantee that the line buffer will be null terminated (which is important when copying with snprintf()). --- swaynag/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swaynag/config.c b/swaynag/config.c index 200611f4b..fb2aa820b 100644 --- a/swaynag/config.c +++ b/swaynag/config.c @@ -18,7 +18,7 @@ static char *read_from_stdin(void) { size_t line_size = 0; ssize_t nread; while ((nread = getline(&line, &line_size, stdin)) != -1) { - buffer = realloc(buffer, buffer_len + nread); + buffer = realloc(buffer, buffer_len + nread + 1); snprintf(&buffer[buffer_len], nread + 1, "%s", line); buffer_len += nread; } From 1bab5a95531826fa54097bed48f8cc05d4233a9f Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 11 Mar 2019 22:21:38 -0400 Subject: [PATCH 0194/2054] get_deco_rect: fix floaters on tabbed/stacked ws This fixes the decoration rects for floating containers on a workspace that is either tabbed or stacked. Without this, the floater would incorrectly try to calculate where it's tab or stack decorations were on the workspace. This would cause a SIGFPE (due to a divide-by-zero) when the floater was on a tabbed workspace without any tiling children. Furthermore, the floater does not care what the workspace's layout is and should just use the location relative to the workspace. This should have no effect on children of a floating container. --- sway/ipc-json.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index bebe6dd7b..f61e1a8c8 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -352,8 +352,9 @@ static void ipc_json_describe_workspace(struct sway_workspace *workspace, static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { enum sway_container_layout parent_layout = container_parent_layout(c); - if ((parent_layout != L_TABBED && parent_layout != L_STACKED && - c->current.border != B_NORMAL) || + bool tab_or_stack = parent_layout == L_TABBED || parent_layout == L_STACKED; + if (((!tab_or_stack || container_is_floating(c)) && + c->current.border != B_NORMAL) || c->fullscreen_mode != FULLSCREEN_NONE || c->workspace == NULL) { deco_rect->x = deco_rect->y = deco_rect->width = deco_rect->height = 0; @@ -370,17 +371,19 @@ static void get_deco_rect(struct sway_container *c, struct wlr_box *deco_rect) { deco_rect->width = c->width; deco_rect->height = container_titlebar_height(); - if (parent_layout == L_TABBED) { - deco_rect->width = c->parent - ? c->parent->width / c->parent->children->length - : c->workspace->width / c->workspace->tiling->length; - deco_rect->x += deco_rect->width * container_sibling_index(c); - } else if (container_parent_layout(c) == L_STACKED) { - if (!c->view) { - size_t siblings = container_get_siblings(c)->length; - deco_rect->y -= deco_rect->height * siblings; + if (!container_is_floating(c)) { + if (parent_layout == L_TABBED) { + deco_rect->width = c->parent + ? c->parent->width / c->parent->children->length + : c->workspace->width / c->workspace->tiling->length; + deco_rect->x += deco_rect->width * container_sibling_index(c); + } else if (parent_layout == L_STACKED) { + if (!c->view) { + size_t siblings = container_get_siblings(c)->length; + deco_rect->y -= deco_rect->height * siblings; + } + deco_rect->y += deco_rect->height * container_sibling_index(c); } - deco_rect->y += deco_rect->height * container_sibling_index(c); } } From a280facd5fb1c07dab97bbef1d8db534edf7dfa6 Mon Sep 17 00:00:00 2001 From: 3ap Date: Tue, 12 Mar 2019 15:46:23 +0300 Subject: [PATCH 0195/2054] config.in: allow launch apps with args via dmenu Without this change you can't execute apps with command line arguments (e.g. firefox -P profile) because -P will be parsed as argument for "swaymsg exec". --- config.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.in b/config.in index fbd952353..9d6f2e666 100644 --- a/config.in +++ b/config.in @@ -17,7 +17,7 @@ set $right l set $term urxvt # Your preferred application launcher # Note: it's recommended that you pass the final command to sway -set $menu dmenu_path | dmenu | xargs swaymsg exec +set $menu dmenu_path | dmenu | xargs swaymsg exec -- ### Output configuration # From 3330faded5ed59b19b2a1982bb52c05322b03510 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 12 Mar 2019 13:57:40 -0400 Subject: [PATCH 0196/2054] Handle seat_get_focused_workspace returning NULL This modifiers the callers of seat_get_focused_workspace to handle getting NULL as the return value, if they did not already. --- sway/commands/focus.c | 5 +++++ sway/commands/scratchpad.c | 5 +++++ sway/commands/workspace.c | 6 ++++-- sway/desktop/layer_shell.c | 1 - sway/tree/root.c | 4 ++++ sway/tree/workspace.c | 13 +++++++++---- 6 files changed, 27 insertions(+), 7 deletions(-) diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 25df51304..6344a7658 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -208,6 +208,11 @@ static struct cmd_results *focus_output(struct sway_seat *seat, "There is no output with that name"); } struct sway_workspace *ws = seat_get_focused_workspace(seat); + if (!ws) { + free(identifier); + return cmd_results_new(CMD_FAILURE, + "No focused workspace to base directions off of"); + } output = output_get_in_direction(ws->output, direction); if (!output) { diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c index 71afa306d..34871bc6a 100644 --- a/sway/commands/scratchpad.c +++ b/sway/commands/scratchpad.c @@ -12,6 +12,11 @@ static void scratchpad_toggle_auto(void) { struct sway_seat *seat = input_manager_current_seat(); struct sway_container *focus = seat_get_focused_container(seat); struct sway_workspace *ws = seat_get_focused_workspace(seat); + if (!ws) { + sway_log(SWAY_DEBUG, + "No focused workspace to toggle scratchpad windows on"); + return; + } // If the focus is in a floating split container, // operate on the split container instead of the child. diff --git a/sway/commands/workspace.c b/sway/commands/workspace.c index 362dcd1b2..b911b2f61 100644 --- a/sway/commands/workspace.c +++ b/sway/commands/workspace.c @@ -185,8 +185,7 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { struct sway_seat *seat = config->handler_context.seat; struct sway_workspace *current = seat_get_focused_workspace(seat); if (!current) { - return cmd_results_new(CMD_FAILURE, "workspace", - "No workspace to switch from"); + return cmd_results_new(CMD_FAILURE, "No workspace to switch from"); } struct sway_workspace *ws = NULL; @@ -227,6 +226,9 @@ struct cmd_results *cmd_workspace(int argc, char **argv) { } free(name); } + if (!ws) { + return cmd_results_new(CMD_FAILURE, "No workspace to switch to"); + } workspace_switch(ws, no_auto_back_and_forth); seat_consider_warp_to_focus(seat); } diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index b2fea880f..1667995c7 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -373,7 +373,6 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { struct sway_seat *seat = input_manager_get_default_seat(); if (seat) { struct sway_workspace *ws = seat_get_focused_workspace(seat); - if (ws != NULL) { output = ws->output; } diff --git a/sway/tree/root.c b/sway/tree/root.c index 6e13d6ceb..0744192b2 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -96,6 +96,10 @@ void root_scratchpad_remove_container(struct sway_container *con) { void root_scratchpad_show(struct sway_container *con) { struct sway_seat *seat = input_manager_current_seat(); struct sway_workspace *new_ws = seat_get_focused_workspace(seat); + if (!new_ws) { + sway_log(SWAY_DEBUG, "No focused workspace to show scratchpad on"); + return; + } struct sway_workspace *old_ws = con->workspace; // If the current con or any of its parents are in fullscreen mode, we diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 73322491b..5e28197be 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -368,13 +368,13 @@ struct sway_workspace *workspace_by_name(const char *name) { struct sway_seat *seat = input_manager_current_seat(); struct sway_workspace *current = seat_get_focused_workspace(seat); - if (strcmp(name, "prev") == 0) { + if (current && strcmp(name, "prev") == 0) { return workspace_prev(current); - } else if (strcmp(name, "prev_on_output") == 0) { + } else if (current && strcmp(name, "prev_on_output") == 0) { return workspace_output_prev(current, false); - } else if (strcmp(name, "next") == 0) { + } else if (current && strcmp(name, "next") == 0) { return workspace_next(current); - } else if (strcmp(name, "next_on_output") == 0) { + } else if (current && strcmp(name, "next_on_output") == 0) { return workspace_output_next(current, false); } else if (strcmp(name, "current") == 0) { return current; @@ -399,6 +399,11 @@ static struct sway_workspace *workspace_output_prev_next_impl( struct sway_output *output, int dir, bool create) { struct sway_seat *seat = input_manager_current_seat(); struct sway_workspace *workspace = seat_get_focused_workspace(seat); + if (!workspace) { + sway_log(SWAY_DEBUG, + "No focused workspace to base prev/next on output off of"); + return NULL; + } int index = list_find(output->workspaces, workspace); if (!workspace_is_empty(workspace) && create && From 52a61671e93953a06b6b440ede5512e8fe45b35e Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 12 Mar 2019 22:33:49 -0400 Subject: [PATCH 0197/2054] criteria: change workspace to support regex This changes the workspace criteria to support regex instead of basic strings. This matches i3's behavior. --- include/sway/criteria.h | 2 +- sway/criteria.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/sway/criteria.h b/include/sway/criteria.h index 3eb583d5a..f7e788c8b 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -35,7 +35,7 @@ struct criteria { bool floating; bool tiling; char urgent; // 'l' for latest or 'o' for oldest - char *workspace; + pcre *workspace; }; bool criteria_is_empty(struct criteria *criteria); diff --git a/sway/criteria.c b/sway/criteria.c index 5ad481452..f2db6c184 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -208,7 +208,7 @@ static bool criteria_matches_view(struct criteria *criteria, if (criteria->workspace) { struct sway_workspace *ws = view->container->workspace; - if (!ws || strcmp(ws->name, criteria->workspace) != 0) { + if (!ws || regex_cmp(ws->name, criteria->workspace) != 0) { return false; } } @@ -515,7 +515,7 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { } break; case T_WORKSPACE: - criteria->workspace = strdup(effective_value); + generate_regex(&criteria->workspace, effective_value); break; case T_INVALID: break; From acdb4ed7a32a7cefd4ac37190ba65d33d752bee0 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Mon, 11 Mar 2019 12:19:20 -0400 Subject: [PATCH 0198/2054] xwayland: handle floating configure request size This makes it so the container gets resized by a configure request for xwayland floating views. The minimum and maximum sizes are also respected. Previously, the configure request was resizing the surface to the size requested, but never changing the container size. This caused the surface to be rendered outside of the container or to be smaller than the container. The former is never ideal and the latter makes no sense for floating views since the container itself can just be shrunk. --- sway/desktop/xwayland.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index e0d307e86..a2aa2e08e 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -432,8 +432,16 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { return; } if (container_is_floating(view->container)) { - configure(view, view->container->current.content_x, - view->container->current.content_y, ev->width, ev->height); + // Respect minimum and maximum sizes + view->natural_width = ev->width; + view->natural_height = ev->height; + container_init_floating(view->container); + + configure(view, view->container->content_x, + view->container->content_y, + view->container->content_width, + view->container->content_height); + node_set_dirty(&view->container->node); } else { configure(view, view->container->current.content_x, view->container->current.content_y, From d64e8ba9469f4fa0ff693a22c88c1afa687c4c6c Mon Sep 17 00:00:00 2001 From: Stephan Hilb Date: Wed, 13 Mar 2019 21:32:02 +0100 Subject: [PATCH 0199/2054] swaybar: fix loading of malformed icon theme If the icon index.theme contained a key-value pair without a preceding group header, entry_handler() would be called with a zero pointer and lead to a segfault. Set the error flag and break on such malformed files. --- swaybar/tray/icon.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c index 2276e36de..8587f3f7b 100644 --- a/swaybar/tray/icon.c +++ b/swaybar/tray/icon.c @@ -243,6 +243,10 @@ static struct icon_theme *read_theme_file(char *basedir, char *theme_name) { free(group); group = strdup(&line[1]); } else { // key-value pair + if (!group) { + error = true; + break; + } // check well-formed int eok = 0; for (; isalnum(line[eok]) || line[eok] == '-'; ++eok) {} // TODO locale? From d8f74e4706104ac751706d5071838f97e3956a5e Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Thu, 14 Mar 2019 11:43:39 -0400 Subject: [PATCH 0200/2054] Set prev_workspace_name based off of focus This moves setting `seat->prev_workspace_name` from `workspace_switch` to `set_workspace`. `workspace_switch` is only called when using a `workspace` command to change the workspace so any workspace change based on criteria was not altering `seat->prev_workspace_name`. By moving it to `set_workspace`, which is called by `seat_set_focus`, it will change any time focus changes to a node on a different workspace --- sway/input/seat.c | 12 ++++++++++++ sway/tree/workspace.c | 12 ------------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/sway/input/seat.c b/sway/input/seat.c index be5235390..e56a65105 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -763,6 +763,18 @@ static void set_workspace(struct sway_seat *seat, if (seat->workspace == new_ws) { return; } + + if (seat->workspace) { + free(seat->prev_workspace_name); + seat->prev_workspace_name = malloc(strlen(seat->workspace->name) + 1); + if (!seat->prev_workspace_name) { + sway_log(SWAY_ERROR, "Unable to allocate previous workspace name"); + seat->prev_workspace_name = NULL; + } else { + strcpy(seat->prev_workspace_name, seat->workspace->name); + } + } + ipc_event_workspace(seat->workspace, new_ws, "focus"); seat->workspace = new_ws; } diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 5e28197be..68a55e036 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -481,18 +481,6 @@ bool workspace_switch(struct sway_workspace *workspace, workspace_create(NULL, seat->prev_workspace_name); } - if (active_ws && (!seat->prev_workspace_name || - (strcmp(seat->prev_workspace_name, active_ws->name) - && active_ws != workspace))) { - free(seat->prev_workspace_name); - seat->prev_workspace_name = malloc(strlen(active_ws->name) + 1); - if (!seat->prev_workspace_name) { - sway_log(SWAY_ERROR, "Unable to allocate previous workspace name"); - return false; - } - strcpy(seat->prev_workspace_name, active_ws->name); - } - sway_log(SWAY_DEBUG, "Switching to workspace %p:%s", workspace, workspace->name); struct sway_node *next = seat_get_focus_inactive(seat, &workspace->node); From e687e120e0cd49157a2bc2d1a509d4fb1fa43490 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 15 Mar 2019 03:00:40 -0400 Subject: [PATCH 0201/2054] output_cmd_background: validate colors This validates the color and fallback color in `output_cmd_background` to ensure that only colors of the form `#RRGGBB` are accepted. --- sway/commands/output/background.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 5a15ed0fc..054fb707e 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -20,6 +20,16 @@ static const char *bg_options[] = { "tile", }; +static bool validate_color(const char *color) { + if (strlen(color) != 7 || color[0] != '#') { + return false; + } + + char *ptr = NULL; + strtol(&color[1], &ptr, 16); + return *ptr == '\0'; +} + struct cmd_results *output_cmd_background(int argc, char **argv) { if (!config->handler_context.output_config) { return cmd_results_new(CMD_FAILURE, "Missing output config"); @@ -36,6 +46,10 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { struct output_config *output = config->handler_context.output_config; if (strcasecmp(argv[1], "solid_color") == 0) { + if (!validate_color(argv[0])) { + return cmd_results_new(CMD_INVALID, + "Colors should be of the form #RRGGBB"); + } output->background = strdup(argv[0]); output->background_option = strdup("solid_color"); output->background_fallback = NULL; @@ -130,6 +144,11 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { output->background_fallback = NULL; if (argc && *argv[0] == '#') { + if (!validate_color(argv[0])) { + return cmd_results_new(CMD_INVALID, + "fallback color should be of the form #RRGGBB"); + } + output->background_fallback = strdup(argv[0]); argc--; argv++; From 264e213c08bf1e184f7e540ae841996292ed16bd Mon Sep 17 00:00:00 2001 From: minus Date: Fri, 15 Mar 2019 19:37:20 +0100 Subject: [PATCH 0202/2054] Fix quoting of commands passed to for_window E.g. `for_window [class="mpv"] move container to output "Dell Inc. ..."` does not work because the executed move command only uses `Dell` as output name. --- sway/commands.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/commands.c b/sway/commands.c index 522a5fd47..cb32169f6 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -387,6 +387,7 @@ struct cmd_results *config_command(char *exec, char **new_block) { && handler->handle != cmd_bindsym && handler->handle != cmd_bindcode && handler->handle != cmd_set + && handler->handle != cmd_for_window && (*argv[i] == '\"' || *argv[i] == '\'')) { strip_quotes(argv[i]); } From 2578669de76c6d4b2038a648d1cfcd3c7e9e320a Mon Sep 17 00:00:00 2001 From: Flakebi Date: Sat, 16 Mar 2019 17:20:54 +0100 Subject: [PATCH 0203/2054] Flush stdout in swaymsg when in subscribe mode --- swaymsg/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/swaymsg/main.c b/swaymsg/main.c index a0ef7e3d9..65cc4bbb1 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -493,6 +493,7 @@ int main(int argc, char **argv) { printf("%s\n", json_object_to_json_string_ext(obj, JSON_C_TO_STRING_PRETTY | JSON_C_TO_STRING_SPACED)); } + fflush(stdout); json_object_put(obj); } From 3106ef23a7b4f7f7efeb43d47e724f5b23c4fd78 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 15 Mar 2019 15:09:55 -0400 Subject: [PATCH 0204/2054] Fix output config retrieval for new outputs This removes `output_find_config`, which would take the first matching output config it found. This is fine if only a name output config, identifier output config, or even just wildcard exist, but if there is a name output config and identifier output config, they are not merged. Instead, this introduces find_output_config, which is just a wrapper for `get_output_config`. This ensures that both the name and identifier output configs are respected. This fixes the following case: - For simplicity in this example, remove all output configs from config - Run `swaymsg output bg #ff0000 solid_color` - Run `swaymsg output scale 2` - Disconnect and reconnect output Without this, the output will have the background, but not the scale. With this, the output will have both the background and scale --- include/sway/config.h | 2 ++ include/sway/output.h | 2 -- sway/config/output.c | 6 ++++++ sway/desktop/output.c | 4 ++-- sway/tree/output.c | 30 ------------------------------ 5 files changed, 10 insertions(+), 34 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 46ca7cee4..7c5445412 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -585,6 +585,8 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output); struct output_config *store_output_config(struct output_config *oc); +struct output_config *find_output_config(struct sway_output *output); + void apply_output_config_to_outputs(struct output_config *oc); void reset_outputs(void); diff --git a/include/sway/output.h b/include/sway/output.h index 32ed1e28e..8015f2110 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -88,8 +88,6 @@ struct sway_output *output_by_name_or_id(const char *name_or_id); void output_sort_workspaces(struct sway_output *output); -struct output_config *output_find_config(struct sway_output *output); - void output_enable(struct sway_output *output, struct output_config *oc); void output_disable(struct sway_output *output); diff --git a/sway/config/output.c b/sway/config/output.c index 1f55fd6ab..a20c5ad4a 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -438,6 +438,12 @@ static struct output_config *get_output_config(char *identifier, return result; } +struct output_config *find_output_config(struct sway_output *output) { + char id[128]; + output_get_identifier(id, sizeof(id), output); + return get_output_config(id, output); +} + void apply_output_config_to_outputs(struct output_config *oc) { // Try to find the output container and apply configuration now. If // this is during startup then there will be no container and config diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 1d9abbfd9..9d0c0ef5d 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -533,7 +533,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { static void handle_mode(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, mode); if (!output->configured && !output->enabled) { - struct output_config *oc = output_find_config(output); + struct output_config *oc = find_output_config(output); if (output->wlr_output->current_mode != NULL && (!oc || oc->enabled)) { // We want to enable this output, but it didn't work last time, @@ -634,7 +634,7 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->damage_destroy.notify = damage_handle_destroy; wl_list_init(&output->swaybg_client_destroy.link); - struct output_config *oc = output_find_config(output); + struct output_config *oc = find_output_config(output); if (!oc || oc->enabled) { output_enable(output, oc); } else { diff --git a/sway/tree/output.c b/sway/tree/output.c index 227d487c1..1202ba3c5 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -267,36 +267,6 @@ void output_begin_destroy(struct sway_output *output) { output->wlr_output = NULL; } -struct output_config *output_find_config(struct sway_output *output) { - const char *name = output->wlr_output->name; - char identifier[128]; - output_get_identifier(identifier, sizeof(identifier), output); - - struct output_config *oc = NULL, *all = NULL; - for (int i = 0; i < config->output_configs->length; ++i) { - struct output_config *cur = config->output_configs->items[i]; - - if (strcasecmp(name, cur->name) == 0 || - strcasecmp(identifier, cur->name) == 0) { - sway_log(SWAY_DEBUG, "Matched output config for %s", name); - oc = cur; - } - if (strcasecmp("*", cur->name) == 0) { - sway_log(SWAY_DEBUG, "Matched wildcard output config for %s", name); - all = cur; - } - - if (oc && all) { - break; - } - } - if (!oc) { - oc = all; - } - - return oc; -} - struct sway_output *output_from_wlr_output(struct wlr_output *output) { return output->data; } From 73605dac2ae18e2ca84a74da895c4098a1bee6f8 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 15 Mar 2019 02:20:07 -0400 Subject: [PATCH 0205/2054] config/output: revamp identifier/name layering This revamps the way that output configs are handled when referencing an output by both identifier and name. If an output is always referred to by name or by identifier, this should have no noticeable change. As soon as there is a name output config and an identifier output config that matches an output, an output config is generated that is named ` on ` that is generated with the identifier output config merged on top of the name output config and stored. When a change to either is stored, the delta is merged on top of that "id on name" output config, as well. If the "id on name" output config exists, it has the highest precedence and will be used when applying a config to the output. This fixes the following case: - `swaymsg output bg /path/to/wallpaper1 fill` - `swaymsg output bg /path/to/wallpaper2 fill` - `swaymsg output dpms on` Without this, the wallpaper is changed to `/path/to/wallpaper1`. With this, the wallpaper remains `/path/to/wallpaper2`. --- sway/config/output.c | 116 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 94 insertions(+), 22 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index a20c5ad4a..5656e2c10 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -98,10 +98,67 @@ static void merge_wildcard_on_all(struct output_config *wildcard) { } } +static void merge_id_on_name(struct output_config *oc) { + char *id_on_name = NULL; + char id[128]; + char *name = NULL; + struct sway_output *output; + wl_list_for_each(output, &root->all_outputs, link) { + name = output->wlr_output->name; + output_get_identifier(id, sizeof(id), output); + if (strcmp(name, oc->name) != 0 || strcmp(id, oc->name) != 0) { + size_t length = snprintf(NULL, 0, "%s on %s", id, name) + 1; + id_on_name = malloc(length); + if (!id_on_name) { + sway_log(SWAY_ERROR, "Failed to allocate id on name string"); + return; + } + snprintf(id_on_name, length, "%s on %s", id, name); + break; + } + } + + if (!id_on_name) { + return; + } + + int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); + if (i >= 0) { + sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); + merge_output_config(config->output_configs->items[i], oc); + } else { + // If both a name and identifier config, exist generate an id on name + int ni = list_seq_find(config->output_configs, output_name_cmp, name); + int ii = list_seq_find(config->output_configs, output_name_cmp, id); + if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0) + || (ii >= 0 && strcmp(oc->name, name) == 0)) { + struct output_config *ion_oc = new_output_config(id_on_name); + if (ni >= 0) { + merge_output_config(ion_oc, config->output_configs->items[ni]); + } + if (ii >= 0) { + merge_output_config(ion_oc, config->output_configs->items[ii]); + } + merge_output_config(ion_oc, oc); + list_add(config->output_configs, ion_oc); + sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\"" + " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f " + "transform %d) (bg %s %s) (dpms %d)", ion_oc->name, + ion_oc->enabled, ion_oc->width, ion_oc->height, + ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale, + ion_oc->transform, ion_oc->background, + ion_oc->background_option, ion_oc->dpms_state); + } + } + free(id_on_name); +} + struct output_config *store_output_config(struct output_config *oc) { bool wildcard = strcmp(oc->name, "*") == 0; if (wildcard) { merge_wildcard_on_all(oc); + } else { + merge_id_on_name(oc); } int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); @@ -374,35 +431,51 @@ static void default_output_config(struct output_config *oc, static struct output_config *get_output_config(char *identifier, struct sway_output *sway_output) { const char *name = sway_output->wlr_output->name; - struct output_config *oc_name = NULL; - int i = list_seq_find(config->output_configs, output_name_cmp, name); - if (i >= 0) { - oc_name = config->output_configs->items[i]; - } + struct output_config *oc_id_on_name = NULL; + struct output_config *oc_name = NULL; struct output_config *oc_id = NULL; - i = list_seq_find(config->output_configs, output_name_cmp, identifier); + + size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; + char *id_on_name = malloc(length); + snprintf(id_on_name, length, "%s on %s", identifier, name); + int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); if (i >= 0) { - oc_id = config->output_configs->items[i]; + oc_id_on_name = config->output_configs->items[i]; + } else { + i = list_seq_find(config->output_configs, output_name_cmp, name); + if (i >= 0) { + oc_name = config->output_configs->items[i]; + } + + i = list_seq_find(config->output_configs, output_name_cmp, identifier); + if (i >= 0) { + oc_id = config->output_configs->items[i]; + } } struct output_config *result = new_output_config("temp"); if (config->reloading) { default_output_config(result, sway_output->wlr_output); } - if (oc_name && oc_id) { + if (oc_id_on_name) { + // Already have an identifier on name config, use that + free(result->name); + result->name = strdup(id_on_name); + merge_output_config(result, oc_id_on_name); + } else if (oc_name && oc_id) { // Generate a config named ` on ` which contains a // merged copy of the identifier on name. This will make sure that both // identifier and name configs are respected, with identifier getting // priority - size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1; - char *temp = malloc(length); - snprintf(temp, length, "%s on %s", identifier, name); + struct output_config *temp = new_output_config(id_on_name); + merge_output_config(temp, oc_name); + merge_output_config(temp, oc_id); + list_add(config->output_configs, temp); free(result->name); - result->name = temp; - merge_output_config(result, oc_name); - merge_output_config(result, oc_id); + result->name = strdup(id_on_name); + merge_output_config(result, temp); sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)" " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)" @@ -435,6 +508,7 @@ static struct output_config *get_output_config(char *identifier, result = NULL; } + free(id_on_name); return result; } @@ -455,14 +529,12 @@ void apply_output_config_to_outputs(struct output_config *oc) { char *name = sway_output->wlr_output->name; output_get_identifier(id, sizeof(id), sway_output); if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { - struct output_config *current = new_output_config(oc->name); - merge_output_config(current, oc); - if (wildcard) { - struct output_config *tmp = get_output_config(id, sway_output); - if (tmp) { - free_output_config(current); - current = tmp; - } + struct output_config *current = get_output_config(id, sway_output); + if (!current) { + // No stored output config matched, apply oc directly + sway_log(SWAY_DEBUG, "Applying oc directly"); + current = new_output_config(oc->name); + merge_output_config(current, oc); } apply_output_config(current, sway_output); free_output_config(current); From fb3475e291ea6be94131c19fdc006e9ad873ea5f Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 16 Mar 2019 09:18:54 +1000 Subject: [PATCH 0206/2054] Replace seatup allows_events with button callback --- include/sway/input/seat.h | 10 +++++++--- sway/input/cursor.c | 4 +--- sway/input/seat.c | 12 ++++++++---- sway/input/seatop_down.c | 8 +++++++- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index eb674b705..ff4476d19 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -9,13 +9,15 @@ struct sway_seat; struct sway_seatop_impl { + void (*button)(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state); void (*motion)(struct sway_seat *seat, uint32_t time_msec); void (*finish)(struct sway_seat *seat, uint32_t time_msec); void (*abort)(struct sway_seat *seat); void (*unref)(struct sway_seat *seat, struct sway_container *con); void (*render)(struct sway_seat *seat, struct sway_output *output, pixman_region32_t *damage); - bool allows_events; }; struct sway_seat_device { @@ -214,6 +216,10 @@ void seat_consider_warp_to_focus(struct sway_seat *seat); bool seat_doing_seatop(struct sway_seat *seat); +void seatop_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state); + void seatop_motion(struct sway_seat *seat, uint32_t time_msec); /** @@ -240,6 +246,4 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con); void seatop_render(struct sway_seat *seat, struct sway_output *output, pixman_region32_t *damage); -bool seatop_allows_events(struct sway_seat *seat); - #endif diff --git a/sway/input/cursor.c b/sway/input/cursor.c index d531a20e3..011b4929a 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -610,9 +610,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, } else { state_erase_button(cursor, button); } - if (seatop_allows_events(seat)) { - seat_pointer_notify_button(seat, time_msec, button, state); - } + seatop_button(seat, time_msec, device, button, state); if (button == seat->seatop_button && state == WLR_BUTTON_RELEASED) { seatop_finish(seat, time_msec); } diff --git a/sway/input/seat.c b/sway/input/seat.c index e56a65105..2c9a85c49 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1216,6 +1216,14 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con) { } } +void seatop_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + if (seat->seatop_impl && seat->seatop_impl->button) { + seat->seatop_impl->button(seat, time_msec, device, button, state); + } +} + void seatop_motion(struct sway_seat *seat, uint32_t time_msec) { if (seat->seatop_impl && seat->seatop_impl->motion) { seat->seatop_impl->motion(seat, time_msec); @@ -1246,7 +1254,3 @@ void seatop_render(struct sway_seat *seat, struct sway_output *output, seat->seatop_impl->render(seat, output, damage); } } - -bool seatop_allows_events(struct sway_seat *seat) { - return seat->seatop_impl && seat->seatop_impl->allows_events; -} diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index 895571b10..fb2cf1d07 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -11,6 +11,12 @@ struct seatop_down_event { bool moved; }; +static void handle_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + seat_pointer_notify_button(seat, time_msec, button, state); +} + static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { struct seatop_down_event *e = seat->seatop_data; struct sway_container *con = e->con; @@ -54,11 +60,11 @@ static void handle_unref(struct sway_seat *seat, struct sway_container *con) { } static const struct sway_seatop_impl seatop_impl = { + .button = handle_button, .motion = handle_motion, .finish = handle_finish, .abort = handle_abort, .unref = handle_unref, - .allows_events = true, }; void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, From 7b9ae42331e49fea0f566fa7592855dee5da1991 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Sat, 16 Mar 2019 17:47:39 +1000 Subject: [PATCH 0207/2054] Introduce default seatop This introduces a `default` seat operation which is used when no mouse buttons are being held. This means there is now always a seat operation in progress. It allows us to separate `default` code from the standard cursor management code. The sway_seatop_impl struct has gained callbacks `axis`, `rebase` and `end`, and lost callbacks `finish` and `abort`. `axis` and `rebase` are only used by the default seatop. `end` is called when a seatop is being replaced by another one and allows the seatop to free any resources, though no seatop currently needs to do this. `finish` is no longer required, as each seatop can gracefully finish in their `button` callback. And `abort` is not needed, as calling `end` would achieve the same thing. The struct has also gained a bool named allow_set_cursor which allows the client to set a new cursor during `default` and `down` seatops. Seatops would previously store which button they were started with and stop when that button was released. This behaviour is changed so that it only ends once all buttons are released. So you can start a drag with $mod+left, then click and hold right, release left and it'll continue dragging while the right button is held. The motion callback now accepts dx and dy. Most seatops don't use this as they store the cursor position when the seatop is started and compare it with the current cursor position. This approach doesn't make sense for the default seatop though, hence why dx and dy are needed. The pressed_buttons array has been moved from the sway_cursor struct to the default seatop's data. This is only used for the default seatop to check bindings. The total pressed button count remains in the sway_cursor struct though, because all the other seatops check it to know if they should end. The `down` seatop no longer has a `moved` property. This was used to track if the cursor moved and to recheck focus_follows_mouse, but seems to work without it. The logic for focus_follows_mouse has been refactored. As part of this I've removed the call to wlr_seat_keyboard_has_grab as we don't appear to use keyboard grabs. The functions for handling relative motion, absolute motion and tool axis have been changed. Previously the handler functions were handle_cursor_motion, handle_cursor_motion_absolute and handle_tool_axis. The latter two both called cursor_motion_absolute. Both handle_cursor_motion and cursor_motion_absolute did very similar things. These are now simplified into three handlers and a single common function called cursor_motion. All three handlers call cursor_motion. As cursor_motion works with relative distances, the absolute and tool axis handlers convert them to relative first. --- include/sway/input/cursor.h | 9 - include/sway/input/seat.h | 48 +- sway/desktop/xdg_shell.c | 5 +- sway/desktop/xdg_shell_v6.c | 5 +- sway/desktop/xwayland.c | 5 +- sway/input/cursor.c | 680 ++-------------------------- sway/input/seat.c | 47 +- sway/input/seatop_default.c | 629 +++++++++++++++++++++++++ sway/input/seatop_down.c | 43 +- sway/input/seatop_move_floating.c | 44 +- sway/input/seatop_move_tiling.c | 33 +- sway/input/seatop_resize_floating.c | 29 +- sway/input/seatop_resize_tiling.c | 29 +- sway/meson.build | 1 + 14 files changed, 811 insertions(+), 796 deletions(-) create mode 100644 sway/input/seatop_default.c diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 98eb4679b..516718c97 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -52,8 +52,6 @@ struct sway_cursor { struct wl_event_source *hide_source; bool hidden; - // Mouse binding state - uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; size_t pressed_button_count; }; @@ -78,13 +76,6 @@ void cursor_handle_activity(struct sway_cursor *cursor); void cursor_unhide(struct sway_cursor *cursor); int cursor_get_timeout(struct sway_cursor *cursor); -/** - * Like cursor_rebase, but also allows focus to change when the cursor enters a - * new container. - */ -void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, - struct sway_node *node, struct wlr_surface *surface, double sx, double sy); - void dispatch_cursor_button(struct sway_cursor *cursor, struct wlr_input_device *device, uint32_t time_msec, uint32_t button, enum wlr_button_state state); diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index ff4476d19..a5361e8c1 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -12,12 +12,15 @@ struct sway_seatop_impl { void (*button)(struct sway_seat *seat, uint32_t time_msec, struct wlr_input_device *device, uint32_t button, enum wlr_button_state state); - void (*motion)(struct sway_seat *seat, uint32_t time_msec); - void (*finish)(struct sway_seat *seat, uint32_t time_msec); - void (*abort)(struct sway_seat *seat); + void (*motion)(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy); + void (*axis)(struct sway_seat *seat, struct wlr_event_pointer_axis *event); + void (*rebase)(struct sway_seat *seat, uint32_t time_msec); + void (*end)(struct sway_seat *seat); void (*unref)(struct sway_seat *seat, struct sway_container *con); void (*render)(struct sway_seat *seat, struct sway_output *output, pixman_region32_t *damage); + bool allow_set_cursor; }; struct sway_seat_device { @@ -71,9 +74,7 @@ struct sway_seat { // Seat operations (drag and resize) const struct sway_seatop_impl *seatop_impl; void *seatop_data; - uint32_t seatop_button; - uint32_t last_button; uint32_t last_button_serial; struct wl_listener focus_destroy; @@ -188,23 +189,25 @@ bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface); void drag_icon_update_position(struct sway_drag_icon *icon); +void seatop_begin_default(struct sway_seat *seat); + void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, - uint32_t time_msec, uint32_t button, int sx, int sy); + uint32_t time_msec, int sx, int sy); void seatop_begin_move_floating(struct sway_seat *seat, - struct sway_container *con, uint32_t button); + struct sway_container *con); void seatop_begin_move_tiling_threshold(struct sway_seat *seat, - struct sway_container *con, uint32_t button); + struct sway_container *con); void seatop_begin_move_tiling(struct sway_seat *seat, - struct sway_container *con, uint32_t button); + struct sway_container *con); void seatop_begin_resize_floating(struct sway_seat *seat, - struct sway_container *con, uint32_t button, enum wlr_edges edge); + struct sway_container *con, enum wlr_edges edge); void seatop_begin_resize_tiling(struct sway_seat *seat, - struct sway_container *con, uint32_t button, enum wlr_edges edge); + struct sway_container *con, enum wlr_edges edge); struct sway_container *seat_get_focus_inactive_floating(struct sway_seat *seat, struct sway_workspace *workspace); @@ -214,23 +217,24 @@ void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, void seat_consider_warp_to_focus(struct sway_seat *seat); -bool seat_doing_seatop(struct sway_seat *seat); - void seatop_button(struct sway_seat *seat, uint32_t time_msec, struct wlr_input_device *device, uint32_t button, enum wlr_button_state state); -void seatop_motion(struct sway_seat *seat, uint32_t time_msec); +/** + * dx and dy are distances relative to previous position. + */ +void seatop_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy); + +void seatop_axis(struct sway_seat *seat, struct wlr_event_pointer_axis *event); + +void seatop_rebase(struct sway_seat *seat, uint32_t time_msec); /** - * End a seatop and apply the affects. + * End a seatop (ie. free any seatop specific resources). */ -void seatop_finish(struct sway_seat *seat, uint32_t time_msec); - -/** - * End a seatop without applying the affects. - */ -void seatop_abort(struct sway_seat *seat); +void seatop_end(struct sway_seat *seat); /** * Instructs the seatop implementation to drop any references to the given @@ -246,4 +250,6 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con); void seatop_render(struct sway_seat *seat, struct sway_output *output, pixman_region32_t *damage); +bool seatop_allows_set_cursor(struct sway_seat *seat); + #endif diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index c84ca1115..ea5dcd16c 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -355,7 +355,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_move_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { - seatop_begin_move_floating(seat, view->container, seat->last_button); + seatop_begin_move_floating(seat, view->container); } } @@ -369,8 +369,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_resize_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { - seatop_begin_resize_floating(seat, view->container, - seat->last_button, e->edges); + seatop_begin_resize_floating(seat, view->container, e->edges); } } diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 3eed54ab0..7ff4c4de8 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -355,7 +355,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_v6_move_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { - seatop_begin_move_floating(seat, view->container, seat->last_button); + seatop_begin_move_floating(seat, view->container); } } @@ -369,8 +369,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { struct wlr_xdg_toplevel_v6_resize_event *e = data; struct sway_seat *seat = e->seat->seat->data; if (e->serial == seat->last_button_serial) { - seatop_begin_resize_floating(seat, view->container, - seat->last_button, e->edges); + seatop_begin_resize_floating(seat, view->container, e->edges); } } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index a2aa2e08e..f5ade8dcb 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -476,7 +476,7 @@ static void handle_request_move(struct wl_listener *listener, void *data) { return; } struct sway_seat *seat = input_manager_current_seat(); - seatop_begin_move_floating(seat, view->container, seat->last_button); + seatop_begin_move_floating(seat, view->container); } static void handle_request_resize(struct wl_listener *listener, void *data) { @@ -492,8 +492,7 @@ static void handle_request_resize(struct wl_listener *listener, void *data) { } struct wlr_xwayland_resize_event *e = data; struct sway_seat *seat = input_manager_current_seat(); - seatop_begin_resize_floating(seat, view->container, - seat->last_button, e->edges); + seatop_begin_resize_floating(seat, view->container, e->edges); } static void handle_request_activate(struct wl_listener *listener, void *data) { diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 011b4929a..610844479 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -158,132 +158,9 @@ struct sway_node *node_at_coords( return &ws->node; } -/** - * Determine if the edge of the given container is on the edge of the - * workspace/output. - */ -static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { - enum sway_container_layout layout = L_NONE; - switch (edge) { - case WLR_EDGE_TOP: - case WLR_EDGE_BOTTOM: - layout = L_VERT; - break; - case WLR_EDGE_LEFT: - case WLR_EDGE_RIGHT: - layout = L_HORIZ; - break; - case WLR_EDGE_NONE: - sway_assert(false, "Never reached"); - return false; - } - - // Iterate the parents until we find one with the layout we want, - // then check if the child has siblings between it and the edge. - while (cont) { - if (container_parent_layout(cont) == layout) { - list_t *siblings = container_get_siblings(cont); - int index = list_find(siblings, cont); - if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { - return false; - } - if (index < siblings->length - 1 && - (edge == WLR_EDGE_RIGHT || edge == WLR_EDGE_BOTTOM)) { - return false; - } - } - cont = cont->parent; - } - return true; -} - -static enum wlr_edges find_edge(struct sway_container *cont, - struct sway_cursor *cursor) { - if (!cont->view) { - return WLR_EDGE_NONE; - } - if (cont->border == B_NONE || !cont->border_thickness || - cont->border == B_CSD) { - return WLR_EDGE_NONE; - } - - enum wlr_edges edge = 0; - if (cursor->cursor->x < cont->x + cont->border_thickness) { - edge |= WLR_EDGE_LEFT; - } - if (cursor->cursor->y < cont->y + cont->border_thickness) { - edge |= WLR_EDGE_TOP; - } - if (cursor->cursor->x >= cont->x + cont->width - cont->border_thickness) { - edge |= WLR_EDGE_RIGHT; - } - if (cursor->cursor->y >= cont->y + cont->height - cont->border_thickness) { - edge |= WLR_EDGE_BOTTOM; - } - - return edge; -} - -/** - * If the cursor is over a _resizable_ edge, return the edge. - * Edges that can't be resized are edges of the workspace. - */ -static enum wlr_edges find_resize_edge(struct sway_container *cont, - struct sway_cursor *cursor) { - enum wlr_edges edge = find_edge(cont, cursor); - if (edge && !container_is_floating(cont) && edge_is_external(cont, edge)) { - return WLR_EDGE_NONE; - } - return edge; -} - -static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec, - struct sway_node *node, struct wlr_surface *surface, - double sx, double sy) { - // Handle cursor image - if (surface) { - // Reset cursor if switching between clients - struct wl_client *client = wl_resource_get_client(surface->resource); - if (client != cursor->image_client) { - cursor_set_image(cursor, "left_ptr", client); - } - } else if (node && node->type == N_CONTAINER) { - // Try a node's resize edge - enum wlr_edges edge = find_resize_edge(node->sway_container, cursor); - if (edge == WLR_EDGE_NONE) { - cursor_set_image(cursor, "left_ptr", NULL); - } else if (container_is_floating(node->sway_container)) { - cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); - } else { - if (edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) { - cursor_set_image(cursor, "col-resize", NULL); - } else { - cursor_set_image(cursor, "row-resize", NULL); - } - } - } else { - cursor_set_image(cursor, "left_ptr", NULL); - } - - // Send pointer enter/leave - struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; - if (surface) { - if (seat_is_input_allowed(cursor->seat, surface)) { - wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy); - wlr_seat_pointer_notify_motion(wlr_seat, time_msec, sx, sy); - } - } else { - wlr_seat_pointer_clear_focus(wlr_seat); - } -} - void cursor_rebase(struct sway_cursor *cursor) { uint32_t time_msec = get_current_time_msec(); - struct wlr_surface *surface = NULL; - double sx = 0.0, sy = 0.0; - cursor->previous.node = node_at_coords(cursor->seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - cursor_do_rebase(cursor, time_msec, cursor->previous.node, surface, sx, sy); + seatop_rebase(cursor->seat, time_msec); } void cursor_rebase_all(void) { @@ -293,9 +170,7 @@ void cursor_rebase_all(void) { struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { - if (!seat_doing_seatop(seat)) { - cursor_rebase(seat->cursor); - } + cursor_rebase(seat->cursor); } } @@ -345,90 +220,17 @@ void cursor_unhide(struct sway_cursor *cursor) { cursor_rebase(cursor); } -void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, - struct sway_node *node, struct wlr_surface *surface, - double sx, double sy) { - if (time_msec == 0) { - time_msec = get_current_time_msec(); - } - - struct sway_seat *seat = cursor->seat; - - if (seat_doing_seatop(seat)) { - seatop_motion(seat, time_msec); - cursor->previous.x = cursor->cursor->x; - cursor->previous.y = cursor->cursor->y; - return; - } - - struct sway_node *prev_node = cursor->previous.node; - - // Update the stored previous position - cursor->previous.x = cursor->cursor->x; - cursor->previous.y = cursor->cursor->y; - cursor->previous.node = node; - - if (node && (config->focus_follows_mouse == FOLLOWS_YES || - config->focus_follows_mouse == FOLLOWS_ALWAYS)) { - struct sway_node *focus = seat_get_focus(seat); - if (focus && node->type == N_WORKSPACE) { - // Only follow the mouse if it would move to a new output - // Otherwise we'll focus the workspace, which is probably wrong - struct sway_output *focused_output = node_get_output(focus); - struct sway_output *output = node_get_output(node); - if (output != focused_output) { - seat_set_focus(seat, seat_get_focus_inactive(seat, node)); - } - } else if (node->type == N_CONTAINER && node->sway_container->view) { - // Focus node 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; and - // - the seat does not have a keyboard grab - if ((!wlr_seat_keyboard_has_grab(cursor->seat->wlr_seat) && - node != prev_node && - view_is_visible(node->sway_container->view)) || - config->focus_follows_mouse == FOLLOWS_ALWAYS) { - seat_set_focus(seat, node); - } else { - struct sway_node *next_focus = - seat_get_focus_inactive(seat, &root->node); - if (next_focus && next_focus->type == N_CONTAINER && - next_focus->sway_container->view && - view_is_visible(next_focus->sway_container->view)) { - seat_set_focus(seat, next_focus); - } - } - } - } - - cursor_do_rebase(cursor, time_msec, node, surface, sx, sy); - - struct sway_drag_icon *drag_icon; - wl_list_for_each(drag_icon, &root->drag_icons, link) { - if (drag_icon->seat == seat) { - drag_icon_update_position(drag_icon); - } - } -} - -static void handle_cursor_motion(struct wl_listener *listener, void *data) { - struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); - struct wlr_event_pointer_motion *event = data; +static void cursor_motion(struct sway_cursor *cursor, uint32_t time_msec, + struct wlr_input_device *device, double dx, double dy, + double dx_unaccel, double dy_unaccel) { cursor_handle_activity(cursor); - double dx = event->delta_x; - double dy = event->delta_y; - - double dx_unaccel = event->unaccel_dx; - double dy_unaccel = event->unaccel_dy; - wlr_relative_pointer_manager_v1_send_relative_motion( server.relative_pointer_manager, - cursor->seat->wlr_seat, (uint64_t)event->time_msec * 1000, + cursor->seat->wlr_seat, (uint64_t)time_msec * 1000, dx, dy, dx_unaccel, dy_unaccel); struct wlr_surface *surface = NULL; - struct sway_node *node = NULL; double sx, sy; if (cursor->active_constraint) { node_at_coords(cursor->seat, @@ -448,50 +250,18 @@ static void handle_cursor_motion(struct wl_listener *listener, void *data) { dy = sy_confined - sy; } - wlr_cursor_move(cursor->cursor, event->device, dx, dy); + wlr_cursor_move(cursor->cursor, device, dx, dy); - // Recalculate pointer location after layout checks - node = node_at_coords(cursor->seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - - cursor_send_pointer_motion(cursor, event->time_msec, node, surface, - sx, sy); - transaction_commit_dirty(); + seatop_motion(cursor->seat, time_msec, dx, dy); } -static void cursor_motion_absolute(struct sway_cursor *cursor, - uint32_t time_msec, struct wlr_input_device *dev, - double x, double y) { - cursor_handle_activity(cursor); +static void handle_cursor_motion_relative( + struct wl_listener *listener, void *data) { + struct sway_cursor *cursor = wl_container_of(listener, cursor, motion); + struct wlr_event_pointer_motion *e = data; - double lx, ly; - wlr_cursor_absolute_to_layout_coords(cursor->cursor, dev, - x, y, &lx, &ly); - - double dx = lx - cursor->cursor->x; - double dy = ly - cursor->cursor->y; - wlr_relative_pointer_manager_v1_send_relative_motion( - server.relative_pointer_manager, - cursor->seat->wlr_seat, (uint64_t)time_msec * 1000, - dx, dy, dx, dy); - - struct wlr_surface *surface = NULL; - double sx = 0.0, sy = 0.0; - struct sway_node *node = node_at_coords(cursor->seat, - lx, ly, &surface, &sx, &sy); - - if (cursor->active_constraint) { - if (cursor->active_constraint->surface != surface) { - return; - } - if (!pixman_region32_contains_point(&cursor->confine, - floor(sx), floor(sy), NULL)) { - return; - } - } - - wlr_cursor_warp_closest(cursor->cursor, dev, lx, ly); - cursor_send_pointer_motion(cursor, time_msec, node, surface, sx, sy); + cursor_motion(cursor, e->time_msec, e->device, e->delta_x, e->delta_y, + e->unaccel_dx, e->unaccel_dy); transaction_commit_dirty(); } @@ -501,98 +271,15 @@ static void handle_cursor_motion_absolute( wl_container_of(listener, cursor, motion_absolute); struct wlr_event_pointer_motion_absolute *event = data; - cursor_motion_absolute(cursor, event->time_msec, event->device, - event->x, event->y); -} + double lx, ly; + wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, + event->x, event->y, &lx, &ly); -/** - * Remove a button (and duplicates) to the sorted list of currently pressed buttons - */ -static void state_erase_button(struct sway_cursor *cursor, uint32_t button) { - size_t j = 0; - for (size_t i = 0; i < cursor->pressed_button_count; ++i) { - if (i > j) { - cursor->pressed_buttons[j] = cursor->pressed_buttons[i]; - } - if (cursor->pressed_buttons[i] != button) { - ++j; - } - } - while (cursor->pressed_button_count > j) { - --cursor->pressed_button_count; - cursor->pressed_buttons[cursor->pressed_button_count] = 0; - } -} + double dx = lx - cursor->cursor->x; + double dy = ly - cursor->cursor->y; -/** - * Add a button to the sorted list of currently pressed buttons, if there - * is space. - */ -static void state_add_button(struct sway_cursor *cursor, uint32_t button) { - if (cursor->pressed_button_count >= SWAY_CURSOR_PRESSED_BUTTONS_CAP) { - return; - } - size_t i = 0; - while (i < cursor->pressed_button_count && cursor->pressed_buttons[i] < button) { - ++i; - } - size_t j = cursor->pressed_button_count; - while (j > i) { - cursor->pressed_buttons[j] = cursor->pressed_buttons[j - 1]; - --j; - } - cursor->pressed_buttons[i] = button; - cursor->pressed_button_count++; -} - -/** - * Return the mouse binding which matches modifier, click location, release, - * and pressed button state, otherwise return null. - */ -static struct sway_binding* get_active_mouse_binding( - const struct sway_cursor *cursor, list_t *bindings, uint32_t modifiers, - bool release, bool on_titlebar, bool on_border, bool on_content, - bool on_workspace, const char *identifier) { - uint32_t click_region = - ((on_titlebar || on_workspace) ? BINDING_TITLEBAR : 0) | - ((on_border || on_workspace) ? BINDING_BORDER : 0) | - ((on_content || on_workspace) ? BINDING_CONTENTS : 0); - - struct sway_binding *current = NULL; - for (int i = 0; i < bindings->length; ++i) { - struct sway_binding *binding = bindings->items[i]; - if (modifiers ^ binding->modifiers || - cursor->pressed_button_count != (size_t)binding->keys->length || - release != (binding->flags & BINDING_RELEASE) || - !(click_region & binding->flags) || - (on_workspace && - (click_region & binding->flags) != click_region) || - (strcmp(binding->input, identifier) != 0 && - strcmp(binding->input, "*") != 0)) { - continue; - } - - bool match = true; - for (size_t j = 0; j < cursor->pressed_button_count; j++) { - uint32_t key = *(uint32_t *)binding->keys->items[j]; - if (key != cursor->pressed_buttons[j]) { - match = false; - break; - } - } - if (!match) { - continue; - } - - if (!current || strcmp(current->input, "*") == 0) { - current = binding; - if (strcmp(current->input, identifier) == 0) { - // If a binding is found for the exact input, quit searching - break; - } - } - } - return current; + cursor_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); + transaction_commit_dirty(); } void dispatch_cursor_button(struct sway_cursor *cursor, @@ -601,318 +288,33 @@ void dispatch_cursor_button(struct sway_cursor *cursor, if (time_msec == 0) { time_msec = get_current_time_msec(); } - struct sway_seat *seat = cursor->seat; - // Handle existing seat operation - if (seat_doing_seatop(seat)) { - if (state == WLR_BUTTON_PRESSED) { - state_add_button(cursor, button); - } else { - state_erase_button(cursor, button); - } - seatop_button(seat, time_msec, device, button, state); - if (button == seat->seatop_button && state == WLR_BUTTON_RELEASED) { - seatop_finish(seat, time_msec); - } - return; - } - - // Determine what's under the cursor - struct wlr_surface *surface = NULL; - double sx, sy; - struct sway_node *node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - struct sway_container *cont = node && node->type == N_CONTAINER ? - node->sway_container : NULL; - bool is_floating = cont && container_is_floating(cont); - bool is_floating_or_child = cont && container_is_floating_or_child(cont); - bool is_fullscreen_or_child = cont && container_is_fullscreen_or_child(cont); - enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; - enum wlr_edges resize_edge = edge ? - find_resize_edge(cont, cursor) : WLR_EDGE_NONE; - bool on_border = edge != WLR_EDGE_NONE; - bool on_contents = cont && !on_border && surface; - bool on_workspace = node && node->type == N_WORKSPACE; - bool on_titlebar = cont && !on_border && !surface; - - // Handle mouse bindings - struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); - uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; - - char *device_identifier = device ? input_device_get_identifier(device) - : strdup("*"); - struct sway_binding *binding = NULL; - if (state == WLR_BUTTON_PRESSED) { - state_add_button(cursor, button); - binding = get_active_mouse_binding(cursor, - config->current_mode->mouse_bindings, modifiers, false, - on_titlebar, on_border, on_contents, on_workspace, - device_identifier); - } else { - binding = get_active_mouse_binding(cursor, - config->current_mode->mouse_bindings, modifiers, true, - on_titlebar, on_border, on_contents, on_workspace, - device_identifier); - state_erase_button(cursor, button); - } - free(device_identifier); - if (binding) { - seat_execute_command(seat, binding); - return; - } - - // Handle clicking an empty workspace - if (node && node->type == N_WORKSPACE) { - seat_set_focus(seat, node); - return; - } - - // Handle clicking a layer surface - if (surface && wlr_surface_is_layer_surface(surface)) { - struct wlr_layer_surface_v1 *layer = - wlr_layer_surface_v1_from_wlr_surface(surface); - if (layer->current.keyboard_interactive) { - seat_set_focus_layer(seat, layer); - } - seat_pointer_notify_button(seat, time_msec, button, state); - return; - } - - // Handle tiling resize via border - if (cont && resize_edge && button == BTN_LEFT && - state == WLR_BUTTON_PRESSED && !is_floating) { - seat_set_focus_container(seat, cont); - seatop_begin_resize_tiling(seat, cont, button, edge); - return; - } - - // Handle tiling resize via mod - bool mod_pressed = keyboard && - (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); - if (cont && !is_floating_or_child && mod_pressed && - state == WLR_BUTTON_PRESSED) { - uint32_t btn_resize = config->floating_mod_inverse ? - BTN_LEFT : BTN_RIGHT; - if (button == btn_resize) { - edge = 0; - edge |= cursor->cursor->x > cont->x + cont->width / 2 ? - WLR_EDGE_RIGHT : WLR_EDGE_LEFT; - edge |= cursor->cursor->y > cont->y + cont->height / 2 ? - WLR_EDGE_BOTTOM : WLR_EDGE_TOP; - - const char *image = NULL; - if (edge == (WLR_EDGE_LEFT | WLR_EDGE_TOP)) { - image = "nw-resize"; - } else if (edge == (WLR_EDGE_TOP | WLR_EDGE_RIGHT)) { - image = "ne-resize"; - } else if (edge == (WLR_EDGE_RIGHT | WLR_EDGE_BOTTOM)) { - image = "se-resize"; - } else if (edge == (WLR_EDGE_BOTTOM | WLR_EDGE_LEFT)) { - image = "sw-resize"; - } - cursor_set_image(seat->cursor, image, NULL); - seat_set_focus_container(seat, cont); - seatop_begin_resize_tiling(seat, cont, button, edge); - return; - } - } - - // Handle beginning floating move - if (cont && is_floating_or_child && !is_fullscreen_or_child && - state == WLR_BUTTON_PRESSED) { - uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; - if (button == btn_move && state == WLR_BUTTON_PRESSED && - (mod_pressed || on_titlebar)) { - while (cont->parent) { - cont = cont->parent; - } - seat_set_focus_container(seat, cont); - seatop_begin_move_floating(seat, cont, button); - return; - } - } - - // Handle beginning floating resize - if (cont && is_floating_or_child && !is_fullscreen_or_child && - state == WLR_BUTTON_PRESSED) { - // Via border - if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { - seatop_begin_resize_floating(seat, cont, button, resize_edge); - return; - } - - // Via mod+click - uint32_t btn_resize = config->floating_mod_inverse ? - BTN_LEFT : BTN_RIGHT; - if (mod_pressed && button == btn_resize) { - struct sway_container *floater = cont; - while (floater->parent) { - floater = floater->parent; - } - edge = 0; - edge |= cursor->cursor->x > floater->x + floater->width / 2 ? - WLR_EDGE_RIGHT : WLR_EDGE_LEFT; - edge |= cursor->cursor->y > floater->y + floater->height / 2 ? - WLR_EDGE_BOTTOM : WLR_EDGE_TOP; - seatop_begin_resize_floating(seat, floater, button, edge); - return; - } - } - - // Handle moving a tiling container - if (config->tiling_drag && (mod_pressed || on_titlebar) && - state == WLR_BUTTON_PRESSED && !is_floating_or_child && - cont && cont->fullscreen_mode == FULLSCREEN_NONE) { - struct sway_container *focus = seat_get_focused_container(seat); - bool focused = focus == cont || container_has_ancestor(focus, cont); - if (on_titlebar && !focused) { - node = seat_get_focus_inactive(seat, &cont->node); - seat_set_focus(seat, node); - } - - // If moving a container by it's title bar, use a threshold for the drag - if (!mod_pressed && config->tiling_drag_threshold > 0) { - seatop_begin_move_tiling_threshold(seat, cont, button); - } else { - seatop_begin_move_tiling(seat, cont, button); - } - return; - } - - // Handle mousedown on a container surface - if (surface && cont && state == WLR_BUTTON_PRESSED) { - seat_set_focus_container(seat, cont); - seatop_begin_down(seat, cont, time_msec, button, sx, sy); - seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); - return; - } - - // Handle clicking a container surface or decorations - if (cont) { - node = seat_get_focus_inactive(seat, &cont->node); - seat_set_focus(seat, node); - seat_pointer_notify_button(seat, time_msec, button, state); - return; - } - - seat_pointer_notify_button(seat, time_msec, button, state); + seatop_button(cursor->seat, time_msec, device, button, state); } static void handle_cursor_button(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, button); struct wlr_event_pointer_button *event = data; cursor_handle_activity(cursor); + + if (event->state == WLR_BUTTON_PRESSED) { + cursor->pressed_button_count++; + } else { + if (cursor->pressed_button_count > 0) { + cursor->pressed_button_count--; + } else { + sway_log(SWAY_ERROR, "Pressed button count was wrong"); + } + } + dispatch_cursor_button(cursor, event->device, event->time_msec, event->button, event->state); transaction_commit_dirty(); } -static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { - switch (event->orientation) { - case WLR_AXIS_ORIENTATION_VERTICAL: - return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; - case WLR_AXIS_ORIENTATION_HORIZONTAL: - return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; - default: - sway_log(SWAY_DEBUG, "Unknown axis orientation"); - return 0; - } -} - void dispatch_cursor_axis(struct sway_cursor *cursor, struct wlr_event_pointer_axis *event) { - struct sway_seat *seat = cursor->seat; - struct sway_input_device *input_device = - event->device ? event->device->data : NULL; - struct input_config *ic = - input_device ? input_device_get_config(input_device) : NULL; - - // Determine what's under the cursor - struct wlr_surface *surface = NULL; - double sx, sy; - struct sway_node *node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - struct sway_container *cont = node && node->type == N_CONTAINER ? - node->sway_container : NULL; - enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; - bool on_border = edge != WLR_EDGE_NONE; - bool on_titlebar = cont && !on_border && !surface; - bool on_titlebar_border = cont && on_border && - cursor->cursor->y < cont->content_y; - bool on_contents = cont && !on_border && surface; - bool on_workspace = node && node->type == N_WORKSPACE; - float scroll_factor = - (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor; - - bool handled = false; - - // Gather information needed for mouse bindings - struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); - uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; - struct wlr_input_device *device = - input_device ? input_device->wlr_device : NULL; - char *dev_id = device ? input_device_get_identifier(device) : strdup("*"); - uint32_t button = wl_axis_to_button(event); - - // Handle mouse bindings - x11 mouse buttons 4-7 - press event - struct sway_binding *binding = NULL; - state_add_button(cursor, button); - binding = get_active_mouse_binding(cursor, - config->current_mode->mouse_bindings, modifiers, false, - on_titlebar, on_border, on_contents, on_workspace, dev_id); - if (binding) { - seat_execute_command(seat, binding); - handled = true; - } - - // Scrolling on a tabbed or stacked title bar (handled as press event) - if (!handled && (on_titlebar || on_titlebar_border)) { - enum sway_container_layout layout = container_parent_layout(cont); - if (layout == L_TABBED || layout == L_STACKED) { - struct sway_node *tabcontainer = node_get_parent(node); - struct sway_node *active = - seat_get_active_tiling_child(seat, tabcontainer); - list_t *siblings = container_get_siblings(cont); - int desired = list_find(siblings, active->sway_container) + - round(scroll_factor * event->delta_discrete); - if (desired < 0) { - desired = 0; - } else if (desired >= siblings->length) { - desired = siblings->length - 1; - } - struct sway_node *old_focus = seat_get_focus(seat); - struct sway_container *new_sibling_con = siblings->items[desired]; - struct sway_node *new_sibling = &new_sibling_con->node; - struct sway_node *new_focus = - seat_get_focus_inactive(seat, new_sibling); - if (node_has_ancestor(old_focus, tabcontainer)) { - seat_set_focus(seat, new_focus); - } else { - // Scrolling when focus is not in the tabbed container at all - seat_set_raw_focus(seat, new_sibling); - seat_set_raw_focus(seat, new_focus); - seat_set_raw_focus(seat, old_focus); - } - handled = true; - } - } - - // Handle mouse bindings - x11 mouse buttons 4-7 - release event - binding = get_active_mouse_binding(cursor, - config->current_mode->mouse_bindings, modifiers, true, - on_titlebar, on_border, on_contents, on_workspace, dev_id); - state_erase_button(cursor, button); - if (binding) { - seat_execute_command(seat, binding); - handled = true; - } - free(dev_id); - - if (!handled) { - wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, - event->orientation, scroll_factor * event->delta, - round(scroll_factor * event->delta_discrete), event->source); - } + seatop_axis(cursor->seat, event); } static void handle_cursor_axis(struct wl_listener *listener, void *data) { @@ -1054,8 +456,16 @@ static void handle_tool_axis(struct wl_listener *listener, void *data) { apply_mapping_from_region(event->device, ic->mapped_from_region, &x, &y); } - cursor_motion_absolute(cursor, event->time_msec, event->device, x, y); + double lx, ly; + wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, + x, y, &lx, &ly); + + double dx = lx - cursor->cursor->x; + double dy = ly - cursor->cursor->y; + + cursor_motion(cursor, event->time_msec, event->device, dx, dy, dx, dy); wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); + transaction_commit_dirty(); } static void handle_tool_tip(struct wl_listener *listener, void *data) { @@ -1142,7 +552,7 @@ static void handle_request_set_cursor(struct wl_listener *listener, void *data) { struct sway_cursor *cursor = wl_container_of(listener, cursor, request_set_cursor); - if (seat_doing_seatop(cursor->seat)) { + if (!seatop_allows_set_cursor(cursor->seat)) { return; } struct wlr_seat_pointer_request_set_cursor_event *event = data; @@ -1257,7 +667,7 @@ struct sway_cursor *sway_cursor_create(struct sway_seat *seat) { // input events wl_signal_add(&wlr_cursor->events.motion, &cursor->motion); - cursor->motion.notify = handle_cursor_motion; + cursor->motion.notify = handle_cursor_motion_relative; wl_signal_add(&wlr_cursor->events.motion_absolute, &cursor->motion_absolute); diff --git a/sway/input/seat.c b/sway/input/seat.c index 2c9a85c49..2d3552753 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -365,7 +365,7 @@ static void handle_start_drag(struct wl_listener *listener, void *data) { wl_list_insert(&root->drag_icons, &icon->link); drag_icon_update_position(icon); - seatop_abort(seat); + seatop_begin_default(seat); } static void handle_request_set_selection(struct wl_listener *listener, @@ -461,6 +461,8 @@ struct sway_seat *seat_create(const char *seat_name) { wl_list_insert(&server.input->seats, &seat->link); + seatop_begin_default(seat); + return seat; } @@ -1175,7 +1177,6 @@ struct seat_config *seat_get_config_by_name(const char *name) { void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec, uint32_t button, enum wlr_button_state state) { - seat->last_button = button; seat->last_button_serial = wlr_seat_pointer_notify_button(seat->wlr_seat, time_msec, button, state); } @@ -1206,12 +1207,8 @@ void seat_consider_warp_to_focus(struct sway_seat *seat) { } } -bool seat_doing_seatop(struct sway_seat *seat) { - return seat->seatop_impl != NULL; -} - void seatop_unref(struct sway_seat *seat, struct sway_container *con) { - if (seat->seatop_impl && seat->seatop_impl->unref) { + if (seat->seatop_impl->unref) { seat->seatop_impl->unref(seat, con); } } @@ -1219,29 +1216,33 @@ void seatop_unref(struct sway_seat *seat, struct sway_container *con) { void seatop_button(struct sway_seat *seat, uint32_t time_msec, struct wlr_input_device *device, uint32_t button, enum wlr_button_state state) { - if (seat->seatop_impl && seat->seatop_impl->button) { + if (seat->seatop_impl->button) { seat->seatop_impl->button(seat, time_msec, device, button, state); } } -void seatop_motion(struct sway_seat *seat, uint32_t time_msec) { - if (seat->seatop_impl && seat->seatop_impl->motion) { - seat->seatop_impl->motion(seat, time_msec); +void seatop_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { + if (seat->seatop_impl->motion) { + seat->seatop_impl->motion(seat, time_msec, dx, dy); } } -void seatop_finish(struct sway_seat *seat, uint32_t time_msec) { - if (seat->seatop_impl && seat->seatop_impl->finish) { - seat->seatop_impl->finish(seat, time_msec); +void seatop_axis(struct sway_seat *seat, struct wlr_event_pointer_axis *event) { + if (seat->seatop_impl->axis) { + seat->seatop_impl->axis(seat, event); } - free(seat->seatop_data); - seat->seatop_data = NULL; - seat->seatop_impl = NULL; } -void seatop_abort(struct sway_seat *seat) { - if (seat->seatop_impl && seat->seatop_impl->abort) { - seat->seatop_impl->abort(seat); +void seatop_rebase(struct sway_seat *seat, uint32_t time_msec) { + if (seat->seatop_impl->rebase) { + seat->seatop_impl->rebase(seat, time_msec); + } +} + +void seatop_end(struct sway_seat *seat) { + if (seat->seatop_impl && seat->seatop_impl->end) { + seat->seatop_impl->end(seat); } free(seat->seatop_data); seat->seatop_data = NULL; @@ -1250,7 +1251,11 @@ void seatop_abort(struct sway_seat *seat) { void seatop_render(struct sway_seat *seat, struct sway_output *output, pixman_region32_t *damage) { - if (seat->seatop_impl && seat->seatop_impl->render) { + if (seat->seatop_impl->render) { seat->seatop_impl->render(seat, output, damage); } } + +bool seatop_allows_set_cursor(struct sway_seat *seat) { + return seat->seatop_impl->allow_set_cursor; +} diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c new file mode 100644 index 000000000..fc785cda4 --- /dev/null +++ b/sway/input/seatop_default.c @@ -0,0 +1,629 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include "sway/input/cursor.h" +#include "sway/input/seat.h" +#include "sway/tree/view.h" +#include "log.h" + +struct seatop_default_event { + struct sway_node *previous_node; + uint32_t pressed_buttons[SWAY_CURSOR_PRESSED_BUTTONS_CAP]; + size_t pressed_button_count; +}; + +/*-----------------------------------------\ + * Functions shared by multiple callbacks / + *---------------------------------------*/ + +/** + * Determine if the edge of the given container is on the edge of the + * workspace/output. + */ +static bool edge_is_external(struct sway_container *cont, enum wlr_edges edge) { + enum sway_container_layout layout = L_NONE; + switch (edge) { + case WLR_EDGE_TOP: + case WLR_EDGE_BOTTOM: + layout = L_VERT; + break; + case WLR_EDGE_LEFT: + case WLR_EDGE_RIGHT: + layout = L_HORIZ; + break; + case WLR_EDGE_NONE: + sway_assert(false, "Never reached"); + return false; + } + + // Iterate the parents until we find one with the layout we want, + // then check if the child has siblings between it and the edge. + while (cont) { + if (container_parent_layout(cont) == layout) { + list_t *siblings = container_get_siblings(cont); + int index = list_find(siblings, cont); + if (index > 0 && (edge == WLR_EDGE_LEFT || edge == WLR_EDGE_TOP)) { + return false; + } + if (index < siblings->length - 1 && + (edge == WLR_EDGE_RIGHT || edge == WLR_EDGE_BOTTOM)) { + return false; + } + } + cont = cont->parent; + } + return true; +} + +static enum wlr_edges find_edge(struct sway_container *cont, + struct sway_cursor *cursor) { + if (!cont->view) { + return WLR_EDGE_NONE; + } + if (cont->border == B_NONE || !cont->border_thickness || + cont->border == B_CSD) { + return WLR_EDGE_NONE; + } + + enum wlr_edges edge = 0; + if (cursor->cursor->x < cont->x + cont->border_thickness) { + edge |= WLR_EDGE_LEFT; + } + if (cursor->cursor->y < cont->y + cont->border_thickness) { + edge |= WLR_EDGE_TOP; + } + if (cursor->cursor->x >= cont->x + cont->width - cont->border_thickness) { + edge |= WLR_EDGE_RIGHT; + } + if (cursor->cursor->y >= cont->y + cont->height - cont->border_thickness) { + edge |= WLR_EDGE_BOTTOM; + } + + return edge; +} + +/** + * If the cursor is over a _resizable_ edge, return the edge. + * Edges that can't be resized are edges of the workspace. + */ +static enum wlr_edges find_resize_edge(struct sway_container *cont, + struct sway_cursor *cursor) { + enum wlr_edges edge = find_edge(cont, cursor); + if (edge && !container_is_floating(cont) && edge_is_external(cont, edge)) { + return WLR_EDGE_NONE; + } + return edge; +} + +/** + * Return the mouse binding which matches modifier, click location, release, + * and pressed button state, otherwise return null. + */ +static struct sway_binding* get_active_mouse_binding( + struct seatop_default_event *e, list_t *bindings, uint32_t modifiers, + bool release, bool on_titlebar, bool on_border, bool on_content, + bool on_workspace, const char *identifier) { + uint32_t click_region = + ((on_titlebar || on_workspace) ? BINDING_TITLEBAR : 0) | + ((on_border || on_workspace) ? BINDING_BORDER : 0) | + ((on_content || on_workspace) ? BINDING_CONTENTS : 0); + + struct sway_binding *current = NULL; + for (int i = 0; i < bindings->length; ++i) { + struct sway_binding *binding = bindings->items[i]; + if (modifiers ^ binding->modifiers || + e->pressed_button_count != (size_t)binding->keys->length || + release != (binding->flags & BINDING_RELEASE) || + !(click_region & binding->flags) || + (on_workspace && + (click_region & binding->flags) != click_region) || + (strcmp(binding->input, identifier) != 0 && + strcmp(binding->input, "*") != 0)) { + continue; + } + + bool match = true; + for (size_t j = 0; j < e->pressed_button_count; j++) { + uint32_t key = *(uint32_t *)binding->keys->items[j]; + if (key != e->pressed_buttons[j]) { + match = false; + break; + } + } + if (!match) { + continue; + } + + if (!current || strcmp(current->input, "*") == 0) { + current = binding; + if (strcmp(current->input, identifier) == 0) { + // If a binding is found for the exact input, quit searching + break; + } + } + } + return current; +} + +/** + * Remove a button (and duplicates) from the sorted list of currently pressed + * buttons. + */ +static void state_erase_button(struct seatop_default_event *e, + uint32_t button) { + size_t j = 0; + for (size_t i = 0; i < e->pressed_button_count; ++i) { + if (i > j) { + e->pressed_buttons[j] = e->pressed_buttons[i]; + } + if (e->pressed_buttons[i] != button) { + ++j; + } + } + while (e->pressed_button_count > j) { + --e->pressed_button_count; + e->pressed_buttons[e->pressed_button_count] = 0; + } +} + +/** + * Add a button to the sorted list of currently pressed buttons, if there + * is space. + */ +static void state_add_button(struct seatop_default_event *e, uint32_t button) { + if (e->pressed_button_count >= SWAY_CURSOR_PRESSED_BUTTONS_CAP) { + return; + } + size_t i = 0; + while (i < e->pressed_button_count && e->pressed_buttons[i] < button) { + ++i; + } + size_t j = e->pressed_button_count; + while (j > i) { + e->pressed_buttons[j] = e->pressed_buttons[j - 1]; + --j; + } + e->pressed_buttons[i] = button; + e->pressed_button_count++; +} + +static void cursor_do_rebase(struct sway_cursor *cursor, uint32_t time_msec, + struct sway_node *node, struct wlr_surface *surface, + double sx, double sy) { + // Handle cursor image + if (surface) { + // Reset cursor if switching between clients + struct wl_client *client = wl_resource_get_client(surface->resource); + if (client != cursor->image_client) { + cursor_set_image(cursor, "left_ptr", client); + } + } else if (node && node->type == N_CONTAINER) { + // Try a node's resize edge + enum wlr_edges edge = find_resize_edge(node->sway_container, cursor); + if (edge == WLR_EDGE_NONE) { + cursor_set_image(cursor, "left_ptr", NULL); + } else if (container_is_floating(node->sway_container)) { + cursor_set_image(cursor, wlr_xcursor_get_resize_name(edge), NULL); + } else { + if (edge & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) { + cursor_set_image(cursor, "col-resize", NULL); + } else { + cursor_set_image(cursor, "row-resize", NULL); + } + } + } else { + cursor_set_image(cursor, "left_ptr", NULL); + } + + // Send pointer enter/leave + struct wlr_seat *wlr_seat = cursor->seat->wlr_seat; + if (surface) { + if (seat_is_input_allowed(cursor->seat, surface)) { + wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy); + wlr_seat_pointer_notify_motion(wlr_seat, time_msec, sx, sy); + } + } else { + wlr_seat_pointer_clear_focus(wlr_seat); + } +} + +/*----------------------------------\ + * Functions used by handle_button / + *--------------------------------*/ + +static void handle_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + struct seatop_default_event *e = seat->seatop_data; + struct sway_cursor *cursor = seat->cursor; + + // Determine what's under the cursor + struct wlr_surface *surface = NULL; + double sx, sy; + struct sway_node *node = node_at_coords(seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + struct sway_container *cont = node && node->type == N_CONTAINER ? + node->sway_container : NULL; + bool is_floating = cont && container_is_floating(cont); + bool is_floating_or_child = cont && container_is_floating_or_child(cont); + bool is_fullscreen_or_child = cont && container_is_fullscreen_or_child(cont); + enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; + enum wlr_edges resize_edge = edge ? + find_resize_edge(cont, cursor) : WLR_EDGE_NONE; + bool on_border = edge != WLR_EDGE_NONE; + bool on_contents = cont && !on_border && surface; + bool on_workspace = node && node->type == N_WORKSPACE; + bool on_titlebar = cont && !on_border && !surface; + + // Handle mouse bindings + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + + char *device_identifier = device ? input_device_get_identifier(device) + : strdup("*"); + struct sway_binding *binding = NULL; + if (state == WLR_BUTTON_PRESSED) { + state_add_button(e, button); + binding = get_active_mouse_binding(e, + config->current_mode->mouse_bindings, modifiers, false, + on_titlebar, on_border, on_contents, on_workspace, + device_identifier); + } else { + binding = get_active_mouse_binding(e, + config->current_mode->mouse_bindings, modifiers, true, + on_titlebar, on_border, on_contents, on_workspace, + device_identifier); + state_erase_button(e, button); + } + free(device_identifier); + if (binding) { + seat_execute_command(seat, binding); + return; + } + + // Handle clicking an empty workspace + if (node && node->type == N_WORKSPACE) { + seat_set_focus(seat, node); + return; + } + + // Handle clicking a layer surface + if (surface && wlr_surface_is_layer_surface(surface)) { + struct wlr_layer_surface_v1 *layer = + wlr_layer_surface_v1_from_wlr_surface(surface); + if (layer->current.keyboard_interactive) { + seat_set_focus_layer(seat, layer); + } + seat_pointer_notify_button(seat, time_msec, button, state); + return; + } + + // Handle tiling resize via border + if (cont && resize_edge && button == BTN_LEFT && + state == WLR_BUTTON_PRESSED && !is_floating) { + seat_set_focus_container(seat, cont); + seatop_begin_resize_tiling(seat, cont, edge); + return; + } + + // Handle tiling resize via mod + bool mod_pressed = keyboard && + (wlr_keyboard_get_modifiers(keyboard) & config->floating_mod); + if (cont && !is_floating_or_child && mod_pressed && + state == WLR_BUTTON_PRESSED) { + uint32_t btn_resize = config->floating_mod_inverse ? + BTN_LEFT : BTN_RIGHT; + if (button == btn_resize) { + edge = 0; + edge |= cursor->cursor->x > cont->x + cont->width / 2 ? + WLR_EDGE_RIGHT : WLR_EDGE_LEFT; + edge |= cursor->cursor->y > cont->y + cont->height / 2 ? + WLR_EDGE_BOTTOM : WLR_EDGE_TOP; + + const char *image = NULL; + if (edge == (WLR_EDGE_LEFT | WLR_EDGE_TOP)) { + image = "nw-resize"; + } else if (edge == (WLR_EDGE_TOP | WLR_EDGE_RIGHT)) { + image = "ne-resize"; + } else if (edge == (WLR_EDGE_RIGHT | WLR_EDGE_BOTTOM)) { + image = "se-resize"; + } else if (edge == (WLR_EDGE_BOTTOM | WLR_EDGE_LEFT)) { + image = "sw-resize"; + } + cursor_set_image(seat->cursor, image, NULL); + seat_set_focus_container(seat, cont); + seatop_begin_resize_tiling(seat, cont, edge); + return; + } + } + + // Handle beginning floating move + if (cont && is_floating_or_child && !is_fullscreen_or_child && + state == WLR_BUTTON_PRESSED) { + uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; + if (button == btn_move && state == WLR_BUTTON_PRESSED && + (mod_pressed || on_titlebar)) { + while (cont->parent) { + cont = cont->parent; + } + seat_set_focus_container(seat, cont); + seatop_begin_move_floating(seat, cont); + return; + } + } + + // Handle beginning floating resize + if (cont && is_floating_or_child && !is_fullscreen_or_child && + state == WLR_BUTTON_PRESSED) { + // Via border + if (button == BTN_LEFT && resize_edge != WLR_EDGE_NONE) { + seatop_begin_resize_floating(seat, cont, resize_edge); + return; + } + + // Via mod+click + uint32_t btn_resize = config->floating_mod_inverse ? + BTN_LEFT : BTN_RIGHT; + if (mod_pressed && button == btn_resize) { + struct sway_container *floater = cont; + while (floater->parent) { + floater = floater->parent; + } + edge = 0; + edge |= cursor->cursor->x > floater->x + floater->width / 2 ? + WLR_EDGE_RIGHT : WLR_EDGE_LEFT; + edge |= cursor->cursor->y > floater->y + floater->height / 2 ? + WLR_EDGE_BOTTOM : WLR_EDGE_TOP; + seatop_begin_resize_floating(seat, floater, edge); + return; + } + } + + // Handle moving a tiling container + if (config->tiling_drag && (mod_pressed || on_titlebar) && + state == WLR_BUTTON_PRESSED && !is_floating_or_child && + cont && cont->fullscreen_mode == FULLSCREEN_NONE) { + struct sway_container *focus = seat_get_focused_container(seat); + bool focused = focus == cont || container_has_ancestor(focus, cont); + if (on_titlebar && !focused) { + node = seat_get_focus_inactive(seat, &cont->node); + seat_set_focus(seat, node); + } + + // If moving a container by it's title bar, use a threshold for the drag + if (!mod_pressed && config->tiling_drag_threshold > 0) { + seatop_begin_move_tiling_threshold(seat, cont); + } else { + seatop_begin_move_tiling(seat, cont); + } + return; + } + + // Handle mousedown on a container surface + if (surface && cont && state == WLR_BUTTON_PRESSED) { + seat_set_focus_container(seat, cont); + seatop_begin_down(seat, cont, time_msec, sx, sy); + seat_pointer_notify_button(seat, time_msec, button, WLR_BUTTON_PRESSED); + return; + } + + // Handle clicking a container surface or decorations + if (cont) { + node = seat_get_focus_inactive(seat, &cont->node); + seat_set_focus(seat, node); + seat_pointer_notify_button(seat, time_msec, button, state); + return; + } + + seat_pointer_notify_button(seat, time_msec, button, state); +} + +/*----------------------------------\ + * Functions used by handle_motion / + *--------------------------------*/ + +static void check_focus_follows_mouse(struct sway_seat *seat, + struct seatop_default_event *e, struct sway_node *hovered_node) { + struct sway_node *focus = seat_get_focus(seat); + + // If a workspace node is hovered (eg. in the gap area), only set focus if + // the workspace is on a different output to the previous focus. + if (focus && hovered_node->type == N_WORKSPACE) { + struct sway_output *focused_output = node_get_output(focus); + struct sway_output *hovered_output = node_get_output(hovered_node); + if (hovered_output != focused_output) { + seat_set_focus(seat, seat_get_focus_inactive(seat, hovered_node)); + } + return; + } + + if (node_is_view(hovered_node)) { + if (hovered_node != e->previous_node || + config->focus_follows_mouse == FOLLOWS_ALWAYS) { + seat_set_focus(seat, hovered_node); + } else { + // Focusing a tab which contains a split child + struct sway_node *next_focus = + seat_get_focus_inactive(seat, &root->node); + if (next_focus && node_is_view(next_focus) && + view_is_visible(next_focus->sway_container->view)) { + seat_set_focus(seat, next_focus); + } + } + } +} + +static void handle_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { + struct seatop_default_event *e = seat->seatop_data; + struct sway_cursor *cursor = seat->cursor; + + struct wlr_surface *surface = NULL; + double sx, sy; + struct sway_node *node = node_at_coords(seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + + if (node && config->focus_follows_mouse != FOLLOWS_NO) { + check_focus_follows_mouse(seat, e, node); + } + + cursor_do_rebase(cursor, time_msec, node, surface, sx, sy); + + struct sway_drag_icon *drag_icon; + wl_list_for_each(drag_icon, &root->drag_icons, link) { + if (drag_icon->seat == seat) { + drag_icon_update_position(drag_icon); + } + } + + e->previous_node = node; +} + +/*--------------------------------\ + * Functions used by handle_axis / + *------------------------------*/ + +static uint32_t wl_axis_to_button(struct wlr_event_pointer_axis *event) { + switch (event->orientation) { + case WLR_AXIS_ORIENTATION_VERTICAL: + return event->delta < 0 ? SWAY_SCROLL_UP : SWAY_SCROLL_DOWN; + case WLR_AXIS_ORIENTATION_HORIZONTAL: + return event->delta < 0 ? SWAY_SCROLL_LEFT : SWAY_SCROLL_RIGHT; + default: + sway_log(SWAY_DEBUG, "Unknown axis orientation"); + return 0; + } +} + +static void handle_axis(struct sway_seat *seat, + struct wlr_event_pointer_axis *event) { + struct sway_input_device *input_device = + event->device ? event->device->data : NULL; + struct input_config *ic = + input_device ? input_device_get_config(input_device) : NULL; + struct sway_cursor *cursor = seat->cursor; + struct seatop_default_event *e = seat->seatop_data; + + // Determine what's under the cursor + struct wlr_surface *surface = NULL; + double sx, sy; + struct sway_node *node = node_at_coords(seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + struct sway_container *cont = node && node->type == N_CONTAINER ? + node->sway_container : NULL; + enum wlr_edges edge = cont ? find_edge(cont, cursor) : WLR_EDGE_NONE; + bool on_border = edge != WLR_EDGE_NONE; + bool on_titlebar = cont && !on_border && !surface; + bool on_titlebar_border = cont && on_border && + cursor->cursor->y < cont->content_y; + bool on_contents = cont && !on_border && surface; + bool on_workspace = node && node->type == N_WORKSPACE; + float scroll_factor = + (ic == NULL || ic->scroll_factor == FLT_MIN) ? 1.0f : ic->scroll_factor; + + bool handled = false; + + // Gather information needed for mouse bindings + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); + uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + struct wlr_input_device *device = + input_device ? input_device->wlr_device : NULL; + char *dev_id = device ? input_device_get_identifier(device) : strdup("*"); + uint32_t button = wl_axis_to_button(event); + + // Handle mouse bindings - x11 mouse buttons 4-7 - press event + struct sway_binding *binding = NULL; + state_add_button(e, button); + binding = get_active_mouse_binding(e, config->current_mode->mouse_bindings, + modifiers, false, on_titlebar, on_border, on_contents, on_workspace, + dev_id); + if (binding) { + seat_execute_command(seat, binding); + handled = true; + } + + // Scrolling on a tabbed or stacked title bar (handled as press event) + if (!handled && (on_titlebar || on_titlebar_border)) { + enum sway_container_layout layout = container_parent_layout(cont); + if (layout == L_TABBED || layout == L_STACKED) { + struct sway_node *tabcontainer = node_get_parent(node); + struct sway_node *active = + seat_get_active_tiling_child(seat, tabcontainer); + list_t *siblings = container_get_siblings(cont); + int desired = list_find(siblings, active->sway_container) + + round(scroll_factor * event->delta_discrete); + if (desired < 0) { + desired = 0; + } else if (desired >= siblings->length) { + desired = siblings->length - 1; + } + struct sway_node *old_focus = seat_get_focus(seat); + struct sway_container *new_sibling_con = siblings->items[desired]; + struct sway_node *new_sibling = &new_sibling_con->node; + struct sway_node *new_focus = + seat_get_focus_inactive(seat, new_sibling); + if (node_has_ancestor(old_focus, tabcontainer)) { + seat_set_focus(seat, new_focus); + } else { + // Scrolling when focus is not in the tabbed container at all + seat_set_raw_focus(seat, new_sibling); + seat_set_raw_focus(seat, new_focus); + seat_set_raw_focus(seat, old_focus); + } + handled = true; + } + } + + // Handle mouse bindings - x11 mouse buttons 4-7 - release event + binding = get_active_mouse_binding(e, config->current_mode->mouse_bindings, + modifiers, true, on_titlebar, on_border, on_contents, on_workspace, + dev_id); + state_erase_button(e, button); + if (binding) { + seat_execute_command(seat, binding); + handled = true; + } + free(dev_id); + + if (!handled) { + wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, + event->orientation, scroll_factor * event->delta, + round(scroll_factor * event->delta_discrete), event->source); + } +} + +/*----------------------------------\ + * Functions used by handle_rebase / + *--------------------------------*/ + +static void handle_rebase(struct sway_seat *seat, uint32_t time_msec) { + struct seatop_default_event *e = seat->seatop_data; + struct sway_cursor *cursor = seat->cursor; + struct wlr_surface *surface = NULL; + double sx = 0.0, sy = 0.0; + e->previous_node = node_at_coords(seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + cursor_do_rebase(cursor, time_msec, e->previous_node, surface, sx, sy); +} + +static const struct sway_seatop_impl seatop_impl = { + .button = handle_button, + .motion = handle_motion, + .axis = handle_axis, + .rebase = handle_rebase, + .allow_set_cursor = true, +}; + +void seatop_begin_default(struct sway_seat *seat) { + seatop_end(seat); + + struct seatop_default_event *e = + calloc(1, sizeof(struct seatop_default_event)); + sway_assert(e, "Unable to allocate seatop_default_event"); + seat->seatop_impl = &seatop_impl; + seat->seatop_data = e; + + seatop_rebase(seat, 0); +} diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index fb2cf1d07..95ea7cbb2 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -3,21 +3,26 @@ #include "sway/input/cursor.h" #include "sway/input/seat.h" #include "sway/tree/view.h" +#include "log.h" struct seatop_down_event { struct sway_container *con; double ref_lx, ref_ly; // cursor's x/y at start of op double ref_con_lx, ref_con_ly; // container's x/y at start of op - bool moved; }; static void handle_button(struct sway_seat *seat, uint32_t time_msec, struct wlr_input_device *device, uint32_t button, enum wlr_button_state state) { seat_pointer_notify_button(seat, time_msec, button, state); + + if (seat->cursor->pressed_button_count == 0) { + seatop_begin_default(seat); + } } -static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { +static void handle_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { struct seatop_down_event *e = seat->seatop_data; struct sway_container *con = e->con; if (seat_is_input_allowed(seat, con->view->surface)) { @@ -27,49 +32,25 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { double sy = e->ref_con_ly + moved_y; wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy); } - e->moved = true; -} - -static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { - struct seatop_down_event *e = seat->seatop_data; - struct sway_cursor *cursor = seat->cursor; - // Set the cursor's previous coords to the x/y at the start of the - // operation, so the container change will be detected if using - // focus_follows_mouse and the cursor moved off the original container - // during the operation. - cursor->previous.x = e->ref_lx; - cursor->previous.y = e->ref_ly; - if (e->moved) { - struct wlr_surface *surface = NULL; - double sx, sy; - struct sway_node *node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); - cursor_send_pointer_motion(cursor, 0, node, surface, sx, sy); - } -} - -static void handle_abort(struct sway_seat *seat) { - cursor_set_image(seat->cursor, "left_ptr", NULL); } static void handle_unref(struct sway_seat *seat, struct sway_container *con) { struct seatop_down_event *e = seat->seatop_data; if (e->con == con) { - seatop_abort(seat); + seatop_begin_default(seat); } } static const struct sway_seatop_impl seatop_impl = { .button = handle_button, .motion = handle_motion, - .finish = handle_finish, - .abort = handle_abort, .unref = handle_unref, + .allow_set_cursor = true, }; void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, - uint32_t time_msec, uint32_t button, int sx, int sy) { - seatop_abort(seat); + uint32_t time_msec, int sx, int sy) { + seatop_end(seat); struct seatop_down_event *e = calloc(1, sizeof(struct seatop_down_event)); @@ -81,11 +62,9 @@ void seatop_begin_down(struct sway_seat *seat, struct sway_container *con, e->ref_ly = seat->cursor->cursor->y; e->ref_con_lx = sx; e->ref_con_ly = sy; - e->moved = false; seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - seat->seatop_button = button; container_raise_floating(con); } diff --git a/sway/input/seatop_move_floating.c b/sway/input/seatop_move_floating.c index 8a48a9681..73e489642 100644 --- a/sway/input/seatop_move_floating.c +++ b/sway/input/seatop_move_floating.c @@ -8,45 +8,44 @@ struct seatop_move_floating_event { struct sway_container *con; }; -static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { - struct seatop_move_floating_event *e = seat->seatop_data; - desktop_damage_whole_container(e->con); - container_floating_translate(e->con, - seat->cursor->cursor->x - seat->cursor->previous.x, - seat->cursor->cursor->y - seat->cursor->previous.y); - desktop_damage_whole_container(e->con); +static void handle_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + if (seat->cursor->pressed_button_count == 0) { + struct seatop_move_floating_event *e = seat->seatop_data; + + // We "move" the container to its own location + // so it discovers its output again. + container_floating_move_to(e->con, e->con->x, e->con->y); + + seatop_begin_default(seat); + } } -static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { +static void handle_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { struct seatop_move_floating_event *e = seat->seatop_data; - - // We "move" the container to its own location - // so it discovers its output again. - container_floating_move_to(e->con, e->con->x, e->con->y); - cursor_set_image(seat->cursor, "left_ptr", NULL); -} - -static void handle_abort(struct sway_seat *seat) { - cursor_set_image(seat->cursor, "left_ptr", NULL); + desktop_damage_whole_container(e->con); + container_floating_translate(e->con, dx, dy); + desktop_damage_whole_container(e->con); } static void handle_unref(struct sway_seat *seat, struct sway_container *con) { struct seatop_move_floating_event *e = seat->seatop_data; if (e->con == con) { - seatop_abort(seat); + seatop_begin_default(seat); } } static const struct sway_seatop_impl seatop_impl = { + .button = handle_button, .motion = handle_motion, - .finish = handle_finish, - .abort = handle_abort, .unref = handle_unref, }; void seatop_begin_move_floating(struct sway_seat *seat, - struct sway_container *con, uint32_t button) { - seatop_abort(seat); + struct sway_container *con) { + seatop_end(seat); struct seatop_move_floating_event *e = calloc(1, sizeof(struct seatop_move_floating_event)); @@ -57,7 +56,6 @@ void seatop_begin_move_floating(struct sway_seat *seat, seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - seat->seatop_button = button; container_raise_floating(con); diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 2904792bd..0a2480919 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -206,7 +206,8 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { desktop_damage_box(&e->drop_box); } -static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { +static void handle_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { struct seatop_move_tiling_event *e = seat->seatop_data; if (e->threshold_reached) { handle_motion_postthreshold(seat); @@ -215,10 +216,6 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { } } -static void handle_abort(struct sway_seat *seat) { - cursor_set_image(seat->cursor, "left_ptr", NULL); -} - static bool is_parallel(enum sway_container_layout layout, enum wlr_edges edge) { bool layout_is_horiz = layout == L_HORIZ || layout == L_TABBED; @@ -226,11 +223,17 @@ static bool is_parallel(enum sway_container_layout layout, return layout_is_horiz == edge_is_horiz; } -static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { +static void handle_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + if (seat->cursor->pressed_button_count != 0) { + return; + } + struct seatop_move_tiling_event *e = seat->seatop_data; if (!e->target_node) { - handle_abort(seat); + seatop_begin_default(seat); return; } @@ -287,7 +290,7 @@ static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { arrange_workspace(new_ws); } - cursor_set_image(seat->cursor, "left_ptr", NULL); + seatop_begin_default(seat); } static void handle_unref(struct sway_seat *seat, struct sway_container *con) { @@ -296,21 +299,20 @@ static void handle_unref(struct sway_seat *seat, struct sway_container *con) { e->target_node = NULL; } if (e->con == con) { // The container being moved - seatop_abort(seat); + seatop_begin_default(seat); } } static const struct sway_seatop_impl seatop_impl = { + .button = handle_button, .motion = handle_motion, - .finish = handle_finish, - .abort = handle_abort, .unref = handle_unref, .render = handle_render, }; void seatop_begin_move_tiling_threshold(struct sway_seat *seat, - struct sway_container *con, uint32_t button) { - seatop_abort(seat); + struct sway_container *con) { + seatop_end(seat); struct seatop_move_tiling_event *e = calloc(1, sizeof(struct seatop_move_tiling_event)); @@ -323,14 +325,13 @@ void seatop_begin_move_tiling_threshold(struct sway_seat *seat, seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - seat->seatop_button = button; container_raise_floating(con); } void seatop_begin_move_tiling(struct sway_seat *seat, - struct sway_container *con, uint32_t button) { - seatop_begin_move_tiling_threshold(seat, con, button); + struct sway_container *con) { + seatop_begin_move_tiling_threshold(seat, con); struct seatop_move_tiling_event *e = seat->seatop_data; if (e) { e->threshold_reached = true; diff --git a/sway/input/seatop_resize_floating.c b/sway/input/seatop_resize_floating.c index 18c6db73b..b6950bbf3 100644 --- a/sway/input/seatop_resize_floating.c +++ b/sway/input/seatop_resize_floating.c @@ -17,7 +17,16 @@ struct seatop_resize_floating_event { double ref_con_lx, ref_con_ly; // container's x/y at start of op }; -static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { +static void handle_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + if (seat->cursor->pressed_button_count == 0) { + seatop_begin_default(seat); + } +} + +static void handle_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { struct seatop_resize_floating_event *e = seat->seatop_data; struct sway_container *con = e->con; enum wlr_edges edge = e->edge; @@ -107,31 +116,22 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { arrange_container(con); } -static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { - cursor_set_image(seat->cursor, "left_ptr", NULL); -} - -static void handle_abort(struct sway_seat *seat) { - cursor_set_image(seat->cursor, "left_ptr", NULL); -} - static void handle_unref(struct sway_seat *seat, struct sway_container *con) { struct seatop_resize_floating_event *e = seat->seatop_data; if (e->con == con) { - seatop_abort(seat); + seatop_begin_default(seat); } } static const struct sway_seatop_impl seatop_impl = { + .button = handle_button, .motion = handle_motion, - .finish = handle_finish, - .abort = handle_abort, .unref = handle_unref, }; void seatop_begin_resize_floating(struct sway_seat *seat, - struct sway_container *con, uint32_t button, enum wlr_edges edge) { - seatop_abort(seat); + struct sway_container *con, enum wlr_edges edge) { + seatop_end(seat); struct seatop_resize_floating_event *e = calloc(1, sizeof(struct seatop_resize_floating_event)); @@ -154,7 +154,6 @@ void seatop_begin_resize_floating(struct sway_seat *seat, seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - seat->seatop_button = button; container_raise_floating(con); diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c index db32065c7..6b705823a 100644 --- a/sway/input/seatop_resize_tiling.c +++ b/sway/input/seatop_resize_tiling.c @@ -19,7 +19,16 @@ struct seatop_resize_tiling_event { double v_con_orig_height; // height of the vertical ancestor at start }; -static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { +static void handle_button(struct sway_seat *seat, uint32_t time_msec, + struct wlr_input_device *device, uint32_t button, + enum wlr_button_state state) { + if (seat->cursor->pressed_button_count == 0) { + seatop_begin_default(seat); + } +} + +static void handle_motion(struct sway_seat *seat, uint32_t time_msec, + double dx, double dy) { struct seatop_resize_tiling_event *e = seat->seatop_data; int amount_x = 0; int amount_y = 0; @@ -49,31 +58,22 @@ static void handle_motion(struct sway_seat *seat, uint32_t time_msec) { } } -static void handle_finish(struct sway_seat *seat, uint32_t time_msec) { - cursor_set_image(seat->cursor, "left_ptr", NULL); -} - -static void handle_abort(struct sway_seat *seat) { - cursor_set_image(seat->cursor, "left_ptr", NULL); -} - static void handle_unref(struct sway_seat *seat, struct sway_container *con) { struct seatop_resize_tiling_event *e = seat->seatop_data; if (e->con == con) { - seatop_abort(seat); + seatop_begin_default(seat); } } static const struct sway_seatop_impl seatop_impl = { + .button = handle_button, .motion = handle_motion, - .finish = handle_finish, - .abort = handle_abort, .unref = handle_unref, }; void seatop_begin_resize_tiling(struct sway_seat *seat, - struct sway_container *con, uint32_t button, enum wlr_edges edge) { - seatop_abort(seat); + struct sway_container *con, enum wlr_edges edge) { + seatop_end(seat); struct seatop_resize_tiling_event *e = calloc(1, sizeof(struct seatop_resize_tiling_event)); @@ -105,5 +105,4 @@ void seatop_begin_resize_tiling(struct sway_seat *seat, seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - seat->seatop_button = button; } diff --git a/sway/meson.build b/sway/meson.build index 293a4ed20..cb1f8e258 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -23,6 +23,7 @@ sway_sources = files( 'input/input-manager.c', 'input/seat.c', + 'input/seatop_default.c', 'input/seatop_down.c', 'input/seatop_move_floating.c', 'input/seatop_move_tiling.c', From 0327c999d7f69de6356b3b30c19b131d029f6e95 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 16 Mar 2019 22:45:06 -0400 Subject: [PATCH 0208/2054] config/output: handle wildcard in get_output_config In #3916, I overlooked that `get_output_config` does not handle wildcards unless the config is reloading, which is a remnant of older iterations of the output config handling that went unnoticed due to `output_find_config` handling it. With the current version of the output config handling, having `get_output_config` handle wildcard configs is actually preferable. This fixes having only a wildcard output config in the config file or when connecting/enabling a new output with only a wildcard config existing. --- sway/config/output.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index 5656e2c10..44aae03aa 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -493,19 +493,20 @@ static struct output_config *get_output_config(char *identifier, free(result->name); result->name = strdup(identifier); merge_output_config(result, oc_id); - } else if (config->reloading) { - // Neither config exists, but we need to reset the output so create a - // default config for the output and if a wildcard config exists, merge - // that on top - free(result->name); - result->name = strdup("*"); + } else { i = list_seq_find(config->output_configs, output_name_cmp, "*"); if (i >= 0) { + // No name or identifier config, but there is a wildcard config + free(result->name); + result->name = strdup("*"); merge_output_config(result, config->output_configs->items[i]); + } else if (!config->reloading) { + // No name, identifier, or wildcard config. Since we are not + // reloading with defaults, the output config will be empty, so + // just return NULL + free_output_config(result); + result = NULL; } - } else { - free_output_config(result); - result = NULL; } free(id_on_name); From 38bd60c4b3e70cb5584529358162f8b37d43669f Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 18 Mar 2019 21:23:28 +1000 Subject: [PATCH 0209/2054] Document the title_format command --- sway/sway.5.scd | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 4f4522fb5..989717cb1 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -265,6 +265,24 @@ set|plus|minus becomes fullscreen on the same workspace as the first container. In either of those cases, the second container will gain focus. +*title_format* + Sets the format of window titles. The following placeholders may be used: + + %title - The title supplied by the window + %app_id - The wayland app ID (applicable to wayland windows only) + %class - The X11 classname (applicable to xwayland windows only) + %instance - The X11 instance (applicable to xwayland windows only) + %shell - The protocol the window is using (typically xwayland or + xdg_shell) + + This command is typically used with *for_window* criteria. For example: + + for_window [title="."] title_format "%title (%app_id)" + + Note that markup requires pango to be enabled via the *font* command. + + The default format is "%title". + The following commands may be used either in the configuration file or at runtime. @@ -300,7 +318,7 @@ runtime. values in X11 (1=left, 2=middle, 3=right, 4=scroll up, 5=scroll down, 6=scroll left, 7=scroll right, 8=back, 9=forward). For the latter option, you can find the event names using _libinput debug-events_. - + _--whole-window_, _--border_, and _--exclude-titlebar_ are mouse-only options which affect the region in which the mouse bindings can be triggered. By default, mouse bindings are only triggered when over the title bar. With the From e9a476244df7a8886fc6fc6785251198ed76e601 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Mon, 18 Mar 2019 20:52:56 +1000 Subject: [PATCH 0210/2054] Remove debug tree This feature has served its purpose. It's better to use IPC now. --- include/sway/debug.h | 22 ----- include/sway/server.h | 14 ++++ include/sway/tree/root.h | 2 - sway/debug-tree.c | 161 ------------------------------------- sway/desktop/render.c | 7 -- sway/desktop/transaction.c | 6 -- sway/input/seat.c | 4 - sway/main.c | 3 - sway/meson.build | 1 - 9 files changed, 14 insertions(+), 206 deletions(-) delete mode 100644 include/sway/debug.h delete mode 100644 sway/debug-tree.c diff --git a/include/sway/debug.h b/include/sway/debug.h deleted file mode 100644 index 0e9bb0565..000000000 --- a/include/sway/debug.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef SWAY_DEBUG_H -#define SWAY_DEBUG_H -#include - -struct sway_debug { - bool noatomic; // Ignore atomic layout updates - bool render_tree; // Render the tree overlay - bool txn_timings; // Log verbose messages about transactions - bool txn_wait; // Always wait for the timeout before applying - - enum { - DAMAGE_DEFAULT, // Default behaviour - DAMAGE_HIGHLIGHT, // Highlight regions of the screen being damaged - DAMAGE_RERENDER, // Render the full output when any damage occurs - } damage; -}; - -extern struct sway_debug debug; - -void update_debug_tree(void); - -#endif diff --git a/include/sway/server.h b/include/sway/server.h index 5eef7c1a9..39cf4f18b 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -74,6 +74,20 @@ struct sway_server { struct sway_server server; +struct sway_debug { + bool noatomic; // Ignore atomic layout updates + bool txn_timings; // Log verbose messages about transactions + bool txn_wait; // Always wait for the timeout before applying + + enum { + DAMAGE_DEFAULT, // Default behaviour + DAMAGE_HIGHLIGHT, // Highlight regions of the screen being damaged + DAMAGE_RERENDER, // Render the full output when any damage occurs + } damage; +}; + +struct sway_debug debug; + /* Prepares an unprivileged server_init by performing all privileged operations in advance */ bool server_privileged_prepare(struct sway_server *server); bool server_init(struct sway_server *server); diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index 9ff45eb5f..9f6cd3bb6 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -21,8 +21,6 @@ struct sway_root { #endif struct wl_list drag_icons; // sway_drag_icon::link - struct wlr_texture *debug_tree; - // Includes disabled outputs struct wl_list all_outputs; // sway_output::link diff --git a/sway/debug-tree.c b/sway/debug-tree.c deleted file mode 100644 index 0444bb3fd..000000000 --- a/sway/debug-tree.c +++ /dev/null @@ -1,161 +0,0 @@ -#include -#include -#include -#include "config.h" -#include "sway/debug.h" -#include "sway/input/input-manager.h" -#include "sway/input/seat.h" -#include "sway/output.h" -#include "sway/server.h" -#include "sway/tree/container.h" -#include "sway/tree/root.h" -#include "sway/tree/workspace.h" -#include "cairo.h" -#include "config.h" -#include "pango.h" - -struct sway_debug debug; - -static const char *layout_to_str(enum sway_container_layout layout) { - switch (layout) { - case L_HORIZ: - return "L_HORIZ"; - case L_VERT: - return "L_VERT"; - case L_STACKED: - return "L_STACKED"; - case L_TABBED: - return "L_TABBED"; - case L_NONE: - return "L_NONE"; - } - return "L_NONE"; -} - -static char *get_string(struct sway_node *node) { - char *buffer = malloc(512); - switch (node->type) { - case N_ROOT: - snprintf(buffer, 512, "N_ROOT id:%zd %.fx%.f@%.f,%.f", node->id, - root->width, root->height, root->x, root->y); - break; - case N_OUTPUT: - snprintf(buffer, 512, "N_OUTPUT id:%zd '%s' %dx%d@%d,%d", node->id, - node->sway_output->wlr_output->name, - node->sway_output->width, - node->sway_output->height, - node->sway_output->lx, - node->sway_output->ly); - break; - case N_WORKSPACE: - snprintf(buffer, 512, "N_WORKSPACE id:%zd '%s' %s %dx%d@%.f,%.f", - node->id, node->sway_workspace->name, - layout_to_str(node->sway_workspace->layout), - node->sway_workspace->width, node->sway_workspace->height, - node->sway_workspace->x, node->sway_workspace->y); - break; - case N_CONTAINER: - snprintf(buffer, 512, "N_CONTAINER id:%zd '%s' %s %.fx%.f@%.f,%.f", - node->id, node->sway_container->title, - layout_to_str(node->sway_container->layout), - node->sway_container->width, node->sway_container->height, - node->sway_container->x, node->sway_container->y); - break; - } - return buffer; -} - -static list_t *get_children(struct sway_node *node) { - switch (node->type) { - case N_ROOT: - return root->outputs; - case N_OUTPUT: - return node->sway_output->workspaces; - case N_WORKSPACE: - return node->sway_workspace->tiling; - case N_CONTAINER: - return node->sway_container->children; - } - return NULL; -} - -static int draw_node(cairo_t *cairo, struct sway_node *node, - struct sway_node *focus, int x, int y) { - int text_width, text_height; - char *buffer = get_string(node); - get_text_size(cairo, "monospace", &text_width, &text_height, NULL, - 1, false, buffer); - cairo_save(cairo); - cairo_rectangle(cairo, x + 2, y, text_width - 2, text_height); - cairo_set_source_u32(cairo, 0xFFFFFFE0); - cairo_fill(cairo); - int height = text_height; - list_t *children = get_children(node); - if (children) { - for (int i = 0; i < children->length; ++i) { - // This is really dirty - the list contains specific structs but - // we're casting them as nodes. This works because node is the first - // item in each specific struct. This is acceptable because this is - // debug code. - struct sway_node *child = children->items[i]; - if (node_get_parent(child) == node) { - cairo_set_source_u32(cairo, 0x000000FF); - } else { - cairo_set_source_u32(cairo, 0xFF0000FF); - } - height += draw_node(cairo, child, focus, x + 10, y + height); - } - } - cairo_set_source_u32(cairo, 0xFFFFFFE0); - cairo_rectangle(cairo, x, y, 2, height); - cairo_fill(cairo); - cairo_restore(cairo); - cairo_move_to(cairo, x, y); - if (focus == node) { - cairo_set_source_u32(cairo, 0x0000FFFF); - } - pango_printf(cairo, "monospace", 1, false, buffer); - free(buffer); - return height; -} - -void update_debug_tree(void) { - if (!debug.render_tree) { - return; - } - - int width = 640, height = 480; - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; - if (output->width > width) { - width = output->width; - } - if (output->height > height) { - height = output->height; - } - } - cairo_surface_t *surface = - cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); - cairo_t *cairo = cairo_create(surface); - PangoContext *pango = pango_cairo_create_context(cairo); - - struct sway_seat *seat = input_manager_current_seat(); - struct sway_node *focus = seat_get_focus(seat); - - cairo_set_source_u32(cairo, 0x000000FF); - draw_node(cairo, &root->node, focus, 0, 0); - - cairo_surface_flush(surface); - struct wlr_renderer *renderer = wlr_backend_get_renderer(server.backend); - if (root->debug_tree) { - wlr_texture_destroy(root->debug_tree); - } - unsigned char *data = cairo_image_surface_get_data(surface); - int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width); - struct wlr_texture *texture = wlr_texture_from_pixels(renderer, - WL_SHM_FORMAT_ARGB8888, stride, width, height, data); - root->debug_tree = texture; - cairo_surface_destroy(surface); - g_object_unref(pango); - cairo_destroy(cairo); -} diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 9e936bb53..7216e30bb 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -16,7 +16,6 @@ #include "log.h" #include "config.h" #include "sway/config.h" -#include "sway/debug.h" #include "sway/input/input-manager.h" #include "sway/input/seat.h" #include "sway/layers.h" @@ -1075,12 +1074,6 @@ render_overlay: render_drag_icons(output, damage, &root->drag_icons); renderer_end: - if (debug.render_tree) { - wlr_renderer_scissor(renderer, NULL); - wlr_render_texture(renderer, root->debug_tree, - wlr_output->transform_matrix, 0, 40, 1); - } - wlr_renderer_scissor(renderer, NULL); wlr_output_render_software_cursors(wlr_output, damage); wlr_renderer_end(renderer); diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 4098ed22c..e5f0ee3d1 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -5,7 +5,6 @@ #include #include #include "sway/config.h" -#include "sway/debug.h" #include "sway/desktop.h" #include "sway/desktop/idle_inhibit_v1.h" #include "sway/desktop/transaction.h" @@ -465,11 +464,6 @@ static void transaction_commit(struct sway_transaction *transaction) { transaction->num_waiting = 0; } } - - // The debug tree shows the pending/live tree. Here is a good place to - // update it, because we make a transaction every time we change the pending - // tree. - update_debug_tree(); } static void set_instruction_ready( diff --git a/sway/input/seat.c b/sway/input/seat.c index 2d3552753..7fc87ee4a 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -10,7 +10,6 @@ #include #include "config.h" #include "log.h" -#include "sway/debug.h" #include "sway/desktop.h" #include "sway/input/cursor.h" #include "sway/input/input-manager.h" @@ -812,7 +811,6 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { } seat_send_unfocus(last_focus, seat); seat->has_focus = false; - update_debug_tree(); return; } @@ -937,8 +935,6 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { // the workspace needs to be arranged arrange_workspace(new_workspace); } - - update_debug_tree(); } void seat_set_focus_container(struct sway_seat *seat, diff --git a/sway/main.c b/sway/main.c index f3cc1bc8b..ba4e25624 100644 --- a/sway/main.c +++ b/sway/main.c @@ -15,7 +15,6 @@ #include #include "sway/commands.h" #include "sway/config.h" -#include "sway/debug.h" #include "sway/server.h" #include "sway/swaynag.h" #include "sway/tree/root.h" @@ -206,8 +205,6 @@ void enable_debug_flag(const char *flag) { debug.damage = DAMAGE_RERENDER; } else if (strcmp(flag, "noatomic") == 0) { debug.noatomic = true; - } else if (strcmp(flag, "render-tree") == 0) { - debug.render_tree = true; } else if (strcmp(flag, "txn-wait") == 0) { debug.txn_wait = true; } else if (strcmp(flag, "txn-timings") == 0) { diff --git a/sway/meson.build b/sway/meson.build index cb1f8e258..1138bc73f 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -2,7 +2,6 @@ sway_sources = files( 'commands.c', 'config.c', 'criteria.c', - 'debug-tree.c', 'decoration.c', 'ipc-json.c', 'ipc-server.c', From bfa20e65d846f8e8bf7b967b2440d99d82ca9a86 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Tue, 19 Mar 2019 19:41:24 +1000 Subject: [PATCH 0211/2054] Clean up focus follows mouse logic Firstly, this fixes a recent regression where having `focus_follows_mouse yes` and hovering an inactive tab caused it to gain focus. The code was missing a view_is_visible check. The code is handling the logic for both focus_follows_mouse yes and focus_follows_mouse always, where the latter will apply when nudging the mouse after a workspace switch. However, the view_is_visible check didn't apply when using focus_follows_mouse always, so hovering a tab with that configuration would cause is to focus. This was a bug. When adding the view_is_visible check, it now applies to both yes and always. Note that the comment about the split container was wrong. At this point the hovered node cannot be a split container because it passed the node_is_view check. The comment has been removed. Lastly, the else condition is completely removed. This didn't appear to have any practical use. Setting focus to the result of seat_get_focus_inactive is very likely going to be a no op. There is a slim chance that this will break something, and if so I'd like to find out what so it can be properly documented in the code. --- sway/input/seatop_default.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index fc785cda4..9cf58c47f 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -439,18 +439,17 @@ static void check_focus_follows_mouse(struct sway_seat *seat, return; } - if (node_is_view(hovered_node)) { + // This is where we handle the common case. We don't want to focus inactive + // tabs, hence the view_is_visible check. + if (node_is_view(hovered_node) && + view_is_visible(hovered_node->sway_container->view)) { + // e->previous_node is the node which the cursor was over previously. + // If focus_follows_mouse is yes and the cursor got over the view due + // to, say, a workspace switch, we don't want to set the focus. + // But if focus_follows_mouse is "always", we do. if (hovered_node != e->previous_node || config->focus_follows_mouse == FOLLOWS_ALWAYS) { seat_set_focus(seat, hovered_node); - } else { - // Focusing a tab which contains a split child - struct sway_node *next_focus = - seat_get_focus_inactive(seat, &root->node); - if (next_focus && node_is_view(next_focus) && - view_is_visible(next_focus->sway_container->view)) { - seat_set_focus(seat, next_focus); - } } } } From bdb402404cd6d54242b0b1dc2360cfc5679e52f2 Mon Sep 17 00:00:00 2001 From: Ryan Walklin Date: Wed, 20 Mar 2019 14:47:29 +1100 Subject: [PATCH 0212/2054] Support WLR_INPUT_DEVICE_SWITCH in sway This commit adds support for laptop lid and tablet mode switches as provided by evdev/libinput and handled by wlroots. Adds a new bindswitch command with syntax: bindswitch : Where is one of: tablet for WLR_SWITCH_TYPE_TABLET_MODE lid for WLR_SWITCH_TYPE_LID is one of: on for WLR_SWITCH_STATE_ON off for WLR_SWITCH_STATE_OFF toggle for WLR_SWITCH_STATE_TOGGLE (Note that WLR_SWITCH_STATE_TOGGLE doesn't map to libinput and will trigger at both on and off events) --- include/sway/commands.h | 1 + include/sway/config.h | 15 +++++ include/sway/input/seat.h | 1 + include/sway/input/switch.h | 19 ++++++ sway/commands.c | 2 + sway/commands/bind.c | 118 ++++++++++++++++++++++++++++++++++++ sway/commands/mode.c | 2 + sway/config.c | 7 +++ sway/input/input-manager.c | 47 ++++++++++++++ sway/input/seat.c | 18 ++++-- sway/input/switch.c | 85 ++++++++++++++++++++++++++ sway/ipc-json.c | 4 +- sway/meson.build | 1 + sway/sway.5.scd | 28 ++++++++- 14 files changed, 339 insertions(+), 9 deletions(-) create mode 100644 include/sway/input/switch.h create mode 100644 sway/input/switch.c diff --git a/include/sway/commands.h b/include/sway/commands.h index 764821a0e..1c147c5ab 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -101,6 +101,7 @@ struct sway_container *container_find_resize_parent(struct sway_container *con, sway_cmd cmd_assign; sway_cmd cmd_bar; sway_cmd cmd_bindcode; +sway_cmd cmd_bindswitch; sway_cmd cmd_bindsym; sway_cmd cmd_border; sway_cmd cmd_client_noop; diff --git a/include/sway/config.h b/include/sway/config.h index 7c5445412..8970696c4 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "../include/config.h" @@ -29,6 +30,7 @@ enum binding_input_type { BINDING_KEYSYM, BINDING_MOUSECODE, BINDING_MOUSESYM, + BINDING_SWITCH }; enum binding_flags { @@ -60,6 +62,16 @@ struct sway_mouse_binding { char *command; }; +/** + * A laptop switch binding and an associated command. + */ +struct sway_switch_binding { + enum wlr_switch_type type; + enum wlr_switch_state state; + uint32_t flags; + char *command; +}; + /** * Focus on window activation. */ @@ -78,6 +90,7 @@ struct sway_mode { list_t *keysym_bindings; list_t *keycode_bindings; list_t *mouse_bindings; + list_t *switch_bindings; bool pango; }; @@ -603,6 +616,8 @@ int sway_binding_cmp_keys(const void *a, const void *b); void free_sway_binding(struct sway_binding *sb); +void free_switch_binding(struct sway_switch_binding *binding); + void seat_execute_command(struct sway_seat *seat, struct sway_binding *binding); void load_swaybar(struct bar_config *bar); diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index a5361e8c1..f2af1066b 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -27,6 +27,7 @@ struct sway_seat_device { struct sway_seat *sway_seat; struct sway_input_device *input_device; struct sway_keyboard *keyboard; + struct sway_switch *switch_device; struct wl_list link; // sway_seat::devices }; diff --git a/include/sway/input/switch.h b/include/sway/input/switch.h new file mode 100644 index 000000000..19bb1e772 --- /dev/null +++ b/include/sway/input/switch.h @@ -0,0 +1,19 @@ +#ifndef _SWAY_INPUT_SWITCH_H +#define _SWAY_INPUT_SWITCH_H + +#include "sway/input/seat.h" + +struct sway_switch { + struct sway_seat_device *seat_device; + + struct wl_listener switch_toggle; +}; + +struct sway_switch *sway_switch_create(struct sway_seat *seat, + struct sway_seat_device *device); + +void sway_switch_configure(struct sway_switch *sway_switch); + +void sway_switch_destroy(struct sway_switch *sway_switch); + +#endif diff --git a/sway/commands.c b/sway/commands.c index cb32169f6..18b95c738 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -47,6 +47,7 @@ static struct cmd_handler handlers[] = { { "assign", cmd_assign }, { "bar", cmd_bar }, { "bindcode", cmd_bindcode }, + { "bindswitch", cmd_bindswitch }, { "bindsym", cmd_bindsym }, { "client.background", cmd_client_noop }, { "client.focused", cmd_client_focused }, @@ -386,6 +387,7 @@ struct cmd_results *config_command(char *exec, char **new_block) { && handler->handle != cmd_mode && handler->handle != cmd_bindsym && handler->handle != cmd_bindcode + && handler->handle != cmd_bindswitch && handler->handle != cmd_set && handler->handle != cmd_for_window && (*argv[i] == '\"' || *argv[i] == '\'')) { diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 172e6b8ae..b3d391da6 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -29,6 +29,28 @@ void free_sway_binding(struct sway_binding *binding) { free(binding); } +void free_switch_binding(struct sway_switch_binding *binding) { + if (!binding) { + return; + } + free(binding->command); + free(binding); +} + +/** + * Returns true if the bindings have the same switch type and state combinations. + */ +static bool binding_switch_compare(struct sway_switch_binding *binding_a, + struct sway_switch_binding *binding_b) { + if (binding_a->type != binding_b->type) { + return false; + } + if (binding_a->state != binding_b->state) { + return false; + } + return true; +} + /** * Returns true if the bindings have the same key and modifier combinations. * Note that keyboard layout is not considered, so the bindings might actually @@ -325,6 +347,102 @@ struct cmd_results *cmd_bindcode(int argc, char **argv) { return cmd_bindsym_or_bindcode(argc, argv, true); } +struct cmd_results *cmd_bindswitch(int argc, char **argv) { + + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "bindswitch", EXPECTED_AT_LEAST, 2))) { + return error; + } + struct sway_switch_binding *binding = calloc(1, sizeof(struct sway_switch_binding)); + if (!binding) { + return cmd_results_new(CMD_FAILURE, "Unable to allocate binding"); + } + + bool warn = true; + + // Handle flags + while (argc > 0) { + if (strcmp("--locked", argv[0]) == 0) { + binding->flags |= BINDING_LOCKED; + } else if (strcmp("--no-warn", argv[0]) == 0) { + warn = false; + } else { + break; + } + argv++; + argc--; + } + + if (argc < 2) { + free(binding); + return cmd_results_new(CMD_FAILURE, + "Invalid bindswitch command (expected at least 2 " + "non-option arguments, got %d)", argc); + } + binding->command = join_args(argv + 1, argc - 1); + + list_t *split = split_string(argv[0], ":"); + if (split->length != 2) { + free_switch_binding(binding); + return cmd_results_new(CMD_FAILURE, + "Invalid bindswitch command (expected two arguments with " + "format : , got %d)", argc); + } + if (strcmp(split->items[0], "tablet") == 0) { + binding->type = WLR_SWITCH_TYPE_TABLET_MODE; + } else if (strcmp(split->items[0], "lid") == 0) { + binding->type = WLR_SWITCH_TYPE_LID; + } else { + free_switch_binding(binding); + return cmd_results_new(CMD_FAILURE, + "Invalid bindswitch command (expected switch binding: " + "unknown switch %s)", split->items[0]); + } + if (strcmp(split->items[1], "on") == 0) { + binding->state = WLR_SWITCH_STATE_ON; + } else if (strcmp(split->items[1], "off") == 0) { + binding->state = WLR_SWITCH_STATE_OFF; + } else if (strcmp(split->items[1], "toggle") == 0) { + binding->state = WLR_SWITCH_STATE_TOGGLE; + } else { + free_switch_binding(binding); + return cmd_results_new(CMD_FAILURE, + "Invalid bindswitch command " + "(expected switch state: unknown state %d)", + split->items[0]); + } + list_free_items_and_destroy(split); + + list_t *mode_bindings = config->current_mode->switch_bindings; + + // overwrite the binding if it already exists + bool overwritten = false; + for (int i = 0; i < mode_bindings->length; ++i) { + struct sway_switch_binding *config_binding = mode_bindings->items[i]; + if (binding_switch_compare(binding, config_binding)) { + sway_log(SWAY_INFO, "Overwriting binding '%s' from `%s` to `%s`", + argv[0], binding->command, config_binding->command); + if (warn) { + config_add_swaynag_warning("Overwriting binding" + "'%s' to `%s` from `%s`", + argv[0], binding->command, + config_binding->command); + } + free_switch_binding(config_binding); + mode_bindings->items[i] = binding; + overwritten = true; + } + } + + if (!overwritten) { + list_add(mode_bindings, binding); + } + + sway_log(SWAY_DEBUG, "bindswitch - Bound %s to command `%s`", + argv[0], binding->command); + return cmd_results_new(CMD_SUCCESS, NULL); +} + /** * Execute the command associated to a binding */ diff --git a/sway/commands/mode.c b/sway/commands/mode.c index d424eb1ce..3e0ee80cb 100644 --- a/sway/commands/mode.c +++ b/sway/commands/mode.c @@ -12,6 +12,7 @@ // Must be in order for the bsearch static struct cmd_handler mode_handlers[] = { { "bindcode", cmd_bindcode }, + { "bindswitch", cmd_bindswitch }, { "bindsym", cmd_bindsym } }; @@ -54,6 +55,7 @@ struct cmd_results *cmd_mode(int argc, char **argv) { mode->keysym_bindings = create_list(); mode->keycode_bindings = create_list(); mode->mouse_bindings = create_list(); + mode->switch_bindings = create_list(); mode->pango = pango; list_add(config->modes, mode); } diff --git a/sway/config.c b/sway/config.c index 48bbd1ea8..7104f55d7 100644 --- a/sway/config.c +++ b/sway/config.c @@ -56,6 +56,12 @@ static void free_mode(struct sway_mode *mode) { } list_free(mode->mouse_bindings); } + if (mode->switch_bindings) { + for (int i = 0; i < mode->switch_bindings->length; i++) { + free_switch_binding(mode->switch_bindings->items[i]); + } + list_free(mode->switch_bindings); + } free(mode); } @@ -195,6 +201,7 @@ static void config_defaults(struct sway_config *config) { if (!(config->current_mode->keysym_bindings = create_list())) goto cleanup; if (!(config->current_mode->keycode_bindings = create_list())) goto cleanup; if (!(config->current_mode->mouse_bindings = create_list())) goto cleanup; + if (!(config->current_mode->switch_bindings = create_list())) goto cleanup; list_add(config->modes, config->current_mode); config->floating_mod = 0; diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index f99fc395b..adb36af93 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -155,6 +155,47 @@ static void input_manager_libinput_reset_keyboard( libinput_device, send_events)); } +static void input_manager_libinput_config_switch( + struct sway_input_device *input_device) { + struct wlr_input_device *wlr_device = input_device->wlr_device; + struct input_config *ic = input_device_get_config(input_device); + struct libinput_device *libinput_device; + + if (!ic || !wlr_input_device_is_libinput(wlr_device)) { + return; + } + + libinput_device = wlr_libinput_get_device_handle(wlr_device); + sway_log(SWAY_DEBUG, "input_manager_libinput_config_switch(%s)", + ic->identifier); + + if (ic->send_events != INT_MIN) { + sway_log(SWAY_DEBUG, "libinput_config_switch(%s) send_events_set_mode(%d)", + ic->identifier, ic->send_events); + log_libinput_config_status(libinput_device_config_send_events_set_mode( + libinput_device, ic->send_events)); + } +} + +static void input_manager_libinput_reset_switch( + struct sway_input_device *input_device) { + struct wlr_input_device *wlr_device = input_device->wlr_device; + struct libinput_device *libinput_device; + + if (!wlr_input_device_is_libinput(wlr_device)) { + return; + } + + libinput_device = wlr_libinput_get_device_handle(wlr_device); + + uint32_t send_events = + libinput_device_config_send_events_get_default_mode(libinput_device); + sway_log(SWAY_DEBUG, "libinput_reset_switch(%s) send_events_set_mode(%d)", + input_device->identifier, send_events); + log_libinput_config_status(libinput_device_config_send_events_set_mode( + libinput_device, send_events)); +} + static void input_manager_libinput_config_touch( struct sway_input_device *input_device) { struct wlr_input_device *wlr_device = input_device->wlr_device; @@ -471,6 +512,8 @@ static void handle_new_input(struct wl_listener *listener, void *data) { input_manager_libinput_config_pointer(input_device); } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { input_manager_libinput_config_keyboard(input_device); + } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_SWITCH) { + input_manager_libinput_config_switch(input_device); } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_TOUCH) { input_manager_libinput_config_touch(input_device); } @@ -624,6 +667,8 @@ void input_manager_apply_input_config(struct input_config *input_config) { input_manager_libinput_config_pointer(input_device); } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { input_manager_libinput_config_keyboard(input_device); + } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_SWITCH) { + input_manager_libinput_config_switch(input_device); } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_TOUCH) { input_manager_libinput_config_touch(input_device); } @@ -642,6 +687,8 @@ void input_manager_reset_input(struct sway_input_device *input_device) { input_manager_libinput_reset_pointer(input_device); } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { input_manager_libinput_reset_keyboard(input_device); + } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_SWITCH) { + input_manager_libinput_reset_switch(input_device); } else if (input_device->wlr_device->type == WLR_INPUT_DEVICE_TOUCH) { input_manager_libinput_reset_touch(input_device); } diff --git a/sway/input/seat.c b/sway/input/seat.c index 7fc87ee4a..d58ff9e6f 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -15,6 +15,7 @@ #include "sway/input/input-manager.h" #include "sway/input/keyboard.h" #include "sway/input/seat.h" +#include "sway/input/switch.h" #include "sway/ipc-server.h" #include "sway/layers.h" #include "sway/output.h" @@ -482,8 +483,8 @@ static void seat_update_capabilities(struct sway_seat *seat) { case WLR_INPUT_DEVICE_TABLET_TOOL: caps |= WL_SEAT_CAPABILITY_POINTER; break; - case WLR_INPUT_DEVICE_TABLET_PAD: case WLR_INPUT_DEVICE_SWITCH: + case WLR_INPUT_DEVICE_TABLET_PAD: break; } } @@ -570,6 +571,15 @@ static void seat_configure_keyboard(struct sway_seat *seat, } } +static void seat_configure_switch(struct sway_seat *seat, + struct sway_seat_device *seat_device) { + if (!seat_device->switch_device) { + sway_switch_create(seat, seat_device); + } + seat_apply_input_config(seat, seat_device); + sway_switch_configure(seat_device->switch_device); +} + static void seat_configure_touch(struct sway_seat *seat, struct sway_seat_device *sway_device) { wlr_cursor_attach_input_device(seat->cursor->cursor, @@ -611,6 +621,9 @@ void seat_configure_device(struct sway_seat *seat, case WLR_INPUT_DEVICE_KEYBOARD: seat_configure_keyboard(seat, seat_device); break; + case WLR_INPUT_DEVICE_SWITCH: + seat_configure_switch(seat, seat_device); + break; case WLR_INPUT_DEVICE_TOUCH: seat_configure_touch(seat, seat_device); break; @@ -620,9 +633,6 @@ void seat_configure_device(struct sway_seat *seat, case WLR_INPUT_DEVICE_TABLET_PAD: sway_log(SWAY_DEBUG, "TODO: configure tablet pad"); break; - case WLR_INPUT_DEVICE_SWITCH: - sway_log(SWAY_DEBUG, "TODO: configure switch device"); - break; } } diff --git a/sway/input/switch.c b/sway/input/switch.c new file mode 100644 index 000000000..d56e65254 --- /dev/null +++ b/sway/input/switch.c @@ -0,0 +1,85 @@ +#include "sway/config.h" +#include "sway/desktop/transaction.h" +#include "sway/input/switch.h" +#include +#include "log.h" + +struct sway_switch *sway_switch_create(struct sway_seat *seat, + struct sway_seat_device *device) { + struct sway_switch *switch_device = + calloc(1, sizeof(struct sway_switch)); + if (!sway_assert(switch_device, "could not allocate switch")) { + return NULL; + } + device->switch_device = switch_device; + switch_device->seat_device = device; + wl_list_init(&switch_device->switch_toggle.link); + sway_log(SWAY_DEBUG, "Allocated switch for device"); + + return switch_device; +} + +static void handle_switch_toggle(struct wl_listener *listener, void *data) { + struct sway_switch *sway_switch = + wl_container_of(listener, sway_switch, switch_toggle); + struct sway_seat* seat = sway_switch->seat_device->sway_seat; + struct wlr_seat *wlr_seat = seat->wlr_seat; + struct wlr_input_device *wlr_device = + sway_switch->seat_device->input_device->wlr_device; + + wlr_idle_notify_activity(server.idle, wlr_seat); + bool input_inhibited = seat->exclusive_client != NULL; + + char *device_identifier = input_device_get_identifier(wlr_device); + + struct wlr_event_switch_toggle *event = data; + enum wlr_switch_type type = event->switch_type; + enum wlr_switch_state state = event->switch_state; + sway_log(SWAY_DEBUG, "%s: type %d state %d", device_identifier, type, state); + + list_t *bindings = config->current_mode->switch_bindings; + for (int i = 0; i < bindings->length; ++i) { + struct sway_switch_binding *binding = bindings->items[i]; + if (binding->type != type) { + continue; + } + if (binding->state != WLR_SWITCH_STATE_TOGGLE && + binding->state != state) { + continue; + } + bool binding_locked = binding->flags & BINDING_LOCKED; + if (!binding_locked && input_inhibited) { + continue; + } + + struct sway_binding *dummy_binding = calloc(1, sizeof(struct sway_binding)); + dummy_binding->type = BINDING_SWITCH; + dummy_binding->flags = binding->flags; + dummy_binding->command = binding->command; + + seat_execute_command(seat, dummy_binding); + free(dummy_binding); + } + + transaction_commit_dirty(); + + free(device_identifier); +} + +void sway_switch_configure(struct sway_switch *sway_switch) { + struct wlr_input_device *wlr_device = + sway_switch->seat_device->input_device->wlr_device; + wl_list_remove(&sway_switch->switch_toggle.link); + wl_signal_add(&wlr_device->switch_device->events.toggle, + &sway_switch->switch_toggle); + sway_switch->switch_toggle.notify = handle_switch_toggle; + sway_log(SWAY_DEBUG, "Configured switch for device"); +} + +void sway_switch_destroy(struct sway_switch *sway_switch) { + if (!sway_switch) { + return; + } + wl_list_remove(&sway_switch->switch_toggle.link); + free(sway_switch); +} diff --git a/sway/ipc-json.c b/sway/ipc-json.c index f61e1a8c8..c320d9586 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -91,14 +91,14 @@ static const char *ipc_json_device_type_description(struct sway_input_device *de return "pointer"; case WLR_INPUT_DEVICE_KEYBOARD: return "keyboard"; + case WLR_INPUT_DEVICE_SWITCH: + return "switch"; case WLR_INPUT_DEVICE_TOUCH: return "touch"; case WLR_INPUT_DEVICE_TABLET_TOOL: return "tablet_tool"; case WLR_INPUT_DEVICE_TABLET_PAD: return "tablet_pad"; - case WLR_INPUT_DEVICE_SWITCH: - return "switch"; } return "unknown"; } diff --git a/sway/meson.build b/sway/meson.build index 1138bc73f..66876bdcd 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -30,6 +30,7 @@ sway_sources = files( 'input/seatop_resize_tiling.c', 'input/cursor.c', 'input/keyboard.c', + 'input/switch.c', 'config/bar.c', 'config/output.c', diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 989717cb1..18fc28a30 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -341,6 +341,28 @@ runtime. *bindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] [--locked] [--input-device=] [--no-warn] is also available for binding with key/button codes instead of key/button names. +*bindswitch* [--locked] [--no-warn] : + Binds to execute the sway command _command_ on state changes. + Supported switches are _lid_ (laptop lid) and _tablet_ (tablet mode) + switches. Valid values for _state_ are _on_, _off_ and _toggle. These + switches are on when the device lid is shut and when tablet mode is active + respectively. _toggle_ is also supported to run a command both when the + switch is toggled on or off. + + Unless the flag _--locked_ is set, the command will not be run + when a screen locking program is active. By default, if you + overwrite a binding, swaynag will give you a warning. To silence this, use + the _--no-warn_ flag. + + Example: +``` + # Show the virtual keyboard when tablet mode is entered. + bindswitch tablet:on busctl call --user sm.puri.OSK0 /sm/puri/OSK0 sm.puri.OSK0 SetVisible b true + + # Log a message when the laptop lid is opened or closed. + bindswitch lid:toggle exec echo "Lid moved" +``` + *client.* Configures the color of window borders and title bars. All 5 colors are required, with the exception of *client.background*, which requires exactly @@ -551,9 +573,9 @@ The default colors are: Switches to the specified mode. The default mode _default_. *mode* [--pango_markup] - The only two valid _mode-subcommands..._ are *bindsym* and *bindcode*. - If _--pango_markup_ is given, then _mode_ will be interpreted as pango - markup. + The only valid _mode-subcommands..._ are *bindsym*, *bindcode* and + *bindswitch*. If _--pango_markup_ is given, then _mode_ will be interpreted + as pango markup. *mouse_warping* output|container|none If _output_ is specified, the mouse will be moved to new outputs as you From cdcc2a5bb52a34865eee2ec3d09a38b047c08ffd Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Wed, 20 Mar 2019 22:16:53 +1000 Subject: [PATCH 0213/2054] Support focus for floating containers This kind of worked before in that focus would change, but it wasn't intentionally supported and had side effects such as not raising the container, and being unable to cycle through all floaters depending on the direction used. This commit makes it properly supported. The new focus is chosen based on the distance to the center point of each floating container in the workspace, and the container is raised. In a multi output setup, if both visible workspaces have floating containers, focus will NOT cross into the other output. It is assumed the user will use a workspace binding in this case. If two floating containers occupy the exact same center point and you try to focus in a direction, the behaviour is undefined. --- sway/commands/focus.c | 49 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 6344a7658..33d014050 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -1,3 +1,4 @@ +#include #include #include #include "log.h" @@ -90,8 +91,9 @@ static struct sway_node *get_node_in_output_direction( return &ws->node; } -static struct sway_node *node_get_in_direction(struct sway_container *container, - struct sway_seat *seat, enum wlr_direction dir) { +static struct sway_node *node_get_in_direction_tiling( + struct sway_container *container, struct sway_seat *seat, + enum wlr_direction dir) { struct sway_container *wrap_candidate = NULL; struct sway_container *current = container; while (current) { @@ -172,6 +174,37 @@ static struct sway_node *node_get_in_direction(struct sway_container *container, return NULL; } +static struct sway_node *node_get_in_direction_floating( + struct sway_container *con, struct sway_seat *seat, + enum wlr_direction dir) { + double ref_lx = con->x + con->width / 2; + double ref_ly = con->y + con->height / 2; + double closest_distance = DBL_MAX; + struct sway_container *closest_con = NULL; + + for (int i = 0; i < con->workspace->floating->length; i++) { + struct sway_container *floater = con->workspace->floating->items[i]; + if (floater == con) { + continue; + } + float distance = dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_RIGHT + ? (floater->x + floater->width / 2) - ref_lx + : (floater->y + floater->height / 2) - ref_ly; + if (dir == WLR_DIRECTION_LEFT || dir == WLR_DIRECTION_UP) { + distance = -distance; + } + if (distance < 0) { + continue; + } + if (distance < closest_distance) { + closest_distance = distance; + closest_con = floater; + } + } + + return closest_con ? &closest_con->node : NULL; +} + static struct cmd_results *focus_mode(struct sway_workspace *ws, struct sway_seat *seat, bool floating) { struct sway_container *new_focus = NULL; @@ -330,11 +363,19 @@ struct cmd_results *cmd_focus(int argc, char **argv) { return cmd_results_new(CMD_SUCCESS, NULL); } - struct sway_node *next_focus = - node_get_in_direction(container, seat, direction); + struct sway_node *next_focus = NULL; + if (container_is_floating(container)) { + next_focus = node_get_in_direction_floating(container, seat, direction); + } else { + next_focus = node_get_in_direction_tiling(container, seat, direction); + } if (next_focus) { seat_set_focus(seat, next_focus); seat_consider_warp_to_focus(seat); + + if (next_focus->type == N_CONTAINER) { + container_raise_floating(next_focus->sway_container); + } } return cmd_results_new(CMD_SUCCESS, NULL); From 8cd7f0171ad8ffc4ce490f6f4b18522cb178c951 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 19 Mar 2019 23:07:32 -0400 Subject: [PATCH 0214/2054] cmd_mode: allow cmd_set to be a subcommand This allows set to be used in mode blocks --- sway/commands/mode.c | 3 ++- sway/sway.5.scd | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sway/commands/mode.c b/sway/commands/mode.c index 3e0ee80cb..ef2c5d794 100644 --- a/sway/commands/mode.c +++ b/sway/commands/mode.c @@ -13,7 +13,8 @@ static struct cmd_handler mode_handlers[] = { { "bindcode", cmd_bindcode }, { "bindswitch", cmd_bindswitch }, - { "bindsym", cmd_bindsym } + { "bindsym", cmd_bindsym }, + { "set", cmd_set }, }; struct cmd_results *cmd_mode(int argc, char **argv) { diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 18fc28a30..dbfeefe3c 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -573,9 +573,9 @@ The default colors are: Switches to the specified mode. The default mode _default_. *mode* [--pango_markup] - The only valid _mode-subcommands..._ are *bindsym*, *bindcode* and - *bindswitch*. If _--pango_markup_ is given, then _mode_ will be interpreted - as pango markup. + The only valid _mode-subcommands..._ are *bindsym*, *bindcode*, + *bindswitch*, and *set*. If _--pango_markup_ is given, then _mode_ will be + interpreted as pango markup. *mouse_warping* output|container|none If _output_ is specified, the mouse will be moved to new outputs as you From 7d2076cbff2b363061d52362927d0da2f3c4865b Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 23 Mar 2019 03:49:29 -0400 Subject: [PATCH 0215/2054] criteria: fix __focused__ when no focus or unset This fixes the behavior of `__focused__` when there is no focused view to match i3's behavior of successfully matching no views instead of returning an error of a missing value. It also applies the same logic when a token is not applicable (or unset) for a view such as `app_id` for a focused xwayland view or `class` for a focused xdg-shell view. This adds an `autofail` boolean to `struct criteria`. If it is set to `true`, then `criteria_matches_view` will immediately bail out as a no match. If `autofail` is set, the criteria will also not be considered empty by `criteria_is_empty`. To set this new `autofail` property, `get_focused_prop` will now take in a boolean pointer of the same name. If `__focused__` is supported for the token and there is no focused view or the focused view does not have a value for the token, then the boolean will be set to true. In `parse_token`, the boolean value will be checked and if set to true, then `criteria->autofail` will be set to true and `parse_token` will bail successfully. Tokens will still be parsed to make sure the whole criteria is syntactically valid, which is also why `&criteria->autofail` is not passed to `get_focused_prop` and a local boolean is declared in `parse_token`. --- include/sway/criteria.h | 1 + sway/criteria.c | 69 ++++++++++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/include/sway/criteria.h b/include/sway/criteria.h index f7e788c8b..8a1d9e5ef 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -20,6 +20,7 @@ struct criteria { char *cmdlist; char *target; // workspace or output name for `assign` criteria + bool autofail; // __focused__ while no focus or n/a for focused view pcre *title; pcre *shell; pcre *app_id; diff --git a/sway/criteria.c b/sway/criteria.c index f2db6c184..11b41f356 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -16,7 +16,8 @@ #include "config.h" bool criteria_is_empty(struct criteria *criteria) { - return !criteria->title + return !criteria->autofail + && !criteria->title && !criteria->shell && !criteria->app_id && !criteria->con_mark @@ -98,6 +99,10 @@ static void find_urgent_iterator(struct sway_container *con, void *data) { static bool criteria_matches_view(struct criteria *criteria, struct sway_view *view) { + if (criteria->autofail) { + return false; + } + if (criteria->title) { const char *title = view_get_title(view); if (!title || regex_cmp(title, criteria->title) != 0) { @@ -366,50 +371,66 @@ static enum criteria_token token_from_name(char *name) { * using criteria via IPC. Using __focused__ in config is not useful because * criteria is only executed once per view. */ -static char *get_focused_prop(enum criteria_token token) { +static char *get_focused_prop(enum criteria_token token, bool *autofail) { struct sway_seat *seat = input_manager_current_seat(); struct sway_container *focus = seat_get_focused_container(seat); - if (!focus || !focus->view) { - return NULL; - } - struct sway_view *view = focus->view; + struct sway_view *view = focus ? focus->view : NULL; const char *value = NULL; switch (token) { case T_APP_ID: - value = view_get_app_id(view); + *autofail = true; + if (view) { + value = view_get_app_id(view); + } break; case T_SHELL: - value = view_get_shell(view); + *autofail = true; + if (view) { + value = view_get_shell(view); + } break; case T_TITLE: - value = view_get_title(view); + *autofail = true; + if (view) { + value = view_get_title(view); + } break; case T_WORKSPACE: - if (focus->workspace) { + *autofail = true; + if (focus && focus->workspace) { value = focus->workspace->name; } break; case T_CON_ID: - if (view->container == NULL) { - return NULL; + *autofail = true; + if (view && view->container) { + size_t id = view->container->node.id; + size_t id_size = snprintf(NULL, 0, "%zu", id) + 1; + char *id_str = malloc(id_size); + snprintf(id_str, id_size, "%zu", id); + value = id_str; } - size_t id = view->container->node.id; - size_t id_size = snprintf(NULL, 0, "%zu", id) + 1; - char *id_str = malloc(id_size); - snprintf(id_str, id_size, "%zu", id); - value = id_str; break; #if HAVE_XWAYLAND case T_CLASS: - value = view_get_class(view); + *autofail = true; + if (view) { + value = view_get_class(view); + } break; case T_INSTANCE: - value = view_get_instance(view); + *autofail = true; + if (view) { + value = view_get_instance(view); + } break; case T_WINDOW_ROLE: - value = view_get_window_role(view); + *autofail = true; + if (view) { + value = view_get_window_role(view); + } break; case T_WINDOW_TYPE: // These do not support __focused__ case T_ID: @@ -419,6 +440,7 @@ static char *get_focused_prop(enum criteria_token token) { case T_TILING: case T_URGENT: case T_INVALID: + *autofail = false; break; } if (value) { @@ -439,7 +461,12 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { char *effective_value = NULL; if (value && strcmp(value, "__focused__") == 0) { - effective_value = get_focused_prop(token); + bool autofail = false; + effective_value = get_focused_prop(token, &autofail); + if (!effective_value && autofail) { + criteria->autofail = true; + return true; + } } else if (value) { effective_value = strdup(value); } From cd8b4ace926bd5a5289a5d5d6f6dddc1e4732287 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sat, 23 Mar 2019 17:24:53 -0400 Subject: [PATCH 0216/2054] fix opening a floating view on the NOOP output Since the NOOP output has no size, the minimum floating size is greater than the workspace size for the NOOP output. In this case, the floater gets centered in the output instead of the workspace. However, the NOOP output is not part of the output layout and thus has a NULL box. Attempting to access the properties of this box was causing a segfault. This fixes the issue by just setting the floater's box to all zeroes when mapping on the NOOP output. When the workspace gets moved from the NOOP output to a new output, any floater whose width or height is zero or has an x or y location outside of the output, gets passed to `container_init_floating` again. This will then set the appropriate size and centering. For any floater that has a valid size and location, they are preserved. --- sway/tree/container.c | 15 +++++++++++---- sway/tree/output.c | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 93cff7ff5..f84ce3602 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -651,6 +651,17 @@ void floating_calculate_constraints(int *min_width, int *max_width, void container_init_floating(struct sway_container *con) { struct sway_workspace *ws = con->workspace; + struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, + ws->output->wlr_output); + if (!ob) { + // On NOOP output. Will be called again when moved to an output + con->x = 0; + con->y = 0; + con->width = 0; + con->height = 0; + return; + } + int min_width, max_width, min_height, max_height; floating_calculate_constraints(&min_width, &max_width, &min_height, &max_height); @@ -659,8 +670,6 @@ void container_init_floating(struct sway_container *con) { con->width = max_width; con->height = max_height; if (con->width > ws->width || con->height > ws->height) { - struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, - ws->output->wlr_output); con->x = ob->x + (ob->width - con->width) / 2; con->y = ob->y + (ob->height - con->height) / 2; } else { @@ -675,8 +684,6 @@ void container_init_floating(struct sway_container *con) { fmax(min_height, fmin(view->natural_height, max_height)); if (con->content_width > ws->width || con->content_height > ws->height) { - struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, - ws->output->wlr_output); con->content_x = ob->x + (ob->width - con->content_width) / 2; con->content_y = ob->y + (ob->height - con->content_height) / 2; } else { diff --git a/sway/tree/output.c b/sway/tree/output.c index 1202ba3c5..7867c6bc6 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -60,6 +60,27 @@ static void restore_workspaces(struct sway_output *output) { struct sway_workspace *ws = root->noop_output->workspaces->items[0]; workspace_detach(ws); output_add_workspace(output, ws); + + // If the floater was made floating while on the NOOP output, its width + // and height will be zero and it should be reinitialized as a floating + // container to get the appropriate size and location. Additionally, if + // the floater is wider or taller than the output or is completely + // outside of the output's bounds, do the same as the output layout has + // likely changed and the maximum size needs to be checked and the + // floater re-centered + for (int i = 0; i < ws->floating->length; i++) { + struct sway_container *floater = ws->floating->items[i]; + if (floater->width == 0 || floater->height == 0 || + floater->width > output->width || + floater->height > output->height || + floater->x > output->lx + output->width || + floater->y > output->ly + output->height || + floater->x + floater->width < output->lx || + floater->y + floater->height < output->ly) { + container_init_floating(floater); + } + } + ipc_event_workspace(NULL, ws, "move"); } From 200833caaea36dd65324e5460520731f5c98ff8a Mon Sep 17 00:00:00 2001 From: mliszcz Date: Sat, 23 Mar 2019 11:32:44 +0100 Subject: [PATCH 0217/2054] Allow for workspace renaming during exec handling This change adds support for renaming a workspace when `exec` command is being processed by keeping sway_workspace and pid_workspace names in sync. The change can be verified by running following command: swaymsg exec ; swaymsg rename workspace number 1 to 5 Fixes: #3952 --- include/sway/tree/root.h | 2 ++ sway/commands/rename.c | 4 ++++ sway/tree/root.c | 14 ++++++++++++++ 3 files changed, 20 insertions(+) diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index 9f6cd3bb6..c4f84207d 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -85,4 +85,6 @@ struct sway_container *root_find_container( void root_get_box(struct sway_root *root, struct wlr_box *box); +void root_rename_pid_workspaces(const char *old_name, const char *new_name); + #endif diff --git a/sway/commands/rename.c b/sway/commands/rename.c index 88377b092..3b855fdf7 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c @@ -9,6 +9,7 @@ #include "sway/output.h" #include "sway/tree/container.h" #include "sway/tree/workspace.h" +#include "sway/tree/root.h" static const char expected_syntax[] = "Expected 'rename workspace to ' or " @@ -89,6 +90,9 @@ struct cmd_results *cmd_rename(int argc, char **argv) { } sway_log(SWAY_DEBUG, "renaming workspace '%s' to '%s'", workspace->name, new_name); + + root_rename_pid_workspaces(workspace->name, new_name); + free(workspace->name); workspace->name = new_name; diff --git a/sway/tree/root.c b/sway/tree/root.c index 0744192b2..5dde9f22d 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -390,3 +390,17 @@ void root_get_box(struct sway_root *root, struct wlr_box *box) { box->width = root->width; box->height = root->height; } + +void root_rename_pid_workspaces(const char *old_name, const char *new_name) { + if (!pid_workspaces.prev && !pid_workspaces.next) { + wl_list_init(&pid_workspaces); + } + + struct pid_workspace *pw = NULL; + wl_list_for_each(pw, &pid_workspaces, link) { + if (strcmp(pw->workspace, old_name) == 0) { + free(pw->workspace); + pw->workspace = strdup(new_name); + } + } +} From 6e3046878d4dced3f2e503973ad31d7921c0c400 Mon Sep 17 00:00:00 2001 From: Geoff Greer Date: Sun, 10 Feb 2019 16:56:57 -0800 Subject: [PATCH 0218/2054] Add support for manually setting subpixel hinting on outputs. Many laptop screens report unknown subpixel order. Allow users to manually set subpixel hinting to work around this. Addresses https://github.com/swaywm/sway/issues/3163 --- common/util.c | 21 +++++++++++++++++++ include/sway/commands.h | 1 + include/sway/config.h | 1 + include/sway/output.h | 1 + include/util.h | 3 +++ sway/commands/output.c | 1 + sway/commands/output/subpixel.c | 36 +++++++++++++++++++++++++++++++++ sway/config/output.c | 23 +++++++++++++++++---- sway/ipc-server.c | 2 ++ sway/meson.build | 1 + sway/sway-ipc.7.scd | 4 ++++ sway/sway-output.5.scd | 7 +++++++ sway/tree/output.c | 1 + swaymsg/main.c | 5 ++++- 14 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 sway/commands/output/subpixel.c diff --git a/common/util.c b/common/util.c index edbbf3f77..c43c5ddf6 100644 --- a/common/util.c +++ b/common/util.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "log.h" #include "util.h" @@ -54,3 +55,23 @@ float parse_float(const char *value) { } return flt; } + + +const char *sway_wl_output_subpixel_to_string(enum wl_output_subpixel subpixel) { + switch (subpixel) { + case WL_OUTPUT_SUBPIXEL_UNKNOWN: + return "unknown"; + case WL_OUTPUT_SUBPIXEL_NONE: + return "none"; + case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB: + return "rgb"; + case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR: + return "bgr"; + case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB: + return "vrgb"; + case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR: + return "vbgr"; + } + sway_assert(false, "Unknown value for wl_output_subpixel."); + return NULL; +} diff --git a/include/sway/commands.h b/include/sway/commands.h index 1c147c5ab..7533a14d3 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -261,6 +261,7 @@ sway_cmd output_cmd_enable; sway_cmd output_cmd_mode; sway_cmd output_cmd_position; sway_cmd output_cmd_scale; +sway_cmd output_cmd_subpixel; sway_cmd output_cmd_transform; sway_cmd seat_cmd_attach; diff --git a/include/sway/config.h b/include/sway/config.h index 8970696c4..d49120a06 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -184,6 +184,7 @@ struct output_config { int x, y; float scale; int32_t transform; + enum wl_output_subpixel subpixel; char *background; char *background_option; diff --git a/include/sway/output.h b/include/sway/output.h index 8015f2110..c336c5592 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -31,6 +31,7 @@ struct sway_output { int lx, ly; // layout coords int width, height; // transformed buffer size + enum wl_output_subpixel detected_subpixel; bool enabled, configured; list_t *workspaces; diff --git a/include/util.h b/include/util.h index 1fd772c0c..6a668fd65 100644 --- a/include/util.h +++ b/include/util.h @@ -3,6 +3,7 @@ #include #include +#include /** * Wrap i into the range [0, max[ @@ -29,4 +30,6 @@ bool parse_boolean(const char *boolean, bool current); */ float parse_float(const char *value); +const char *sway_wl_output_subpixel_to_string(enum wl_output_subpixel subpixel); + #endif diff --git a/sway/commands/output.c b/sway/commands/output.c index 40dbf3ca4..44e28512f 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -18,6 +18,7 @@ static struct cmd_handler output_handlers[] = { { "res", output_cmd_mode }, { "resolution", output_cmd_mode }, { "scale", output_cmd_scale }, + { "subpixel", output_cmd_subpixel }, { "transform", output_cmd_transform }, }; diff --git a/sway/commands/output/subpixel.c b/sway/commands/output/subpixel.c new file mode 100644 index 000000000..63191ee62 --- /dev/null +++ b/sway/commands/output/subpixel.c @@ -0,0 +1,36 @@ +#include +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/output.h" + +struct cmd_results *output_cmd_subpixel(int argc, char **argv) { + if (!config->handler_context.output_config) { + return cmd_results_new(CMD_FAILURE, "Missing output config"); + } + if (!argc) { + return cmd_results_new(CMD_INVALID, "Missing subpixel argument."); + } + enum wl_output_subpixel subpixel; + + if (strcmp(*argv, "rgb") == 0) { + subpixel = WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB; + } else if (strcmp(*argv, "bgr") == 0) { + subpixel = WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR; + } else if (strcmp(*argv, "vrgb") == 0) { + subpixel = WL_OUTPUT_SUBPIXEL_VERTICAL_RGB; + } else if (strcmp(*argv, "vbgr") == 0) { + subpixel = WL_OUTPUT_SUBPIXEL_VERTICAL_BGR; + } else if (strcmp(*argv, "none") == 0) { + subpixel = WL_OUTPUT_SUBPIXEL_NONE; + } else { + return cmd_results_new(CMD_INVALID, "Invalid output subpixel."); + } + + struct output_config *oc = config->handler_context.output_config; + config->handler_context.leftovers.argc = argc - 1; + config->handler_context.leftovers.argv = argv + 1; + + oc->subpixel = subpixel; + return NULL; +} diff --git a/sway/config/output.c b/sway/config/output.c index 44aae03aa..d06051b34 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -8,10 +8,11 @@ #include #include #include -#include "log.h" #include "sway/config.h" #include "sway/output.h" #include "sway/tree/root.h" +#include "log.h" +#include "util.h" int output_name_cmp(const void *item, const void *data) { const struct output_config *output = item; @@ -43,6 +44,7 @@ struct output_config *new_output_config(const char *name) { oc->x = oc->y = -1; oc->scale = -1; oc->transform = -1; + oc->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; return oc; } @@ -65,6 +67,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src) { if (src->scale != -1) { dst->scale = src->scale; } + if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) { + dst->subpixel = src->subpixel; + } if (src->refresh_rate != -1) { dst->refresh_rate = src->refresh_rate; } @@ -187,10 +192,10 @@ struct output_config *store_output_config(struct output_config *oc) { } sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " - "position %d,%d scale %f transform %d) (bg %s %s) (dpms %d)", + "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (dpms %d)", oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, - oc->x, oc->y, oc->scale, oc->transform, oc->background, - oc->background_option, oc->dpms_state); + oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), + oc->transform, oc->background, oc->background_option, oc->dpms_state); return oc; } @@ -363,6 +368,14 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { sway_log(SWAY_DEBUG, "Set %s scale to %f", oc->name, oc->scale); wlr_output_set_scale(wlr_output, oc->scale); } + + if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { + sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, + sway_wl_output_subpixel_to_string(oc->subpixel)); + wlr_output_set_subpixel(wlr_output, oc->subpixel); + output_damage_whole(output); + } + if (oc && oc->transform >= 0) { sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, oc->transform); wlr_output_set_transform(wlr_output, oc->transform); @@ -424,6 +437,8 @@ static void default_output_config(struct output_config *oc, } oc->x = oc->y = -1; oc->scale = 1; + struct sway_output *output = wlr_output->data; + oc->subpixel = output->detected_subpixel; oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; oc->dpms_state = DPMS_ON; } diff --git a/sway/ipc-server.c b/sway/ipc-server.c index df57cba51..e133a5bf1 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -644,6 +644,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_object_add(output_json, "focused", json_object_new_boolean(focused)); + const char *subpixel = sway_wl_output_subpixel_to_string(output->wlr_output->subpixel); + json_object_object_add(output_json, "subpixel_hinting", json_object_new_string(subpixel)); json_object_array_add(outputs, output_json); } struct sway_output *output; diff --git a/sway/meson.build b/sway/meson.build index 66876bdcd..9f79fb6ce 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -169,6 +169,7 @@ sway_sources = files( 'commands/output/mode.c', 'commands/output/position.c', 'commands/output/scale.c', + 'commands/output/subpixel.c', 'commands/output/transform.c', 'tree/arrange.c', diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index b43b30302..3d22008e4 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -211,6 +211,9 @@ following properties: |- scale : float : The scale currently in use on the output or _-1_ for disabled outputs +|- subpixel_hinting +: string +: The subpixel hinting current in use on the output. This can be _rgb_, _bgr_, _vrgb_, _vbgr_, or _none_ |- transform : string : The transform currently in use for the output. This can be _normal_, _90_, @@ -242,6 +245,7 @@ following properties: "active": true, "primary": false, "scale": 1.0, + "subpixel_hinting": "rgb", "transform": "normal", "current_workspace": "1", "modes": [ diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index bb3745f36..1efe2f7bf 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -64,6 +64,13 @@ must be separated by one space. For example: applications to taste. HiDPI isn't supported with Xwayland clients (windows will blur). +*output* subpixel rgb|bgr|vrgb|vbgr|none + Manually sets the subpixel hinting for the specified output. This value is + usually auto-detected, but some displays may misreport their subpixel + geometry. Using the correct subpixel hinting allows for sharper text. + Incorrect values will result in blurrier text. When changing this via + *swaymsg*, some applications may need to be restarted to use the new value. + *output* background|bg [] Sets the wallpaper for the given output to the specified file, using the given scaling mode (one of "stretch", "fill", "fit", "center", "tile"). If diff --git a/sway/tree/output.c b/sway/tree/output.c index 7867c6bc6..283036524 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -92,6 +92,7 @@ struct sway_output *output_create(struct wlr_output *wlr_output) { node_init(&output->node, N_OUTPUT, output); output->wlr_output = wlr_output; wlr_output->data = output; + output->detected_subpixel = wlr_output->subpixel; wl_signal_init(&output->events.destroy); diff --git a/swaymsg/main.c b/swaymsg/main.c index 65cc4bbb1..f86000a42 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -186,11 +186,12 @@ static void pretty_print_output(json_object *o) { json_object_object_get_ex(o, "focused", &focused); json_object_object_get_ex(o, "active", &active); json_object_object_get_ex(o, "current_workspace", &ws); - json_object *make, *model, *serial, *scale, *transform; + json_object *make, *model, *serial, *scale, *subpixel, *transform; json_object_object_get_ex(o, "make", &make); json_object_object_get_ex(o, "model", &model); json_object_object_get_ex(o, "serial", &serial); json_object_object_get_ex(o, "scale", &scale); + json_object_object_get_ex(o, "subpixel_hinting", &subpixel); json_object_object_get_ex(o, "transform", &transform); json_object *x, *y; json_object_object_get_ex(rect, "x", &x); @@ -209,6 +210,7 @@ static void pretty_print_output(json_object *o) { " Current mode: %dx%d @ %f Hz\n" " Position: %d,%d\n" " Scale factor: %f\n" + " Subpixel hinting: %s\n" " Transform: %s\n" " Workspace: %s\n", json_object_get_string(name), @@ -221,6 +223,7 @@ static void pretty_print_output(json_object *o) { (float)json_object_get_int(refresh) / 1000, json_object_get_int(x), json_object_get_int(y), json_object_get_double(scale), + json_object_get_string(subpixel), json_object_get_string(transform), json_object_get_string(ws) ); From c3d7036867b958b08f6506737ffafe9052c17194 Mon Sep 17 00:00:00 2001 From: Philz69 Date: Sun, 24 Mar 2019 15:19:13 -0400 Subject: [PATCH 0219/2054] Updated the french readme (#3964) * Updated the french readme Removed the image at the start of the readme. Removed the mention of bounties. Updated the dependencies. Removed a few lines that were not present in the english readme anymore. * Fix errors. Fixed capitalisation. Changed "root" to italics. --- README.fr.md | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/README.fr.md b/README.fr.md index ad627ba54..97c09667b 100644 --- a/README.fr.md +++ b/README.fr.md @@ -6,14 +6,8 @@ avec i3, **en cours de développement**. Lisez la IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway sur irc.freenode.net). -[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) - -Si vous souhaitez soutenir le développement de Sway, vous pouvez contribuer à [ma page -Patreon](https://patreon.com/sircmpwn) ou aux [primes](https://github.com/swaywm/sway/issues/986) -pour des fonctionnalités spécifiques. -Tout le monde est invité à réclamer une prime et vous pouvez donner une prime pour n'importe quelle -fonctionnalité souhaitée. Patreon est plus utile pour supporter l'état général et la -maintenance de Sway. +Si vous souhaitez soutenir le développement de Sway, vous pouvez contribuer à [la page +Patreon de SirCmpwn](https://patreon.com/sircmpwn). ## Aide en français @@ -39,21 +33,21 @@ IRC ou envoyez un e-mail à sir@cmpwn.com (en anglais seulement) pour des consei Installez les dépendances : -* meson -* [wlc](https://github.com/Cloudef/wlc) +* meson \* +* [wlroots](https://github.com/swaywm/wlroots) * wayland -* xwayland -* libinput >= 1.6.0 -* libcap +* wayland-protocols \* * pcre -* json-c >= 0.13 +* json-c * pango * cairo -* gdk-pixbuf2 * -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (requis pour les pages man) -* git +* gdk-pixbuf2 \*\* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (optionnel: requis pour les pages man) \* +* git \* -_\*Uniquement requis pour swaybar, swaybg_ +_\*Requis uniquement pour la compilation_ + +_\*\*Optionnel: requis uniquement pour swaybg_ Exécutez ces commandes : @@ -65,6 +59,8 @@ Sur les systèmes sans logind, vous devez suid le binaire de sway : sudo chmod a+s /usr/local/bin/sway +Sway se débarassera des permissions *root* peu de temps après le démarrage. + ## Configuration Si vous utilisez déjà i3, copiez votre configuration i3 à `~/.config/sway/config` et @@ -72,10 +68,6 @@ cela va fonctionner. Sinon, copiez l'exemple de fichier de configuration à `~/.config/sway/config`. Il se trouve généralement dans `/etc/sway/config`. Exécutez `man 5 sway` pour l'information sur la configuration. -Mes propres dotfiles sont disponibles [ici](https://git.sr.ht/~sircmpwn/dotfiles) si -vous voulez un peu d'inspiration. Je vous recommande aussi de consulter le -[wiki](https://github.com/swaywm/sway/wiki). - ## Exécution Exécutez `sway` à partir d'un TTY. Certains gestionnaires d'affichage peuvent fonctionner, From 8d2c982f3fea5b7f83680809bf4d986edfd625a3 Mon Sep 17 00:00:00 2001 From: Philipe Goulet Date: Fri, 22 Mar 2019 20:51:10 -0400 Subject: [PATCH 0220/2054] Fix #3924 Removes "unescape_string(argv[i]);". Since "do_var_replacement(argv[i])" never adds escape characters, it is both wrong and unnecessary to remove escape characters on the next line. This caused characters that were meant to be escaped to not be anymore. --- sway/commands.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sway/commands.c b/sway/commands.c index 18b95c738..0d9460a21 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -276,7 +276,6 @@ list_t *execute_command(char *_exec, struct sway_seat *seat, // Var replacement, for all but first argument of set for (int i = handler->handle == cmd_set ? 2 : 1; i < argc; ++i) { argv[i] = do_var_replacement(argv[i]); - unescape_string(argv[i]); } if (!config->handler_context.using_criteria) { From d9de5b87583ccf8b633980ebbdec67227bbe7db4 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 24 Mar 2019 21:21:24 -0400 Subject: [PATCH 0221/2054] Implement inhibit_idle command This implements the following command to set/unset a user idle inhibitor for a view: `inhibit_idle focus|fullscreen|open|none|visible` The modes are as follows: - focus: inhibited when the view is focused by any seat - fullscreen: inhibited when the view is fullscreen (or a descendant of a fullscreen container) and is visible on any output - open: inhibited until the view is closed or the inhibitor is unset or changed - none: unsets any user set idle inhibitors for the view - visible: inhibited when the view is visible on any output This should have no effect on idle inhibitors set by the applications themselves and those should still work as intended. Since this operates on the view in the handler context, it is possible to set it on the currently focused view, on any existing view with criteria, or for any future view with for_window. --- include/sway/commands.h | 1 + include/sway/desktop/idle_inhibit_v1.h | 20 +++++- sway/commands.c | 1 + sway/commands/inhibit_idle.c | 51 +++++++++++++ sway/desktop/idle_inhibit_v1.c | 99 ++++++++++++++++++++++---- sway/desktop/transaction.c | 2 +- sway/meson.build | 1 + sway/sway.5.scd | 12 ++++ 8 files changed, 171 insertions(+), 16 deletions(-) create mode 100644 sway/commands/inhibit_idle.c diff --git a/include/sway/commands.h b/include/sway/commands.h index 7533a14d3..9bd0f1cb5 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -136,6 +136,7 @@ sway_cmd cmd_fullscreen; sway_cmd cmd_gaps; sway_cmd cmd_hide_edge_borders; sway_cmd cmd_include; +sway_cmd cmd_inhibit_idle; sway_cmd cmd_input; sway_cmd cmd_seat; sway_cmd cmd_ipc; diff --git a/include/sway/desktop/idle_inhibit_v1.h b/include/sway/desktop/idle_inhibit_v1.h index e5ed8a3de..4d4e59b0b 100644 --- a/include/sway/desktop/idle_inhibit_v1.h +++ b/include/sway/desktop/idle_inhibit_v1.h @@ -4,6 +4,14 @@ #include #include "sway/server.h" +enum sway_idle_inhibit_mode { + INHIBIT_IDLE_APPLICATION, // Application set inhibitor (when visible) + INHIBIT_IDLE_FOCUS, // User set inhibitor when focused + INHIBIT_IDLE_FULLSCREEN, // User set inhibitor when fullscreen + visible + INHIBIT_IDLE_OPEN, // User set inhibitor while open + INHIBIT_IDLE_VISIBLE // User set inhibitor when visible +}; + struct sway_idle_inhibit_manager_v1 { struct wlr_idle_inhibit_manager_v1 *wlr_manager; struct wl_listener new_idle_inhibitor_v1; @@ -15,14 +23,24 @@ struct sway_idle_inhibit_manager_v1 { struct sway_idle_inhibitor_v1 { struct sway_idle_inhibit_manager_v1 *manager; struct sway_view *view; + enum sway_idle_inhibit_mode mode; struct wl_list link; struct wl_listener destroy; }; -void idle_inhibit_v1_check_active( +void sway_idle_inhibit_v1_check_active( struct sway_idle_inhibit_manager_v1 *manager); +void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, + enum sway_idle_inhibit_mode mode); + +struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( + struct sway_view *view); + +void sway_idle_inhibit_v1_user_inhibitor_destroy( + struct sway_idle_inhibitor_v1 *inhibitor); + struct sway_idle_inhibit_manager_v1 *sway_idle_inhibit_manager_v1_create( struct wl_display *wl_display, struct wlr_idle *idle); #endif diff --git a/sway/commands.c b/sway/commands.c index 0d9460a21..abdaa3b87 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -112,6 +112,7 @@ static struct cmd_handler command_handlers[] = { { "exit", cmd_exit }, { "floating", cmd_floating }, { "fullscreen", cmd_fullscreen }, + { "inhibit_idle", cmd_inhibit_idle }, { "kill", cmd_kill }, { "layout", cmd_layout }, { "mark", cmd_mark }, diff --git a/sway/commands/inhibit_idle.c b/sway/commands/inhibit_idle.c new file mode 100644 index 000000000..aebc2bf9f --- /dev/null +++ b/sway/commands/inhibit_idle.c @@ -0,0 +1,51 @@ +#include +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/desktop/idle_inhibit_v1.h" +#include "sway/tree/container.h" +#include "sway/tree/view.h" + +struct cmd_results *cmd_inhibit_idle(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "inhibit_idle", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + struct sway_container *con = config->handler_context.container; + if (!con || !con->view) { + return cmd_results_new(CMD_INVALID, + "Only views can have idle inhibitors"); + } + + bool clear = false; + enum sway_idle_inhibit_mode mode; + if (strcmp(argv[0], "focus") == 0) { + mode = INHIBIT_IDLE_FOCUS; + } else if (strcmp(argv[0], "fullscreen") == 0) { + mode = INHIBIT_IDLE_FULLSCREEN; + } else if (strcmp(argv[0], "open") == 0) { + mode = INHIBIT_IDLE_OPEN; + } else if (strcmp(argv[0], "none") == 0) { + clear = true; + } else if (strcmp(argv[0], "visible") == 0) { + mode = INHIBIT_IDLE_VISIBLE; + } else { + return cmd_results_new(CMD_INVALID, + "Expected `inhibit_idle focus|fullscreen|open|none|visible`"); + } + + struct sway_idle_inhibitor_v1 *inhibitor = + sway_idle_inhibit_v1_user_inhibitor_for_view(con->view); + if (inhibitor) { + if (clear) { + sway_idle_inhibit_v1_user_inhibitor_destroy(inhibitor); + } else { + inhibitor->mode = mode; + sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); + } + } else if (!clear) { + sway_idle_inhibit_v1_user_inhibitor_register(con->view, mode); + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index 87b4ef43d..b981e5e5a 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -2,18 +2,24 @@ #include #include "log.h" #include "sway/desktop/idle_inhibit_v1.h" +#include "sway/input/seat.h" +#include "sway/tree/container.h" #include "sway/tree/view.h" #include "sway/server.h" +static void destroy_inhibitor(struct sway_idle_inhibitor_v1 *inhibitor) { + wl_list_remove(&inhibitor->link); + wl_list_remove(&inhibitor->destroy.link); + sway_idle_inhibit_v1_check_active(inhibitor->manager); + free(inhibitor); +} + static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_idle_inhibitor_v1 *inhibitor = wl_container_of(listener, inhibitor, destroy); sway_log(SWAY_DEBUG, "Sway idle inhibitor destroyed"); - wl_list_remove(&inhibitor->link); - wl_list_remove(&inhibitor->destroy.link); - idle_inhibit_v1_check_active(inhibitor->manager); - free(inhibitor); + destroy_inhibitor(inhibitor); } void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { @@ -29,28 +35,93 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { } inhibitor->manager = manager; + inhibitor->mode = INHIBIT_IDLE_APPLICATION; inhibitor->view = view_from_wlr_surface(wlr_inhibitor->surface); wl_list_insert(&manager->inhibitors, &inhibitor->link); - inhibitor->destroy.notify = handle_destroy; wl_signal_add(&wlr_inhibitor->events.destroy, &inhibitor->destroy); - idle_inhibit_v1_check_active(manager); + sway_idle_inhibit_v1_check_active(manager); } -void idle_inhibit_v1_check_active( +void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, + enum sway_idle_inhibit_mode mode) { + struct sway_idle_inhibitor_v1 *inhibitor = + calloc(1, sizeof(struct sway_idle_inhibitor_v1)); + if (!inhibitor) { + return; + } + + inhibitor->manager = server.idle_inhibit_manager_v1; + inhibitor->mode = mode; + inhibitor->view = view; + wl_list_insert(&inhibitor->manager->inhibitors, &inhibitor->link); + + inhibitor->destroy.notify = handle_destroy; + wl_signal_add(&view->events.unmap, &inhibitor->destroy); + + sway_idle_inhibit_v1_check_active(inhibitor->manager); +} + +struct sway_idle_inhibitor_v1 *sway_idle_inhibit_v1_user_inhibitor_for_view( + struct sway_view *view) { + struct sway_idle_inhibitor_v1 *inhibitor; + wl_list_for_each(inhibitor, &server.idle_inhibit_manager_v1->inhibitors, + link) { + if (inhibitor->view == view && + inhibitor->mode != INHIBIT_IDLE_APPLICATION) { + return inhibitor; + } + } + return NULL; +} + +void sway_idle_inhibit_v1_user_inhibitor_destroy( + struct sway_idle_inhibitor_v1 *inhibitor) { + if (!inhibitor) { + return; + } + if (!sway_assert(inhibitor->mode != INHIBIT_IDLE_APPLICATION, + "User should not be able to destroy application inhibitor")) { + return; + } + destroy_inhibitor(inhibitor); +} + +static bool check_active(struct sway_idle_inhibitor_v1 *inhibitor) { + switch (inhibitor->mode) { + case INHIBIT_IDLE_APPLICATION: + // If there is no view associated with the inhibitor, assume visible + return !inhibitor->view || view_is_visible(inhibitor->view); + case INHIBIT_IDLE_FOCUS:; + struct sway_seat *seat = NULL; + wl_list_for_each(seat, &server.input->seats, link) { + struct sway_container *con = seat_get_focused_container(seat); + if (con && con->view && con->view == inhibitor->view) { + return true; + } + } + return false; + case INHIBIT_IDLE_FULLSCREEN: + return inhibitor->view->container && + container_is_fullscreen_or_child(inhibitor->view->container) && + view_is_visible(inhibitor->view); + case INHIBIT_IDLE_OPEN: + // Inhibitor is destroyed on unmap so it must be open/mapped + return true; + case INHIBIT_IDLE_VISIBLE: + return view_is_visible(inhibitor->view); + } + return false; +} + +void sway_idle_inhibit_v1_check_active( struct sway_idle_inhibit_manager_v1 *manager) { struct sway_idle_inhibitor_v1 *inhibitor; bool inhibited = false; wl_list_for_each(inhibitor, &manager->inhibitors, link) { - if (!inhibitor->view || !inhibitor->view->container) { - /* Cannot guess if view is visible so assume it is */ - inhibited = true; - break; - } - if (view_is_visible(inhibitor->view)) { - inhibited = true; + if ((inhibited = check_active(inhibitor))) { break; } } diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index e5f0ee3d1..51c6e7fca 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -349,7 +349,7 @@ static void transaction_progress_queue(void) { list_del(server.transactions, 0); if (!server.transactions->length) { - idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); + sway_idle_inhibit_v1_check_active(server.idle_inhibit_manager_v1); return; } diff --git a/sway/meson.build b/sway/meson.build index 9f79fb6ce..12b86efb8 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -63,6 +63,7 @@ sway_sources = files( 'commands/fullscreen.c', 'commands/gaps.c', 'commands/hide_edge_borders.c', + 'commands/inhibit_idle.c', 'commands/kill.c', 'commands/mark.c', 'commands/opacity.c', diff --git a/sway/sway.5.scd b/sway/sway.5.scd index dbfeefe3c..1650cd60c 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -146,6 +146,18 @@ set|plus|minus _right_, _bottom_, and _left_ or per direction with _horizontal_ and _vertical_. +*inhibit_idle* focus|fullscreen|open|none|visible + Set/unset an idle inhibitor for the view. _focus_ will inhibit idle when + the view is focused by any seat. _fullscreen_ will inhibit idle when the + view is fullscreen (or a descendant of a fullscreen container) and is + visible. _open_ will inhibit idle until the view is closed (or the + inhibitor is unset/changed). _visible_ will inhibit idle when the view is + visible on any output. _none_ will remove any existing idle inhibitor for + the view. + + This can also be used with criteria to set an idle inhibitor for any + existing view or with _for_window_ to set idle inhibitors for future views. + *layout* default|splith|splitv|stacking|tabbed Sets the layout mode of the focused container. From e9d096468a3fe9d8346dd97a67e050938f0befde Mon Sep 17 00:00:00 2001 From: Igor Sviatniy Date: Mon, 25 Mar 2019 17:53:10 +0200 Subject: [PATCH 0222/2054] Update README.uk.md --- README.uk.md | 49 +++++++++++++++++++------------------------------ 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/README.uk.md b/README.uk.md index 4c378a89f..b0c81c512 100644 --- a/README.uk.md +++ b/README.uk.md @@ -1,20 +1,12 @@ # sway -**Sway** це сумісний з i3 композитор [Wayland](http://wayland.freedesktop.org/) -(**у стані розробки**). Ознайомтесь з -[ЧаПами](https://github.com/swaywm/sway/wiki). Приєднуйтесь до [спільноти в +Sway це сумісний з i3 композитор [Wayland](http://wayland.freedesktop.org/). +Ознайомтесь з [ЧаПами](https://github.com/swaywm/sway/wiki). Приєднуйтесь до [спільноти в IRC](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway на irc.freenode.net). -[![](https://sr.ht/ICd5.png)](https://sr.ht/ICd5.png) - -Якщо ви хочете підтримати розробку Sway, ви можете зробити свій внесок у -[SirCmpwn'ову сторінку Patreon](https://patreon.com/sircmpwn) або до -[фонду винагород](https://github.com/swaywm/sway/issues/986) за реалізацію -певного функціоналу. -Кожен може виставити винагороду за реалізацію довільної функції -(і, відповідно, забрати її собі, виконавши це завдання); -кошти від сторінки Patreon підтримують загальну розробку та підтримку Sway. +Якщо ви маєте бажання підтримати розробку sway, ви можете зробити свій внесок на сторінці +[SirCmpwn у Patreon](https://patreon.com/sircmpwn). ## Підтримка українською мовою @@ -39,28 +31,28 @@ Sway доступний у багатьох дистрибутивах Linux (а для інформації щодо встановлення на вашому дистрибутиві. Якщо ви готові та зацікавлені запакувати і підтримувати Sway у вашому -дистрибутиві, будемо раді вас бачити у нашому каналі IRC. Ви також можете -спитати порад за адресою sir@cmpwn.com. +дистрибутиві, звертайтесь за порадами до нашого каналу в IRC або +пишіть на електронну пошту [sir@cmpwn.com](mailto:sir@cmpwn.com). ### З вихідного коду Встановіть залежності: -* meson -* [wlc](https://github.com/Cloudef/wlc) +* meson \* +* [wlroots](https://github.com/swaywm/wlroots) * wayland -* xwayland -* libinput >= 1.6.0 -* libcap +* wayland-protocols \* * pcre -* json-c >= 0.13 +* json-c * pango * cairo -* gdk-pixbuf2 * -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (required for man pages) -* git +* gdk-pixbuf2 \*\* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (необов'язково, необхідно для сторінок man) \* +* git \* -_\*Лише для swaybar, swaybg_ +_\*Лише для компіляції_ + +_\*\*Необов'язково, необхідно для swaybg_ Виконайте ці команди: @@ -68,15 +60,12 @@ _\*Лише для swaybar, swaybg_ ninja -C build sudo ninja -C build install -На системах **з** logind, варто встановити декілька можливостей (caps) -на виконуваний файл sway: - - sudo setcap "cap_sys_ptrace,cap_sys_tty_config=eip" /usr/local/bin/sway - -На системах **без** logind, необхідно встановити біт SUID на виконуваний файл sway: +На системах без logind, необхідно встановити біт SUID на виконуваний файл sway: sudo chmod a+s /usr/local/bin/sway +Sway втратить права доступу root незабаром після запуску. + ## Налаштування Якщо ви вже використовуєте i3, скопіюйте свій файл налаштувань From 3a31889d7cb00c28724bc093653b3015393c5cb4 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Tue, 26 Mar 2019 20:32:50 -0700 Subject: [PATCH 0223/2054] Fix crash for floating command on scratchpad window --- sway/commands/floating.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sway/commands/floating.c b/sway/commands/floating.c index 821093695..5df9b1bfb 100644 --- a/sway/commands/floating.c +++ b/sway/commands/floating.c @@ -45,7 +45,10 @@ struct cmd_results *cmd_floating(int argc, char **argv) { container_set_floating(container, wants_floating); - arrange_workspace(container->workspace); + // Floating containers in the scratchpad should be ignored + if (container->workspace) { + arrange_workspace(container->workspace); + } return cmd_results_new(CMD_SUCCESS, NULL); } From bfdee1319ffc8a720d0536a752a19ba23615a1e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Pokorn=C3=BD?= Date: Tue, 26 Mar 2019 23:21:30 +0100 Subject: [PATCH 0224/2054] bindings: fix overwrite log argument mismatch Thanks, @RedSoxFan, for the review spotting another instance. --- sway/commands/bind.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/commands/bind.c b/sway/commands/bind.c index b3d391da6..9a937c611 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -316,7 +316,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, struct sway_binding *config_binding = mode_bindings->items[i]; if (binding_key_compare(binding, config_binding)) { sway_log(SWAY_INFO, "Overwriting binding '%s' for device '%s' " - "from `%s` to `%s`", argv[0], binding->input, + "to `%s` from `%s`", argv[0], binding->input, binding->command, config_binding->command); if (warn) { config_add_swaynag_warning("Overwriting binding" @@ -420,7 +420,7 @@ struct cmd_results *cmd_bindswitch(int argc, char **argv) { for (int i = 0; i < mode_bindings->length; ++i) { struct sway_switch_binding *config_binding = mode_bindings->items[i]; if (binding_switch_compare(binding, config_binding)) { - sway_log(SWAY_INFO, "Overwriting binding '%s' from `%s` to `%s`", + sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`", argv[0], binding->command, config_binding->command); if (warn) { config_add_swaynag_warning("Overwriting binding" From 2cae0d5e3a9c5abc7c2aa2aad4021c86730b2ec9 Mon Sep 17 00:00:00 2001 From: Ryan Dwyer Date: Fri, 29 Mar 2019 18:29:34 +1000 Subject: [PATCH 0225/2054] Fix null pointer crash when doing tiling drag * Create layout S[V[view view] view] * Drag bottom view to the top * Sway would crash when the cursor hovers the V[view view] title while dragging --- sway/input/seatop_move_tiling.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 0a2480919..64a16c098 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -108,7 +108,7 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { } if (node->type == N_WORKSPACE) { - // Emtpy workspace + // Empty workspace e->target_node = node; e->target_edge = WLR_EDGE_NONE; workspace_get_box(node->sway_workspace, &e->drop_box); @@ -164,7 +164,7 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { // Use the hovered view - but we must be over the actual surface con = node->sway_container; - if (!con->view->surface || node == &e->con->node + if (!con->view || !con->view->surface || node == &e->con->node || node_has_ancestor(node, &e->con->node)) { e->target_node = NULL; e->target_edge = WLR_EDGE_NONE; From 31eeda11b0952e7520a5171c5b683ad6fba0f519 Mon Sep 17 00:00:00 2001 From: Alex Maese Date: Sat, 30 Mar 2019 13:01:38 -0500 Subject: [PATCH 0226/2054] Fix a crash in swaybar when an icon dir is not readable --- swaybar/tray/icon.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c index 8587f3f7b..56f230e14 100644 --- a/swaybar/tray/icon.c +++ b/swaybar/tray/icon.c @@ -348,6 +348,9 @@ void init_themes(list_t **themes, list_t **basedirs) { *themes = create_list(); for (int i = 0; i < (*basedirs)->length; ++i) { list_t *dir_themes = load_themes_in_dir((*basedirs)->items[i]); + if (dir_themes == NULL) { + continue; + } list_cat(*themes, dir_themes); list_free(dir_themes); } From dd28e6a6d6abf06d2d16e6c91aeaf942bf225af7 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 27 Mar 2019 14:00:19 -0400 Subject: [PATCH 0227/2054] Fix xwayland configure request scratchpad crash This fixes a crash in `container_init_floating` when a xwayland view sends a configure request while in the scratchpad. `container_init_floating` gets called so the configured minimum and maximum sizes gets respected when resizing to the requested size. Since the workspace was NULL, it would SIGSEGV when attempting to get the workspace's output for the output box retrieval. This extracts the resizing portion of `container_init_floating` into a separate function. If the container is in the scratchpad, it will just be resized and skip the centering. Additionally, `container_init_floating` has been renamed to `container_floating_resize_and_center` to more accurately describe what it does. --- include/sway/tree/container.h | 5 +---- sway/desktop/xwayland.c | 2 +- sway/tree/container.c | 41 +++++++++++++++++++++++------------ sway/tree/output.c | 2 +- sway/tree/root.c | 10 +-------- 5 files changed, 31 insertions(+), 29 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index d25089949..964061db6 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -215,10 +215,7 @@ size_t container_titlebar_height(void); void floating_calculate_constraints(int *min_width, int *max_width, int *min_height, int *max_height); -/** - * Resize and center the container in its workspace. - */ -void container_init_floating(struct sway_container *container); +void container_floating_resize_and_center(struct sway_container *con); void container_set_floating(struct sway_container *container, bool enable); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index f5ade8dcb..37d0b986a 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -435,7 +435,7 @@ static void handle_request_configure(struct wl_listener *listener, void *data) { // Respect minimum and maximum sizes view->natural_width = ev->width; view->natural_height = ev->height; - container_init_floating(view->container); + container_floating_resize_and_center(view->container); configure(view, view->container->content_x, view->container->content_y, diff --git a/sway/tree/container.c b/sway/tree/container.c index f84ce3602..b7ea2b7e2 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -649,8 +649,31 @@ void floating_calculate_constraints(int *min_width, int *max_width, } -void container_init_floating(struct sway_container *con) { +static void floating_natural_resize(struct sway_container *con) { + int min_width, max_width, min_height, max_height; + floating_calculate_constraints(&min_width, &max_width, + &min_height, &max_height); + if (!con->view) { + con->width = max_width; + con->height = max_height; + } else { + struct sway_view *view = con->view; + con->content_width = + fmax(min_width, fmin(view->natural_width, max_width)); + con->content_height = + fmax(min_height, fmin(view->natural_height, max_height)); + container_set_geometry_from_content(con); + } +} + +void container_floating_resize_and_center(struct sway_container *con) { struct sway_workspace *ws = con->workspace; + if (!ws) { + // On scratchpad, just resize + floating_natural_resize(con); + return; + } + struct wlr_box *ob = wlr_output_layout_get_box(root->output_layout, ws->output->wlr_output); if (!ob) { @@ -662,13 +685,8 @@ void container_init_floating(struct sway_container *con) { return; } - int min_width, max_width, min_height, max_height; - floating_calculate_constraints(&min_width, &max_width, - &min_height, &max_height); - + floating_natural_resize(con); if (!con->view) { - con->width = max_width; - con->height = max_height; if (con->width > ws->width || con->height > ws->height) { con->x = ob->x + (ob->width - con->width) / 2; con->y = ob->y + (ob->height - con->height) / 2; @@ -677,11 +695,6 @@ void container_init_floating(struct sway_container *con) { con->y = ws->y + (ws->height - con->height) / 2; } } else { - struct sway_view *view = con->view; - con->content_width = - fmax(min_width, fmin(view->natural_width, max_width)); - con->content_height = - fmax(min_height, fmin(view->natural_height, max_height)); if (con->content_width > ws->width || con->content_height > ws->height) { con->content_x = ob->x + (ob->width - con->content_width) / 2; @@ -711,7 +724,7 @@ void container_set_floating(struct sway_container *container, bool enable) { struct sway_container *old_parent = container->parent; container_detach(container); workspace_add_floating(workspace, container); - container_init_floating(container); + container_floating_resize_and_center(container); if (container->view) { view_set_tiled(container->view, false); if (container->view->using_csd) { @@ -995,7 +1008,7 @@ void container_fullscreen_disable(struct sway_container *con) { // criteria, it needs to be reinitialized as floating to get the proper // size and location if (container_is_floating(con) && (con->width == 0 || con->height == 0)) { - container_init_floating(con); + container_floating_resize_and_center(con); } con->fullscreen_mode = FULLSCREEN_NONE; diff --git a/sway/tree/output.c b/sway/tree/output.c index 283036524..b3589be5d 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -77,7 +77,7 @@ static void restore_workspaces(struct sway_output *output) { floater->y > output->ly + output->height || floater->x + floater->width < output->lx || floater->y + floater->height < output->ly) { - container_init_floating(floater); + container_floating_resize_and_center(floater); } } diff --git a/sway/tree/root.c b/sway/tree/root.c index 5dde9f22d..bc9c610df 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -124,15 +124,7 @@ void root_scratchpad_show(struct sway_container *con) { struct wlr_box workspace_box; workspace_get_box(new_ws, &workspace_box); if (!wlr_box_contains_point(&workspace_box, center_lx, center_ly)) { - // Maybe resize it - if (con->width > new_ws->width || con->height > new_ws->height) { - container_init_floating(con); - } - - // Center it - double new_lx = new_ws->x + (new_ws->width - con->width) / 2; - double new_ly = new_ws->y + (new_ws->height - con->height) / 2; - container_floating_move_to(con, new_lx, new_ly); + container_floating_resize_and_center(con); } arrange_workspace(new_ws); From 0676ace97fdd0054af6b0a4950e219ebd0a18de4 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 29 Mar 2019 12:15:17 -0400 Subject: [PATCH 0228/2054] floating: fix size of non-view containers This fixes the sizing of floating non-view containers. On master, the floater will get set to the maximum width and height, which by default is the entire output layout. When setting a non-view container to floating, this will set a sane default size of 50% of the workspace width and 75% of the workspace height, or whatever the closest is that the minimum and maximum floating width/height values allow for. On all future calls to `floating_natural_resize`, the width and height will be kept unless they need to be changed to respect the min/max floating width/height values. --- sway/tree/container.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index b7ea2b7e2..064d85f61 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -654,8 +654,8 @@ static void floating_natural_resize(struct sway_container *con) { floating_calculate_constraints(&min_width, &max_width, &min_height, &max_height); if (!con->view) { - con->width = max_width; - con->height = max_height; + con->width = fmax(min_width, fmin(con->width, max_width)); + con->height = fmax(min_height, fmin(con->height, max_height)); } else { struct sway_view *view = con->view; con->content_width = @@ -712,6 +712,22 @@ void container_floating_resize_and_center(struct sway_container *con) { } } +static void container_floating_set_default_size(struct sway_container *con) { + if (!sway_assert(con->workspace, "Expected a container on a workspace")) { + return; + } + int min_width, max_width, min_height, max_height; + floating_calculate_constraints(&min_width, &max_width, + &min_height, &max_height); + struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); + workspace_get_box(con->workspace, box); + if (!con->view) { + con->width = fmax(min_width, fmin(box->width * 0.5, max_width)); + con->height = fmax(min_height, fmin(box->height * 0.75, max_height)); + } + free(box); +} + void container_set_floating(struct sway_container *container, bool enable) { if (container_is_floating(container) == enable) { return; @@ -724,6 +740,7 @@ void container_set_floating(struct sway_container *container, bool enable) { struct sway_container *old_parent = container->parent; container_detach(container); workspace_add_floating(workspace, container); + container_floating_set_default_size(container); container_floating_resize_and_center(container); if (container->view) { view_set_tiled(container->view, false); From 679c058fac986314675bacf7a7b01d263fb0db39 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Fri, 29 Mar 2019 12:26:08 -0400 Subject: [PATCH 0229/2054] scratchpad: set initial size This matches i3's behavior of setting scratchpad containers to 50% of the workspace's width and 75% of the workspace's height, bound by the minimum and maximum floating width/height. --- include/sway/tree/container.h | 2 ++ sway/tree/container.c | 15 ++++++++++++--- sway/tree/root.c | 2 ++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 964061db6..8448d7059 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -217,6 +217,8 @@ void floating_calculate_constraints(int *min_width, int *max_width, void container_floating_resize_and_center(struct sway_container *con); +void container_floating_set_default_size(struct sway_container *con); + void container_set_floating(struct sway_container *container, bool enable); void container_set_geometry_from_content(struct sway_container *con); diff --git a/sway/tree/container.c b/sway/tree/container.c index 064d85f61..02b4d1b0e 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -712,19 +712,28 @@ void container_floating_resize_and_center(struct sway_container *con) { } } -static void container_floating_set_default_size(struct sway_container *con) { +void container_floating_set_default_size(struct sway_container *con) { if (!sway_assert(con->workspace, "Expected a container on a workspace")) { return; } + int min_width, max_width, min_height, max_height; floating_calculate_constraints(&min_width, &max_width, &min_height, &max_height); struct wlr_box *box = calloc(1, sizeof(struct wlr_box)); workspace_get_box(con->workspace, box); + + double width = fmax(min_width, fmin(box->width * 0.5, max_width)); + double height = fmax(min_height, fmin(box->height * 0.75, max_height)); if (!con->view) { - con->width = fmax(min_width, fmin(box->width * 0.5, max_width)); - con->height = fmax(min_height, fmin(box->height * 0.75, max_height)); + con->width = width; + con->height = height; + } else { + con->content_width = width; + con->content_height = height; + container_set_geometry_from_content(con); } + free(box); } diff --git a/sway/tree/root.c b/sway/tree/root.c index bc9c610df..3d93405e4 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -62,6 +62,8 @@ void root_scratchpad_add_container(struct sway_container *con) { struct sway_container *parent = con->parent; struct sway_workspace *workspace = con->workspace; container_set_floating(con, true); + container_floating_set_default_size(con); + container_floating_move_to_center(con); container_detach(con); con->scratchpad = true; list_add(root->scratchpad, con); From a32125c984a41dbbfbe5a12b450a84d78e04867a Mon Sep 17 00:00:00 2001 From: Dacheng Gao <13791720+gaodacheng@users.noreply.github.com> Date: Wed, 27 Mar 2019 21:55:59 +0800 Subject: [PATCH 0230/2054] add chinese translation --- README.zh-CN.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 README.zh-CN.md diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 000000000..3e56f229b --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,68 @@ +# sway + +sway 是和 i3 兼容的 [Wayland](http://wayland.freedesktop.org/) compositor. +阅读 [FAQ](https://github.com/swaywm/sway/wiki). 加入 [IRC +频道](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on +irc.freenode.net). + +如果你想要支持 sway 的发展, 请到这 [SirCmpwn's +Patreon page](https://patreon.com/sircmpwn)贡献. + +## 发布签名 + +发布以 [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 签名 +并发布在 [GitHub](https://github.com/swaywm/sway/releases). + +## 安装 + +### 从软件包中 + +Sway 在很多发行版中可用. 尝试在你的发行版中安装 "sway" 包. +如何这不可用, 请到 [此 wiki 页](https://github.com/swaywm/sway/wiki/Unsupported-packages) +检查针对你的发行版关于安装的信息. + +如果你有兴趣给你的发行版打包 sway, 停下来到 IRC 频道或者发邮件至 sir@cmpwn.com 获取建议. + +### 从源代码编译 + +安装依赖: + +* meson \* +* [wlroots](https://github.com/swaywm/wlroots) +* wayland +* wayland-protocols \* +* pcre +* json-c +* pango +* cairo +* gdk-pixbuf2 \*\* +* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (可选的: man pages) \* +* git \* + +_\*编译时依赖_ + +_\*\*可选的: swaybg 依赖_ + +运行这些命令: + + meson build + ninja -C build + sudo ninja -C build install + +在没有 logind 的系统上, 你需要给 sway 二进制设置 suid: + + sudo chmod a+s /usr/local/bin/sway + +Sway 将会在启动后尽快丢掉 root 权限. + +## 配置 + +如果你已经在使用 i3, 接下来复制你的 i3 配置到 `~/.config/sway/config` +它可以直接工作. 或者, 复制样本配置文件到 +`~/.config/sway/config`. 它通常位于 `/etc/sway/config`. +运行 `man 5 sway` 获取关于配置的信息. + +## 运行 + +从 TTY 中运行 `sway` . 某些显示管理器可能会工作但并不被 sway 支持 +(已知的 gdm 工作得非常好. From 3353bb58329aab882ff1d547448fa89d166d53d3 Mon Sep 17 00:00:00 2001 From: Dacheng Gao <13791720+gaodacheng@users.noreply.github.com> Date: Wed, 27 Mar 2019 21:58:41 +0800 Subject: [PATCH 0231/2054] fix cs --- README.zh-CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index 3e56f229b..a443889da 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -65,4 +65,4 @@ Sway 将会在启动后尽快丢掉 root 权限. ## 运行 从 TTY 中运行 `sway` . 某些显示管理器可能会工作但并不被 sway 支持 -(已知的 gdm 工作得非常好. +(已知的 gdm 工作得非常好). From 078935ac8856a3c6ee5d0f61e2f9760520465520 Mon Sep 17 00:00:00 2001 From: Dacheng Gao <13791720+gaodacheng@users.noreply.github.com> Date: Wed, 27 Mar 2019 22:00:40 +0800 Subject: [PATCH 0232/2054] fix cs --- README.zh-CN.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index a443889da..a60090f6a 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -5,12 +5,12 @@ sway 是和 i3 兼容的 [Wayland](http://wayland.freedesktop.org/) compositor. 频道](http://webchat.freenode.net/?channels=sway&uio=d4) (#sway on irc.freenode.net). -如果你想要支持 sway 的发展, 请到这 [SirCmpwn's +如果你想要支持 sway 的发展, 请到 [SirCmpwn's Patreon page](https://patreon.com/sircmpwn)贡献. ## 发布签名 -发布以 [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 签名 +发布是以 [B22DA89A](http://pgp.mit.edu/pks/lookup?op=vindex&search=0x52CB6609B22DA89A) 签名 并发布在 [GitHub](https://github.com/swaywm/sway/releases). ## 安装 From 4938da2a341888dd5cd0ae3c9bf37ec417e05e25 Mon Sep 17 00:00:00 2001 From: Dacheng Gao <13791720+gaodacheng@users.noreply.github.com> Date: Wed, 27 Mar 2019 22:22:00 +0800 Subject: [PATCH 0233/2054] add link to README-zh-CN --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 99835d6e5..662025ef5 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [Українська](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) - [Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) - [Español](https://github.com/swaywm/sway/blob/master/README.es.md#sway--) - -[Polski](https://github.com/swaywm/sway/blob/master/README.pl.md#sway--) +[Polski](https://github.com/swaywm/sway/blob/master/README.pl.md#sway--) - [**中文-简体**](https://github.com/swaywm/sway/blob/master/README-zh-CN.md#sway--) sway is an i3-compatible [Wayland](http://wayland.freedesktop.org/) compositor. Read the [FAQ](https://github.com/swaywm/sway/wiki). Join the [IRC From f3140506a886107c39da3ea5b17ccc3519b2ac26 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Mon, 1 Apr 2019 10:56:56 -0400 Subject: [PATCH 0234/2054] Update formatting of zh-cn link in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 662025ef5..d1d78799e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [Українська](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) - [Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) - [Español](https://github.com/swaywm/sway/blob/master/README.es.md#sway--) - -[Polski](https://github.com/swaywm/sway/blob/master/README.pl.md#sway--) - [**中文-简体**](https://github.com/swaywm/sway/blob/master/README-zh-CN.md#sway--) +[Polski](https://github.com/swaywm/sway/blob/master/README.pl.md#sway--) - [中文-简体](https://github.com/swaywm/sway/blob/master/README-zh-CN.md#sway--) sway is an i3-compatible [Wayland](http://wayland.freedesktop.org/) compositor. Read the [FAQ](https://github.com/swaywm/sway/wiki). Join the [IRC From 5e0a9282010896ac39305988a914186695f9ed85 Mon Sep 17 00:00:00 2001 From: Dacheng Gao <13791720+gaodacheng@users.noreply.github.com> Date: Tue, 2 Apr 2019 10:47:28 +0800 Subject: [PATCH 0235/2054] fix typo --- README.zh-CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index a60090f6a..6df5a1f32 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -18,7 +18,7 @@ Patreon page](https://patreon.com/sircmpwn)贡献. ### 从软件包中 Sway 在很多发行版中可用. 尝试在你的发行版中安装 "sway" 包. -如何这不可用, 请到 [此 wiki 页](https://github.com/swaywm/sway/wiki/Unsupported-packages) +如果这不可用, 请到 [此 wiki 页](https://github.com/swaywm/sway/wiki/Unsupported-packages) 检查针对你的发行版关于安装的信息. 如果你有兴趣给你的发行版打包 sway, 停下来到 IRC 频道或者发邮件至 sir@cmpwn.com 获取建议. From 1e1d9e12991e9a9be72da1fe0c887d9087dffe3a Mon Sep 17 00:00:00 2001 From: Dacheng Gao <13791720+gdcfivepercent@users.noreply.github.com> Date: Tue, 2 Apr 2019 15:17:58 +0800 Subject: [PATCH 0236/2054] fix broken link to README.zh-CN.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d1d78799e..dfddae416 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [**English**](https://github.com/swaywm/sway/blob/master/README.md#sway--) - [日本語](https://github.com/swaywm/sway/blob/master/README.ja.md#sway--) - [Deutsch](https://github.com/swaywm/sway/blob/master/README.de.md#sway--) - [Ελληνικά](https://github.com/swaywm/sway/blob/master/README.el.md#sway--) - [Français](https://github.com/swaywm/sway/blob/master/README.fr.md#sway--) - [Українська](https://github.com/swaywm/sway/blob/master/README.uk.md#sway--) - [Italiano](https://github.com/swaywm/sway/blob/master/README.it.md#sway--) - [Português](https://github.com/swaywm/sway/blob/master/README.pt.md#sway--) - [Русский](https://github.com/swaywm/sway/blob/master/README.ru.md#sway--) - [Български](https://github.com/swaywm/sway/blob/master/README.bg.md#sway--) - [Español](https://github.com/swaywm/sway/blob/master/README.es.md#sway--) - -[Polski](https://github.com/swaywm/sway/blob/master/README.pl.md#sway--) - [中文-简体](https://github.com/swaywm/sway/blob/master/README-zh-CN.md#sway--) +[Polski](https://github.com/swaywm/sway/blob/master/README.pl.md#sway--) - [中文-简体](https://github.com/swaywm/sway/blob/master/README.zh-CN.md#sway--) sway is an i3-compatible [Wayland](http://wayland.freedesktop.org/) compositor. Read the [FAQ](https://github.com/swaywm/sway/wiki). Join the [IRC From f0f20f96aa5cea382ca079b6a88fbcaf5b3782ad Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 2 Apr 2019 13:17:01 -0400 Subject: [PATCH 0237/2054] root_scratchpad_hide: fix crash when layer focused This fixes a crash in `root_scratchpad_hide` when a layer surface is focused. Since `seat_get_focus` is NULL when a layer surface is focused, the call to `node_has_ancestor` was causing a SIGSEGV since it was attempting to access the parent of NULL. This changes the call to `seat_get_focus_inactive`, which will return a node even when a layer surface is focused and is also guaranteed to have something in the focus stack if a scratchpad container is being hidden (otherwise there would not be any containers yet). --- sway/tree/root.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/tree/root.c b/sway/tree/root.c index 3d93405e4..c7c5e1df3 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -135,7 +135,7 @@ void root_scratchpad_show(struct sway_container *con) { void root_scratchpad_hide(struct sway_container *con) { struct sway_seat *seat = input_manager_current_seat(); - struct sway_node *focus = seat_get_focus(seat); + struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); struct sway_workspace *ws = con->workspace; container_detach(con); From 0ad905f23cc2862c96401b6a27d918b237db35b5 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Tue, 2 Apr 2019 11:51:46 -0400 Subject: [PATCH 0238/2054] idle_inhibit: fix crash during view destruction This fixes a crash for application set idle inhibitors when their associated view is being destroyed. There is a call to `view_is_visible` to determine is the view is visible and it assumes that the view has an container, but it is possible for the container to already have been destroyed at this point. There is a NULL check for the view in `check_active` and this re-adds the NULL check for the container that I accidentally dropped when refactoring during the inhibit_idle command PR --- sway/desktop/idle_inhibit_v1.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index b981e5e5a..73e46a8f8 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -93,7 +93,8 @@ static bool check_active(struct sway_idle_inhibitor_v1 *inhibitor) { switch (inhibitor->mode) { case INHIBIT_IDLE_APPLICATION: // If there is no view associated with the inhibitor, assume visible - return !inhibitor->view || view_is_visible(inhibitor->view); + return !inhibitor->view || !inhibitor->view->container || + view_is_visible(inhibitor->view); case INHIBIT_IDLE_FOCUS:; struct sway_seat *seat = NULL; wl_list_for_each(seat, &server.input->seats, link) { From 75e7bd24ccb9731065bb7f8313aa53ba11ddc420 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 3 Apr 2019 21:53:43 -0400 Subject: [PATCH 0239/2054] swaybg: one instance for all outputs This makes it so there will only be one swaybg instance running instead of one per output. swaybg's cli has been changed to a xrandr like interface, where you select an output and then change properties for that output and then select another output and repeat. This also makes it so swaybg is only killed and respawned when a background changes or when reloading. --- include/sway/config.h | 10 +- include/sway/output.h | 3 - sway/commands/output.c | 5 + sway/config.c | 4 + sway/config/output.c | 252 +++++++++++++--------- sway/desktop/output.c | 2 - sway/tree/output.c | 4 - swaybg/main.c | 468 +++++++++++++++++++++++++++++------------ 8 files changed, 497 insertions(+), 251 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index d49120a06..fe06fb9de 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -424,7 +424,6 @@ struct sway_config { list_t *active_bar_modifiers; struct sway_mode *current_mode; struct bar_config *current_bar; - char *swaybg_command; uint32_t floating_mod; bool floating_mod_inverse; uint32_t dragging_key; @@ -447,6 +446,11 @@ struct sway_config { enum sway_popup_during_fullscreen popup_during_fullscreen; bool xwayland; + // swaybg + char *swaybg_command; + struct wl_client *swaybg_client; + struct wl_listener swaybg_client_destroy; + // Flags enum focus_follows_mouse_mode focus_follows_mouse; enum mouse_warping_mode mouse_warping; @@ -607,6 +611,8 @@ void reset_outputs(void); void free_output_config(struct output_config *oc); +bool spawn_swaybg(void); + int workspace_output_cmp_workspace(const void *a, const void *b); int sway_binding_cmp(const void *a, const void *b); @@ -625,8 +631,6 @@ void load_swaybar(struct bar_config *bar); void load_swaybars(void); -void terminate_swaybg(pid_t pid); - struct bar_config *default_bar_config(void); void free_bar_config(struct bar_config *bar); diff --git a/include/sway/output.h b/include/sway/output.h index c336c5592..cae77e2ec 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -38,8 +38,6 @@ struct sway_output { struct sway_output_state current; - struct wl_client *swaybg_client; - struct wl_listener destroy; struct wl_listener mode; struct wl_listener transform; @@ -47,7 +45,6 @@ struct sway_output { struct wl_listener present; struct wl_listener damage_destroy; struct wl_listener damage_frame; - struct wl_listener swaybg_client_destroy; struct { struct wl_signal destroy; diff --git a/sway/commands/output.c b/sway/commands/output.c index 44e28512f..6b9eafdbe 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -68,6 +68,8 @@ struct cmd_results *cmd_output(int argc, char **argv) { config->handler_context.leftovers.argc = 0; config->handler_context.leftovers.argv = NULL; + bool background = output->background; + output = store_output_config(output); // If reloading, the output configs will be applied after reading the @@ -75,6 +77,9 @@ struct cmd_results *cmd_output(int argc, char **argv) { // workspace name is not given to re-enabled outputs. if (!config->reloading) { apply_output_config_to_outputs(output); + if (background) { + spawn_swaybg(); + } } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/config.c b/sway/config.c index 7104f55d7..d5bfe105f 100644 --- a/sway/config.c +++ b/sway/config.c @@ -104,6 +104,9 @@ void free_config(struct sway_config *config) { } list_free(config->output_configs); } + if (config->swaybg_client != NULL) { + wl_client_destroy(config->swaybg_client); + } if (config->input_configs) { for (int i = 0; i < config->input_configs->length; i++) { free_input_config(config->input_configs->items[i]); @@ -480,6 +483,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { if (is_active) { reset_outputs(); + spawn_swaybg(); config->reloading = false; if (config->swaynag_config_errors.pid > 0) { diff --git a/sway/config/output.c b/sway/config/output.c index d06051b34..0473d0ad5 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -228,91 +228,6 @@ static bool set_mode(struct wlr_output *output, int width, int height, return wlr_output_set_mode(output, best); } -static void handle_swaybg_client_destroy(struct wl_listener *listener, - void *data) { - struct sway_output *output = - wl_container_of(listener, output, swaybg_client_destroy); - wl_list_remove(&output->swaybg_client_destroy.link); - wl_list_init(&output->swaybg_client_destroy.link); - output->swaybg_client = NULL; -} - -static bool set_cloexec(int fd, bool cloexec) { - int flags = fcntl(fd, F_GETFD); - if (flags == -1) { - sway_log_errno(SWAY_ERROR, "fcntl failed"); - return false; - } - if (cloexec) { - flags = flags | FD_CLOEXEC; - } else { - flags = flags & ~FD_CLOEXEC; - } - if (fcntl(fd, F_SETFD, flags) == -1) { - sway_log_errno(SWAY_ERROR, "fcntl failed"); - return false; - } - return true; -} - -static bool spawn_swaybg(struct sway_output *output, char *const cmd[]) { - int sockets[2]; - if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) { - sway_log_errno(SWAY_ERROR, "socketpair failed"); - return false; - } - if (!set_cloexec(sockets[0], true) || !set_cloexec(sockets[1], true)) { - return false; - } - - output->swaybg_client = wl_client_create(server.wl_display, sockets[0]); - if (output->swaybg_client == NULL) { - sway_log_errno(SWAY_ERROR, "wl_client_create failed"); - return false; - } - - output->swaybg_client_destroy.notify = handle_swaybg_client_destroy; - wl_client_add_destroy_listener(output->swaybg_client, - &output->swaybg_client_destroy); - - pid_t pid = fork(); - if (pid < 0) { - sway_log_errno(SWAY_ERROR, "fork failed"); - return false; - } else if (pid == 0) { - pid = fork(); - if (pid < 0) { - sway_log_errno(SWAY_ERROR, "fork failed"); - _exit(EXIT_FAILURE); - } else if (pid == 0) { - if (!set_cloexec(sockets[1], false)) { - _exit(EXIT_FAILURE); - } - - char wayland_socket_str[16]; - snprintf(wayland_socket_str, sizeof(wayland_socket_str), - "%d", sockets[1]); - setenv("WAYLAND_SOCKET", wayland_socket_str, true); - - execvp(cmd[0], cmd); - sway_log_errno(SWAY_ERROR, "execvp failed"); - _exit(EXIT_FAILURE); - } - _exit(EXIT_SUCCESS); - } - - if (close(sockets[1]) != 0) { - sway_log_errno(SWAY_ERROR, "close failed"); - return false; - } - if (waitpid(pid, NULL, 0) < 0) { - sway_log_errno(SWAY_ERROR, "waitpid failed"); - return false; - } - - return true; -} - bool apply_output_config(struct output_config *oc, struct sway_output *output) { if (output == root->noop_output) { return false; @@ -397,25 +312,6 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { wlr_output_transformed_resolution(wlr_output, &output->width, &output->height); - if (output->swaybg_client != NULL) { - wl_client_destroy(output->swaybg_client); - } - if (oc && oc->background && config->swaybg_command) { - sway_log(SWAY_DEBUG, "Setting background for output %s to %s", - wlr_output->name, oc->background); - - char *const cmd[] = { - config->swaybg_command, - wlr_output->name, - oc->background, - oc->background_option, - oc->background_fallback ? oc->background_fallback : NULL, - NULL, - }; - if (!spawn_swaybg(output, cmd)) { - return false; - } - } if (oc && oc->dpms_state == DPMS_OFF) { sway_log(SWAY_DEBUG, "Turning off screen"); @@ -584,3 +480,151 @@ void free_output_config(struct output_config *oc) { free(oc->background_option); free(oc); } + +static void handle_swaybg_client_destroy(struct wl_listener *listener, + void *data) { + wl_list_remove(&config->swaybg_client_destroy.link); + wl_list_init(&config->swaybg_client_destroy.link); + config->swaybg_client = NULL; +} + +static bool set_cloexec(int fd, bool cloexec) { + int flags = fcntl(fd, F_GETFD); + if (flags == -1) { + sway_log_errno(SWAY_ERROR, "fcntl failed"); + return false; + } + if (cloexec) { + flags = flags | FD_CLOEXEC; + } else { + flags = flags & ~FD_CLOEXEC; + } + if (fcntl(fd, F_SETFD, flags) == -1) { + sway_log_errno(SWAY_ERROR, "fcntl failed"); + return false; + } + return true; +} + +static bool _spawn_swaybg(char **command) { + if (config->swaybg_client != NULL) { + wl_client_destroy(config->swaybg_client); + } + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) { + sway_log_errno(SWAY_ERROR, "socketpair failed"); + return false; + } + if (!set_cloexec(sockets[0], true) || !set_cloexec(sockets[1], true)) { + return false; + } + + config->swaybg_client = wl_client_create(server.wl_display, sockets[0]); + if (config->swaybg_client == NULL) { + sway_log_errno(SWAY_ERROR, "wl_client_create failed"); + return false; + } + + config->swaybg_client_destroy.notify = handle_swaybg_client_destroy; + wl_client_add_destroy_listener(config->swaybg_client, + &config->swaybg_client_destroy); + + pid_t pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_ERROR, "fork failed"); + return false; + } else if (pid == 0) { + pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_ERROR, "fork failed"); + _exit(EXIT_FAILURE); + } else if (pid == 0) { + if (!set_cloexec(sockets[1], false)) { + _exit(EXIT_FAILURE); + } + + char wayland_socket_str[16]; + snprintf(wayland_socket_str, sizeof(wayland_socket_str), + "%d", sockets[1]); + setenv("WAYLAND_SOCKET", wayland_socket_str, true); + + execvp(command[0], command); + sway_log_errno(SWAY_ERROR, "execvp failed"); + _exit(EXIT_FAILURE); + } + _exit(EXIT_SUCCESS); + } + + if (close(sockets[1]) != 0) { + sway_log_errno(SWAY_ERROR, "close failed"); + return false; + } + if (waitpid(pid, NULL, 0) < 0) { + sway_log_errno(SWAY_ERROR, "waitpid failed"); + return false; + } + + return true; +} + +bool spawn_swaybg(void) { + if (!config->swaybg_command) { + return true; + } + + size_t length = 2; + for (int i = 0; i < config->output_configs->length; i++) { + struct output_config *oc = config->output_configs->items[i]; + if (!oc->background) { + continue; + } + if (strcmp(oc->background_option, "solid_color") == 0) { + length += 4; + } else if (oc->background_fallback) { + length += 8; + } else { + length += 6; + } + } + + char **cmd = calloc(1, sizeof(char **) * length); + if (!cmd) { + sway_log(SWAY_ERROR, "Failed to allocate spawn_swaybg command"); + return false; + } + + size_t i = 0; + cmd[i++] = config->swaybg_command; + for (int j = 0; j < config->output_configs->length; j++) { + struct output_config *oc = config->output_configs->items[j]; + if (!oc->background) { + continue; + } + if (strcmp(oc->background_option, "solid_color") == 0) { + cmd[i++] = "-o"; + cmd[i++] = oc->name; + cmd[i++] = "-c"; + cmd[i++] = oc->background; + } else { + cmd[i++] = "-o"; + cmd[i++] = oc->name; + cmd[i++] = "-i"; + cmd[i++] = oc->background; + cmd[i++] = "-m"; + cmd[i++] = oc->background_option; + if (oc->background_fallback) { + cmd[i++] = "-c"; + cmd[i++] = oc->background_fallback; + } + } + assert(i <= length); + } + + for (size_t k = 0; k < i; k++) { + sway_log(SWAY_DEBUG, "spawn_swaybg cmd[%ld] = %s", k, cmd[k]); + } + + bool result = _spawn_swaybg(cmd); + free(cmd); + return result; +} diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 9d0c0ef5d..0b3e1edb9 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -525,7 +525,6 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&output->present.link); wl_list_remove(&output->damage_destroy.link); wl_list_remove(&output->damage_frame.link); - wl_list_remove(&output->swaybg_client_destroy.link); transaction_commit_dirty(); } @@ -632,7 +631,6 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->damage_frame.notify = damage_handle_frame; wl_signal_add(&output->damage->events.destroy, &output->damage_destroy); output->damage_destroy.notify = damage_handle_destroy; - wl_list_init(&output->swaybg_client_destroy.link); struct output_config *oc = find_output_config(output); if (!oc || oc->enabled) { diff --git a/sway/tree/output.c b/sway/tree/output.c index b3589be5d..24adc08d6 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -262,10 +262,6 @@ void output_disable(struct sway_output *output) { root_for_each_container(untrack_output, output); - if (output->swaybg_client != NULL) { - wl_client_destroy(output->swaybg_client); - } - int index = list_find(root->outputs, output); list_del(root->outputs, index); diff --git a/swaybg/main.c b/swaybg/main.c index e66221f06..b983dd6a0 100644 --- a/swaybg/main.c +++ b/swaybg/main.c @@ -1,10 +1,12 @@ +#define _POSIX_C_SOURCE 200809L #include #include +#include #include #include #include #include -#include +#include #include #include "background-image.h" #include "cairo.h" @@ -14,49 +16,44 @@ #include "wlr-layer-shell-unstable-v1-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" -struct swaybg_state; - -struct swaybg_args { - const char *output; - const char *path; - enum background_mode mode; - const char *fallback; -}; - -struct swaybg_context { - uint32_t color; - cairo_surface_t *image; -}; - -struct swaybg_output { - struct wl_output *wl_output; - struct zxdg_output_v1 *xdg_output; - struct swaybg_state *state; - struct wl_list link; - - int32_t scale; -}; - struct swaybg_state { - const struct swaybg_args *args; - struct swaybg_context context; - struct wl_display *display; struct wl_compositor *compositor; struct wl_shm *shm; - struct wl_list outputs; struct zwlr_layer_shell_v1 *layer_shell; struct zxdg_output_manager_v1 *xdg_output_manager; - - struct swaybg_output *output; - struct wl_surface *surface; - struct wl_region *input_region; - struct zwlr_layer_surface_v1 *layer_surface; - + struct wl_list configs; // struct swaybg_output_config::link + struct wl_list outputs; // struct swaybg_output::link bool run_display; - uint32_t width, height; +}; + +struct swaybg_output_config { + char *output; + cairo_surface_t *image; + enum background_mode mode; + uint32_t color; + struct wl_list link; +}; + +struct swaybg_output { + uint32_t wl_name; + struct wl_output *wl_output; + struct zxdg_output_v1 *xdg_output; + char *name; + char *identifier; + + struct swaybg_state *state; + struct swaybg_output_config *config; + + struct wl_surface *surface; + struct zwlr_layer_surface_v1 *layer_surface; struct pool_buffer buffers[2]; struct pool_buffer *current_buffer; + + uint32_t width, height; + int32_t scale; + + struct wl_list link; }; bool is_valid_color(const char *color) { @@ -77,68 +74,82 @@ bool is_valid_color(const char *color) { return true; } -static void render_frame(struct swaybg_state *state) { - int buffer_width = state->width * state->output->scale, - buffer_height = state->height * state->output->scale; - state->current_buffer = get_next_buffer(state->shm, - state->buffers, buffer_width, buffer_height); - if (!state->current_buffer) { +static void render_frame(struct swaybg_output *output) { + int buffer_width = output->width * output->scale, + buffer_height = output->height * output->scale; + output->current_buffer = get_next_buffer(output->state->shm, + output->buffers, buffer_width, buffer_height); + if (!output->current_buffer) { return; } - cairo_t *cairo = state->current_buffer->cairo; + cairo_t *cairo = output->current_buffer->cairo; cairo_save(cairo); cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); cairo_paint(cairo); cairo_restore(cairo); - if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) { - cairo_set_source_u32(cairo, state->context.color); + if (output->config->mode == BACKGROUND_MODE_SOLID_COLOR) { + cairo_set_source_u32(cairo, output->config->color); cairo_paint(cairo); } else { - if (state->args->fallback && state->context.color) { - cairo_set_source_u32(cairo, state->context.color); + if (output->config->color) { + cairo_set_source_u32(cairo, output->config->color); cairo_paint(cairo); } - render_background_image(cairo, state->context.image, - state->args->mode, buffer_width, buffer_height); + render_background_image(cairo, output->config->image, + output->config->mode, buffer_width, buffer_height); } - wl_surface_set_buffer_scale(state->surface, state->output->scale); - wl_surface_attach(state->surface, state->current_buffer->buffer, 0, 0); - wl_surface_damage_buffer(state->surface, 0, 0, INT32_MAX, INT32_MAX); - wl_surface_commit(state->surface); + wl_surface_set_buffer_scale(output->surface, output->scale); + wl_surface_attach(output->surface, output->current_buffer->buffer, 0, 0); + wl_surface_damage_buffer(output->surface, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_commit(output->surface); } -static bool prepare_context(struct swaybg_state *state) { - if (state->args->mode == BACKGROUND_MODE_SOLID_COLOR) { - state->context.color = parse_color(state->args->path); - return is_valid_color(state->args->path); +static void destroy_swaybg_output_config(struct swaybg_output_config *config) { + if (!config) { + return; } - if (state->args->fallback && is_valid_color(state->args->fallback)) { - state->context.color = parse_color(state->args->fallback); + wl_list_remove(&config->link); + free(config->output); + free(config); +} + +static void destroy_swaybg_output(struct swaybg_output *output) { + if (!output) { + return; } - if (!(state->context.image = load_background_image(state->args->path))) { - return false; + wl_list_remove(&output->link); + if (output->layer_surface != NULL) { + zwlr_layer_surface_v1_destroy(output->layer_surface); } - return true; + if (output->surface != NULL) { + wl_surface_destroy(output->surface); + } + zxdg_output_v1_destroy(output->xdg_output); + wl_output_destroy(output->wl_output); + destroy_buffer(&output->buffers[0]); + destroy_buffer(&output->buffers[1]); + free(output->name); + free(output->identifier); + free(output); } static void layer_surface_configure(void *data, struct zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t width, uint32_t height) { - struct swaybg_state *state = data; - state->width = width; - state->height = height; + struct swaybg_output *output = data; + output->width = width; + output->height = height; zwlr_layer_surface_v1_ack_configure(surface, serial); - render_frame(state); + render_frame(output); } static void layer_surface_closed(void *data, struct zwlr_layer_surface_v1 *surface) { - struct swaybg_state *state = data; - zwlr_layer_surface_v1_destroy(state->layer_surface); - wl_surface_destroy(state->surface); - wl_region_destroy(state->input_region); - state->run_display = false; + struct swaybg_output *output = data; + sway_log(SWAY_DEBUG, "Destroying output %s (%s)", + output->name, output->identifier); + destroy_swaybg_output(output); } static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { @@ -164,12 +175,9 @@ static void output_done(void *data, struct wl_output *output) { static void output_scale(void *data, struct wl_output *wl_output, int32_t scale) { struct swaybg_output *output = data; - struct swaybg_state *state = output->state; - output->scale = scale; - - if (state->output == output && state->run_display) { - render_frame(state); + if (output->state->run_display && output->width > 0 && output->height > 0) { + render_frame(output); } } @@ -190,24 +198,91 @@ static void xdg_output_handle_logical_size(void *data, // Who cares } +static void find_config(struct swaybg_output *output, const char *name) { + struct swaybg_output_config *config = NULL; + wl_list_for_each(config, &output->state->configs, link) { + if (strcmp(config->output, name) == 0) { + output->config = config; + return; + } else if (!output->config && strcmp(config->output, "*") == 0) { + output->config = config; + } + } +} + static void xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) { struct swaybg_output *output = data; - struct swaybg_state *state = output->state; - if (strcmp(name, state->args->output) == 0) { - assert(state->output == NULL); - state->output = output; + output->name = strdup(name); + + // If description was sent first, the config may already be populated. If + // there is an identifier config set, keep it. + if (!output->config || strcmp(output->config->output, "*") == 0) { + find_config(output, name); } } static void xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, const char *description) { - // Who cares + struct swaybg_output *output = data; + + // wlroots currently sets the description to `make model serial (name)` + // If this changes in the future, this will need to be modified. + char *paren = strrchr(description, '('); + if (paren) { + size_t length = paren - description; + output->identifier = malloc(length); + if (!output->identifier) { + sway_log(SWAY_ERROR, "Failed to allocate output identifier"); + return; + } + strncpy(output->identifier, description, length); + output->identifier[length - 1] = '\0'; + + find_config(output, output->identifier); + } +} + +static void create_layer_surface(struct swaybg_output *output) { + output->surface = wl_compositor_create_surface(output->state->compositor); + assert(output->surface); + + // Empty input region + struct wl_region *input_region = + wl_compositor_create_region(output->state->compositor); + assert(input_region); + wl_surface_set_input_region(output->surface, input_region); + wl_region_destroy(input_region); + + output->layer_surface = zwlr_layer_shell_v1_get_layer_surface( + output->state->layer_shell, output->surface, output->wl_output, + ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper"); + assert(output->layer_surface); + + zwlr_layer_surface_v1_set_size(output->layer_surface, 0, 0); + zwlr_layer_surface_v1_set_anchor(output->layer_surface, + ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); + zwlr_layer_surface_v1_set_exclusive_zone(output->layer_surface, -1); + zwlr_layer_surface_v1_add_listener(output->layer_surface, + &layer_surface_listener, output); + wl_surface_commit(output->surface); } static void xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) { - // Who cares + struct swaybg_output *output = data; + if (!output->config) { + sway_log(SWAY_DEBUG, "Could not find config for output %s (%s)", + output->name, output->identifier); + destroy_swaybg_output(output); + } else if (!output->layer_surface) { + sway_log(SWAY_DEBUG, "Found config %s for output %s (%s)", + output->config->output, output->name, output->identifier); + create_layer_surface(output); + } } static const struct zxdg_output_v1_listener xdg_output_listener = { @@ -229,10 +304,18 @@ static void handle_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, wl_output_interface.name) == 0) { struct swaybg_output *output = calloc(1, sizeof(struct swaybg_output)); output->state = state; + output->wl_name = name; output->wl_output = wl_registry_bind(registry, name, &wl_output_interface, 3); wl_output_add_listener(output->wl_output, &output_listener, output); wl_list_insert(&state->outputs, &output->link); + + if (state->run_display) { + output->xdg_output = zxdg_output_manager_v1_get_xdg_output( + state->xdg_output_manager, output->wl_output); + zxdg_output_v1_add_listener(output->xdg_output, + &xdg_output_listener, output); + } } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { state->layer_shell = wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1); @@ -244,7 +327,16 @@ static void handle_global(void *data, struct wl_registry *registry, static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { - // who cares + struct swaybg_state *state = data; + struct swaybg_output *output, *tmp; + wl_list_for_each_safe(output, tmp, &state->outputs, link) { + if (output->wl_name == name) { + sway_log(SWAY_DEBUG, "Destroying output %s (%s)", + output->name, output->identifier); + destroy_swaybg_output(output); + break; + } + } } static const struct wl_registry_listener registry_listener = { @@ -252,32 +344,159 @@ static const struct wl_registry_listener registry_listener = { .global_remove = handle_global_remove, }; -int main(int argc, const char **argv) { +static bool store_swaybg_output_config(struct swaybg_state *state, + struct swaybg_output_config *config) { + struct swaybg_output_config *oc = NULL; + wl_list_for_each(oc, &state->configs, link) { + if (strcmp(config->output, oc->output) == 0) { + // Merge on top + if (config->image) { + free(oc->image); + oc->image = config->image; + config->image = NULL; + } + if (config->color) { + oc->color = config->color; + } + if (config->mode != BACKGROUND_MODE_INVALID) { + oc->mode = config->mode; + } + return false; + } + } + // New config, just add it + wl_list_insert(&state->configs, &config->link); + return true; +} + +static void parse_command_line(int argc, char **argv, + struct swaybg_state *state) { + static struct option long_options[] = { + {"color", required_argument, NULL, 'c'}, + {"help", no_argument, NULL, 'h'}, + {"image", required_argument, NULL, 'i'}, + {"mode", required_argument, NULL, 'm'}, + {"output", required_argument, NULL, 'o'}, + {"version", no_argument, NULL, 'v'}, + {0, 0, 0, 0} + }; + + const char *usage = + "Usage: swaybg \n" + "\n" + " -c, --color Set the background color.\n" + " -h, --help Show help message and quit.\n" + " -i, --image Set the image to display.\n" + " -m, --mode Set the mode to use for the image.\n" + " -o, --output Set the output to operate on or * for all.\n" + " -v, --version Show the version number and quit.\n" + "\n" + "Background Modes:\n" + " stretch, fit, fill, center, tile, or solid_color\n"; + + struct swaybg_output_config *config = NULL; + + int c; + while (1) { + int option_index = 0; + c = getopt_long(argc, argv, "c:hi:m:o:v", long_options, &option_index); + if (c == -1) { + break; + } + switch (c) { + case 'c': // color + if (!config) { + goto no_output; + } + if (!is_valid_color(optarg)) { + sway_log(SWAY_ERROR, "Invalid color: %s", optarg); + continue; + } + config->color = parse_color(optarg); + break; + case 'i': // image + if (!config) { + goto no_output; + } + free(config->image); + config->image = load_background_image(optarg); + if (!config->image) { + sway_log(SWAY_ERROR, "Failed to load image: %s", optarg); + } + break; + case 'm': // mode + if (!config) { + goto no_output; + } + config->mode = parse_background_mode(optarg); + if (config->mode == BACKGROUND_MODE_INVALID) { + sway_log(SWAY_ERROR, "Invalid mode: %s", optarg); + } + break; + case 'o': // output + if (config && !store_swaybg_output_config(state, config)) { + // Empty config or merged on top of an existing one + destroy_swaybg_output_config(config); + } + config = calloc(sizeof(struct swaybg_output_config), 1); + config->output = strdup(optarg); + config->mode = BACKGROUND_MODE_INVALID; + wl_list_init(&config->link); // init for safe removal + break; + case 'v': // version + fprintf(stdout, "swaybg version " SWAY_VERSION "\n"); + exit(EXIT_SUCCESS); + break; + default: + fprintf(c == 'h' ? stdout : stderr, "%s", usage); + exit(c == 'h' ? EXIT_SUCCESS : EXIT_FAILURE); + } + } + if (config && !store_swaybg_output_config(state, config)) { + // Empty config or merged on top of an existing one + destroy_swaybg_output_config(config); + } + + // Check for invalid options + if (optind < argc) { + config = NULL; + struct swaybg_output_config *tmp = NULL; + wl_list_for_each_safe(config, tmp, &state->configs, link) { + destroy_swaybg_output_config(config); + } + // continue into empty list + } + if (wl_list_empty(&state->configs)) { + fprintf(stderr, "%s", usage); + exit(EXIT_FAILURE); + } + + // Set default mode and remove empties + config = NULL; + struct swaybg_output_config *tmp = NULL; + wl_list_for_each_safe(config, tmp, &state->configs, link) { + if (!config->image && !config->color) { + destroy_swaybg_output_config(config); + } else if (config->mode == BACKGROUND_MODE_INVALID) { + config->mode = config->image + ? BACKGROUND_MODE_STRETCH + : BACKGROUND_MODE_SOLID_COLOR; + } + } + return; +no_output: + fprintf(stderr, "Cannot operate on NULL output config\n"); + exit(EXIT_FAILURE); +} + +int main(int argc, char **argv) { sway_log_init(SWAY_DEBUG, NULL); - struct swaybg_args args = {0}; - struct swaybg_state state = { .args = &args }; + struct swaybg_state state = {0}; + wl_list_init(&state.configs); wl_list_init(&state.outputs); - if (argc < 4 || argc > 5) { - sway_log(SWAY_ERROR, "Do not run this program manually. " - "See `man 5 sway-output` and look for background options."); - return 1; - } - - args.output = argv[1]; - args.path = argv[2]; - - args.mode = parse_background_mode(argv[3]); - if (args.mode == BACKGROUND_MODE_INVALID) { - return 1; - } - - args.fallback = argc == 5 ? argv[4] : NULL; - - if (!prepare_context(&state)) { - return 1; - } + parse_command_line(argc, argv, &state); state.display = wl_display_connect(NULL); if (!state.display) { @@ -303,42 +522,21 @@ int main(int argc, const char **argv) { zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output); } - // Second roundtrip to get xdg_output properties - wl_display_roundtrip(state.display); - if (state.output == NULL) { - sway_log(SWAY_ERROR, "Cannot find output '%s'", args.output); - return 1; - } - - state.surface = wl_compositor_create_surface(state.compositor); - assert(state.surface); - - // Empty input region - state.input_region = wl_compositor_create_region(state.compositor); - assert(state.input_region); - wl_surface_set_input_region(state.surface, state.input_region); - - state.layer_surface = zwlr_layer_shell_v1_get_layer_surface( - state.layer_shell, state.surface, state.output->wl_output, - ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "wallpaper"); - assert(state.layer_surface); - - zwlr_layer_surface_v1_set_size(state.layer_surface, 0, 0); - zwlr_layer_surface_v1_set_anchor(state.layer_surface, - ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | - ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | - ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | - ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT); - zwlr_layer_surface_v1_set_exclusive_zone(state.layer_surface, -1); - zwlr_layer_surface_v1_add_listener(state.layer_surface, - &layer_surface_listener, &state); - wl_surface_commit(state.surface); - wl_display_roundtrip(state.display); state.run_display = true; while (wl_display_dispatch(state.display) != -1 && state.run_display) { // This space intentionally left blank } + struct swaybg_output *tmp_output; + wl_list_for_each_safe(output, tmp_output, &state.outputs, link) { + destroy_swaybg_output(output); + } + + struct swaybg_output_config *config = NULL, *tmp_config = NULL; + wl_list_for_each_safe(config, tmp_config, &state.configs, link) { + destroy_swaybg_output_config(config); + } + return 0; } From 0553e75b53137e6d509b6e336c21586f2b75d527 Mon Sep 17 00:00:00 2001 From: Dmitri Kourennyi Date: Fri, 22 Mar 2019 17:18:21 -0400 Subject: [PATCH 0240/2054] Implement handling of short_text field of i3 input protocol. Matches i3bar behavior of setting all blocks to use the short_text if the full text width does not fit. --- swaybar/render.c | 174 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 170 insertions(+), 4 deletions(-) diff --git a/swaybar/render.c b/swaybar/render.c index 116cc595b..faf175096 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -144,16 +144,21 @@ static void i3bar_block_unref_callback(void *data) { static uint32_t render_status_block(cairo_t *cairo, struct swaybar_output *output, struct i3bar_block *block, double *x, - bool edge) { + bool edge, bool use_short_text) { if (!block->full_text || !*block->full_text) { return 0; } + char* text = block->full_text; + if (use_short_text && block->short_text && *block->short_text) { + text = block->short_text; + } + struct swaybar_config *config = output->bar->config; int text_width, text_height; get_text_size(cairo, config->font, &text_width, &text_height, NULL, - output->scale, block->markup, "%s", block->full_text); + output->scale, block->markup, "%s", text); int margin = 3 * output->scale; double ws_vertical_padding = config->status_padding * output->scale; @@ -263,7 +268,7 @@ static uint32_t render_status_block(cairo_t *cairo, color = block->urgent ? config->colors.urgent_workspace.text : color; cairo_set_source_u32(cairo, color); pango_printf(cairo, config->font, output->scale, - block->markup, "%s", block->full_text); + block->markup, "%s", text); x_pos += width; if (block->border && block->border_right > 0) { @@ -294,13 +299,174 @@ static uint32_t render_status_block(cairo_t *cairo, return output->height; } +static void predict_status_block_pos(cairo_t *cairo, + struct swaybar_output *output, struct i3bar_block *block, double *x, + bool edge) { + if (!block->full_text || !*block->full_text) { + return; + } + + struct swaybar_config *config = output->bar->config; + + int text_width, text_height; + get_text_size(cairo, config->font, &text_width, &text_height, NULL, + output->scale, block->markup, "%s", block->full_text); + + int margin = 3 * output->scale; + double ws_vertical_padding = config->status_padding * output->scale; + + int width = text_width; + + if (block->min_width_str) { + int w; + get_text_size(cairo, config->font, &w, NULL, NULL, + output->scale, block->markup, "%s", block->min_width_str); + block->min_width = w; + } + if (width < block->min_width) { + width = block->min_width; + } + + double block_width = width; + uint32_t ideal_height = text_height + ws_vertical_padding * 2; + uint32_t ideal_surface_height = ideal_height / output->scale; + if (!output->bar->config->height && + output->height < ideal_surface_height) { + return; + } + + *x -= width; + if ((block->border || block->urgent) && block->border_left > 0) { + *x -= (block->border_left * output->scale + margin); + block_width += block->border_left * output->scale + margin; + } + if ((block->border || block->urgent) && block->border_right > 0) { + *x -= (block->border_right * output->scale + margin); + block_width += block->border_right * output->scale + margin; + } + + int sep_width, sep_height; + int sep_block_width = block->separator_block_width; + if (!edge) { + if (config->sep_symbol) { + get_text_size(cairo, config->font, &sep_width, &sep_height, NULL, + output->scale, false, "%s", config->sep_symbol); + uint32_t _ideal_height = sep_height + ws_vertical_padding * 2; + uint32_t _ideal_surface_height = _ideal_height / output->scale; + if (!output->bar->config->height && + output->height < _ideal_surface_height) { + return; + } + if (sep_width > sep_block_width) { + sep_block_width = sep_width + margin * 2; + } + } + *x -= sep_block_width; + } else if (config->status_edge_padding) { + *x -= config->status_edge_padding * output->scale; + } +} + +static double predict_status_line_pos(cairo_t *cairo, + struct swaybar_output *output, double x) { + bool edge = x == output->width * output->scale; + struct i3bar_block *block; + wl_list_for_each(block, &output->bar->status->blocks, link) { + predict_status_block_pos(cairo, output, block, &x, edge); + edge = false; + } + return x; +} + +static uint32_t predict_workspace_button_length(cairo_t *cairo, + struct swaybar_output *output, + struct swaybar_workspace *ws) { + struct swaybar_config *config = output->bar->config; + + int text_width, text_height; + get_text_size(cairo, config->font, &text_width, &text_height, NULL, + output->scale, config->pango_markup, "%s", ws->label); + + int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; + int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; + int border_width = BORDER_WIDTH * output->scale; + + uint32_t ideal_height = ws_vertical_padding * 2 + text_height + + border_width * 2; + uint32_t ideal_surface_height = ideal_height / output->scale; + if (!output->bar->config->height && + output->height < ideal_surface_height) { + return 0; + } + + return ws_horizontal_padding * 2 + text_width + border_width * 2; +} + +static uint32_t predict_workspace_buttons_length(cairo_t *cairo, + struct swaybar_output *output) { + uint32_t width = 0; + if (output->bar->config->workspace_buttons) { + struct swaybar_workspace *ws; + wl_list_for_each(ws, &output->workspaces, link) { + width += predict_workspace_button_length(cairo, output, ws); + } + } + return width; +} + +static uint32_t predict_binding_mode_indicator_length(cairo_t *cairo, + struct swaybar_output *output) { + const char *mode = output->bar->mode; + if (!mode) { + return 0; + } + + struct swaybar_config *config = output->bar->config; + + if (!config->binding_mode_indicator) { + return 0; + } + + int text_width, text_height; + get_text_size(cairo, config->font, &text_width, &text_height, NULL, + output->scale, output->bar->mode_pango_markup, + "%s", mode); + + int ws_vertical_padding = WS_VERTICAL_PADDING * output->scale; + int ws_horizontal_padding = WS_HORIZONTAL_PADDING * output->scale; + int border_width = BORDER_WIDTH * output->scale; + + uint32_t ideal_height = text_height + ws_vertical_padding * 2 + + border_width * 2; + uint32_t ideal_surface_height = ideal_height / output->scale; + if (!output->bar->config->height && + output->height < ideal_surface_height) { + return 0; + } + return text_width + ws_horizontal_padding * 2 + border_width * 2; +} + static uint32_t render_status_line_i3bar(cairo_t *cairo, struct swaybar_output *output, double *x) { uint32_t max_height = 0; bool edge = *x == output->width * output->scale; struct i3bar_block *block; + bool use_short_text = false; + + // TODO: Add margin here? + uint32_t reserved_width = predict_workspace_buttons_length(cairo, output) + + predict_binding_mode_indicator_length(cairo, output); + + uint32_t predicted_full_pos = + predict_status_line_pos(cairo, output, *x); + + if (predicted_full_pos < reserved_width) { + use_short_text = true; + } + wl_list_for_each(block, &output->bar->status->blocks, link) { - uint32_t h = render_status_block(cairo, output, block, x, edge); + uint32_t h = render_status_block(cairo, output, block, x, edge, + use_short_text); max_height = h > max_height ? h : max_height; edge = false; } From 94dc57f3c5e147ebcb378104d51352c33f2fa9da Mon Sep 17 00:00:00 2001 From: Dmitri Kourennyi Date: Mon, 1 Apr 2019 16:12:42 -0400 Subject: [PATCH 0241/2054] Ensure predicted position for short text handling doesn't overflow. - Predicted status line can be negative, so corresponding variables should not be unsigned. Changed to double as position is actually calculated as double. --- swaybar/render.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/swaybar/render.c b/swaybar/render.c index faf175096..a0200f0d8 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -454,10 +454,11 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo, bool use_short_text = false; // TODO: Add margin here? - uint32_t reserved_width = predict_workspace_buttons_length(cairo, output) + + double reserved_width = + predict_workspace_buttons_length(cairo, output) + predict_binding_mode_indicator_length(cairo, output); - uint32_t predicted_full_pos = + double predicted_full_pos = predict_status_line_pos(cairo, output, *x); if (predicted_full_pos < reserved_width) { From 4b892a79aae47ef2a7a4776e959eca612e0df08b Mon Sep 17 00:00:00 2001 From: Dmitri Kourennyi Date: Mon, 1 Apr 2019 16:13:39 -0400 Subject: [PATCH 0242/2054] Add margin to short_text handling. Add a 3xscale margin matching other spacing in swaybar as part of short text width calculations --- swaybar/render.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/swaybar/render.c b/swaybar/render.c index a0200f0d8..e17c13a88 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -453,10 +453,10 @@ static uint32_t render_status_line_i3bar(cairo_t *cairo, struct i3bar_block *block; bool use_short_text = false; - // TODO: Add margin here? double reserved_width = predict_workspace_buttons_length(cairo, output) + - predict_binding_mode_indicator_length(cairo, output); + predict_binding_mode_indicator_length(cairo, output) + + 3 * output->scale; // require a bit of space for margin double predicted_full_pos = predict_status_line_pos(cairo, output, *x); From e0324fc88c4a9041e8c97bdf99ee33eb198c1310 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Mon, 8 Apr 2019 19:18:42 +0000 Subject: [PATCH 0243/2054] config/output: unbreak on 32-bit architectures sway/config/output.c:624:54: error: format specifies type 'long' but the argument has type 'size_t' (aka 'unsigned int') [-Werror,-Wformat] sway_log(SWAY_DEBUG, "spawn_swaybg cmd[%ld] = %s", k, cmd[k]); ~~~ ^ %zu include/log.h:40:74: note: expanded from macro 'sway_log' _sway_log(verb, "[%s:%d] " fmt, _sway_strip_path(__FILE__), __LINE__, ##__VA_ARGS__) ^~~~~~~~~~~ --- sway/config/output.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/config/output.c b/sway/config/output.c index 0473d0ad5..747ab28b7 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -621,7 +621,7 @@ bool spawn_swaybg(void) { } for (size_t k = 0; k < i; k++) { - sway_log(SWAY_DEBUG, "spawn_swaybg cmd[%ld] = %s", k, cmd[k]); + sway_log(SWAY_DEBUG, "spawn_swaybg cmd[%zd] = %s", k, cmd[k]); } bool result = _spawn_swaybg(cmd); From 195226120fcf4854c90e544a6e7e0ec0b1c3312c Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 24 Mar 2019 23:24:02 -0400 Subject: [PATCH 0244/2054] Honor output for xdg_toplevel_set_fullscreen This honors the fullscreen output request for `xdg_toplevel_set_fullscreen` and `zxdg_toplevel_v6_set_fullscreen`. If the request was sent before mapping, the fullscreen output request will be retrieved from the client_pending state for the toplevel. The output will be passed to `view_map` and if there is a workspace on the output, the view will be placed on that workspace. If the request comes in after being mapped, the view will be moved to the workspace on the output (if there is one) before becoming fullscreen. --- include/sway/tree/view.h | 2 +- sway/desktop/xdg_shell.c | 16 +++++++++++++++- sway/desktop/xdg_shell_v6.c | 16 +++++++++++++++- sway/desktop/xwayland.c | 2 +- sway/tree/view.c | 16 ++++++++++++++-- 5 files changed, 46 insertions(+), 6 deletions(-) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index ac203ac79..bdd8960cd 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -314,7 +314,7 @@ void view_destroy(struct sway_view *view); void view_begin_destroy(struct sway_view *view); void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, - bool fullscreen, bool decoration); + bool fullscreen, struct wlr_output *fullscreen_output, bool decoration); void view_unmap(struct sway_view *view); diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index ea5dcd16c..9e914f144 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -339,6 +339,18 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) return; } + if (e->fullscreen && e->output && e->output->data) { + struct sway_output *output = e->output->data; + struct sway_workspace *ws = output_get_active_workspace(output); + if (ws && !container_is_scratchpad_hidden(view->container)) { + if (container_is_floating(view->container)) { + workspace_add_floating(ws, view->container); + } else { + workspace_add_tiling(ws, view->container); + } + } + } + container_set_fullscreen(view->container, e->fullscreen); arrange_root(); @@ -417,7 +429,9 @@ static void handle_map(struct wl_listener *listener, void *data) { } view_map(view, view->wlr_xdg_surface->surface, - xdg_surface->toplevel->client_pending.fullscreen, csd); + xdg_surface->toplevel->client_pending.fullscreen, + xdg_surface->toplevel->client_pending.fullscreen_output, + csd); transaction_commit_dirty(); diff --git a/sway/desktop/xdg_shell_v6.c b/sway/desktop/xdg_shell_v6.c index 7ff4c4de8..5ac589cfd 100644 --- a/sway/desktop/xdg_shell_v6.c +++ b/sway/desktop/xdg_shell_v6.c @@ -339,6 +339,18 @@ static void handle_request_fullscreen(struct wl_listener *listener, void *data) return; } + if (e->fullscreen && e->output && e->output->data) { + struct sway_output *output = e->output->data; + struct sway_workspace *ws = output_get_active_workspace(output); + if (ws && !container_is_scratchpad_hidden(view->container)) { + if (container_is_floating(view->container)) { + workspace_add_floating(ws, view->container); + } else { + workspace_add_tiling(ws, view->container); + } + } + } + container_set_fullscreen(view->container, e->fullscreen); arrange_root(); @@ -411,7 +423,9 @@ static void handle_map(struct wl_listener *listener, void *data) { == WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT; view_map(view, view->wlr_xdg_surface_v6->surface, - xdg_surface->toplevel->client_pending.fullscreen, csd); + xdg_surface->toplevel->client_pending.fullscreen, + xdg_surface->toplevel->client_pending.fullscreen_output, + csd); transaction_commit_dirty(); diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 37d0b986a..f6ca8f818 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -415,7 +415,7 @@ static void handle_map(struct wl_listener *listener, void *data) { xwayland_view->commit.notify = handle_commit; // Put it back into the tree - view_map(view, xsurface->surface, xsurface->fullscreen, false); + view_map(view, xsurface->surface, xsurface->fullscreen, NULL, false); transaction_commit_dirty(); } diff --git a/sway/tree/view.c b/sway/tree/view.c index 4759c9982..2c8839f59 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -561,14 +561,26 @@ static bool should_focus(struct sway_view *view) { } void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, - bool fullscreen, bool decoration) { + bool fullscreen, struct wlr_output *fullscreen_output, + bool decoration) { if (!sway_assert(view->surface == NULL, "cannot map mapped view")) { return; } view->surface = wlr_surface; + // If there is a request to be opened fullscreen on a specific output, try + // to honor that request. Otherwise, fallback to assigns, pid mappings, + // focused workspace, etc + struct sway_workspace *ws = NULL; + if (fullscreen_output && fullscreen_output->data) { + struct sway_output *output = fullscreen_output->data; + ws = output_get_active_workspace(output); + } + if (!ws) { + ws = select_workspace(view); + } + struct sway_seat *seat = input_manager_current_seat(); - struct sway_workspace *ws = select_workspace(view); struct sway_node *node = seat_get_focus_inactive(seat, &ws->node); struct sway_container *target_sibling = node->type == N_CONTAINER ? node->sway_container : NULL; From bd4c3957bab5837a9a0b4b8d4690d6b50ba5763e Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 10 Apr 2019 23:31:50 -0400 Subject: [PATCH 0245/2054] cmd_focus: raise floating for ` focus` Floaters are currently raised for `focus `. This extends the same functionality to ` focus`. --- sway/commands/focus.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 33d014050..14e909551 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -317,6 +317,7 @@ struct cmd_results *cmd_focus(int argc, char **argv) { } seat_set_focus_container(seat, container); seat_consider_warp_to_focus(seat); + container_raise_floating(container); return cmd_results_new(CMD_SUCCESS, NULL); } From 090e1015812e8b26d008fb570152804b506b48f7 Mon Sep 17 00:00:00 2001 From: Martin Michlmayr Date: Sat, 13 Apr 2019 01:39:14 +0700 Subject: [PATCH 0246/2054] sway-input.5: add xkeyboard-config(7) to "see also" Since xkeyboard-config(7) is mentioned in the man page, add a reference to the "see also" section. --- sway/sway-input.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 8c3a8225f..1a8062fb9 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -193,4 +193,4 @@ correct seat. # SEE ALSO -*sway*(5) *sway-output*(5) +*sway*(5) *sway-output*(5) *xkeyboard-config*(7) From 27f51360b7cabc990258544ed6a74013db76e9df Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Fri, 12 Apr 2019 00:54:09 -0700 Subject: [PATCH 0247/2054] Fix scratchpad logic for floating windows When a tiled window is sent to the scratchpad, we want to use sane defaults, which is to center it and resize it to the default. For floating windows, we want to use their existing geometry. --- sway/tree/root.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sway/tree/root.c b/sway/tree/root.c index c7c5e1df3..a9d306a43 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -61,9 +61,14 @@ void root_scratchpad_add_container(struct sway_container *con) { struct sway_container *parent = con->parent; struct sway_workspace *workspace = con->workspace; - container_set_floating(con, true); - container_floating_set_default_size(con); - container_floating_move_to_center(con); + + // When a tiled window is sent to scratchpad, center and resize it. + if (!container_is_floating(con)) { + container_set_floating(con, true); + container_floating_set_default_size(con); + container_floating_move_to_center(con); + } + container_detach(con); con->scratchpad = true; list_add(root->scratchpad, con); From e7d6b8ec3e88564617c6c05656548a37b5dc6d27 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 13 Apr 2019 11:41:40 +0300 Subject: [PATCH 0248/2054] swaybg: add manpage --- meson.build | 1 + swaybg/swaybg.1.scd | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 swaybg/swaybg.1.scd diff --git a/meson.build b/meson.build index 02b5d606c..3ed0c5515 100644 --- a/meson.build +++ b/meson.build @@ -107,6 +107,7 @@ if scdoc.found() 'sway/sway-input.5.scd', 'sway/sway-ipc.7.scd', 'sway/sway-output.5.scd', + 'swaybg/swaybg.1.scd', 'swaymsg/swaymsg.1.scd', 'swaynag/swaynag.1.scd', 'swaynag/swaynag.5.scd', diff --git a/swaybg/swaybg.1.scd b/swaybg/swaybg.1.scd new file mode 100644 index 000000000..027e5d631 --- /dev/null +++ b/swaybg/swaybg.1.scd @@ -0,0 +1,44 @@ +swaybg(1) + +# NAME + +swaybg - Background for Wayland + +# SYNOPSIS + +*swaybg* [options...] + +Displays a background image on all outputs of your Wayland session. + +Without an output specified, appearance options apply to all outputs. +Per-output appearance options can be set by passing _-o, --output_ followed by +these options. + +# OPTIONS + +*-c, --color* + Set the background color. + +*-h, --help* + Show help message and quit. + +*-i, --image* + Set the background image. + +*-m, --mode* + Scaling mode for images: _stretch_, _fill_, _fit_, _center_, or _tile_. Use + the additional mode _solid\_color_ to display only the background color, + even if a background image is specified. + +*-o, --output* + Select an output to configure. Subsequent appearance options will only + apply to this output. The special value _\*_ selects all outputs. + +*-v, --version* + Show the version number and quit. + +# AUTHORS + +Maintained by Drew DeVault , who is assisted by other open +source contributors. For more information about swaybg development, see +https://github.com/swaywm/sway. From 913445e112b3ceca4ece731a6e57b19cab9d0c6a Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Sat, 13 Apr 2019 03:33:07 -0700 Subject: [PATCH 0249/2054] Fix potential null accesses --- sway/commands/move.c | 90 +++++++++++++++++++++++--------------------- sway/input/seat.c | 12 +++--- 2 files changed, 54 insertions(+), 48 deletions(-) diff --git a/sway/commands/move.c b/sway/commands/move.c index 926b2e8e9..f642f0231 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -311,37 +311,39 @@ static bool container_move_in_direction(struct sway_container *container, while (current) { list_t *siblings = container_get_siblings(current); - enum sway_container_layout layout = container_parent_layout(current); - int index = list_find(siblings, current); - int desired = index + offs; + if (siblings) { + enum sway_container_layout layout = container_parent_layout(current); + int index = list_find(siblings, current); + int desired = index + offs; - // Don't allow containers to move out of their - // fullscreen or floating parent - if (current->fullscreen_mode || container_is_floating(current)) { - return false; - } + // Don't allow containers to move out of their + // fullscreen or floating parent + if (current->fullscreen_mode || container_is_floating(current)) { + return false; + } - if (is_parallel(layout, move_dir)) { - if (desired == -1 || desired == siblings->length) { - if (current->parent == container->parent) { - current = current->parent; - continue; - } else { - // Reparenting - if (current->parent) { - container_insert_child(current->parent, container, - index + (offs < 0 ? 0 : 1)); + if (is_parallel(layout, move_dir)) { + if (desired == -1 || desired == siblings->length) { + if (current->parent == container->parent) { + current = current->parent; + continue; } else { - workspace_insert_tiling(current->workspace, container, - index + (offs < 0 ? 0 : 1)); + // Reparenting + if (current->parent) { + container_insert_child(current->parent, container, + index + (offs < 0 ? 0 : 1)); + } else { + workspace_insert_tiling(current->workspace, container, + index + (offs < 0 ? 0 : 1)); + } + return true; } + } else { + // Container can move within its siblings + container_move_to_container_from_direction(container, + siblings->items[desired], move_dir); return true; } - } else { - // Container can move within its siblings - container_move_to_container_from_direction(container, - siblings->items[desired], move_dir); - return true; } } @@ -350,26 +352,28 @@ static bool container_move_in_direction(struct sway_container *container, // Maybe rejigger the workspace struct sway_workspace *ws = container->workspace; - if (!is_parallel(ws->layout, move_dir)) { - workspace_rejigger(ws, container, move_dir); - return true; - } else if (ws->layout == L_TABBED || ws->layout == L_STACKED) { - workspace_rejigger(ws, container, move_dir); - return true; - } - - // Try adjacent output - struct sway_output *output = - output_get_in_direction(container->workspace->output, move_dir); - if (output) { - struct sway_workspace *ws = output_get_active_workspace(output); - if (!sway_assert(ws, "Expected output to have a workspace")) { - return false; + if (ws) { + if (!is_parallel(ws->layout, move_dir)) { + workspace_rejigger(ws, container, move_dir); + return true; + } else if (ws->layout == L_TABBED || ws->layout == L_STACKED) { + workspace_rejigger(ws, container, move_dir); + return true; } - container_move_to_workspace_from_direction(container, ws, move_dir); - return true; + + // Try adjacent output + struct sway_output *output = + output_get_in_direction(container->workspace->output, move_dir); + if (output) { + struct sway_workspace *ws = output_get_active_workspace(output); + if (!sway_assert(ws, "Expected output to have a workspace")) { + return false; + } + container_move_to_workspace_from_direction(container, ws, move_dir); + return true; + } + sway_log(SWAY_DEBUG, "Hit edge of output, nowhere else to go"); } - sway_log(SWAY_DEBUG, "Hit edge of output, nowhere else to go"); return false; } diff --git a/sway/input/seat.c b/sway/input/seat.c index d58ff9e6f..bdab8b81c 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1194,11 +1194,13 @@ void seat_consider_warp_to_focus(struct sway_seat *seat) { } if (config->mouse_warping == WARP_OUTPUT) { struct sway_output *output = node_get_output(focus); - struct wlr_box box; - output_get_box(output, &box); - if (wlr_box_contains_point(&box, - seat->cursor->cursor->x, seat->cursor->cursor->y)) { - return; + if (output) { + struct wlr_box box; + output_get_box(output, &box); + if (wlr_box_contains_point(&box, + seat->cursor->cursor->x, seat->cursor->cursor->y)) { + return; + } } } From 69a1a0ff99171f15c7842bfde23ed90f09a37256 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 31 Mar 2019 23:27:18 -0400 Subject: [PATCH 0250/2054] Fix scratchpad fullscreen behavior and crash When setting fullscreen on a hidden scratchpad container, there was a check to see if there was an existing fullscreen container on the workspace so it could be fullscreen disabled first. Since the workspace is NULL, it would cause a SIGSEGV. This adds a NULL check to avoid the crash. This also changes the behavior of how fullscreen is handled when adding a container to the scratchpad or changing visibility of a scratchpad container to match i3's. The behavior is as follows: - When adding a container to the scratchpad or hiding a container back into the scratchpad, there is an implicit fullscreen disable - When setting fullscreen on a container that is hidden in the scratchpad, it will be fullscreen when shown (and fullscreen disabled when hidden as stated above) - When setting fullscreen global on a container that is hidden in the scratchpad, it will be shown immediately as fullscreen global. The container is not moved to a workspace and remains in the scratchpad. The container will be visible until fullscreen disabled or killed. Since the container is in the scratchpad, running `scratchpad show` or `move container to scratchpad` will have no effect This also changes `container_replace` to transfer fullscreen and scratchpad status. --- include/sway/input/seat.h | 5 +++ sway/commands/floating.c | 5 +++ sway/commands/focus.c | 4 ++ sway/commands/fullscreen.c | 7 +++ sway/commands/layout.c | 6 ++- sway/commands/move.c | 9 ++++ sway/commands/split.c | 3 +- sway/desktop/output.c | 9 ++-- sway/desktop/render.c | 3 -- sway/input/seat.c | 50 ++++++++++++++++++--- sway/tree/container.c | 92 ++++++++++++++++++++++++++++++-------- sway/tree/node.c | 8 ++++ sway/tree/root.c | 14 ++++++ sway/tree/view.c | 62 ++++++++++++++++--------- 14 files changed, 220 insertions(+), 57 deletions(-) diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h index f2af1066b..eb6c09a1d 100644 --- a/include/sway/input/seat.h +++ b/include/sway/input/seat.h @@ -143,6 +143,11 @@ struct sway_node *seat_get_focus(struct sway_seat *seat); struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat); +// If a scratchpad container is fullscreen global, this can be used to try to +// determine the last focused workspace. Otherwise, this should yield the same +// results as seat_get_focused_workspace. +struct sway_workspace *seat_get_last_known_workspace(struct sway_seat *seat); + struct sway_container *seat_get_focused_container(struct sway_seat *seat); /** diff --git a/sway/commands/floating.c b/sway/commands/floating.c index 5df9b1bfb..ce1233454 100644 --- a/sway/commands/floating.c +++ b/sway/commands/floating.c @@ -32,6 +32,11 @@ struct cmd_results *cmd_floating(int argc, char **argv) { seat_set_focus_container(config->handler_context.seat, container); } + if (container_is_scratchpad_hidden(container)) { + return cmd_results_new(CMD_INVALID, + "Can't change floating on hidden scratchpad container"); + } + // If the container is in a floating split container, // operate on the split container instead of the child. if (container_is_floating_or_child(container)) { diff --git a/sway/commands/focus.c b/sway/commands/focus.c index 14e909551..8baa616dd 100644 --- a/sway/commands/focus.c +++ b/sway/commands/focus.c @@ -182,6 +182,10 @@ static struct sway_node *node_get_in_direction_floating( double closest_distance = DBL_MAX; struct sway_container *closest_con = NULL; + if (!con->workspace) { + return NULL; + } + for (int i = 0; i < con->workspace->floating->length; i++) { struct sway_container *floater = con->workspace->floating->items[i]; if (floater == con) { diff --git a/sway/commands/fullscreen.c b/sway/commands/fullscreen.c index 52248ce4a..a268ba034 100644 --- a/sway/commands/fullscreen.c +++ b/sway/commands/fullscreen.c @@ -26,6 +26,13 @@ struct cmd_results *cmd_fullscreen(int argc, char **argv) { "Can't fullscreen an empty workspace"); } + // If in the scratchpad, operate on the highest container + if (container && !container->workspace) { + while (container->parent) { + container = container->parent; + } + } + bool is_fullscreen = container && container->fullscreen_mode != FULLSCREEN_NONE; bool global = false; diff --git a/sway/commands/layout.c b/sway/commands/layout.c index 7d61c3be3..32f8fb520 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -147,7 +147,11 @@ struct cmd_results *cmd_layout(int argc, char **argv) { workspace->layout = new_layout; workspace_update_representation(workspace); } - arrange_workspace(workspace); + if (root->fullscreen_global) { + arrange_root(); + } else { + arrange_workspace(workspace); + } } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/commands/move.c b/sway/commands/move.c index f642f0231..4ebc949bf 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -395,6 +395,11 @@ static struct cmd_results *cmd_move_container(int argc, char **argv) { container = workspace_wrap_children(workspace); } + if (container->fullscreen_mode == FULLSCREEN_GLOBAL) { + return cmd_results_new(CMD_FAILURE, + "Can't move fullscreen global container"); + } + bool no_auto_back_and_forth = false; while (strcasecmp(argv[0], "--no-auto-back-and-forth") == 0) { no_auto_back_and_forth = true; @@ -646,6 +651,10 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) { } struct sway_workspace *workspace = config->handler_context.workspace; + if (!workspace) { + return cmd_results_new(CMD_FAILURE, "No workspace to move"); + } + struct sway_output *old_output = workspace->output; int center_x = workspace->width / 2 + workspace->x, center_y = workspace->height / 2 + workspace->y; diff --git a/sway/commands/split.c b/sway/commands/split.c index e96707228..8702f39e6 100644 --- a/sway/commands/split.c +++ b/sway/commands/split.c @@ -13,7 +13,8 @@ static struct cmd_results *do_split(int layout) { struct sway_container *con = config->handler_context.container; struct sway_workspace *ws = config->handler_context.workspace; if (con) { - if (container_is_scratchpad_hidden(con)) { + if (container_is_scratchpad_hidden(con) && + con->fullscreen_mode != FULLSCREEN_GLOBAL) { return cmd_results_new(CMD_FAILURE, "Cannot split a hidden scratchpad container"); } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 0b3e1edb9..b1e3464a7 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -251,14 +251,11 @@ static void output_for_each_surface(struct sway_output *output, }; struct sway_workspace *workspace = output_get_active_workspace(output); - if (!workspace) { - return; - } struct sway_container *fullscreen_con = root->fullscreen_global; - if (fullscreen_con && container_is_scratchpad_hidden(fullscreen_con)) { - fullscreen_con = NULL; - } if (!fullscreen_con) { + if (!workspace) { + return; + } fullscreen_con = workspace->current.fullscreen; } if (fullscreen_con) { diff --git a/sway/desktop/render.c b/sway/desktop/render.c index 7216e30bb..771bd9082 100644 --- a/sway/desktop/render.c +++ b/sway/desktop/render.c @@ -997,9 +997,6 @@ void output_render(struct sway_output *output, struct timespec *when, } struct sway_container *fullscreen_con = root->fullscreen_global; - if (fullscreen_con && container_is_scratchpad_hidden(fullscreen_con)) { - fullscreen_con = NULL; - } if (!fullscreen_con) { fullscreen_con = workspace->current.fullscreen; } diff --git a/sway/input/seat.c b/sway/input/seat.c index bdab8b81c..ce009d7e4 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -172,14 +172,14 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { seat_node_destroy(seat_node); - if (!parent) { + if (!parent && !needs_new_focus) { // Destroying a container that is no longer in the tree return; } // Find new focus_inactive (ie. sibling, or workspace if no siblings left) struct sway_node *next_focus = NULL; - while (next_focus == NULL) { + while (next_focus == NULL && parent != NULL) { struct sway_container *con = seat_get_focus_inactive_view(seat, parent); next_focus = con ? &con->node : NULL; @@ -192,6 +192,16 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { parent = node_get_parent(parent); } + if (!next_focus) { + struct sway_workspace *ws = seat_get_last_known_workspace(seat); + if (!ws) { + return; + } + struct sway_container *con = + seat_get_focus_inactive_view(seat, &ws->node); + next_focus = con ? &(con->node) : &(ws->node); + } + if (next_focus->type == N_WORKSPACE && !workspace_is_visible(next_focus->sway_workspace)) { // Do not change focus to a non-visible workspace @@ -199,6 +209,10 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { } if (needs_new_focus) { + // Make sure the workspace IPC event gets sent + if (node->type == N_CONTAINER && node->sway_container->scratchpad) { + seat_set_focus(seat, NULL); + } // The structure change might have caused it to move up to the top of // the focus stack without sending focus notifications to the view seat_send_focus(next_focus, seat); @@ -207,7 +221,7 @@ static void handle_seat_node_destroy(struct wl_listener *listener, void *data) { // Setting focus_inactive focus = seat_get_focus_inactive(seat, &root->node); seat_set_raw_focus(seat, next_focus); - if (focus->type == N_CONTAINER) { + if (focus->type == N_CONTAINER && focus->sway_container->workspace) { seat_set_raw_focus(seat, &focus->sway_container->workspace->node); } seat_set_raw_focus(seat, focus); @@ -795,7 +809,13 @@ void seat_set_raw_focus(struct sway_seat *seat, struct sway_node *node) { wl_list_remove(&seat_node->link); wl_list_insert(&seat->focus_stack, &seat_node->link); node_set_dirty(node); - node_set_dirty(node_get_parent(node)); + + // If focusing a scratchpad container that is fullscreen global, parent + // will be NULL + struct sway_node *parent = node_get_parent(node); + if (parent) { + node_set_dirty(parent); + } } void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { @@ -850,7 +870,8 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { } } - struct sway_output *new_output = new_workspace->output; + struct sway_output *new_output = + new_workspace ? new_workspace->output : NULL; if (last_workspace != new_workspace && new_output) { node_set_dirty(&new_output->node); @@ -894,7 +915,8 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { } // Move sticky containers to new workspace - if (new_output_last_ws && new_workspace != new_output_last_ws) { + if (new_workspace && new_output_last_ws + && new_workspace != new_output_last_ws) { for (int i = 0; i < new_output_last_ws->floating->length; ++i) { struct sway_container *floater = new_output_last_ws->floating->items[i]; @@ -940,7 +962,7 @@ void seat_set_focus(struct sway_seat *seat, struct sway_node *node) { seat->has_focus = true; - if (config->smart_gaps) { + if (config->smart_gaps && new_workspace) { // When smart gaps is on, gaps may change when the focus changes so // the workspace needs to be arranged arrange_workspace(new_workspace); @@ -1134,6 +1156,20 @@ struct sway_workspace *seat_get_focused_workspace(struct sway_seat *seat) { return NULL; // output doesn't have a workspace yet } +struct sway_workspace *seat_get_last_known_workspace(struct sway_seat *seat) { + struct sway_seat_node *current; + wl_list_for_each(current, &seat->focus_stack, link) { + struct sway_node *node = current->node; + if (node->type == N_CONTAINER && + node->sway_container->workspace) { + return node->sway_container->workspace; + } else if (node->type == N_WORKSPACE) { + return node->sway_workspace; + } + } + return NULL; +} + struct sway_container *seat_get_focused_container(struct sway_seat *seat) { struct sway_node *focus = seat_get_focus(seat); if (focus && focus->type == N_CONTAINER) { diff --git a/sway/tree/container.c b/sway/tree/container.c index 02b4d1b0e..11ed4f98d 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -95,6 +95,10 @@ void container_begin_destroy(struct sway_container *con) { if (con->fullscreen_mode == FULLSCREEN_WORKSPACE && con->workspace) { con->workspace->fullscreen = NULL; } + if (con->scratchpad && con->fullscreen_mode == FULLSCREEN_GLOBAL) { + container_fullscreen_disable(con); + } + wl_signal_emit(&con->node.events.destroy, &con->node); container_end_mouse_operation(con); @@ -128,7 +132,9 @@ void container_reap_empty(struct sway_container *con) { container_begin_destroy(con); con = parent; } - workspace_consider_destroy(ws); + if (ws) { + workspace_consider_destroy(ws); + } } struct sway_container *container_flatten(struct sway_container *container) { @@ -954,18 +960,20 @@ static void container_fullscreen_workspace(struct sway_container *con) { set_fullscreen_iterator(con, &enable); container_for_each_child(con, set_fullscreen_iterator, &enable); - con->workspace->fullscreen = con; con->saved_x = con->x; con->saved_y = con->y; con->saved_width = con->width; con->saved_height = con->height; - struct sway_seat *seat; - struct sway_workspace *focus_ws; - wl_list_for_each(seat, &server.input->seats, link) { - focus_ws = seat_get_focused_workspace(seat); - if (focus_ws == con->workspace) { - seat_set_focus_container(seat, con); + if (con->workspace) { + con->workspace->fullscreen = con; + struct sway_seat *seat; + struct sway_workspace *focus_ws; + wl_list_for_each(seat, &server.input->seats, link) { + focus_ws = seat_get_focused_workspace(seat); + if (focus_ws == con->workspace) { + seat_set_focus_container(seat, con); + } } } @@ -1019,11 +1027,14 @@ void container_fullscreen_disable(struct sway_container *con) { con->height = con->saved_height; if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { - con->workspace->fullscreen = NULL; - if (container_is_floating(con)) { - struct sway_output *output = container_floating_find_output(con); - if (con->workspace->output != output) { - container_floating_move_to_center(con); + if (con->workspace) { + con->workspace->fullscreen = NULL; + if (container_is_floating(con)) { + struct sway_output *output = + container_floating_find_output(con); + if (con->workspace->output != output) { + container_floating_move_to_center(con); + } } } } else { @@ -1040,6 +1051,17 @@ void container_fullscreen_disable(struct sway_container *con) { con->fullscreen_mode = FULLSCREEN_NONE; container_end_mouse_operation(con); ipc_event_window(con, "fullscreen_mode"); + + if (con->scratchpad) { + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + struct sway_container *focus = seat_get_focused_container(seat); + if (focus == con || container_has_ancestor(focus, con)) { + seat_set_focus(seat, + seat_get_focus_inactive(seat, &root->node)); + } + } + } } void container_set_fullscreen(struct sway_container *con, @@ -1056,7 +1078,7 @@ void container_set_fullscreen(struct sway_container *con, if (root->fullscreen_global) { container_fullscreen_disable(root->fullscreen_global); } - if (con->workspace->fullscreen) { + if (con->workspace && con->workspace->fullscreen) { container_fullscreen_disable(con->workspace->fullscreen); } container_fullscreen_workspace(con); @@ -1171,6 +1193,11 @@ void container_add_gaps(struct sway_container *c) { c->current_gaps.bottom > 0 || c->current_gaps.left > 0) { return; } + // Fullscreen global scratchpad containers cannot have gaps + struct sway_workspace *ws = c->workspace; + if (!ws) { + return; + } // Linear containers don't have gaps because it'd create double gaps if (!c->view && c->layout != L_TABBED && c->layout != L_STACKED) { return; @@ -1199,8 +1226,6 @@ void container_add_gaps(struct sway_container *c) { } } - struct sway_workspace *ws = c->workspace; - c->current_gaps.top = c->y == ws->y ? ws->gaps_inner : 0; c->current_gaps.right = ws->gaps_inner; c->current_gaps.bottom = ws->gaps_inner; @@ -1308,6 +1333,10 @@ void container_add_child(struct sway_container *parent, child->parent = parent; child->workspace = parent->workspace; container_for_each_child(child, set_workspace, NULL); + bool fullscreen = child->fullscreen_mode != FULLSCREEN_NONE || + parent->fullscreen_mode != FULLSCREEN_NONE; + set_fullscreen_iterator(child, &fullscreen); + container_for_each_child(child, set_fullscreen_iterator, &fullscreen); container_handle_fullscreen_reparent(child); container_update_representation(parent); node_set_dirty(&child->node); @@ -1347,8 +1376,31 @@ void container_detach(struct sway_container *child) { void container_replace(struct sway_container *container, struct sway_container *replacement) { + enum sway_fullscreen_mode fullscreen = container->fullscreen_mode; + bool scratchpad = container->scratchpad; + if (fullscreen != FULLSCREEN_NONE) { + container_fullscreen_disable(container); + } + if (scratchpad) { + root_scratchpad_show(container); + root_scratchpad_remove_container(container); + } container_add_sibling(container, replacement, 1); container_detach(container); + if (scratchpad) { + root_scratchpad_add_container(replacement); + } + switch (fullscreen) { + case FULLSCREEN_WORKSPACE: + container_fullscreen_workspace(replacement); + break; + case FULLSCREEN_GLOBAL: + container_fullscreen_global(replacement); + break; + case FULLSCREEN_NONE: + // noop + break; + } } struct sway_container *container_split(struct sway_container *child, @@ -1369,7 +1421,11 @@ struct sway_container *container_split(struct sway_container *child, if (set_focus) { seat_set_raw_focus(seat, &cont->node); - seat_set_raw_focus(seat, &child->node); + if (cont->fullscreen_mode == FULLSCREEN_GLOBAL) { + seat_set_focus(seat, &child->node); + } else { + seat_set_raw_focus(seat, &child->node); + } } return cont; @@ -1529,7 +1585,7 @@ void container_raise_floating(struct sway_container *con) { while (floater->parent) { floater = floater->parent; } - if (container_is_floating(floater)) { + if (container_is_floating(floater) && floater->workspace) { list_move_to_end(floater->workspace->floating, floater); node_set_dirty(&floater->workspace->node); } diff --git a/sway/tree/node.c b/sway/tree/node.c index dcab1c9b6..ffa7f2cc8 100644 --- a/sway/tree/node.c +++ b/sway/tree/node.c @@ -142,11 +142,19 @@ list_t *node_get_children(struct sway_node *node) { } bool node_has_ancestor(struct sway_node *node, struct sway_node *ancestor) { + if (ancestor->type == N_ROOT && node->type == N_CONTAINER && + node->sway_container->fullscreen_mode == FULLSCREEN_GLOBAL) { + return true; + } struct sway_node *parent = node_get_parent(node); while (parent) { if (parent == ancestor) { return true; } + if (ancestor->type == N_ROOT && parent->type == N_CONTAINER && + parent->sway_container->fullscreen_mode == FULLSCREEN_GLOBAL) { + return true; + } parent = node_get_parent(parent); } return false; diff --git a/sway/tree/root.c b/sway/tree/root.c index a9d306a43..1dabc2870 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -62,6 +62,11 @@ void root_scratchpad_add_container(struct sway_container *con) { struct sway_container *parent = con->parent; struct sway_workspace *workspace = con->workspace; + // Clear the fullscreen mode when sending to the scratchpad + if (con->fullscreen_mode != FULLSCREEN_NONE) { + container_fullscreen_disable(con); + } + // When a tiled window is sent to scratchpad, center and resize it. if (!container_is_floating(con)) { container_set_floating(con, true); @@ -143,6 +148,15 @@ void root_scratchpad_hide(struct sway_container *con) { struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); struct sway_workspace *ws = con->workspace; + if (con->fullscreen_mode == FULLSCREEN_GLOBAL && !con->workspace) { + // If the container was made fullscreen global while in the scratchpad, + // it should be shown until fullscreen has been disabled + return; + } + + if (con->fullscreen_mode != FULLSCREEN_NONE) { + container_fullscreen_disable(con); + } container_detach(con); arrange_workspace(ws); if (&con->node == focus || node_has_ancestor(focus, &con->node)) { diff --git a/sway/tree/view.c b/sway/tree/view.c index 2c8839f59..c241b2b3a 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -197,10 +197,11 @@ static bool gaps_to_edge(struct sway_view *view) { void view_autoconfigure(struct sway_view *view) { struct sway_container *con = view->container; - if (container_is_scratchpad_hidden(con)) { + if (container_is_scratchpad_hidden(con) && + con->fullscreen_mode != FULLSCREEN_GLOBAL) { return; } - struct sway_output *output = con->workspace->output; + struct sway_output *output = con->workspace ? con->workspace->output : NULL; if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { con->content_x = output->lx; @@ -226,19 +227,21 @@ void view_autoconfigure(struct sway_view *view) { con->border_top = con->border_bottom = true; con->border_left = con->border_right = true; - if (config->hide_edge_borders == E_BOTH - || config->hide_edge_borders == E_VERTICAL - || (smart && !other_views && no_gaps)) { - con->border_left = con->x - con->current_gaps.left != ws->x; - int right_x = con->x + con->width + con->current_gaps.right; - con->border_right = right_x != ws->x + ws->width; - } - if (config->hide_edge_borders == E_BOTH - || config->hide_edge_borders == E_HORIZONTAL - || (smart && !other_views && no_gaps)) { - con->border_top = con->y - con->current_gaps.top != ws->y; - int bottom_y = con->y + con->height + con->current_gaps.bottom; - con->border_bottom = bottom_y != ws->y + ws->height; + if (ws) { + if (config->hide_edge_borders == E_BOTH + || config->hide_edge_borders == E_VERTICAL + || (smart && !other_views && no_gaps)) { + con->border_left = con->x - con->current_gaps.left != ws->x; + int right_x = con->x + con->width + con->current_gaps.right; + con->border_right = right_x != ws->x + ws->width; + } + if (config->hide_edge_borders == E_BOTH + || config->hide_edge_borders == E_HORIZONTAL + || (smart && !other_views && no_gaps)) { + con->border_top = con->y - con->current_gaps.top != ws->y; + int bottom_y = con->y + con->height + con->current_gaps.bottom; + con->border_bottom = bottom_y != ws->y + ws->height; + } } double y_offset = 0; @@ -247,7 +250,8 @@ void view_autoconfigure(struct sway_view *view) { // title area. We have to offset the surface y by the height of the title, // bar, and disable any top border because we'll always have the title bar. list_t *siblings = container_get_siblings(con); - bool show_titlebar = siblings->length > 1 || !config->hide_lone_tab; + bool show_titlebar = (siblings && siblings->length > 1) + || !config->hide_lone_tab; if (show_titlebar && !container_is_floating(con)) { enum sway_container_layout layout = container_parent_layout(con); if (layout == L_TABBED) { @@ -538,6 +542,10 @@ static bool should_focus(struct sway_view *view) { struct sway_workspace *prev_ws = seat_get_focused_workspace(seat); struct sway_workspace *map_ws = view->container->workspace; + if (view->container->fullscreen_mode == FULLSCREEN_GLOBAL) { + return true; + } + // Views can only take focus if they are mapped into the active workspace if (prev_ws != map_ws) { return false; @@ -581,7 +589,8 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, } struct sway_seat *seat = input_manager_current_seat(); - struct sway_node *node = seat_get_focus_inactive(seat, &ws->node); + struct sway_node *node = ws ? seat_get_focus_inactive(seat, &ws->node) + : seat_get_focus_inactive(seat, &root->node); struct sway_container *target_sibling = node->type == N_CONTAINER ? node->sway_container : NULL; @@ -589,12 +598,13 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, // launch it as a tiled view in the root of the workspace instead. if (target_sibling && container_is_floating(target_sibling)) { target_sibling = NULL; + ws = seat_get_last_known_workspace(seat); } view->container = container_create(view); if (target_sibling) { container_add_sibling(target_sibling, view->container, 1); - } else { + } else if (ws) { workspace_add_tiling(ws, view->container); } ipc_event_window(view->container, "new"); @@ -1032,8 +1042,18 @@ bool view_is_visible(struct sway_view *view) { return false; } struct sway_workspace *workspace = view->container->workspace; - if (!workspace) { - return false; + if (!workspace && view->container->fullscreen_mode != FULLSCREEN_GLOBAL) { + bool fs_global_descendant = false; + struct sway_container *parent = view->container->parent; + while (parent) { + if (parent->fullscreen_mode == FULLSCREEN_GLOBAL) { + fs_global_descendant = true; + } + parent = parent->parent; + } + if (!fs_global_descendant) { + return false; + } } // Determine if view is nested inside a floating container which is sticky struct sway_container *floater = view->container; @@ -1041,7 +1061,7 @@ bool view_is_visible(struct sway_view *view) { floater = floater->parent; } bool is_sticky = container_is_floating(floater) && floater->is_sticky; - if (!is_sticky && !workspace_is_visible(workspace)) { + if (!is_sticky && workspace && !workspace_is_visible(workspace)) { return false; } // Check view isn't in a tabbed or stacked container on an inactive tab From 8c69da11bbe70caa2f5db1665e9d60387604c5e4 Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 14 Apr 2019 01:06:09 -0400 Subject: [PATCH 0251/2054] swaynag: fix pointer management Currently on master, swaynag will retrieve a pointer instance whenever the capabilities change and WL_SEAT_CAPBILITY_POINTER is set. The pointer instances were never being destroyed so swaynag received events multiple times due to having several instances of the pointer. This fixes it so if there is already a pointer instance, swaynag does not attempt to retrieve another. Additionally, if the pointer capability is removed, the pointer instance is destroyed. --- swaynag/swaynag.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c index f0c6a3c54..eb31da57e 100644 --- a/swaynag/swaynag.c +++ b/swaynag/swaynag.c @@ -239,10 +239,14 @@ static struct wl_pointer_listener pointer_listener = { static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) { struct swaynag *swaynag = data; - if ((caps & WL_SEAT_CAPABILITY_POINTER)) { + bool cap_pointer = caps & WL_SEAT_CAPABILITY_POINTER; + if (cap_pointer && !swaynag->pointer.pointer) { swaynag->pointer.pointer = wl_seat_get_pointer(wl_seat); wl_pointer_add_listener(swaynag->pointer.pointer, &pointer_listener, swaynag); + } else if (!cap_pointer && swaynag->pointer.pointer) { + wl_pointer_destroy(swaynag->pointer.pointer); + swaynag->pointer.pointer = NULL; } } From 6961bf2e4ce2c116e41a8db158691f6c993707ce Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Sun, 14 Apr 2019 00:27:47 -0400 Subject: [PATCH 0252/2054] Spawn swaynag as a wayland client This spawns swaynag as a wayland client similar to how swaybar and swaybg are already done --- common/util.c | 19 +++++++ include/sway/swaynag.h | 8 +-- include/util.h | 2 + sway/config.c | 21 +++---- sway/config/output.c | 19 ------- sway/main.c | 2 +- sway/swaynag.c | 124 +++++++++++++++++++++++++++++++---------- swaynag/swaynag.c | 23 ++++++-- 8 files changed, 147 insertions(+), 71 deletions(-) diff --git a/common/util.c b/common/util.c index c43c5ddf6..3a807edba 100644 --- a/common/util.c +++ b/common/util.c @@ -1,5 +1,6 @@ #define _POSIX_C_SOURCE 200809L #include +#include #include #include #include @@ -75,3 +76,21 @@ const char *sway_wl_output_subpixel_to_string(enum wl_output_subpixel subpixel) sway_assert(false, "Unknown value for wl_output_subpixel."); return NULL; } + +bool set_cloexec(int fd, bool cloexec) { + int flags = fcntl(fd, F_GETFD); + if (flags == -1) { + sway_log_errno(SWAY_ERROR, "fcntl failed"); + return false; + } + if (cloexec) { + flags = flags | FD_CLOEXEC; + } else { + flags = flags & ~FD_CLOEXEC; + } + if (fcntl(fd, F_SETFD, flags) == -1) { + sway_log_errno(SWAY_ERROR, "fcntl failed"); + return false; + } + return true; +} diff --git a/include/sway/swaynag.h b/include/sway/swaynag.h index 5a1787392..74d9ea18c 100644 --- a/include/sway/swaynag.h +++ b/include/sway/swaynag.h @@ -1,9 +1,12 @@ #ifndef _SWAY_SWAYNAG_H #define _SWAY_SWAYNAG_H +#include struct swaynag_instance { + struct wl_client *client; + struct wl_listener client_destroy; + const char *args; - pid_t pid; int fd[2]; bool detailed; }; @@ -15,9 +18,6 @@ struct swaynag_instance { bool swaynag_spawn(const char *swaynag_command, struct swaynag_instance *swaynag); -// Kill the swaynag instance -void swaynag_kill(struct swaynag_instance *swaynag); - // Write a log message to swaynag->fd[1]. This will fail when swaynag->detailed // is false. void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, diff --git a/include/util.h b/include/util.h index 6a668fd65..6d9454e05 100644 --- a/include/util.h +++ b/include/util.h @@ -32,4 +32,6 @@ float parse_float(const char *value); const char *sway_wl_output_subpixel_to_string(enum wl_output_subpixel subpixel); +bool set_cloexec(int fd, bool cloexec); + #endif diff --git a/sway/config.c b/sway/config.c index d5bfe105f..4944ec024 100644 --- a/sway/config.c +++ b/sway/config.c @@ -175,15 +175,13 @@ static void set_color(float dest[static 4], uint32_t color) { static void config_defaults(struct sway_config *config) { if (!(config->swaynag_command = strdup("swaynag"))) goto cleanup; - config->swaynag_config_errors = (struct swaynag_instance){ - .args = "--type error " + config->swaynag_config_errors = (struct swaynag_instance){0}; + config->swaynag_config_errors.args = "--type error " "--message 'There are errors in your config file' " "--detailed-message " - "--button 'Exit sway' 'swaymsg exit' " - "--button 'Reload sway' 'swaymsg reload'", - .pid = -1, - .detailed = true, - }; + "--button-no-terminal 'Exit sway' 'swaymsg exit' " + "--button-no-terminal 'Reload sway' 'swaymsg reload'"; + config->swaynag_config_errors.detailed = true; if (!(config->symbols = create_list())) goto cleanup; if (!(config->modes = create_list())) goto cleanup; @@ -411,10 +409,9 @@ bool load_main_config(const char *file, bool is_active, bool validating) { config->reloading = true; config->active = true; - swaynag_kill(&old_config->swaynag_config_errors); - memcpy(&config->swaynag_config_errors, - &old_config->swaynag_config_errors, - sizeof(struct swaynag_instance)); + if (old_config->swaynag_config_errors.client != NULL) { + wl_client_destroy(old_config->swaynag_config_errors.client); + } input_manager_reset_all_inputs(); } @@ -486,7 +483,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { spawn_swaybg(); config->reloading = false; - if (config->swaynag_config_errors.pid > 0) { + if (config->swaynag_config_errors.client != NULL) { swaynag_show(&config->swaynag_config_errors); } diff --git a/sway/config/output.c b/sway/config/output.c index 747ab28b7..fb8a9ee52 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1,6 +1,5 @@ #define _POSIX_C_SOURCE 200809L #include -#include #include #include #include @@ -488,24 +487,6 @@ static void handle_swaybg_client_destroy(struct wl_listener *listener, config->swaybg_client = NULL; } -static bool set_cloexec(int fd, bool cloexec) { - int flags = fcntl(fd, F_GETFD); - if (flags == -1) { - sway_log_errno(SWAY_ERROR, "fcntl failed"); - return false; - } - if (cloexec) { - flags = flags | FD_CLOEXEC; - } else { - flags = flags & ~FD_CLOEXEC; - } - if (fcntl(fd, F_SETFD, flags) == -1) { - sway_log_errno(SWAY_ERROR, "fcntl failed"); - return false; - } - return true; -} - static bool _spawn_swaybg(char **command) { if (config->swaybg_client != NULL) { wl_client_destroy(config->swaybg_client); diff --git a/sway/main.c b/sway/main.c index ba4e25624..96f67b36b 100644 --- a/sway/main.c +++ b/sway/main.c @@ -391,7 +391,7 @@ int main(int argc, char **argv) { load_swaybars(); run_deferred_commands(); - if (config->swaynag_config_errors.pid > 0) { + if (config->swaynag_config_errors.client != NULL) { swaynag_show(&config->swaynag_config_errors); } diff --git a/sway/swaynag.c b/sway/swaynag.c index 49027f5d5..0fca6c710 100644 --- a/sway/swaynag.c +++ b/sway/swaynag.c @@ -1,16 +1,32 @@ #define _POSIX_C_SOURCE 200809L -#include #include #include #include #include +#include #include +#include #include #include "log.h" +#include "sway/server.h" #include "sway/swaynag.h" +#include "util.h" + +static void handle_swaynag_client_destroy(struct wl_listener *listener, + void *data) { + struct swaynag_instance *swaynag = + wl_container_of(listener, swaynag, client_destroy); + wl_list_remove(&swaynag->client_destroy.link); + wl_list_init(&swaynag->client_destroy.link); + swaynag->client = NULL; +} bool swaynag_spawn(const char *swaynag_command, struct swaynag_instance *swaynag) { + if (swaynag->client != NULL) { + wl_client_destroy(swaynag->client); + } + if (!swaynag_command) { return true; } @@ -20,44 +36,94 @@ bool swaynag_spawn(const char *swaynag_command, sway_log(SWAY_ERROR, "Failed to create pipe for swaynag"); return false; } - fcntl(swaynag->fd[1], F_SETFD, FD_CLOEXEC); + if (!set_cloexec(swaynag->fd[1], true)) { + goto failed; + } } - pid_t pid; - if ((pid = fork()) == 0) { - if (swaynag->detailed) { - close(swaynag->fd[1]); - dup2(swaynag->fd[0], STDIN_FILENO); - close(swaynag->fd[0]); - } + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) != 0) { + sway_log_errno(SWAY_ERROR, "socketpair failed"); + goto failed; + } + if (!set_cloexec(sockets[0], true) || !set_cloexec(sockets[1], true)) { + goto failed; + } - size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2; - char *cmd = malloc(length); - snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args); - execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); - _exit(0); - } else if (pid < 0) { + swaynag->client = wl_client_create(server.wl_display, sockets[0]); + if (swaynag->client == NULL) { + sway_log_errno(SWAY_ERROR, "wl_client_create failed"); + goto failed; + } + + swaynag->client_destroy.notify = handle_swaynag_client_destroy; + wl_client_add_destroy_listener(swaynag->client, &swaynag->client_destroy); + + pid_t pid = fork(); + if (pid < 0) { sway_log(SWAY_ERROR, "Failed to create fork for swaynag"); - if (swaynag->detailed) { - close(swaynag->fd[0]); - close(swaynag->fd[1]); + goto failed; + } else if (pid == 0) { + pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_ERROR, "fork failed"); + _exit(EXIT_FAILURE); + } else if (pid == 0) { + if (!set_cloexec(sockets[1], false)) { + _exit(EXIT_FAILURE); + } + + if (swaynag->detailed) { + close(swaynag->fd[1]); + dup2(swaynag->fd[0], STDIN_FILENO); + close(swaynag->fd[0]); + } + + char wayland_socket_str[16]; + snprintf(wayland_socket_str, sizeof(wayland_socket_str), + "%d", sockets[1]); + setenv("WAYLAND_SOCKET", wayland_socket_str, true); + + size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2; + char *cmd = malloc(length); + snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args); + execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); + sway_log_errno(SWAY_ERROR, "execl failed"); + _exit(EXIT_FAILURE); } - return false; + _exit(EXIT_SUCCESS); } if (swaynag->detailed) { - close(swaynag->fd[0]); + if (close(swaynag->fd[0]) != 0) { + sway_log_errno(SWAY_ERROR, "close failed"); + return false; + } } - swaynag->pid = pid; + + if (close(sockets[1]) != 0) { + sway_log_errno(SWAY_ERROR, "close failed"); + return false; + } + + if (waitpid(pid, NULL, 0) < 0) { + sway_log_errno(SWAY_ERROR, "waitpid failed"); + return false; + } + return true; -} - -void swaynag_kill(struct swaynag_instance *swaynag) { - if (swaynag->pid > 0) { - kill(swaynag->pid, SIGTERM); - swaynag->pid = -1; +failed: + if (swaynag->detailed) { + if (close(swaynag->fd[0]) != 0) { + sway_log_errno(SWAY_ERROR, "close failed"); + return false; + } + if (close(swaynag->fd[1]) != 0) { + sway_log_errno(SWAY_ERROR, "close failed"); + } } + return false; } void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, @@ -71,7 +137,7 @@ void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, return; } - if (swaynag->pid <= 0 && !swaynag_spawn(swaynag_command, swaynag)) { + if (swaynag->client == NULL && !swaynag_spawn(swaynag_command, swaynag)) { return; } @@ -96,7 +162,7 @@ void swaynag_log(const char *swaynag_command, struct swaynag_instance *swaynag, } void swaynag_show(struct swaynag_instance *swaynag) { - if (swaynag->detailed && swaynag->pid > 0) { + if (swaynag->detailed && swaynag->client != NULL) { close(swaynag->fd[1]); } } diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c index eb31da57e..26411ab31 100644 --- a/swaynag/swaynag.c +++ b/swaynag/swaynag.c @@ -45,17 +45,24 @@ static void swaynag_button_execute(struct swaynag *swaynag, swaynag->details.visible = !swaynag->details.visible; render_frame(swaynag); } else { - if (fork() == 0) { + pid_t pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_DEBUG, "Failed to fork"); + return; + } else if (pid == 0) { // Child process. Will be used to prevent zombie processes - setsid(); - if (fork() == 0) { + pid = fork(); + if (pid < 0) { + sway_log_errno(SWAY_DEBUG, "Failed to fork"); + return; + } else if (pid == 0) { // Child of the child. Will be reparented to the init process char *terminal = getenv("TERMINAL"); if (button->terminal && terminal && strlen(terminal)) { sway_log(SWAY_DEBUG, "Found $TERMINAL: %s", terminal); if (!terminal_execute(terminal, button->action)) { swaynag_destroy(swaynag); - exit(EXIT_FAILURE); + _exit(EXIT_FAILURE); } } else { if (button->terminal) { @@ -63,12 +70,16 @@ static void swaynag_button_execute(struct swaynag *swaynag, "$TERMINAL not found. Running directly"); } execl("/bin/sh", "/bin/sh", "-c", button->action, NULL); + sway_log_errno(SWAY_DEBUG, "execl failed"); + _exit(EXIT_FAILURE); } } - exit(EXIT_SUCCESS); + _exit(EXIT_SUCCESS); + } + if (waitpid(pid, NULL, 0) < 0) { + sway_log_errno(SWAY_DEBUG, "waitpid failed"); } } - wait(0); } static void layer_surface_configure(void *data, From 6737b90cb93d0231bbbc6045adf8a2443bc4e21c Mon Sep 17 00:00:00 2001 From: Benjamin Cheng Date: Tue, 12 Mar 2019 13:17:47 -0400 Subject: [PATCH 0253/2054] Add heuristics to differentiate touchpads Use libinput_device_config_tap_get_finger_count to determine whether a pointer is a touchpad. swaymsg is also updated to reflect the new touchpad type. --- include/sway/input/input-manager.h | 2 ++ sway/input/input-manager.c | 34 ++++++++++++++++++++++++++++++ sway/ipc-json.c | 20 +----------------- swaymsg/main.c | 1 + 4 files changed, 38 insertions(+), 19 deletions(-) diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index e166a2377..8d4a5b005 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h @@ -62,4 +62,6 @@ struct input_config *input_device_get_config(struct sway_input_device *device); char *input_device_get_identifier(struct wlr_input_device *device); +const char *input_device_get_type(struct sway_input_device *device); + #endif diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index adb36af93..0c5254bdf 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -71,6 +71,40 @@ char *input_device_get_identifier(struct wlr_input_device *device) { return identifier; } +static bool device_is_touchpad(struct sway_input_device *device) { + if (device->wlr_device->type != WLR_INPUT_DEVICE_POINTER || + !wlr_input_device_is_libinput(device->wlr_device)) { + return false; + } + + struct libinput_device *libinput_device = + wlr_libinput_get_device_handle(device->wlr_device); + + return libinput_device_config_tap_get_finger_count(libinput_device) > 0; +} + +const char *input_device_get_type(struct sway_input_device *device) { + switch (device->wlr_device->type) { + case WLR_INPUT_DEVICE_POINTER: + if (device_is_touchpad(device)) { + return "touchpad"; + } else { + return "pointer"; + } + case WLR_INPUT_DEVICE_KEYBOARD: + return "keyboard"; + case WLR_INPUT_DEVICE_TOUCH: + return "touch"; + case WLR_INPUT_DEVICE_TABLET_TOOL: + return "tablet_tool"; + case WLR_INPUT_DEVICE_TABLET_PAD: + return "tablet_pad"; + case WLR_INPUT_DEVICE_SWITCH: + return "switch"; + } + return "unknown"; +} + static struct sway_input_device *input_sway_device_from_wlr( struct wlr_input_device *device) { struct sway_input_device *input_device = NULL; diff --git a/sway/ipc-json.c b/sway/ipc-json.c index c320d9586..4ccf6dfd3 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -85,24 +85,6 @@ static const char *ipc_json_output_transform_description(enum wl_output_transfor return NULL; } -static const char *ipc_json_device_type_description(struct sway_input_device *device) { - switch (device->wlr_device->type) { - case WLR_INPUT_DEVICE_POINTER: - return "pointer"; - case WLR_INPUT_DEVICE_KEYBOARD: - return "keyboard"; - case WLR_INPUT_DEVICE_SWITCH: - return "switch"; - case WLR_INPUT_DEVICE_TOUCH: - return "touch"; - case WLR_INPUT_DEVICE_TABLET_TOOL: - return "tablet_tool"; - case WLR_INPUT_DEVICE_TABLET_PAD: - return "tablet_pad"; - } - return "unknown"; -} - json_object *ipc_json_get_version(void) { int major = 0, minor = 0, patch = 0; json_object *version = json_object_new_object(); @@ -819,7 +801,7 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { json_object_new_int(device->wlr_device->product)); json_object_object_add(object, "type", json_object_new_string( - ipc_json_device_type_description(device))); + input_device_get_type(device))); if (device->wlr_device->type == WLR_INPUT_DEVICE_KEYBOARD) { struct wlr_keyboard *keyboard = device->wlr_device->keyboard; diff --git a/swaymsg/main.c b/swaymsg/main.c index f86000a42..e2c43445c 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -98,6 +98,7 @@ static const char *pretty_type_name(const char *name) { } type_names[] = { { "keyboard", "Keyboard" }, { "pointer", "Mouse" }, + { "touchpad", "Touchpad" }, { "tablet_pad", "Tablet pad" }, { "tablet_tool", "Tablet tool" }, { "touch", "Touch" }, From bd3720585e91ae0dfcc4be30149ae4f8f5218174 Mon Sep 17 00:00:00 2001 From: Benjamin Cheng Date: Mon, 25 Mar 2019 22:05:49 -0400 Subject: [PATCH 0254/2054] Implement input type configs (#3784) Add support for configurations that apply to a type of inputs (i.e. natural scrolling on all touchpads). A type config is differentiated by a `type:` prefix followed by the type it corresponds to. When new devices appear, the device config is merged on top of its type config (if it exists). New type configs are applied on top of existing configs. --- include/sway/config.h | 2 ++ sway/config.c | 10 +++++++- sway/config/input.c | 39 ++++++++++++++++++++++++++++--- sway/input/input-manager.c | 47 +++++++++++++++++++++++++++++++++++++- sway/sway-input.5.scd | 17 +++++++++++++- 5 files changed, 109 insertions(+), 6 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index fe06fb9de..864105442 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -105,6 +105,7 @@ struct input_config_mapped_from_region { */ struct input_config { char *identifier; + const char *input_type; int accel_profile; int click_method; @@ -418,6 +419,7 @@ struct sway_config { list_t *workspace_configs; list_t *output_configs; list_t *input_configs; + list_t *input_type_configs; list_t *seat_configs; list_t *criteria; list_t *no_focus; diff --git a/sway/config.c b/sway/config.c index 4944ec024..e14ea83a8 100644 --- a/sway/config.c +++ b/sway/config.c @@ -113,6 +113,12 @@ void free_config(struct sway_config *config) { } list_free(config->input_configs); } + if (config->input_type_configs) { + for (int i = 0; i < config->input_type_configs->length; i++) { + free_input_config(config->input_type_configs->items[i]); + } + list_free(config->input_type_configs); + } if (config->seat_configs) { for (int i = 0; i < config->seat_configs->length; i++) { free_seat_config(config->seat_configs->items[i]); @@ -189,10 +195,12 @@ static void config_defaults(struct sway_config *config) { if (!(config->workspace_configs = create_list())) goto cleanup; if (!(config->criteria = create_list())) goto cleanup; if (!(config->no_focus = create_list())) goto cleanup; - if (!(config->input_configs = create_list())) goto cleanup; if (!(config->seat_configs = create_list())) goto cleanup; if (!(config->output_configs = create_list())) goto cleanup; + if (!(config->input_type_configs = create_list())) goto cleanup; + if (!(config->input_configs = create_list())) goto cleanup; + if (!(config->cmd_queue = create_list())) goto cleanup; if (!(config->current_mode = malloc(sizeof(struct sway_mode)))) diff --git a/sway/config/input.c b/sway/config/input.c index 63c28635b..aa5814313 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -18,6 +18,7 @@ struct input_config *new_input_config(const char* identifier) { return NULL; } + input->input_type = NULL; input->tap = INT_MIN; input->tap_button_map = INT_MIN; input->drag = INT_MIN; @@ -140,6 +141,30 @@ static void merge_wildcard_on_all(struct input_config *wildcard) { merge_input_config(ic, wildcard); } } + + for (int i = 0; i < config->input_type_configs->length; i++) { + struct input_config *ic = config->input_type_configs->items[i]; + if (strcmp(wildcard->identifier, ic->identifier) != 0) { + sway_log(SWAY_DEBUG, "Merging input * config on %s", ic->identifier); + merge_input_config(ic, wildcard); + } + } +} + +static void merge_type_on_existing(struct input_config *type_wildcard) { + for (int i = 0; i < config->input_configs->length; i++) { + struct input_config *ic = config->input_configs->items[i]; + if (ic->input_type == NULL) { + continue; + } + + if (strcmp(ic->input_type, type_wildcard->identifier + 5) == 0) { + sway_log(SWAY_DEBUG, "Merging %s top of %s", + type_wildcard->identifier, + ic->identifier); + merge_input_config(ic, type_wildcard); + } + } } struct input_config *store_input_config(struct input_config *ic) { @@ -148,11 +173,19 @@ struct input_config *store_input_config(struct input_config *ic) { merge_wildcard_on_all(ic); } - int i = list_seq_find(config->input_configs, input_identifier_cmp, + list_t *config_list = NULL; + if (strncmp(ic->identifier, "type:", 5) == 0) { + config_list = config->input_type_configs; + merge_type_on_existing(ic); + } else { + config_list = config->input_configs; + } + + int i = list_seq_find(config_list, input_identifier_cmp, ic->identifier); if (i >= 0) { sway_log(SWAY_DEBUG, "Merging on top of existing input config"); - struct input_config *current = config->input_configs->items[i]; + struct input_config *current = config_list->items[i]; merge_input_config(current, ic); free_input_config(ic); ic = current; @@ -167,7 +200,7 @@ struct input_config *store_input_config(struct input_config *ic) { free_input_config(ic); ic = current; } - list_add(config->input_configs, ic); + list_add(config_list, ic); } else { // New wildcard config. Just add it sway_log(SWAY_DEBUG, "Adding input * config"); diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 0c5254bdf..a2a1e2740 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -105,6 +105,37 @@ const char *input_device_get_type(struct sway_input_device *device) { return "unknown"; } +static void apply_input_type_config(struct sway_input_device *input_device) { + const char *device_type = input_device_get_type(input_device); + struct input_config *type_config = NULL; + for (int i = 0; i < config->input_type_configs->length; i++) { + struct input_config *ic = config->input_type_configs->items[i]; + if (strcmp(ic->identifier + 5, device_type) == 0) { + type_config = ic; + break; + } + } + if (type_config == NULL) { + return; + } + + for (int i = 0; i < config->input_configs->length; i++) { + struct input_config *ic = config->input_configs->items[i]; + if (strcmp(input_device->identifier, ic->identifier) == 0) { + struct input_config *current = new_input_config(ic->identifier); + merge_input_config(current, type_config); + merge_input_config(current, ic); + + current->input_type = device_type; + config->input_configs->items[i] = current; + free_input_config(ic); + ic = NULL; + + break; + } + } +} + static struct sway_input_device *input_sway_device_from_wlr( struct wlr_input_device *device) { struct sway_input_device *input_device = NULL; @@ -541,6 +572,8 @@ static void handle_new_input(struct wl_listener *listener, void *data) { sway_log(SWAY_DEBUG, "adding device: '%s'", input_device->identifier); + apply_input_type_config(input_device); + if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER || input_device->wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { input_manager_libinput_config_pointer(input_device); @@ -693,9 +726,13 @@ void input_manager_set_focus(struct sway_node *node) { void input_manager_apply_input_config(struct input_config *input_config) { struct sway_input_device *input_device = NULL; bool wildcard = strcmp(input_config->identifier, "*") == 0; + bool type_wildcard = strncmp(input_config->identifier, "type:", 5) == 0; wl_list_for_each(input_device, &server.input->devices, link) { + bool type_matches = type_wildcard && + strcmp(input_device_get_type(input_device), input_config->identifier + 5) == 0; if (strcmp(input_device->identifier, input_config->identifier) == 0 - || wildcard) { + || wildcard + || type_matches) { if (input_device->wlr_device->type == WLR_INPUT_DEVICE_POINTER || input_device->wlr_device->type == WLR_INPUT_DEVICE_TABLET_TOOL) { input_manager_libinput_config_pointer(input_device); @@ -829,5 +866,13 @@ struct input_config *input_device_get_config(struct sway_input_device *device) { } } + const char *device_type = input_device_get_type(device); + for (int i = 0; i < config->input_type_configs->length; ++i) { + input_config = config->input_type_configs->items[i]; + if (strcmp(input_config->identifier + 5, device_type) == 0) { + return input_config; + } + } + return wildcard_config; } diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 1a8062fb9..efd3d1afc 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -9,13 +9,28 @@ sway-input - input configuration file and commands Sway allows for configuration of devices within the sway configuration file. To obtain a list of available device identifiers, run *swaymsg -t get_inputs*. Settings can also be applied to all input devices by using the wildcard, _\*_, -in place of _\_ in the commands below. +in place of _\_ in the commands below. In addition, the settings +can be applied to a type of device, by using _type:\_ in place +of _\_. Tip: If the configuration settings do not appear to be taking effect, you could try using _\*_ instead of _\_. If it works with the wildcard, try using a different identifier from *swaymsg -t get_inputs* until you find the correct input device. +Current available input types are: + +- touchpad +- pointer +- keyboard +- touch +- tablet_tool +- tablet_pad +- switch + +Note: The type configurations are applied as the devices appear and get applied +on top of the existing device configurations. + # INPUT COMMANDS ## KEYBOARD CONFIGURATION From b2cfcebef61087f819b55d2f815e62aa4a6f1a52 Mon Sep 17 00:00:00 2001 From: Connor E <38229097+c-edw@users.noreply.github.com> Date: Mon, 15 Apr 2019 01:16:35 +0100 Subject: [PATCH 0255/2054] Add deprecation warnings for new_float, new_window, and force_focus_wrapping. --- include/sway/commands.h | 2 ++ sway/commands.c | 4 ++-- sway/commands/force_focus_wrapping.c | 9 ++++++++- sway/commands/new_float.c | 13 +++++++++++++ sway/commands/new_window.c | 13 +++++++++++++ sway/meson.build | 2 ++ sway/sway.5.scd | 6 ------ 7 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 sway/commands/new_float.c create mode 100644 sway/commands/new_window.c diff --git a/include/sway/commands.h b/include/sway/commands.h index 9bd0f1cb5..2b66904c6 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -147,6 +147,8 @@ sway_cmd cmd_mark; sway_cmd cmd_mode; sway_cmd cmd_mouse_warping; sway_cmd cmd_move; +sway_cmd cmd_new_float; +sway_cmd cmd_new_window; sway_cmd cmd_nop; sway_cmd cmd_opacity; sway_cmd cmd_new_float; diff --git a/sway/commands.c b/sway/commands.c index abdaa3b87..d0bf402bd 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -77,8 +77,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 }, + { "new_float", cmd_new_float }, + { "new_window", cmd_new_window }, { "no_focus", cmd_no_focus }, { "output", cmd_output }, { "popup_during_fullscreen", cmd_popup_during_fullscreen }, diff --git a/sway/commands/force_focus_wrapping.c b/sway/commands/force_focus_wrapping.c index e646ae9b7..fafc1c3ef 100644 --- a/sway/commands/force_focus_wrapping.c +++ b/sway/commands/force_focus_wrapping.c @@ -1,9 +1,16 @@ -#include #include "sway/commands.h" #include "sway/config.h" +#include "log.h" #include "util.h" struct cmd_results *cmd_force_focus_wrapping(int argc, char **argv) { + sway_log(SWAY_INFO, "Warning: force_focus_wrapping is deprecated. " + "Use focus_wrapping instead."); + if (config->reading) { + config_add_swaynag_warning("force_focus_wrapping is deprecated. " + "Use focus_wrapping instead."); + } + struct cmd_results *error = checkarg(argc, "force_focus_wrapping", EXPECTED_EQUAL_TO, 1); if (error) { diff --git a/sway/commands/new_float.c b/sway/commands/new_float.c new file mode 100644 index 000000000..4fedb4cb8 --- /dev/null +++ b/sway/commands/new_float.c @@ -0,0 +1,13 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" + +struct cmd_results *cmd_new_float(int argc, char **argv) { + sway_log(SWAY_INFO, "Warning: new_float is deprecated. " + "Use default_floating_border instead."); + if (config->reading) { + config_add_swaynag_warning("new_float is deprecated. " + "Use default_floating_border instead."); + } + return cmd_default_floating_border(argc, argv); +} diff --git a/sway/commands/new_window.c b/sway/commands/new_window.c new file mode 100644 index 000000000..e8caa4873 --- /dev/null +++ b/sway/commands/new_window.c @@ -0,0 +1,13 @@ +#include "log.h" +#include "sway/commands.h" +#include "sway/config.h" + +struct cmd_results *cmd_new_window(int argc, char **argv) { + sway_log(SWAY_INFO, "Warning: new_window is deprecated. " + "Use default_border instead."); + if (config->reading) { + config_add_swaynag_warning("new_window is deprecated. " + "Use default_border instead."); + } + return cmd_default_border(argc, argv); +} diff --git a/sway/meson.build b/sway/meson.build index 12b86efb8..8254fb5c3 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -73,6 +73,8 @@ sway_sources = files( 'commands/mode.c', 'commands/mouse_warping.c', 'commands/move.c', + 'commands/new_float.c', + 'commands/new_window.c', 'commands/no_focus.c', 'commands/nop.c', 'commands/output.c', diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 1650cd60c..b484607a2 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -529,12 +529,6 @@ The default colors are: Whenever a window that matches _criteria_ appears, run list of commands. See *CRITERIA* for more details. -*force_focus_wrapping* yes|no - This option is a wrapper to support i3's legacy syntax. _no_ is equivalent - to _focus_wrapping yes_ and _yes_ is equivalent to - _focus_wrapping force_. This is only available for convenience. Please - use _focus_wrapping_ instead when possible. - *gaps* inner|outer|horizontal|vertical|top|right|bottom|left Sets default _amount_ pixels of _inner_ or _outer_ gap, where the inner affects spacing around each view and outer affects the spacing around each From fd6903673b084ef16f18e228405cb50adf28550d Mon Sep 17 00:00:00 2001 From: Ranieri Althoff Date: Mon, 15 Apr 2019 21:46:50 -0300 Subject: [PATCH 0256/2054] Send disabled output available modes on IPC get_outputs - Also fix missing trailing newline on pretty print Signed-off-by: Ranieri Althoff <1993083+ranisalt@users.noreply.github.com> --- sway/ipc-json.c | 16 +++++++++++++++- swaymsg/main.c | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 4ccf6dfd3..4af8d34bc 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -240,7 +240,21 @@ json_object *ipc_json_describe_disabled_output(struct sway_output *output) { json_object_new_string(wlr_output->model)); json_object_object_add(object, "serial", json_object_new_string(wlr_output->serial)); - json_object_object_add(object, "modes", json_object_new_array()); + + json_object *modes_array = json_object_new_array(); + struct wlr_output_mode *mode; + wl_list_for_each(mode, &wlr_output->modes, link) { + json_object *mode_object = json_object_new_object(); + json_object_object_add(mode_object, "width", + json_object_new_int(mode->width)); + json_object_object_add(mode_object, "height", + json_object_new_int(mode->height)); + json_object_object_add(mode_object, "refresh", + json_object_new_int(mode->refresh)); + json_object_array_add(modes_array, mode_object); + } + + json_object_object_add(object, "modes", modes_array); json_object_object_add(object, "current_workspace", NULL); diff --git a/swaymsg/main.c b/swaymsg/main.c index e2c43445c..ccc847f5c 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -230,7 +230,7 @@ static void pretty_print_output(json_object *o) { ); } else { printf( - "Output %s '%s %s %s' (inactive)", + "Output %s '%s %s %s' (inactive)\n", json_object_get_string(name), json_object_get_string(make), json_object_get_string(model), From 2f9eed659d290c1a3a008bf104e2a94b6e031915 Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Tue, 16 Apr 2019 02:30:19 -0700 Subject: [PATCH 0257/2054] Add documentation for rename workspace --- sway/sway.5.scd | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index b484607a2..1de06d3e5 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -223,6 +223,9 @@ set|plus|minus *reload* Reloads the sway config file and applies any changes. +*rename workspace* [] to + Rename either or the focused workspace to the + *resize* shrink|grow width|height [ [px|ppt]] Resizes the currently focused container by _amount_, specified in pixels or percentage points. If the units are omitted, floating containers are resized From e9e1fbc5011b8eaaaab73be21266ee1d388d38a7 Mon Sep 17 00:00:00 2001 From: mwenzkowski <29407878+mwenzkowski@users.noreply.github.com> Date: Tue, 16 Apr 2019 21:11:35 +0200 Subject: [PATCH 0258/2054] view.c: refactor view_autoconfigure() --- sway/tree/view.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index c241b2b3a..404326619 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -197,11 +197,13 @@ static bool gaps_to_edge(struct sway_view *view) { void view_autoconfigure(struct sway_view *view) { struct sway_container *con = view->container; + struct sway_workspace *ws = con->workspace; + if (container_is_scratchpad_hidden(con) && con->fullscreen_mode != FULLSCREEN_GLOBAL) { return; } - struct sway_output *output = con->workspace ? con->workspace->output : NULL; + struct sway_output *output = ws ? ws->output : NULL; if (con->fullscreen_mode == FULLSCREEN_WORKSPACE) { con->content_x = output->lx; @@ -217,27 +219,23 @@ void view_autoconfigure(struct sway_view *view) { return; } - struct sway_workspace *ws = view->container->workspace; - - bool smart = config->hide_edge_borders == E_SMART || - config->hide_edge_borders == E_SMART_NO_GAPS; - bool other_views = smart && !view_is_only_visible(view); - bool no_gaps = config->hide_edge_borders != E_SMART_NO_GAPS - || !gaps_to_edge(view); - con->border_top = con->border_bottom = true; con->border_left = con->border_right = true; + if (ws) { + bool smart = config->hide_edge_borders == E_SMART || + (config->hide_edge_borders == E_SMART_NO_GAPS && + !gaps_to_edge(view)); + bool hide_smart = smart && view_is_only_visible(view); + if (config->hide_edge_borders == E_BOTH - || config->hide_edge_borders == E_VERTICAL - || (smart && !other_views && no_gaps)) { + || config->hide_edge_borders == E_VERTICAL || hide_smart) { con->border_left = con->x - con->current_gaps.left != ws->x; int right_x = con->x + con->width + con->current_gaps.right; con->border_right = right_x != ws->x + ws->width; } if (config->hide_edge_borders == E_BOTH - || config->hide_edge_borders == E_HORIZONTAL - || (smart && !other_views && no_gaps)) { + || config->hide_edge_borders == E_HORIZONTAL || hide_smart) { con->border_top = con->y - con->current_gaps.top != ws->y; int bottom_y = con->y + con->height + con->current_gaps.bottom; con->border_bottom = bottom_y != ws->y + ws->height; From 9ac2342a673e899af87b8f8406820f2cc40bb185 Mon Sep 17 00:00:00 2001 From: mwenzkowski <29407878+mwenzkowski@users.noreply.github.com> Date: Tue, 16 Apr 2019 21:20:48 +0200 Subject: [PATCH 0259/2054] Don't apply hide_edge_borders to floating windows This change matches i3's behavior. --- sway/tree/view.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index 404326619..e8f5a2999 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -221,8 +221,10 @@ void view_autoconfigure(struct sway_view *view) { con->border_top = con->border_bottom = true; con->border_left = con->border_right = true; + double y_offset = 0; + + if (!container_is_floating(con) && ws) { - if (ws) { bool smart = config->hide_edge_borders == E_SMART || (config->hide_edge_borders == E_SMART_NO_GAPS && !gaps_to_edge(view)); @@ -240,24 +242,22 @@ void view_autoconfigure(struct sway_view *view) { int bottom_y = con->y + con->height + con->current_gaps.bottom; con->border_bottom = bottom_y != ws->y + ws->height; } - } - double y_offset = 0; - - // In a tabbed or stacked container, the container's y is the top of the - // title area. We have to offset the surface y by the height of the title, - // bar, and disable any top border because we'll always have the title bar. - list_t *siblings = container_get_siblings(con); - bool show_titlebar = (siblings && siblings->length > 1) - || !config->hide_lone_tab; - if (show_titlebar && !container_is_floating(con)) { - enum sway_container_layout layout = container_parent_layout(con); - if (layout == L_TABBED) { - y_offset = container_titlebar_height(); - con->border_top = false; - } else if (layout == L_STACKED) { - y_offset = container_titlebar_height() * siblings->length; - con->border_top = false; + // In a tabbed or stacked container, the container's y is the top of the + // title area. We have to offset the surface y by the height of the title, + // bar, and disable any top border because we'll always have the title bar. + list_t *siblings = container_get_siblings(con); + bool show_titlebar = (siblings && siblings->length > 1) + || !config->hide_lone_tab; + if (show_titlebar) { + enum sway_container_layout layout = container_parent_layout(con); + if (layout == L_TABBED) { + y_offset = container_titlebar_height(); + con->border_top = false; + } else if (layout == L_STACKED) { + y_offset = container_titlebar_height() * siblings->length; + con->border_top = false; + } } } From aa4deef8a86ccddbd0d9a7d894e72ac329d8960d Mon Sep 17 00:00:00 2001 From: Ashkan Kiani Date: Tue, 16 Apr 2019 17:35:39 -0700 Subject: [PATCH 0260/2054] Fix the payload type returned by IPC If a client is subscribed and sends a subsequent ipc command which causes event updates, then those event updates override the `client->current_command` and send the incorrect type for the payload associated with the command. Example: SUBSCRIBE {window} RUN_COMMAND focus -> PAYLOAD_TYPE is 0x80000002 for window events Therefore, we decouple the `client->current_command` by passing it as an argument to the ipc_send_reply function, avoiding a data race. The same is done for the `client->payload_length` as a precautionary measure for the same reason. --- sway/ipc-server.c | 136 ++++++++++++++++++++++++---------------------- 1 file changed, 72 insertions(+), 64 deletions(-) diff --git a/sway/ipc-server.c b/sway/ipc-server.c index e133a5bf1..ca1c1b120 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -47,13 +47,14 @@ struct ipc_client { struct wl_event_source *writable_event_source; struct sway_server *server; int fd; - uint32_t payload_length; uint32_t security_policy; - enum ipc_command_type current_command; enum ipc_command_type subscribed_events; size_t write_buffer_len; size_t write_buffer_size; char *write_buffer; + // The following are for storing data between event_loop calls + uint32_t pending_length; + enum ipc_command_type pending_type; }; struct sockaddr_un *ipc_user_sockaddr(void); @@ -61,8 +62,10 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data); int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data); int ipc_client_handle_writable(int client_fd, uint32_t mask, void *data); void ipc_client_disconnect(struct ipc_client *client); -void ipc_client_handle_command(struct ipc_client *client); -bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length); +void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_length, + enum ipc_command_type payload_type); +bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type, + const char *payload, uint32_t payload_length); static void handle_display_destroy(struct wl_listener *listener, void *data) { if (ipc_event_source) { @@ -178,7 +181,7 @@ int ipc_handle_connection(int fd, uint32_t mask, void *data) { return 0; } client->server = server; - client->payload_length = 0; + client->pending_length = 0; client->fd = client_fd; client->subscribed_events = 0; client->event_source = wl_event_loop_add_fd(server->wl_event_loop, @@ -224,9 +227,13 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { } // Wait for the rest of the command payload in case the header has already been read - if (client->payload_length > 0) { - if ((uint32_t)read_available >= client->payload_length) { - ipc_client_handle_command(client); + if (client->pending_length > 0) { + if ((uint32_t)read_available >= client->pending_length) { + // Reset pending values. + uint32_t pending_length = client->pending_length; + enum ipc_command_type pending_type = client->pending_type; + client->pending_length = 0; + ipc_client_handle_command(client, pending_length, pending_type); } return 0; } @@ -251,11 +258,15 @@ int ipc_client_handle_readable(int client_fd, uint32_t mask, void *data) { return 0; } - memcpy(&client->payload_length, &buf32[0], sizeof(buf32[0])); - memcpy(&client->current_command, &buf32[1], sizeof(buf32[1])); + memcpy(&client->pending_length, &buf32[0], sizeof(buf32[0])); + memcpy(&client->pending_type, &buf32[1], sizeof(buf32[1])); - if (read_available - received >= (long)client->payload_length) { - ipc_client_handle_command(client); + if (read_available - received >= (long)client->pending_length) { + // Reset pending values. + uint32_t pending_length = client->pending_length; + enum ipc_command_type pending_type = client->pending_type; + client->pending_length = 0; + ipc_client_handle_command(client, pending_length, pending_type); } return 0; @@ -278,8 +289,8 @@ static void ipc_send_event(const char *json_string, enum ipc_command_type event) if ((client->subscribed_events & event_mask(event)) == 0) { continue; } - client->current_command = event; - if (!ipc_send_reply(client, json_string, (uint32_t) strlen(json_string))) { + if (!ipc_send_reply(client, event, json_string, + (uint32_t)strlen(json_string))) { sway_log_errno(SWAY_INFO, "Unable to send reply to IPC client"); /* ipc_send_reply destroys client on error, which also * removes it from the list, so we need to process @@ -567,20 +578,21 @@ static void ipc_get_marks_callback(struct sway_container *con, void *data) { } } -void ipc_client_handle_command(struct ipc_client *client) { +void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_length, + enum ipc_command_type payload_type) { if (!sway_assert(client != NULL, "client != NULL")) { return; } - char *buf = malloc(client->payload_length + 1); + char *buf = malloc(payload_length + 1); if (!buf) { sway_log_errno(SWAY_INFO, "Unable to allocate IPC payload"); ipc_client_disconnect(client); return; } - if (client->payload_length > 0) { + if (payload_length > 0) { // Payload should be fully available - ssize_t received = recv(client->fd, buf, client->payload_length, 0); + ssize_t received = recv(client->fd, buf, payload_length, 0); if (received == -1) { sway_log_errno(SWAY_INFO, "Unable to receive payload from IPC client"); @@ -589,16 +601,15 @@ void ipc_client_handle_command(struct ipc_client *client) { return; } } - buf[client->payload_length] = '\0'; + buf[payload_length] = '\0'; - bool client_valid = true; - switch (client->current_command) { + switch (payload_type) { case IPC_COMMAND: { char *line = strtok(buf, "\n"); while (line) { size_t line_length = strlen(line); - if (line + line_length >= buf + client->payload_length) { + if (line + line_length >= buf + payload_length) { break; } line[line_length] = ';'; @@ -609,7 +620,7 @@ void ipc_client_handle_command(struct ipc_client *client) { transaction_commit_dirty(); char *json = cmd_results_to_json(res_list); int length = strlen(json); - client_valid = ipc_send_reply(client, json, (uint32_t)length); + ipc_send_reply(client, payload_type, json, (uint32_t)length); free(json); while (res_list->length) { struct cmd_results *results = res_list->items[0]; @@ -623,7 +634,7 @@ void ipc_client_handle_command(struct ipc_client *client) { case IPC_SEND_TICK: { ipc_event_tick(buf); - ipc_send_reply(client, "{\"success\": true}", 17); + ipc_send_reply(client, payload_type, "{\"success\": true}", 17); goto exit_cleanup; } @@ -656,8 +667,8 @@ void ipc_client_handle_command(struct ipc_client *client) { } } const char *json_string = json_object_to_json_string(outputs); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(outputs); // free goto exit_cleanup; } @@ -667,8 +678,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object *workspaces = json_object_new_array(); root_for_each_workspace(ipc_get_workspaces_callback, workspaces); const char *json_string = json_object_to_json_string(workspaces); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(workspaces); // free goto exit_cleanup; } @@ -679,7 +690,7 @@ void ipc_client_handle_command(struct ipc_client *client) { struct json_object *request = json_tokener_parse(buf); if (request == NULL || !json_object_is_type(request, json_type_array)) { const char msg[] = "{\"success\": false}"; - client_valid = ipc_send_reply(client, msg, strlen(msg)); + ipc_send_reply(client, payload_type, msg, strlen(msg)); sway_log(SWAY_INFO, "Failed to parse subscribe request"); goto exit_cleanup; } @@ -707,7 +718,7 @@ void ipc_client_handle_command(struct ipc_client *client) { is_tick = true; } else { const char msg[] = "{\"success\": false}"; - client_valid = ipc_send_reply(client, msg, strlen(msg)); + ipc_send_reply(client, payload_type, msg, strlen(msg)); json_object_put(request); sway_log(SWAY_INFO, "Unsupported event type in subscribe request"); goto exit_cleanup; @@ -716,11 +727,11 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_put(request); const char msg[] = "{\"success\": true}"; - client_valid = ipc_send_reply(client, msg, strlen(msg)); + ipc_send_reply(client, payload_type, msg, strlen(msg)); if (is_tick) { - client->current_command = IPC_EVENT_TICK; const char tickmsg[] = "{\"first\": true, \"payload\": \"\"}"; - ipc_send_reply(client, tickmsg, strlen(tickmsg)); + ipc_send_reply(client, IPC_EVENT_TICK, tickmsg, + strlen(tickmsg)); } goto exit_cleanup; } @@ -733,8 +744,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_array_add(inputs, ipc_json_describe_input(device)); } const char *json_string = json_object_to_json_string(inputs); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(inputs); // free goto exit_cleanup; } @@ -747,8 +758,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_array_add(seats, ipc_json_describe_seat(seat)); } const char *json_string = json_object_to_json_string(seats); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(seats); // free goto exit_cleanup; } @@ -757,8 +768,8 @@ void ipc_client_handle_command(struct ipc_client *client) { { json_object *tree = ipc_json_describe_node_recursive(&root->node); const char *json_string = json_object_to_json_string(tree); - client_valid = - ipc_send_reply(client, json_string, (uint32_t) strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(tree); goto exit_cleanup; } @@ -768,8 +779,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object *marks = json_object_new_array(); root_for_each_container(ipc_get_marks_callback, marks); const char *json_string = json_object_to_json_string(marks); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(marks); goto exit_cleanup; } @@ -778,8 +789,8 @@ void ipc_client_handle_command(struct ipc_client *client) { { json_object *version = ipc_json_get_version(); const char *json_string = json_object_to_json_string(version); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(version); // free goto exit_cleanup; } @@ -794,9 +805,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_array_add(bars, json_object_new_string(bar->id)); } const char *json_string = json_object_to_json_string(bars); - client_valid = - ipc_send_reply(client, json_string, - (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(bars); // free } else { // Send particular bar's details @@ -810,15 +820,14 @@ void ipc_client_handle_command(struct ipc_client *client) { } if (!bar) { const char *error = "{ \"success\": false, \"error\": \"No bar with that ID\" }"; - client_valid = - ipc_send_reply(client, error, (uint32_t)strlen(error)); + ipc_send_reply(client, payload_type, error, + (uint32_t)strlen(error)); goto exit_cleanup; } json_object *json = ipc_json_describe_bar_config(bar); const char *json_string = json_object_to_json_string(json); - client_valid = - ipc_send_reply(client, json_string, - (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(json); // free } goto exit_cleanup; @@ -832,8 +841,8 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object_array_add(modes, json_object_new_string(mode->name)); } const char *json_string = json_object_to_json_string(modes); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(modes); // free goto exit_cleanup; } @@ -843,34 +852,32 @@ void ipc_client_handle_command(struct ipc_client *client) { json_object *json = json_object_new_object(); json_object_object_add(json, "config", json_object_new_string(config->current_config)); const char *json_string = json_object_to_json_string(json); - client_valid = - ipc_send_reply(client, json_string, (uint32_t)strlen(json_string)); + ipc_send_reply(client, payload_type, json_string, + (uint32_t)strlen(json_string)); json_object_put(json); // free goto exit_cleanup; - } + } case IPC_SYNC: { // It was decided sway will not support this, just return success:false const char msg[] = "{\"success\": false}"; - ipc_send_reply(client, msg, strlen(msg)); + ipc_send_reply(client, payload_type, msg, strlen(msg)); goto exit_cleanup; } default: - sway_log(SWAY_INFO, "Unknown IPC command type %i", client->current_command); + sway_log(SWAY_INFO, "Unknown IPC command type %x", payload_type); goto exit_cleanup; } exit_cleanup: - if (client_valid) { - client->payload_length = 0; - } free(buf); return; } -bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t payload_length) { +bool ipc_send_reply(struct ipc_client *client, enum ipc_command_type payload_type, + const char *payload, uint32_t payload_length) { assert(payload); char data[IPC_HEADER_SIZE]; @@ -878,7 +885,7 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay memcpy(data, ipc_magic, sizeof(ipc_magic)); memcpy(&data32[0], &payload_length, sizeof(payload_length)); - memcpy(&data32[1], &client->current_command, sizeof(client->current_command)); + memcpy(&data32[1], &payload_type, sizeof(payload_type)); while (client->write_buffer_len + IPC_HEADER_SIZE + payload_length >= client->write_buffer_size) { @@ -910,6 +917,7 @@ bool ipc_send_reply(struct ipc_client *client, const char *payload, uint32_t pay ipc_client_handle_writable, client); } - sway_log(SWAY_DEBUG, "Added IPC reply to client %d queue: %s", client->fd, payload); + sway_log(SWAY_DEBUG, "Added IPC reply of type 0x%x to client %d queue: %s", + payload_type, client->fd, payload); return true; } From d7d29fe546ab0acb332013eb380f8193e1d5b9fc Mon Sep 17 00:00:00 2001 From: Brian Ashworth Date: Wed, 17 Apr 2019 01:57:34 -0400 Subject: [PATCH 0261/2054] swaymsg: add timeout and type checks This adds a 3 second timeout to the initial reply in swaymsg. This prevents swaymsg from hanging when `swaymsg -t get_{inputs,seats}` is used in i3. The timeout is removed when waiting for a subscribed event or monitoring for subscribed events. This also adds type checks to commands where i3 does not reply with all of the properties that sway does (such as `modes` in `get_outputs`). This is mostly just a behavioral adjustment since swaymsg should run on i3. When running under i3, some command reply's (such as the one for `get_outputs) may have more useful information in the raw json than the pretty printed version. --- common/ipc-client.c | 10 ++++++++++ include/ipc-client.h | 6 ++++++ swaymsg/main.c | 11 ++++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/common/ipc-client.c b/common/ipc-client.c index 13e2dfa32..b6c03d132 100644 --- a/common/ipc-client.c +++ b/common/ipc-client.c @@ -69,6 +69,16 @@ int ipc_open_socket(const char *socket_path) { return socketfd; } +bool ipc_set_recv_timeout(int socketfd, struct timeval tv) { + if (setsockopt(socketfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) { + sway_log_errno(SWAY_ERROR, "Failed to set ipc recv timeout"); + return false; + } + sway_log(SWAY_DEBUG, "ipc recv timeout set to %ld.%06ld", + tv.tv_sec, tv.tv_usec); + return true; +} + struct ipc_response *ipc_recv_response(int socketfd) { char data[IPC_HEADER_SIZE]; uint32_t *data32 = (uint32_t *)(data + sizeof(ipc_magic)); diff --git a/include/ipc-client.h b/include/ipc-client.h index c9f5b3441..d3895023f 100644 --- a/include/ipc-client.h +++ b/include/ipc-client.h @@ -1,7 +1,9 @@ #ifndef _SWAY_IPC_CLIENT_H #define _SWAY_IPC_CLIENT_H +#include #include +#include #include "ipc.h" @@ -36,5 +38,9 @@ struct ipc_response *ipc_recv_response(int socketfd); * Free ipc_response struct */ void free_ipc_response(struct ipc_response *response); +/** + * Sets the receive timeout for the IPC socket + */ +bool ipc_set_recv_timeout(int socketfd, struct timeval tv); #endif diff --git a/swaymsg/main.c b/swaymsg/main.c index ccc847f5c..518993af7 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -238,7 +239,8 @@ static void pretty_print_output(json_object *o) { ); } - size_t modes_len = json_object_array_length(modes); + size_t modes_len = json_object_is_type(modes, json_type_array) + ? json_object_array_length(modes) : 0; if (modes_len > 0) { printf(" Available modes:\n"); for (size_t i = 0; i < modes_len; ++i) { @@ -448,6 +450,8 @@ int main(int argc, char **argv) { int ret = 0; int socketfd = ipc_open_socket(socket_path); + struct timeval timeout = {.tv_sec = 3, .tv_usec = 0}; + ipc_set_recv_timeout(socketfd, timeout); uint32_t len = strlen(command); char *resp = ipc_single_command(socketfd, type, command, &len); if (!quiet) { @@ -478,6 +482,11 @@ int main(int argc, char **argv) { free(resp); if (type == IPC_SUBSCRIBE && ret == 0) { + // Remove the timeout for subscribed events + timeout.tv_sec = 0; + timeout.tv_usec = 0; + ipc_set_recv_timeout(socketfd, timeout); + do { struct ipc_response *reply = ipc_recv_response(socketfd); if (!reply) { From 852f1f0b349b63fe3c71e39ab1d3e87642a5c2d4 Mon Sep 17 00:00:00 2001 From: Alex Maese Date: Fri, 12 Apr 2019 11:26:23 -0500 Subject: [PATCH 0262/2054] Create unbindsym and unbindcode commands --- include/sway/commands.h | 2 + sway/commands.c | 2 + sway/commands/bind.c | 114 ++++++++++++++++++++++++++++------------ sway/sway.5.scd | 8 +++ 4 files changed, 91 insertions(+), 35 deletions(-) diff --git a/include/sway/commands.h b/include/sway/commands.h index 2b66904c6..96d68e9e2 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -181,6 +181,8 @@ sway_cmd cmd_title_align; sway_cmd cmd_title_format; sway_cmd cmd_titlebar_border_thickness; sway_cmd cmd_titlebar_padding; +sway_cmd cmd_unbindcode; +sway_cmd cmd_unbindsym; sway_cmd cmd_unmark; sway_cmd cmd_urgent; sway_cmd cmd_workspace; diff --git a/sway/commands.c b/sway/commands.c index d0bf402bd..267c7de81 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -92,6 +92,8 @@ static struct cmd_handler handlers[] = { { "title_align", cmd_title_align }, { "titlebar_border_thickness", cmd_titlebar_border_thickness }, { "titlebar_padding", cmd_titlebar_padding }, + { "unbindcode", cmd_unbindcode }, + { "unbindsym", cmd_unbindsym }, { "workspace", cmd_workspace }, { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth }, }; diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 9a937c611..fad7f8500 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -196,12 +196,70 @@ static struct cmd_results *identify_key(const char* name, bool first_key, return NULL; } +static struct cmd_results *binding_add(struct sway_binding *binding, + list_t *mode_bindings, const char *bindtype, + const char *keycombo, bool warn) { + // overwrite the binding if it already exists + bool overwritten = false; + for (int i = 0; i < mode_bindings->length; ++i) { + struct sway_binding *config_binding = mode_bindings->items[i]; + if (binding_key_compare(binding, config_binding)) { + sway_log(SWAY_INFO, "Overwriting binding '%s' for device '%s' " + "to `%s` from `%s`", keycombo, binding->input, + binding->command, config_binding->command); + if (warn) { + config_add_swaynag_warning("Overwriting binding" + "'%s' for device '%s' to `%s` from `%s`", + keycombo, binding->input, binding->command, + config_binding->command); + } + free_sway_binding(config_binding); + mode_bindings->items[i] = binding; + overwritten = true; + } + } + + if (!overwritten) { + list_add(mode_bindings, binding); + sway_log(SWAY_DEBUG, "%s - Bound %s to command `%s` for device '%s'", + bindtype, keycombo, binding->command, binding->input); + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} + +static struct cmd_results *binding_remove(struct sway_binding *binding, + list_t *mode_bindings, const char *bindtype, + const char *keycombo) { + for (int i = 0; i < mode_bindings->length; ++i) { + struct sway_binding *config_binding = mode_bindings->items[i]; + if (binding_key_compare(binding, config_binding)) { + sway_log(SWAY_DEBUG, "%s - Unbound `%s` from device '%s'", + bindtype, keycombo, binding->input); + free_sway_binding(config_binding); + free_sway_binding(binding); + list_del(mode_bindings, i); + return cmd_results_new(CMD_SUCCESS, NULL); + } + } + free_sway_binding(binding); + return cmd_results_new(CMD_FAILURE, "Could not find binding `%s` " + "for the given flags", keycombo); +} + static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, - bool bindcode) { - const char *bindtype = bindcode ? "bindcode" : "bindsym"; + bool bindcode, bool unbind) { + const char *bindtype; + int minargs = 2; + if (unbind) { + bindtype = bindcode ? "unbindcode" : "unbindsym"; + minargs--; + } else { + bindtype = bindcode ? "bindcode": "bindsym"; + } struct cmd_results *error = NULL; - if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, 2))) { + if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) { return error; } @@ -248,15 +306,14 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, BINDING_MOUSECODE : BINDING_MOUSESYM; } - if (argc < 2) { + if (argc < minargs) { free_sway_binding(binding); return cmd_results_new(CMD_FAILURE, "Invalid %s command " - "(expected at least 2 non-option arguments, got %d)", bindtype, argc); + "(expected at least %d non-option arguments, got %d)", + bindtype, minargs, argc); } - binding->command = join_args(argv + 1, argc - 1); - list_t *split = split_string(argv[0], "+"); for (int i = 0; i < split->length; ++i) { // Check for a modifier key @@ -287,7 +344,6 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, list_add(binding->keys, key); } list_free_items_and_destroy(split); - binding->order = binding_order++; // refine region of interest for mouse binding once we are certain // that this is one @@ -310,41 +366,29 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, mode_bindings = config->current_mode->mouse_bindings; } - // overwrite the binding if it already exists - bool overwritten = false; - for (int i = 0; i < mode_bindings->length; ++i) { - struct sway_binding *config_binding = mode_bindings->items[i]; - if (binding_key_compare(binding, config_binding)) { - sway_log(SWAY_INFO, "Overwriting binding '%s' for device '%s' " - "to `%s` from `%s`", argv[0], binding->input, - binding->command, config_binding->command); - if (warn) { - config_add_swaynag_warning("Overwriting binding" - "'%s' for device '%s' to `%s` from `%s`", - argv[0], binding->input, binding->command, - config_binding->command); - } - free_sway_binding(config_binding); - mode_bindings->items[i] = binding; - overwritten = true; - } + if (unbind) { + return binding_remove(binding, mode_bindings, bindtype, argv[0]); } - if (!overwritten) { - list_add(mode_bindings, binding); - } - - sway_log(SWAY_DEBUG, "%s - Bound %s to command `%s` for device '%s'", - bindtype, argv[0], binding->command, binding->input); - return cmd_results_new(CMD_SUCCESS, NULL); + binding->command = join_args(argv + 1, argc - 1); + binding->order = binding_order++; + return binding_add(binding, mode_bindings, bindtype, argv[0], warn); } struct cmd_results *cmd_bindsym(int argc, char **argv) { - return cmd_bindsym_or_bindcode(argc, argv, false); + return cmd_bindsym_or_bindcode(argc, argv, false, false); } struct cmd_results *cmd_bindcode(int argc, char **argv) { - return cmd_bindsym_or_bindcode(argc, argv, true); + return cmd_bindsym_or_bindcode(argc, argv, true, false); +} + +struct cmd_results *cmd_unbindsym(int argc, char **argv) { + return cmd_bindsym_or_bindcode(argc, argv, false, true); +} + +struct cmd_results *cmd_unbindcode(int argc, char **argv) { + return cmd_bindsym_or_bindcode(argc, argv, true, true); } struct cmd_results *cmd_bindswitch(int argc, char **argv) { diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 1de06d3e5..5d94ecedb 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -644,6 +644,14 @@ The default colors are: to _yes_, the marks will be shown on the _left_ side instead of the _right_ side. +*unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--input-device=] + Removes the binding for _key combo_ that was previously bound with the + given flags. If _input-device_ is given, only the binding for that + input device will be unbound. + + *unbindcode* [--whole-window] [--border] [--exclude-titlebar] [--release] [input-device=] + is also available for unbinding with key/button codes instead of key/button names. + *unmark* [] *unmark* will remove _identifier_ from the list of current marks on a window. If _identifier_ is omitted, all marks are removed. From 8d4f8aea4654c6071e243900b5dbf18e1cd62fd6 Mon Sep 17 00:00:00 2001 From: Alex Maese Date: Fri, 12 Apr 2019 11:28:48 -0500 Subject: [PATCH 0263/2054] Add unbindswitch command --- include/sway/commands.h | 1 + sway/commands.c | 1 + sway/commands/bind.c | 152 ++++++++++++++++++++++++++-------------- sway/sway.5.scd | 3 + 4 files changed, 103 insertions(+), 54 deletions(-) diff --git a/include/sway/commands.h b/include/sway/commands.h index 96d68e9e2..0347ad5e3 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -182,6 +182,7 @@ sway_cmd cmd_title_format; sway_cmd cmd_titlebar_border_thickness; sway_cmd cmd_titlebar_padding; sway_cmd cmd_unbindcode; +sway_cmd cmd_unbindswitch; sway_cmd cmd_unbindsym; sway_cmd cmd_unmark; sway_cmd cmd_urgent; diff --git a/sway/commands.c b/sway/commands.c index 267c7de81..237bfc281 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -93,6 +93,7 @@ static struct cmd_handler handlers[] = { { "titlebar_border_thickness", cmd_titlebar_border_thickness }, { "titlebar_padding", cmd_titlebar_padding }, { "unbindcode", cmd_unbindcode }, + { "unbindswitch", cmd_unbindswitch }, { "unbindsym", cmd_unbindsym }, { "workspace", cmd_workspace }, { "workspace_auto_back_and_forth", cmd_ws_auto_back_and_forth }, diff --git a/sway/commands/bind.c b/sway/commands/bind.c index fad7f8500..dc7e0b190 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -196,6 +196,59 @@ static struct cmd_results *identify_key(const char* name, bool first_key, return NULL; } +static struct cmd_results *switch_binding_add( + struct sway_switch_binding *binding, const char *bindtype, + const char *switchcombo, bool warn) { + list_t *mode_bindings = config->current_mode->switch_bindings; + // overwrite the binding if it already exists + bool overwritten = false; + for (int i = 0; i < mode_bindings->length; ++i) { + struct sway_switch_binding *config_binding = mode_bindings->items[i]; + if (binding_switch_compare(binding, config_binding)) { + sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`", + switchcombo, binding->command, config_binding->command); + if (warn) { + config_add_swaynag_warning("Overwriting binding" + "'%s' to `%s` from `%s`", + switchcombo, binding->command, + config_binding->command); + } + free_switch_binding(config_binding); + mode_bindings->items[i] = binding; + overwritten = true; + } + } + + if (!overwritten) { + list_add(mode_bindings, binding); + sway_log(SWAY_DEBUG, "%s - Bound %s to command `%s`", + bindtype, switchcombo, binding->command); + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} + +static struct cmd_results *switch_binding_remove( + struct sway_switch_binding *binding, const char *bindtype, + const char *switchcombo) { + list_t *mode_bindings = config->current_mode->switch_bindings; + for (int i = 0; i < mode_bindings->length; ++i) { + struct sway_switch_binding *config_binding = mode_bindings->items[i]; + if (binding_switch_compare(binding, config_binding)) { + free_switch_binding(config_binding); + free_switch_binding(binding); + list_del(mode_bindings, i); + sway_log(SWAY_DEBUG, "%s - Unbound %s switch", + bindtype, switchcombo); + return cmd_results_new(CMD_SUCCESS, NULL); + } + } + + free_switch_binding(binding); + return cmd_results_new(CMD_FAILURE, "Could not find switch binding `%s`", + switchcombo); +} + static struct cmd_results *binding_add(struct sway_binding *binding, list_t *mode_bindings, const char *bindtype, const char *keycombo, bool warn) { @@ -375,26 +428,17 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, return binding_add(binding, mode_bindings, bindtype, argv[0], warn); } -struct cmd_results *cmd_bindsym(int argc, char **argv) { - return cmd_bindsym_or_bindcode(argc, argv, false, false); -} - -struct cmd_results *cmd_bindcode(int argc, char **argv) { - return cmd_bindsym_or_bindcode(argc, argv, true, false); -} - -struct cmd_results *cmd_unbindsym(int argc, char **argv) { - return cmd_bindsym_or_bindcode(argc, argv, false, true); -} - -struct cmd_results *cmd_unbindcode(int argc, char **argv) { - return cmd_bindsym_or_bindcode(argc, argv, true, true); -} - -struct cmd_results *cmd_bindswitch(int argc, char **argv) { +struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, + bool unbind) { + int minargs = 2; + char *bindtype = "bindswitch"; + if (unbind) { + minargs--; + bindtype = "unbindswitch"; + } struct cmd_results *error = NULL; - if ((error = checkarg(argc, "bindswitch", EXPECTED_AT_LEAST, 2))) { + if ((error = checkarg(argc, bindtype, EXPECTED_AT_LEAST, minargs))) { return error; } struct sway_switch_binding *binding = calloc(1, sizeof(struct sway_switch_binding)); @@ -417,20 +461,19 @@ struct cmd_results *cmd_bindswitch(int argc, char **argv) { argc--; } - if (argc < 2) { + if (argc < minargs) { free(binding); return cmd_results_new(CMD_FAILURE, - "Invalid bindswitch command (expected at least 2 " - "non-option arguments, got %d)", argc); + "Invalid %s command (expected at least %d " + "non-option arguments, got %d)", bindtype, minargs, argc); } - binding->command = join_args(argv + 1, argc - 1); list_t *split = split_string(argv[0], ":"); if (split->length != 2) { free_switch_binding(binding); return cmd_results_new(CMD_FAILURE, - "Invalid bindswitch command (expected two arguments with " - "format : , got %d)", argc); + "Invalid %s command (expected binding with the form " + ":)", bindtype, argc); } if (strcmp(split->items[0], "tablet") == 0) { binding->type = WLR_SWITCH_TYPE_TABLET_MODE; @@ -439,8 +482,8 @@ struct cmd_results *cmd_bindswitch(int argc, char **argv) { } else { free_switch_binding(binding); return cmd_results_new(CMD_FAILURE, - "Invalid bindswitch command (expected switch binding: " - "unknown switch %s)", split->items[0]); + "Invalid %s command (expected switch binding: " + "unknown switch %s)", bindtype, split->items[0]); } if (strcmp(split->items[1], "on") == 0) { binding->state = WLR_SWITCH_STATE_ON; @@ -451,40 +494,41 @@ struct cmd_results *cmd_bindswitch(int argc, char **argv) { } else { free_switch_binding(binding); return cmd_results_new(CMD_FAILURE, - "Invalid bindswitch command " - "(expected switch state: unknown state %d)", - split->items[0]); + "Invalid %s command " + "(expected switch state: unknown state %d)", + bindtype, split->items[0]); } list_free_items_and_destroy(split); - list_t *mode_bindings = config->current_mode->switch_bindings; - - // overwrite the binding if it already exists - bool overwritten = false; - for (int i = 0; i < mode_bindings->length; ++i) { - struct sway_switch_binding *config_binding = mode_bindings->items[i]; - if (binding_switch_compare(binding, config_binding)) { - sway_log(SWAY_INFO, "Overwriting binding '%s' to `%s` from `%s`", - argv[0], binding->command, config_binding->command); - if (warn) { - config_add_swaynag_warning("Overwriting binding" - "'%s' to `%s` from `%s`", - argv[0], binding->command, - config_binding->command); - } - free_switch_binding(config_binding); - mode_bindings->items[i] = binding; - overwritten = true; - } + if (unbind) { + return switch_binding_remove(binding, bindtype, argv[0]); } + binding->command = join_args(argv + 1, argc - 1); + return switch_binding_add(binding, bindtype, argv[0], warn); +} - if (!overwritten) { - list_add(mode_bindings, binding); - } +struct cmd_results *cmd_bindsym(int argc, char **argv) { + return cmd_bindsym_or_bindcode(argc, argv, false, false); +} - sway_log(SWAY_DEBUG, "bindswitch - Bound %s to command `%s`", - argv[0], binding->command); - return cmd_results_new(CMD_SUCCESS, NULL); +struct cmd_results *cmd_bindcode(int argc, char **argv) { + return cmd_bindsym_or_bindcode(argc, argv, true, false); +} + +struct cmd_results *cmd_unbindsym(int argc, char **argv) { + return cmd_bindsym_or_bindcode(argc, argv, false, true); +} + +struct cmd_results *cmd_unbindcode(int argc, char **argv) { + return cmd_bindsym_or_bindcode(argc, argv, true, true); +} + +struct cmd_results *cmd_bindswitch(int argc, char **argv) { + return cmd_bind_or_unbind_switch(argc, argv, false); +} + +struct cmd_results *cmd_unbindswitch(int argc, char **argv) { + return cmd_bind_or_unbind_switch(argc, argv, true); } /** diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 5d94ecedb..801936519 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -644,6 +644,9 @@ The default colors are: to _yes_, the marks will be shown on the _left_ side instead of the _right_ side. +*unbindswitch* : + Removes a binding for when changes to . + *unbindsym* [--whole-window] [--border] [--exclude-titlebar] [--release] [--input-device=] Removes the binding for _key combo_ that was previously bound with the given flags. If _input-device_ is given, only the binding for that From 35ddd9aab3c386345176d7ea1f5814f152ba16bd Mon Sep 17 00:00:00 2001 From: Alex Maese Date: Fri, 12 Apr 2019 11:30:36 -0500 Subject: [PATCH 0264/2054] Add unbindsym/unbindcode command for swaybar --- include/sway/commands.h | 2 + sway/commands/bar.c | 2 + sway/commands/bar/bind.c | 108 ++++++++++++++++++++++++++++----------- sway/sway-bar.5.scd | 6 +++ 4 files changed, 88 insertions(+), 30 deletions(-) diff --git a/include/sway/commands.h b/include/sway/commands.h index 0347ad5e3..cfe2aa079 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -217,6 +217,8 @@ sway_cmd bar_cmd_tray_bindcode; sway_cmd bar_cmd_tray_bindsym; sway_cmd bar_cmd_tray_output; sway_cmd bar_cmd_tray_padding; +sway_cmd bar_cmd_unbindcode; +sway_cmd bar_cmd_unbindsym; sway_cmd bar_cmd_wrap_scroll; sway_cmd bar_cmd_workspace_buttons; diff --git a/sway/commands/bar.c b/sway/commands/bar.c index 82441f9e4..73be70406 100644 --- a/sway/commands/bar.c +++ b/sway/commands/bar.c @@ -32,6 +32,8 @@ static struct cmd_handler bar_handlers[] = { { "tray_bindsym", bar_cmd_tray_bindsym }, { "tray_output", bar_cmd_tray_output }, { "tray_padding", bar_cmd_tray_padding }, + { "unbindcode", bar_cmd_unbindcode }, + { "unbindsym", bar_cmd_unbindsym }, { "workspace_buttons", bar_cmd_workspace_buttons }, { "wrap_scroll", bar_cmd_wrap_scroll }, }; diff --git a/sway/commands/bar/bind.c b/sway/commands/bar/bind.c index 17030a05f..f94b48118 100644 --- a/sway/commands/bar/bind.c +++ b/sway/commands/bar/bind.c @@ -9,10 +9,70 @@ #include "log.h" #include "stringop.h" -static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code) { - const char *command = code ? "bar bindcode" : "bar bindsym"; +static struct cmd_results *binding_add(struct bar_binding *binding, + list_t *mode_bindings) { + const char *name = get_mouse_button_name(binding->button); + bool overwritten = false; + for (int i = 0; i < mode_bindings->length; i++) { + struct bar_binding *other = mode_bindings->items[i]; + if (other->button == binding->button && + other->release == binding->release) { + overwritten = true; + mode_bindings->items[i] = binding; + free_bar_binding(other); + sway_log(SWAY_DEBUG, "[bar %s] Updated binding for %u (%s)%s", + config->current_bar->id, binding->button, name, + binding->release ? " - release" : ""); + break; + } + } + if (!overwritten) { + list_add(mode_bindings, binding); + sway_log(SWAY_DEBUG, "[bar %s] Added binding for %u (%s)%s", + config->current_bar->id, binding->button, name, + binding->release ? " - release" : ""); + } + return cmd_results_new(CMD_SUCCESS, NULL); +} + +static struct cmd_results *binding_remove(struct bar_binding *binding, + list_t *mode_bindings) { + const char *name = get_mouse_button_name(binding->button); + for (int i = 0; i < mode_bindings->length; i++) { + struct bar_binding *other = mode_bindings->items[i]; + if (other->button == binding->button && + other->release == binding->release) { + sway_log(SWAY_DEBUG, "[bar %s] Unbound binding for %u (%s)%s", + config->current_bar->id, binding->button, name, + binding->release ? " - release" : ""); + free_bar_binding(other); + free_bar_binding(binding); + list_del(mode_bindings, i); + return cmd_results_new(CMD_SUCCESS, NULL); + } + } + + struct cmd_results *error = cmd_results_new(CMD_FAILURE, "Could not " + "find binding for [bar %s]" " Button %u (%s)%s", + config->current_bar->id, binding->button, name, + binding->release ? " - release" : ""); + free_bar_binding(binding); + return error; +} + +static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code, + bool unbind) { + int minargs = 2; + const char *command; + if (unbind) { + minargs--; + command = code ? "bar unbindcode" : "bar unbindsym"; + } else { + command = code ? "bar bindcode" : "bar bindsym"; + } + struct cmd_results *error = NULL; - if ((error = checkarg(argc, command, EXPECTED_AT_LEAST, 2))) { + if ((error = checkarg(argc, command, EXPECTED_AT_LEAST, minargs))) { return error; } if (!config->current_bar) { @@ -46,39 +106,27 @@ static struct cmd_results *bar_cmd_bind(int argc, char **argv, bool code) { free_bar_binding(binding); return cmd_results_new(CMD_INVALID, "Unknown button %s", argv[0]); } - const char *name = get_mouse_button_name(binding->button); + list_t *bindings = config->current_bar->bindings; + if (unbind) { + return binding_remove(binding, bindings); + } binding->command = join_args(argv + 1, argc - 1); - - list_t *bindings = config->current_bar->bindings; - bool overwritten = false; - for (int i = 0; i < bindings->length; i++) { - struct bar_binding *other = bindings->items[i]; - if (other->button == binding->button && - other->release == binding->release) { - overwritten = true; - bindings->items[i] = binding; - free_bar_binding(other); - sway_log(SWAY_DEBUG, "[bar %s] Updated binding for %u (%s)%s", - config->current_bar->id, binding->button, name, - binding->release ? " - release" : ""); - break; - } - } - if (!overwritten) { - list_add(bindings, binding); - sway_log(SWAY_DEBUG, "[bar %s] Added binding for %u (%s)%s", - config->current_bar->id, binding->button, name, - binding->release ? " - release" : ""); - } - - return cmd_results_new(CMD_SUCCESS, NULL); + return binding_add(binding, bindings); } struct cmd_results *bar_cmd_bindcode(int argc, char **argv) { - return bar_cmd_bind(argc, argv, true); + return bar_cmd_bind(argc, argv, true, false); } struct cmd_results *bar_cmd_bindsym(int argc, char **argv) { - return bar_cmd_bind(argc, argv, false); + return bar_cmd_bind(argc, argv, false, false); +} + +struct cmd_results *bar_cmd_unbindcode(int argc, char **argv) { + return bar_cmd_bind(argc, argv, true, true); +} + +struct cmd_results *bar_cmd_unbindsym(int argc, char **argv) { + return bar_cmd_bind(argc, argv, false, true); } diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd index 1f4ceaf2f..a3df11601 100644 --- a/sway/sway-bar.5.scd +++ b/sway/sway-bar.5.scd @@ -112,6 +112,12 @@ Sway allows configuring swaybar in the sway configuration file. the bar. This value will be multiplied by the output scale. The default is _3_. +*unbindcode* [--release] + Removes the binding with the given . + +*unbindsym* [--release] button[1-9]| + Removes the binding with the given