From 125c74338ac8bf8c15323a49730352b82d6d51bd Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Sun, 24 Mar 2024 12:20:05 +0100 Subject: [PATCH 001/361] man: document supported modifier names --- sway/sway.5.scd | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 7e58b5286..f73db3ba3 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -400,6 +400,12 @@ runtime. only be available for that group. By default, if you overwrite a binding, swaynag will give you a warning. To silence this, use the _--no-warn_ flag. + For specifying modifier keys, you can use the XKB modifier names _Shift_, + _Lock_ (for Caps Lock), _Control_, _Mod1_ (for Alt), _Mod2_ (for Num Lock), + _Mod3_ (for XKB modifier Mod3), _Mod4_ (for the Logo key), and _Mod5_ (for + AltGr). In addition, you can use the aliases _Ctrl_ (for Control) and _Alt_ + (for Alt). + Unless the flag _--locked_ is set, the command will not be run when a screen locking program is active. If there is a matching binding with and without _--locked_, the one with will be preferred when locked and the From e2f3ebad8c1943800dd5f017d547d9d98bfb8bb1 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 16 Mar 2024 00:37:06 +0100 Subject: [PATCH 002/361] config/output: Split apply_output_config Applying an output config has two stages: Atomic application of wlr_output_state, and applicaiton of non-atomic state like output layout. Split the latter out into finalize_output_config for use in a later commit. --- sway/config/output.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index 1b2332e95..fd1d6e3c8 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -503,25 +503,12 @@ static void queue_output_config(struct output_config *oc, } } -bool apply_output_config(struct output_config *oc, struct sway_output *output) { +static bool finalize_output_config(struct output_config *oc, struct sway_output *output) { if (output == root->fallback_output) { return false; } struct wlr_output *wlr_output = output->wlr_output; - - struct wlr_output_state pending = {0}; - queue_output_config(oc, output, &pending); - - sway_log(SWAY_DEBUG, "Committing output %s", wlr_output->name); - if (!wlr_output_commit_state(wlr_output, &pending)) { - // Failed to commit output changes, maybe the output is missing a CRTC. - // Leave the output disabled for now and try again when the output gets - // the mode we asked for. - sway_log(SWAY_ERROR, "Failed to commit output %s", wlr_output->name); - return false; - } - if (oc && !oc->enabled) { sway_log(SWAY_DEBUG, "Disabling output %s", oc->name); if (output->enabled) { @@ -577,6 +564,30 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output) { output->max_render_time = oc->max_render_time; } + return true; +} + +bool apply_output_config(struct output_config *oc, struct sway_output *output) { + if (output == root->fallback_output) { + return false; + } + + struct wlr_output_state pending = {0}; + queue_output_config(oc, output, &pending); + + sway_log(SWAY_DEBUG, "Committing output %s", output->wlr_output->name); + if (!wlr_output_commit_state(output->wlr_output, &pending)) { + // Failed to commit output changes, maybe the output is missing a CRTC. + // Leave the output disabled for now and try again when the output gets + // the mode we asked for. + sway_log(SWAY_ERROR, "Failed to commit output %s", output->wlr_output->name); + return false; + } + + if (!finalize_output_config(oc, output)) { + return false; + } + // Reconfigure all devices, since input config may have been applied before // this output came online, and some config items (like map_to_output) are // dependent on an output being present. From 3e03eb3a017d144137dbe6591891f3a51a61dea0 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 16 Mar 2024 00:48:56 +0100 Subject: [PATCH 003/361] config/output: Introduce apply_output_configs Introduce apply_output_configs, which applies the specified matched output configs as a single backend commit. Reimplement apply_output_config_to_outputs using apply_output_configs. --- include/sway/config.h | 11 ++++ sway/config/output.c | 148 +++++++++++++++++++++++++++++++++++------- 2 files changed, 135 insertions(+), 24 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index f9da19675..d23fe578a 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -291,6 +291,14 @@ struct output_config { char *background_fallback; }; +/** + * An output config pre-matched to an output + */ +struct matched_output_config { + struct sway_output *output; + struct output_config *config; +}; + /** * Stores size of gaps for each side */ @@ -684,6 +692,9 @@ void merge_output_config(struct output_config *dst, struct output_config *src); bool apply_output_config(struct output_config *oc, struct sway_output *output); +bool apply_output_configs(struct matched_output_config *configs, + size_t configs_len, bool test_only); + bool test_output_config(struct output_config *oc, struct sway_output *output); struct output_config *store_output_config(struct output_config *oc); diff --git a/sway/config/output.c b/sway/config/output.c index fd1d6e3c8..de9515e21 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "sway/config.h" #include "sway/input/cursor.h" #include "sway/output.h" @@ -716,39 +717,138 @@ struct output_config *find_output_config(struct sway_output *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 - // will be applied during normal "new output" event from wlroots. - bool wildcard = strcmp(oc->name, "*") == 0; - struct sway_output *sway_output, *tmp; - wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) { - if (output_match_name_or_id(sway_output, oc->name)) { - char id[128]; - output_get_identifier(id, sizeof(id), sway_output); - 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); +bool apply_output_configs(struct matched_output_config *configs, + size_t configs_len, bool test_only) { + struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states)); + if (!states) { + return false; + } - if (!wildcard) { - // Stop looking if the output config isn't applicable to all - // outputs - break; - } + sway_log(SWAY_DEBUG, "Committing %zd outputs", configs_len); + for (size_t idx = 0; idx < configs_len; idx++) { + struct matched_output_config *cfg = &configs[idx]; + struct wlr_backend_output_state *backend_state = &states[idx]; + + backend_state->output = cfg->output->wlr_output; + wlr_output_state_init(&backend_state->base); + + sway_log(SWAY_DEBUG, "Preparing config for %s", + cfg->output->wlr_output->name); + queue_output_config(cfg->config, cfg->output, &backend_state->base); + } + + struct wlr_output_swapchain_manager swapchain_mgr; + wlr_output_swapchain_manager_init(&swapchain_mgr, server.backend); + + bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len); + if (!ok) { + sway_log(SWAY_ERROR, "Swapchain prepare failed"); + goto out; + } + + if (test_only) { + // The swapchain manager already did a test for us + goto out; + } + + for (size_t idx = 0; idx < configs_len; idx++) { + struct matched_output_config *cfg = &configs[idx]; + struct wlr_backend_output_state *backend_state = &states[idx]; + + struct wlr_scene_output_state_options opts = { + .swapchain = wlr_output_swapchain_manager_get_swapchain( + &swapchain_mgr, backend_state->output), + }; + struct wlr_scene_output *scene_output = cfg->output->scene_output; + struct wlr_output_state *state = &backend_state->base; + if (!wlr_scene_output_build_state(scene_output, state, &opts)) { + sway_log(SWAY_ERROR, "Building output state for '%s' failed", + backend_state->output->name); + goto out; } } + ok = wlr_backend_commit(server.backend, states, configs_len); + if (!ok) { + sway_log(SWAY_ERROR, "Backend commit failed"); + goto out; + } + + sway_log(SWAY_DEBUG, "Commit of %zd outputs succeeded", configs_len); + + wlr_output_swapchain_manager_apply(&swapchain_mgr); + + for (size_t idx = 0; idx < configs_len; idx++) { + struct matched_output_config *cfg = &configs[idx]; + sway_log(SWAY_DEBUG, "Finalizing config for %s", + cfg->output->wlr_output->name); + finalize_output_config(cfg->config, cfg->output); + } + +out: + wlr_output_swapchain_manager_finish(&swapchain_mgr); + for (size_t idx = 0; idx < configs_len; idx++) { + struct wlr_backend_output_state *backend_state = &states[idx]; + wlr_output_state_finish(&backend_state->base); + } + free(states); + + // Reconfigure all devices, since input config may have been applied before + // this output came online, and some config items (like map_to_output) are + // dependent on an output being present. + input_manager_configure_all_input_mappings(); + // Reconfigure the cursor images, since the scale may have changed. + input_manager_configure_xcursor(); + struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { wlr_seat_pointer_notify_clear_focus(seat->wlr_seat); cursor_rebase(seat->cursor); } + + return ok; +} + +void apply_output_config_to_outputs(struct output_config *oc) { + size_t configs_len = wl_list_length(&root->all_outputs); + struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); + if (!configs) { + return; + } + + // Try to find the output container and apply configuration now. If + // this is during startup then there will be no container and config + // will be applied during normal "new output" event from wlroots. + int config_idx = 0; + struct sway_output *sway_output; + wl_list_for_each(sway_output, &root->all_outputs, link) { + if (sway_output == root->fallback_output) { + configs_len--; + continue; + } + + struct matched_output_config *config = &configs[config_idx++]; + config->output = sway_output; + config->config = find_output_config(sway_output); + + if (!output_match_name_or_id(sway_output, oc->name)) { + continue; + } + + if (!config->config && oc) { + // No stored output config matched, apply oc directly + sway_log(SWAY_DEBUG, "Applying oc directly"); + config->config = new_output_config(oc->name); + merge_output_config(config->config, oc); + } + } + + apply_output_configs(configs, configs_len, false); + for (size_t idx = 0; idx < configs_len; idx++) { + struct matched_output_config *cfg = &configs[idx]; + free_output_config(cfg->config); + } + free(configs); } void reset_outputs(void) { From 923f642b704022442cc3245a2fa13278341c14e0 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 16 Mar 2024 00:57:11 +0100 Subject: [PATCH 004/361] output/config: Add apply_all_output_configs Apply all output configs as they are. This differs from apply_output_config_to_outputs, which tries to apply a specific output config. --- include/sway/config.h | 2 ++ sway/config/output.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/include/sway/config.h b/include/sway/config.h index d23fe578a..eff7cfbbd 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -695,6 +695,8 @@ bool apply_output_config(struct output_config *oc, struct sway_output *output); bool apply_output_configs(struct matched_output_config *configs, size_t configs_len, bool test_only); +void apply_all_output_configs(void); + bool test_output_config(struct output_config *oc, struct sway_output *output); struct output_config *store_output_config(struct output_config *oc); diff --git a/sway/config/output.c b/sway/config/output.c index de9515e21..5bf5bed58 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -809,6 +809,34 @@ out: return ok; } +void apply_all_output_configs(void) { + size_t configs_len = wl_list_length(&root->all_outputs); + struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); + if (!configs) { + return; + } + + int config_idx = 0; + struct sway_output *sway_output; + wl_list_for_each(sway_output, &root->all_outputs, link) { + if (sway_output == root->fallback_output) { + configs_len--; + continue; + } + + struct matched_output_config *config = &configs[config_idx++]; + config->output = sway_output; + config->config = find_output_config(sway_output); + } + + apply_output_configs(configs, configs_len, false); + for (size_t idx = 0; idx < configs_len; idx++) { + struct matched_output_config *cfg = &configs[idx]; + free_output_config(cfg->config); + } + free(configs); +} + void apply_output_config_to_outputs(struct output_config *oc) { size_t configs_len = wl_list_length(&root->all_outputs); struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); From 98be797356876153e74405dbef36e3e71875ca2e Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 16 Mar 2024 01:00:46 +0100 Subject: [PATCH 005/361] Use apply_all_output_configs to light up outputs This allows us to test and if necessary degrade the entire backend configuration to light everything up. --- sway/commands/output.c | 4 ++-- sway/desktop/output.c | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/sway/commands/output.c b/sway/commands/output.c index 462dffd23..5e5d31b3e 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -103,13 +103,13 @@ struct cmd_results *cmd_output(int argc, char **argv) { bool background = output->background; - output = store_output_config(output); + store_output_config(output); // If reloading, the output configs will be applied after reading the // entire config and before the deferred commands so that an auto generated // workspace name is not given to re-enabled outputs. if (!config->reloading && !config->validating) { - apply_output_config_to_outputs(output); + apply_all_output_configs(); if (background) { if (!spawn_swaybg()) { return cmd_results_new(CMD_FAILURE, diff --git a/sway/desktop/output.c b/sway/desktop/output.c index b8f2d32d2..b2647219c 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -521,9 +521,7 @@ void handle_new_output(struct wl_listener *listener, void *data) { sway_session_lock_add_output(server->session_lock.lock, output); } - struct output_config *oc = find_output_config(output); - apply_output_config(oc, output); - free_output_config(oc); + apply_all_output_configs(); transaction_commit_dirty(); @@ -652,6 +650,6 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, oc->power = 1; break; } - oc = store_output_config(oc); - apply_output_config(oc, output); + store_output_config(oc); + apply_all_output_configs(); } From 3b419020a32f4f8385e49d2137ceb4d9b8262176 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 16 Mar 2024 01:01:56 +0100 Subject: [PATCH 006/361] desktop/output: Use apply_output_configs for output mgmt --- sway/desktop/output.c | 113 ++++++++++++++++++++++++++---------------- 1 file changed, 69 insertions(+), 44 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index b2647219c..bd3de3fe1 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -550,63 +550,88 @@ void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) { wlr_output_schedule_frame(output->wlr_output); } +static struct output_config *output_config_for_config_head( + struct wlr_output_configuration_head_v1 *config_head, + struct sway_output *output) { + struct output_config *oc = new_output_config(output->wlr_output->name); + oc->enabled = config_head->state.enabled; + if (!oc->enabled) { + return oc; + } + + if (config_head->state.mode != NULL) { + struct wlr_output_mode *mode = config_head->state.mode; + oc->width = mode->width; + oc->height = mode->height; + oc->refresh_rate = mode->refresh / 1000.f; + } else { + oc->width = config_head->state.custom_mode.width; + oc->height = config_head->state.custom_mode.height; + oc->refresh_rate = + config_head->state.custom_mode.refresh / 1000.f; + } + oc->x = config_head->state.x; + oc->y = config_head->state.y; + oc->transform = config_head->state.transform; + oc->scale = config_head->state.scale; + oc->adaptive_sync = config_head->state.adaptive_sync_enabled; + return oc; +} + static void output_manager_apply(struct sway_server *server, struct wlr_output_configuration_v1 *config, bool test_only) { - // TODO: perform atomic tests on the whole backend atomically + size_t configs_len = wl_list_length(&root->all_outputs); + struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); + if (!configs) { + return; + } - struct wlr_output_configuration_head_v1 *config_head; - // First disable outputs we need to disable - bool ok = true; - wl_list_for_each(config_head, &config->heads, link) { - struct wlr_output *wlr_output = config_head->state.output; - struct sway_output *output = wlr_output->data; - if (!output->enabled || config_head->state.enabled) { + int config_idx = 0; + struct sway_output *sway_output; + wl_list_for_each(sway_output, &root->all_outputs, link) { + if (sway_output == root->fallback_output) { + configs_len--; continue; } - struct output_config *oc = new_output_config(output->wlr_output->name); - oc->enabled = false; - if (test_only) { - ok &= test_output_config(oc, output); - } else { - oc = store_output_config(oc); - ok &= apply_output_config(oc, output); + struct matched_output_config *cfg = &configs[config_idx++]; + cfg->output = sway_output; + + struct wlr_output_configuration_head_v1 *config_head; + wl_list_for_each(config_head, &config->heads, link) { + if (config_head->state.output == sway_output->wlr_output) { + cfg->config = output_config_for_config_head(config_head, sway_output); + break; + } + } + if (!cfg->config) { + cfg->config = find_output_config(sway_output); } } - // Then enable outputs that need to - wl_list_for_each(config_head, &config->heads, link) { - struct wlr_output *wlr_output = config_head->state.output; - struct sway_output *output = wlr_output->data; - if (!config_head->state.enabled) { - continue; - } - struct output_config *oc = new_output_config(output->wlr_output->name); - oc->enabled = true; - if (config_head->state.mode != NULL) { - struct wlr_output_mode *mode = config_head->state.mode; - oc->width = mode->width; - oc->height = mode->height; - oc->refresh_rate = mode->refresh / 1000.f; - } else { - oc->width = config_head->state.custom_mode.width; - oc->height = config_head->state.custom_mode.height; - oc->refresh_rate = - config_head->state.custom_mode.refresh / 1000.f; - } - oc->x = config_head->state.x; - oc->y = config_head->state.y; - oc->transform = config_head->state.transform; - oc->scale = config_head->state.scale; - oc->adaptive_sync = config_head->state.adaptive_sync_enabled; + bool ok = apply_output_configs(configs, configs_len, test_only); + for (size_t idx = 0; idx < configs_len; idx++) { + struct matched_output_config *cfg = &configs[idx]; - if (test_only) { - ok &= test_output_config(oc, output); + // Only store new configs for successful non-test commits. Old configs, + // test-only and failed commits just get freed. + bool store_config = false; + if (!test_only && ok) { + struct wlr_output_configuration_head_v1 *config_head; + wl_list_for_each(config_head, &config->heads, link) { + if (config_head->state.output == sway_output->wlr_output) { + store_config = true; + break; + } + } + } + if (store_config) { + store_output_config(cfg->config); } else { - oc = store_output_config(oc); - ok &= apply_output_config(oc, output); + free_output_config(cfg->config); } } + free(configs); if (ok) { wlr_output_configuration_v1_send_succeeded(config); From 56e97b7d60e3723f79fd972061191117bf544f08 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 16 Mar 2024 01:03:37 +0100 Subject: [PATCH 007/361] config/output: Remove apply_output_config --- include/sway/config.h | 2 -- sway/config/output.c | 30 ------------------------------ 2 files changed, 32 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index eff7cfbbd..69b144461 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -690,8 +690,6 @@ struct output_config *new_output_config(const char *name); void merge_output_config(struct output_config *dst, struct output_config *src); -bool apply_output_config(struct output_config *oc, struct sway_output *output); - bool apply_output_configs(struct matched_output_config *configs, size_t configs_len, bool test_only); diff --git a/sway/config/output.c b/sway/config/output.c index 5bf5bed58..cb12683d8 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -568,36 +568,6 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output return true; } -bool apply_output_config(struct output_config *oc, struct sway_output *output) { - if (output == root->fallback_output) { - return false; - } - - struct wlr_output_state pending = {0}; - queue_output_config(oc, output, &pending); - - sway_log(SWAY_DEBUG, "Committing output %s", output->wlr_output->name); - if (!wlr_output_commit_state(output->wlr_output, &pending)) { - // Failed to commit output changes, maybe the output is missing a CRTC. - // Leave the output disabled for now and try again when the output gets - // the mode we asked for. - sway_log(SWAY_ERROR, "Failed to commit output %s", output->wlr_output->name); - return false; - } - - if (!finalize_output_config(oc, output)) { - return false; - } - - // Reconfigure all devices, since input config may have been applied before - // this output came online, and some config items (like map_to_output) are - // dependent on an output being present. - input_manager_configure_all_input_mappings(); - // Reconfigure the cursor images, since the scale may have changed. - input_manager_configure_xcursor(); - return true; -} - bool test_output_config(struct output_config *oc, struct sway_output *output) { if (output == root->fallback_output) { return false; From 9becff0ba56ac7da8b1235aa5740fb04414636a2 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 16 Mar 2024 01:11:35 +0100 Subject: [PATCH 008/361] output/config: Remove reset_outputs and co. apply_output_config_to_outputs uses the specified output config to check which outputs to apply to, and to use as backup when no config is found. If any config matches the output, the specified config will be disregarded. The only remaining user of apply_output_config_to_outputs is reset_outputs, which called apply_output_config_to_outputs with either the first stored wildcard config, or a new empty wildcard config. Providing a stored or empty wildcard config is practically the same as calling `apply_all_output_configs`. Replace uses of `reset_outputs` with `apply_all_output_configs` and remove the now unused functions. --- include/sway/config.h | 4 ---- sway/config.c | 2 +- sway/config/output.c | 53 ------------------------------------------- 3 files changed, 1 insertion(+), 58 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 69b144461..7e67ba218 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -701,10 +701,6 @@ 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); - void free_output_config(struct output_config *oc); bool spawn_swaybg(void); diff --git a/sway/config.c b/sway/config.c index 72fc41e78..f9131e0f7 100644 --- a/sway/config.c +++ b/sway/config.c @@ -532,7 +532,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { } sway_switch_retrigger_bindings_for_all(); - reset_outputs(); + apply_all_output_configs(); spawn_swaybg(); config->reloading = false; diff --git a/sway/config/output.c b/sway/config/output.c index cb12683d8..a7c2f9b8b 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -807,59 +807,6 @@ void apply_all_output_configs(void) { free(configs); } -void apply_output_config_to_outputs(struct output_config *oc) { - size_t configs_len = wl_list_length(&root->all_outputs); - struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); - if (!configs) { - return; - } - - // Try to find the output container and apply configuration now. If - // this is during startup then there will be no container and config - // will be applied during normal "new output" event from wlroots. - int config_idx = 0; - struct sway_output *sway_output; - wl_list_for_each(sway_output, &root->all_outputs, link) { - if (sway_output == root->fallback_output) { - configs_len--; - continue; - } - - struct matched_output_config *config = &configs[config_idx++]; - config->output = sway_output; - config->config = find_output_config(sway_output); - - if (!output_match_name_or_id(sway_output, oc->name)) { - continue; - } - - if (!config->config && oc) { - // No stored output config matched, apply oc directly - sway_log(SWAY_DEBUG, "Applying oc directly"); - config->config = new_output_config(oc->name); - merge_output_config(config->config, oc); - } - } - - apply_output_configs(configs, configs_len, false); - for (size_t idx = 0; idx < configs_len; idx++) { - struct matched_output_config *cfg = &configs[idx]; - free_output_config(cfg->config); - } - free(configs); -} - -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 c3fca26d303617614bee67ad766fd3cb95609245 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 16 Mar 2024 13:25:23 +0100 Subject: [PATCH 009/361] config/output: Make merge_output_config static --- include/sway/config.h | 2 -- sway/config/output.c | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 7e67ba218..5a303d9fe 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -688,8 +688,6 @@ const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filt struct output_config *new_output_config(const char *name); -void merge_output_config(struct output_config *dst, struct output_config *src); - bool apply_output_configs(struct matched_output_config *configs, size_t configs_len, bool test_only); diff --git a/sway/config/output.c b/sway/config/output.c index a7c2f9b8b..72cbf2618 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -79,7 +79,7 @@ struct output_config *new_output_config(const char *name) { return oc; } -void merge_output_config(struct output_config *dst, struct output_config *src) { +static void merge_output_config(struct output_config *dst, struct output_config *src) { if (src->enabled != -1) { dst->enabled = src->enabled; } From 26a9a6b4792a1b2b00406ae6c20ea88325e7c7aa Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 16 Mar 2024 13:25:45 +0100 Subject: [PATCH 010/361] output/config: Remove unused test_output_config --- include/sway/config.h | 2 -- sway/config/output.c | 10 ---------- 2 files changed, 12 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 5a303d9fe..40710199a 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -693,8 +693,6 @@ bool apply_output_configs(struct matched_output_config *configs, void apply_all_output_configs(void); -bool test_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); diff --git a/sway/config/output.c b/sway/config/output.c index 72cbf2618..3f1c3126b 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -568,16 +568,6 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output return true; } -bool test_output_config(struct output_config *oc, struct sway_output *output) { - if (output == root->fallback_output) { - return false; - } - - struct wlr_output_state pending = {0}; - queue_output_config(oc, output, &pending); - return wlr_output_test_state(output->wlr_output, &pending); -} - static void default_output_config(struct output_config *oc, struct wlr_output *wlr_output) { oc->enabled = 1; From a4ef37752fd6ae9e84d60cbe4eaead07f71f9435 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 16 Mar 2024 17:55:07 +0100 Subject: [PATCH 011/361] commands/output/toggle: Use free_output_config --- sway/commands/output/toggle.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/commands/output/toggle.c b/sway/commands/output/toggle.c index 6342d526b..c6b72845b 100644 --- a/sway/commands/output/toggle.c +++ b/sway/commands/output/toggle.c @@ -29,7 +29,7 @@ struct cmd_results *output_cmd_toggle(int argc, char **argv) { config->handler_context.output_config->enabled = 1; } - free(oc); + free_output_config(oc); config->handler_context.leftovers.argc = argc; config->handler_context.leftovers.argv = argv; return NULL; From 9e1465107788af2c8ce93e2a288e9d32bc09711c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 28 Mar 2024 11:45:46 +0100 Subject: [PATCH 012/361] input: pass wlr_seat_client to wlr_seat_touch_notify_cancel() References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4613 --- sway/input/seatop_down.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index 35fd3bcb2..340e334bf 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -117,7 +117,11 @@ static void handle_touch_cancel(struct sway_seat *seat, } if (e->surface) { - wlr_seat_touch_notify_cancel(seat->wlr_seat, e->surface); + struct wl_client *client = wl_resource_get_client(e->surface->resource); + struct wlr_seat_client *seat_client = wlr_seat_client_for_wl_client(seat->wlr_seat, client); + if (seat_client != NULL) { + wlr_seat_touch_notify_cancel(seat->wlr_seat, seat_client); + } } if (wl_list_empty(&e->point_events)) { From dcb142bf5e390250939544075b5852ca21eaf721 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 29 Mar 2024 18:46:08 -0400 Subject: [PATCH 013/361] sway-ipc(7): Escape backslashes correctly in GET_CONFIG output Without this change, i see the following in the sway-ipc manpage: ``` 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 Mod4nbindsym $mod+q exitn" } ``` --- sway/sway-ipc.7.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index c9895e52c..2f6972489 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -1046,7 +1046,7 @@ An object with a single string property containing the contents of the config *Example Reply:* ``` { - "config": "set $mod Mod4\nbindsym $mod+q exit\n" + "config": "set $mod Mod4\\nbindsym $mod+q exit\\n" } ``` From bc258a3be2f946c1c93bcbe40735b2db068e0ea8 Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Fri, 5 Apr 2024 16:40:28 +0200 Subject: [PATCH 014/361] input: add Super as alternative for Mod4 This PR implements alternative human-readable names for the logo key (Mod4) as proposed in #8084. --- sway/input/keyboard.c | 1 + sway/sway.5.scd | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index b97f0152c..f74d0658d 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -32,6 +32,7 @@ static struct modifier_key { { XKB_MOD_NAME_NUM, WLR_MODIFIER_MOD2 }, { "Mod3", WLR_MODIFIER_MOD3 }, { XKB_MOD_NAME_LOGO, WLR_MODIFIER_LOGO }, + { "Super", WLR_MODIFIER_LOGO }, { "Mod5", WLR_MODIFIER_MOD5 }, }; diff --git a/sway/sway.5.scd b/sway/sway.5.scd index f73db3ba3..9f8239473 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -403,8 +403,8 @@ runtime. For specifying modifier keys, you can use the XKB modifier names _Shift_, _Lock_ (for Caps Lock), _Control_, _Mod1_ (for Alt), _Mod2_ (for Num Lock), _Mod3_ (for XKB modifier Mod3), _Mod4_ (for the Logo key), and _Mod5_ (for - AltGr). In addition, you can use the aliases _Ctrl_ (for Control) and _Alt_ - (for Alt). + AltGr). In addition, you can use the aliases _Ctrl_ (for Control), _Alt_ + (for Alt), and _Super_ (for the Logo key). Unless the flag _--locked_ is set, the command will not be run when a screen locking program is active. If there is a matching binding with From 1267e47de913d2cda2644ad89bba4e9c55842cd3 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 16 Mar 2024 17:55:20 +0100 Subject: [PATCH 015/361] config/output: Refactor handling of tiered configs Output configuration can be applied to a particular output in three ways: As a wildcard, by connector name and by identifier. This in turn means that three different configurations must be handled at any given time. In the current model, this is managed by merging new configuration into every other matching configuration. At the same time, an additional synthetic configuration is made which matchehes both identifier and name at the same time, further complicating logic. Instead, manage and store each configuration independently and merge them in order when retrieving configuration for an output. When changes are made to a less specific configuration, clear these fields from more specific configurations to allow the change to take effect regardless of precedence. Fixes: https://github.com/swaywm/sway/issues/8048 --- include/sway/config.h | 9 +- sway/config/output.c | 294 +++++++++++++++++++----------------------- 2 files changed, 142 insertions(+), 161 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 40710199a..0be1cd229 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -693,7 +693,14 @@ bool apply_output_configs(struct matched_output_config *configs, void apply_all_output_configs(void); -struct output_config *store_output_config(struct output_config *oc); +/** + * store_output_config stores a new output config. An output may be matched by + * three different config types, in order of precedence: Identifier, name and + * wildcard. When storing a config type of lower precedence, assume that the + * user wants the config to take immediate effect by superseding (clearing) the + * same values from higher presedence configuration. + */ +void store_output_config(struct output_config *oc); struct output_config *find_output_config(struct sway_output *output); diff --git a/sway/config/output.c b/sway/config/output.c index 3f1c3126b..e5ff240ae 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -79,6 +79,71 @@ struct output_config *new_output_config(const char *name) { return oc; } +// supersede_output_config clears all fields in dst that were set in src +static void supersede_output_config(struct output_config *dst, struct output_config *src) { + if (src->enabled != -1) { + dst->enabled = -1; + } + if (src->width != -1) { + dst->width = -1; + } + if (src->height != -1) { + dst->height = -1; + } + if (src->x != -1) { + dst->x = -1; + } + if (src->y != -1) { + dst->y = -1; + } + if (src->scale != -1) { + dst->scale = -1; + } + if (src->scale_filter != SCALE_FILTER_DEFAULT) { + dst->scale_filter = SCALE_FILTER_DEFAULT; + } + if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) { + dst->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; + } + if (src->refresh_rate != -1) { + dst->refresh_rate = -1; + } + if (src->custom_mode != -1) { + dst->custom_mode = -1; + } + if (src->drm_mode.type != (uint32_t) -1) { + dst->drm_mode.type = -1; + } + if (src->transform != -1) { + dst->transform = -1; + } + if (src->max_render_time != -1) { + dst->max_render_time = -1; + } + if (src->adaptive_sync != -1) { + dst->adaptive_sync = -1; + } + if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { + dst->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; + } + if (src->background) { + free(dst->background); + dst->background = NULL; + } + if (src->background_option) { + free(dst->background_option); + dst->background_option = NULL; + } + if (src->background_fallback) { + free(dst->background_fallback); + dst->background_fallback = NULL; + } + if (src->power != -1) { + dst->power = -1; + } +} + +// merge_output_config sets all fields in dst that were set in src static void merge_output_config(struct output_config *dst, struct output_config *src) { if (src->enabled != -1) { dst->enabled = src->enabled; @@ -142,96 +207,46 @@ static void merge_output_config(struct output_config *dst, struct output_config } } -static void merge_wildcard_on_all(struct output_config *wildcard) { - for (int i = 0; i < config->output_configs->length; i++) { - struct output_config *oc = config->output_configs->items[i]; - if (strcmp(wildcard->name, oc->name) != 0) { - sway_log(SWAY_DEBUG, "Merging output * config on %s", oc->name); - merge_output_config(oc, wildcard); - } - } -} - -static void merge_id_on_name(struct output_config *oc) { - struct sway_output *output = all_output_by_name_or_id(oc->name); - if (output == NULL) { - return; +void store_output_config(struct output_config *oc) { + bool merged = false; + bool wildcard = strcmp(oc->name, "*") == 0; + struct sway_output *output = wildcard ? NULL : output_by_name_or_id(oc->name); + if (!output && !wildcard) { + // There is no config by this name, just add it in + goto done; } - const char *name = output->wlr_output->name; char id[128]; output_get_identifier(id, sizeof(id), output); + for (int i = 0; i < config->output_configs->length; i++) { + struct output_config *old = config->output_configs->items[i]; - char *id_on_name = format_str("%s on %s", id, name); - if (!id_on_name) { - return; - } + // If the old config matches the new config's name, regardless of + // whether it was name or identifier, merge on top of the existing + // config. If the new config is a wildcard, this also merges on top of + // old wildcard configs. + if (strcmp(old->name, oc->name) == 0) { + merge_output_config(old, oc); + merged = true; + continue; + } - 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) (power %d) (max render time: %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->power, - ion_oc->max_render_time); + // If the new config is a wildcard config we supersede all non-wildcard + // configs. Old wildcard configs have already been handled above. + if (wildcard) { + supersede_output_config(old, oc); + continue; + } + + // If the new config matches an output's name, and the old config + // matches on that output's identifier, supersede it. + if (strcmp(old->name, id) == 0 && + strcmp(oc->name, output->wlr_output->name) == 0) { + supersede_output_config(old, oc); } } - 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); - if (i >= 0) { - sway_log(SWAY_DEBUG, "Merging on top of existing output config"); - struct output_config *current = config->output_configs->items[i]; - merge_output_config(current, oc); - free_output_config(oc); - oc = current; - } else if (!wildcard) { - sway_log(SWAY_DEBUG, "Adding non-wildcard output config"); - i = list_seq_find(config->output_configs, output_name_cmp, "*"); - if (i >= 0) { - sway_log(SWAY_DEBUG, "Merging on top of output * config"); - struct output_config *current = new_output_config(oc->name); - merge_output_config(current, config->output_configs->items[i]); - merge_output_config(current, oc); - free_output_config(oc); - oc = current; - } - list_add(config->output_configs, oc); - } else { - // New wildcard config. Just add it - sway_log(SWAY_DEBUG, "Adding output * config"); - list_add(config->output_configs, oc); - } +done: sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) " "(max render time: %d)", @@ -240,7 +255,13 @@ struct output_config *store_output_config(struct output_config *oc) { oc->transform, oc->background, oc->background_option, oc->power, oc->max_render_time); - return oc; + // If the configuration was not merged into an existing configuration, add + // it to the list. Otherwise we're done with it and can free it. + if (!merged) { + list_add(config->output_configs, oc); + } else { + free_output_config(oc); + } } static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, @@ -587,94 +608,47 @@ static void default_output_config(struct output_config *oc, oc->max_render_time = 0; } -static struct output_config *get_output_config(char *identifier, - struct sway_output *sway_output) { +// find_output_config returns a merged output_config containing all stored +// configuration that applies to the specified output. +struct output_config *find_output_config(struct sway_output *sway_output) { const char *name = sway_output->wlr_output->name; + struct output_config *oc = NULL; - struct output_config *oc_id_on_name = NULL; - struct output_config *oc_name = NULL; - struct output_config *oc_id = NULL; - - char *id_on_name = format_str("%s on %s", identifier, name); - int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); - if (i >= 0) { - 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"); + struct output_config *result = new_output_config(name); if (config->reloading) { default_output_config(result, sway_output->wlr_output); } - 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 - 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 = strdup(id_on_name); - merge_output_config(result, temp); + char id[128]; + output_get_identifier(id, sizeof(id), sway_output); - sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)" - " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)" - " (power %d) (max render time: %d)", result->name, result->enabled, - result->width, result->height, result->refresh_rate, - result->x, result->y, result->scale, result->transform, - result->background, result->background_option, result->power, - result->max_render_time); - } else if (oc_name) { - // No identifier config, just return a copy of the name config - free(result->name); - result->name = strdup(name); - merge_output_config(result, oc_name); - } else if (oc_id) { - // No name config, just return a copy of the identifier config - free(result->name); - result->name = strdup(identifier); - merge_output_config(result, oc_id); - } 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; - } + int i; + bool match = false; + if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) { + match = true; + oc = config->output_configs->items[i]; + merge_output_config(result, oc); + } + if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) { + match = true; + oc = config->output_configs->items[i]; + merge_output_config(result, oc); + } + if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) { + match = true; + oc = config->output_configs->items[i]; + merge_output_config(result, oc); } - free(id_on_name); - return result; -} + if (!match && !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); + return NULL; + } -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); + return result; } bool apply_output_configs(struct matched_output_config *configs, From f11c5d562e3507a5e8b21491d61a6e43e81e43ad Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 12 Apr 2024 18:42:50 +0200 Subject: [PATCH 016/361] config/output: fix NULL derefs in store_output_config() ../sway/config/output.c:33:21: runtime error: member access within null pointer of type 'struct sway_output' AddressSanitizer:DEADLYSIGNAL ================================================================= ==7856==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000080 (pc 0x63da8558205c bp 0x7ffdc35881a0 sp 0x7ffdc3588160 T0) ==7856==The signal is caused by a READ memory access. ==7856==Hint: address points to the zero page. #0 0x63da8558205c in output_get_identifier ../sway/config/output.c:33 #1 0x63da855865c3 in store_output_config ../sway/config/output.c:220 #2 0x63da855d4066 in cmd_output ../sway/commands/output.c:106 #3 0x63da8547f2e3 in config_command ../sway/commands.c:425 #4 0x63da8548f3fc in read_config ../sway/config.c:822 #5 0x63da8548a224 in load_config ../sway/config.c:435 #6 0x63da8548b065 in load_main_config ../sway/config.c:507 #7 0x63da854bee8d in main ../sway/main.c:351 #8 0x77e2ea643ccf (/usr/lib/libc.so.6+0x25ccf) (BuildId: c0caa0b7709d3369ee575fcd7d7d0b0fc48733af) #9 0x77e2ea643d89 in __libc_start_main (/usr/lib/libc.so.6+0x25d89) (BuildId: c0caa0b7709d3369ee575fcd7d7d0b0fc48733af) #10 0x63da8547ad64 in _start (/home/simon/src/sway/build/sway/sway+0x372d64) (BuildId: 3fa2e8838c1c32713b40aec6b1e84bbe4db5bde8) Fixes: 1267e47de913 ("config/output: Refactor handling of tiered configs") --- sway/config/output.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index e5ff240ae..aab3f0bd9 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -217,7 +217,10 @@ void store_output_config(struct output_config *oc) { } char id[128]; - output_get_identifier(id, sizeof(id), output); + if (output) { + output_get_identifier(id, sizeof(id), output); + } + for (int i = 0; i < config->output_configs->length; i++) { struct output_config *old = config->output_configs->items[i]; @@ -240,7 +243,7 @@ void store_output_config(struct output_config *oc) { // If the new config matches an output's name, and the old config // matches on that output's identifier, supersede it. - if (strcmp(old->name, id) == 0 && + if (output && strcmp(old->name, id) == 0 && strcmp(oc->name, output->wlr_output->name) == 0) { supersede_output_config(old, oc); } From 087226d997c15f4df30542778854999c632642a3 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 12 Apr 2024 18:44:07 +0200 Subject: [PATCH 017/361] config/output: drop fast path in store_output_config() If there is no output currently connected, we still want to merge to any existing config. It shouldn't matter to iterate over the list of outputs to do nothing anwyays. --- sway/config/output.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index aab3f0bd9..54af5d8e3 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -211,10 +211,6 @@ void store_output_config(struct output_config *oc) { bool merged = false; bool wildcard = strcmp(oc->name, "*") == 0; struct sway_output *output = wildcard ? NULL : output_by_name_or_id(oc->name); - if (!output && !wildcard) { - // There is no config by this name, just add it in - goto done; - } char id[128]; if (output) { @@ -249,7 +245,6 @@ void store_output_config(struct output_config *oc) { } } -done: sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) " "(max render time: %d)", From ffcde7a70c1341d1ec1d38c00ff87faa1a816892 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sun, 21 Apr 2024 16:42:08 +0200 Subject: [PATCH 018/361] server: Use wlr_renderer_get_texture_formats wlr_renderer_get_{dmabuf|shm}_texture_formats have been replaced by a unified wlr_renderer_get_texture_formats interface using buffer caps. References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4644 --- sway/server.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sway/server.c b/sway/server.c index d159dc9bd..180d3a6bc 100644 --- a/sway/server.c +++ b/sway/server.c @@ -240,13 +240,12 @@ bool server_init(struct sway_server *server) { wlr_renderer_init_wl_shm(server->renderer, server->wl_display); - if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL) { + if (wlr_renderer_get_texture_formats(server->renderer, WLR_BUFFER_CAP_DMABUF) != NULL) { server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( server->wl_display, 4, server->renderer); - } - if (wlr_renderer_get_dmabuf_texture_formats(server->renderer) != NULL && - debug.legacy_wl_drm) { - wlr_drm_create(server->wl_display, server->renderer); + if (debug.legacy_wl_drm) { + wlr_drm_create(server->wl_display, server->renderer); + } } server->allocator = wlr_allocator_autocreate(server->backend, From 646019cad9e8a075911e960fc7645471d9c26bf6 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 23 Apr 2024 13:26:14 +0200 Subject: [PATCH 019/361] desktop/output: Fix check if config should be stored We want to check if a config_head existed for the current matched_output_config, so we should check cfg->output. sway_output is a temporary variable from a previous wl_list_for_each, and does not contain anything useful to us. Fixes: https://github.com/swaywm/sway/issues/8128 --- sway/desktop/output.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index bd3de3fe1..70987febf 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -619,7 +619,7 @@ static void output_manager_apply(struct sway_server *server, if (!test_only && ok) { struct wlr_output_configuration_head_v1 *config_head; wl_list_for_each(config_head, &config->heads, link) { - if (config_head->state.output == sway_output->wlr_output) { + if (config_head->state.output == cfg->output->wlr_output) { store_config = true; break; } From ee5c4f38c9db849b6c4034e792b451eb8d6d2627 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Wed, 1 May 2024 14:19:54 +0200 Subject: [PATCH 020/361] config/output: Use all outputs for config merge When storing a config, we need to find the output that is being configured to extract its identifier. output_by_name_or_id does not return outputs that are disabled, and using this makes it impossible to merge configurations related to disabled outputs. Switch to all_outputs_by_name_or_id. Fixes: https://github.com/swaywm/sway/issues/8141 --- 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 54af5d8e3..7e0376760 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -210,7 +210,7 @@ static void merge_output_config(struct output_config *dst, struct output_config void store_output_config(struct output_config *oc) { bool merged = false; bool wildcard = strcmp(oc->name, "*") == 0; - struct sway_output *output = wildcard ? NULL : output_by_name_or_id(oc->name); + struct sway_output *output = wildcard ? NULL : all_output_by_name_or_id(oc->name); char id[128]; if (output) { From 4c28916d685714a74048d798096a6f853bf61000 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 28 Mar 2024 14:26:05 +0100 Subject: [PATCH 021/361] config/output: Search for output config fallbacks The original sway output config implementation enabled one output at a time, testing modes, render formats and VRR support as it went along. While this sort of fallback is easy to do, it has the downside of not considering the effect of neighbor outputs on the configuration viability. With backend-wide commits, we can now better consider the effect of neighbor outputs, but to handle the fact that we commit all outputs at once we need to perform a more elaborate search of viable configurations. Implement a recursive configuration search for when the primary configuration failed to apply. --- include/sway/config.h | 5 +- sway/config/output.c | 308 ++++++++++++++++++++++++++++++++++-------- sway/desktop/output.c | 3 +- 3 files changed, 261 insertions(+), 55 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 0be1cd229..5ccc3e777 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -689,10 +689,13 @@ const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filt struct output_config *new_output_config(const char *name); bool apply_output_configs(struct matched_output_config *configs, - size_t configs_len, bool test_only); + size_t configs_len, bool test_only, bool degrade_to_off); void apply_all_output_configs(void); +void sort_output_configs_by_priority(struct matched_output_config *configs, + size_t configs_len); + /** * store_output_config stores a new output config. An output may be matched by * three different config types, in order of precedence: Identifier, name and diff --git a/sway/config/output.c b/sway/config/output.c index 7e0376760..3ec5d77b9 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -386,22 +386,18 @@ static int compute_default_scale(struct wlr_output *output, return 2; } -/* Lists of formats to try, in order, when a specific render bit depth has - * been asked for. The second to last format in each list should always - * be XRGB8888, as a reliable backup in case the others are not available; - * the last should be DRM_FORMAT_INVALID, to indicate the end of the list. */ -static const uint32_t *bit_depth_preferences[] = { - [RENDER_BIT_DEPTH_8] = (const uint32_t []){ - DRM_FORMAT_XRGB8888, - DRM_FORMAT_INVALID, - }, - [RENDER_BIT_DEPTH_10] = (const uint32_t []){ - DRM_FORMAT_XRGB2101010, - DRM_FORMAT_XBGR2101010, - DRM_FORMAT_XRGB8888, - DRM_FORMAT_INVALID, - }, -}; +static bool render_format_is_10bit(uint32_t render_format) { + return render_format == DRM_FORMAT_XRGB2101010 || + render_format == DRM_FORMAT_XBGR2101010; +} + +static bool render_format_is_bgr(uint32_t fmt) { + return fmt == DRM_FORMAT_XBGR2101010 || fmt == DRM_FORMAT_XBGR8888; +} + +static bool output_config_is_disabling(struct output_config *oc) { + return oc && (!oc->enabled || oc->power == 0); +} static void queue_output_config(struct output_config *oc, struct sway_output *output, struct wlr_output_state *pending) { @@ -411,7 +407,7 @@ static void queue_output_config(struct output_config *oc, struct wlr_output *wlr_output = output->wlr_output; - if (oc && (!oc->enabled || oc->power == 0)) { + if (output_config_is_disabling(oc)) { sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); wlr_output_state_set_enabled(pending, false); return; @@ -434,22 +430,6 @@ static void queue_output_config(struct output_config *oc, struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output); wlr_output_state_set_mode(pending, preferred_mode); - - if (!wlr_output_test_state(wlr_output, pending)) { - sway_log(SWAY_DEBUG, "Preferred mode rejected, " - "falling back to another mode"); - struct wlr_output_mode *mode; - wl_list_for_each(mode, &wlr_output->modes, link) { - if (mode == preferred_mode) { - continue; - } - - wlr_output_state_set_mode(pending, mode); - if (wlr_output_test_state(wlr_output, pending)) { - break; - } - } - } } if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { @@ -500,25 +480,17 @@ static void queue_output_config(struct output_config *oc, sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, oc->adaptive_sync); wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); - if (oc->adaptive_sync == 1 && !wlr_output_test_state(wlr_output, pending)) { - sway_log(SWAY_DEBUG, "Adaptive sync failed, ignoring"); - wlr_output_state_set_adaptive_sync_enabled(pending, false); - } } if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { - const uint32_t *fmts = bit_depth_preferences[oc->render_bit_depth]; - assert(fmts); - - for (size_t i = 0; fmts[i] != DRM_FORMAT_INVALID; i++) { - wlr_output_state_set_render_format(pending, fmts[i]); - if (wlr_output_test_state(wlr_output, pending)) { - break; - } - - sway_log(SWAY_DEBUG, "Preferred output format 0x%08x " - "failed to work, falling back to next in " - "list, 0x%08x", fmts[i], fmts[i + 1]); + if (oc->render_bit_depth == RENDER_BIT_DEPTH_10 && + render_format_is_10bit(output->wlr_output->render_format)) { + // 10-bit was set successfully before, try to save some tests by reusing the format + wlr_output_state_set_render_format(pending, output->wlr_output->render_format); + } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_10) { + wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010); + } else { + wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888); } } } @@ -649,8 +621,227 @@ struct output_config *find_output_config(struct sway_output *sway_output) { return result; } +static bool config_has_auto_mode(struct output_config *oc) { + if (!oc) { + return true; + } + if (oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t)-1) { + return true; + } else if (oc->width > 0 && oc->height > 0) { + return true; + } + return false; +} + +struct search_context { + struct wlr_output_swapchain_manager *swapchain_mgr; + struct wlr_backend_output_state *states; + struct matched_output_config *configs; + size_t configs_len; + bool degrade_to_off; +}; + +static bool search_valid_config(struct search_context *ctx, size_t output_idx); + +static void reset_output_state(struct wlr_output_state *state) { + wlr_output_state_finish(state); + wlr_output_state_init(state); + state->committed = 0; +} + +static void clear_later_output_states(struct wlr_backend_output_state *states, + size_t configs_len, size_t output_idx) { + + // Clear and disable all output states after this one to avoid conflict + // with previous tests. + for (size_t idx = output_idx+1; idx < configs_len; idx++) { + struct wlr_backend_output_state *backend_state = &states[idx]; + struct wlr_output_state *state = &backend_state->base; + + reset_output_state(state); + wlr_output_state_set_enabled(state, false); + } +} + +static bool search_finish(struct search_context *ctx, size_t output_idx) { + clear_later_output_states(ctx->states, ctx->configs_len, output_idx); + return wlr_output_swapchain_manager_prepare(ctx->swapchain_mgr, ctx->states, ctx->configs_len) && + search_valid_config(ctx, output_idx+1); +} + +static bool search_adaptive_sync(struct search_context *ctx, size_t output_idx) { + struct matched_output_config *cfg = &ctx->configs[output_idx]; + struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; + struct wlr_output_state *state = &backend_state->base; + + if (cfg->config && cfg->config->adaptive_sync == 1) { + wlr_output_state_set_adaptive_sync_enabled(state, true); + if (search_finish(ctx, output_idx)) { + return true; + } + } + if (!cfg->config || cfg->config->adaptive_sync != -1) { + sway_log(SWAY_DEBUG, "Trying with adaptive sync disabled for: %s", + backend_state->output->name); + wlr_output_state_set_adaptive_sync_enabled(state, false); + if (search_finish(ctx, output_idx)) { + return true; + } + } + // If adaptive sync has not been set, or fallback in case we are on a + // backend that cannot disable adaptive sync such as the wayland backend. + sway_log(SWAY_DEBUG, "Trying with adaptive sync unset for: %s", + backend_state->output->name); + state->committed &= ~WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED; + return search_finish(ctx, output_idx); +} + +static bool search_mode(struct search_context *ctx, size_t output_idx) { + struct matched_output_config *cfg = &ctx->configs[output_idx]; + struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; + struct wlr_output_state *state = &backend_state->base; + struct wlr_output *wlr_output = backend_state->output; + + if (!config_has_auto_mode(cfg->config)) { + return search_adaptive_sync(ctx, output_idx); + } + + struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output); + if (preferred_mode) { + sway_log(SWAY_DEBUG, "Trying with preferred mode for: %s", backend_state->output->name); + wlr_output_state_set_mode(state, preferred_mode); + if (search_adaptive_sync(ctx, output_idx)) { + return true; + } + } + + if (wl_list_empty(&wlr_output->modes)) { + state->committed &= ~WLR_OUTPUT_STATE_MODE; + return search_adaptive_sync(ctx, output_idx); + } + + struct wlr_output_mode *mode; + wl_list_for_each(mode, &backend_state->output->modes, link) { + if (mode == preferred_mode) { + continue; + } + sway_log(SWAY_DEBUG, "Trying with mode %dx%d@%dmHz for: %s", + mode->width, mode->height, mode->refresh, backend_state->output->name); + wlr_output_state_set_mode(state, mode); + if (search_adaptive_sync(ctx, output_idx)) { + return true; + } + } + + return false; +} + +static bool search_render_format(struct search_context *ctx, size_t output_idx) { + struct matched_output_config *cfg = &ctx->configs[output_idx]; + struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; + struct wlr_output_state *state = &backend_state->base; + struct wlr_output *wlr_output = backend_state->output; + + uint32_t fmts[] = { + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_INVALID, + }; + if (render_format_is_bgr(wlr_output->render_format)) { + // Start with BGR in the unlikely event that we previously required it. + fmts[0] = DRM_FORMAT_XBGR2101010; + fmts[1] = DRM_FORMAT_XRGB2101010; + } + + const struct wlr_drm_format_set *primary_formats = + wlr_output_get_primary_formats(wlr_output, WLR_BUFFER_CAP_DMABUF); + bool need_10bit = cfg->config && cfg->config->render_bit_depth == RENDER_BIT_DEPTH_10; + for (size_t idx = 0; fmts[idx] != DRM_FORMAT_INVALID; idx++) { + if (!need_10bit && render_format_is_10bit(fmts[idx])) { + continue; + } + if (!wlr_drm_format_set_get(primary_formats, fmts[idx])) { + // This is not a supported format for this output + continue; + } + sway_log(SWAY_DEBUG, "Trying with render format %d for: %s", fmts[idx], + wlr_output->name); + wlr_output_state_set_render_format(state, fmts[idx]); + if (search_mode(ctx, output_idx)) { + return true; + } + } + return false; +} + +static bool search_valid_config(struct search_context *ctx, size_t output_idx) { + if (output_idx >= ctx->configs_len) { + // We reached the end of the search, all good! + return true; + } + + struct matched_output_config *cfg = &ctx->configs[output_idx]; + struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; + struct wlr_output_state *state = &backend_state->base; + + sway_log(SWAY_DEBUG, "Finding valid config for: %s", + backend_state->output->name); + + if (!output_config_is_disabling(cfg->config)) { + // Search through our possible configurations, doing a depth-first + // through render_format, modes, adaptive_sync and the next output's + // config. + queue_output_config(cfg->config, cfg->output, &backend_state->base); + if (search_render_format(ctx, output_idx)) { + return true; + } else if (!ctx->degrade_to_off) { + return false; + } + // We could not get anything to work, try to disable this output to see + // if we can at least make the outputs before us work. + sway_log(SWAY_DEBUG, "Trying with disabled output for: %s", + backend_state->output->name); + reset_output_state(state); + } + + wlr_output_state_set_enabled(state, false); + return search_finish(ctx, output_idx); +} + +static int compare_matched_output_config_priority(const void *a, const void *b) { + + const struct matched_output_config *amc = a; + const struct matched_output_config *bmc = b; + bool a_disabling = output_config_is_disabling(amc->config); + bool b_disabling = output_config_is_disabling(bmc->config); + bool a_enabled = amc->output->enabled; + bool b_enabled = bmc->output->enabled; + + // We want to give priority to existing enabled outputs. To do so, we want + // the configuration order to be: + // 1. Existing, enabled outputs + // 2. Outputs that need to be enabled + // 3. Disabled or disabling outputs + if (a_enabled && !a_disabling) { + return -1; + } else if (b_enabled && !b_disabling) { + return 1; + } else if (b_disabling && !a_disabling) { + return -1; + } else if (a_disabling && !b_disabling) { + return 1; + } + return 0; +} + +void sort_output_configs_by_priority(struct matched_output_config *configs, + size_t configs_len) { + qsort(configs, configs_len, sizeof(*configs), compare_matched_output_config_priority); +} + bool apply_output_configs(struct matched_output_config *configs, - size_t configs_len, bool test_only) { + size_t configs_len, bool test_only, bool degrade_to_off) { struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states)); if (!states) { return false; @@ -674,8 +865,18 @@ bool apply_output_configs(struct matched_output_config *configs, bool ok = wlr_output_swapchain_manager_prepare(&swapchain_mgr, states, configs_len); if (!ok) { - sway_log(SWAY_ERROR, "Swapchain prepare failed"); - goto out; + sway_log(SWAY_ERROR, "Requested backend configuration failed, searching for valid fallbacks"); + struct search_context ctx = { + .swapchain_mgr = &swapchain_mgr, + .states = states, + .configs = configs, + .configs_len = configs_len, + .degrade_to_off = degrade_to_off, + }; + if (!search_valid_config(&ctx, 0)) { + sway_log(SWAY_ERROR, "Search for valid config failed"); + goto out; + } } if (test_only) { @@ -761,7 +962,8 @@ void apply_all_output_configs(void) { config->config = find_output_config(sway_output); } - apply_output_configs(configs, configs_len, false); + sort_output_configs_by_priority(configs, configs_len); + apply_output_configs(configs, configs_len, false, true); for (size_t idx = 0; idx < configs_len; idx++) { struct matched_output_config *cfg = &configs[idx]; free_output_config(cfg->config); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 70987febf..2722e5567 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -609,7 +609,8 @@ static void output_manager_apply(struct sway_server *server, } } - bool ok = apply_output_configs(configs, configs_len, test_only); + sort_output_configs_by_priority(configs, configs_len); + bool ok = apply_output_configs(configs, configs_len, test_only, false); for (size_t idx = 0; idx < configs_len; idx++) { struct matched_output_config *cfg = &configs[idx]; From 2686afb95c5dd76b22abdd76ffbb4b30688f8fd3 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sun, 21 Apr 2024 17:41:19 +0200 Subject: [PATCH 022/361] config/output: Print output state during tests Instead of having each search function print its various test decisions, print the full state at the end of every search. This makes it much clearer what state a particular test includes. --- sway/config/output.c | 46 ++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index 3ec5d77b9..fb1956df3 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -641,6 +641,30 @@ struct search_context { bool degrade_to_off; }; +static void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_state *state) { + sway_log(SWAY_DEBUG, "Output state for %s", wlr_output->name); + if (state->committed & WLR_OUTPUT_STATE_ENABLED) { + sway_log(SWAY_DEBUG, " enabled: %s", state->enabled ? "yes" : "no"); + } + if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { + sway_log(SWAY_DEBUG, " render_format: %d", state->render_format); + } + if (state->committed & WLR_OUTPUT_STATE_MODE) { + if (state->mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM) { + sway_log(SWAY_DEBUG, " custom mode: %dx%d@%dmHz", + state->custom_mode.width, state->custom_mode.height, state->custom_mode.refresh); + } else { + sway_log(SWAY_DEBUG, " mode: %dx%d@%dmHz%s", + state->mode->width, state->mode->height, state->mode->refresh, + state->mode->preferred ? " (preferred)" : ""); + } + } + if (state->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) { + sway_log(SWAY_DEBUG, " adaptive_sync: %s", + state->adaptive_sync_enabled ? "enabled": "disabled"); + } +} + static bool search_valid_config(struct search_context *ctx, size_t output_idx); static void reset_output_state(struct wlr_output_state *state) { @@ -664,7 +688,12 @@ static void clear_later_output_states(struct wlr_backend_output_state *states, } static bool search_finish(struct search_context *ctx, size_t output_idx) { + struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; + struct wlr_output_state *state = &backend_state->base; + struct wlr_output *wlr_output = backend_state->output; + clear_later_output_states(ctx->states, ctx->configs_len, output_idx); + dump_output_state(wlr_output, state); return wlr_output_swapchain_manager_prepare(ctx->swapchain_mgr, ctx->states, ctx->configs_len) && search_valid_config(ctx, output_idx+1); } @@ -681,8 +710,6 @@ static bool search_adaptive_sync(struct search_context *ctx, size_t output_idx) } } if (!cfg->config || cfg->config->adaptive_sync != -1) { - sway_log(SWAY_DEBUG, "Trying with adaptive sync disabled for: %s", - backend_state->output->name); wlr_output_state_set_adaptive_sync_enabled(state, false); if (search_finish(ctx, output_idx)) { return true; @@ -690,8 +717,6 @@ static bool search_adaptive_sync(struct search_context *ctx, size_t output_idx) } // If adaptive sync has not been set, or fallback in case we are on a // backend that cannot disable adaptive sync such as the wayland backend. - sway_log(SWAY_DEBUG, "Trying with adaptive sync unset for: %s", - backend_state->output->name); state->committed &= ~WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED; return search_finish(ctx, output_idx); } @@ -708,7 +733,6 @@ static bool search_mode(struct search_context *ctx, size_t output_idx) { struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output); if (preferred_mode) { - sway_log(SWAY_DEBUG, "Trying with preferred mode for: %s", backend_state->output->name); wlr_output_state_set_mode(state, preferred_mode); if (search_adaptive_sync(ctx, output_idx)) { return true; @@ -725,8 +749,6 @@ static bool search_mode(struct search_context *ctx, size_t output_idx) { if (mode == preferred_mode) { continue; } - sway_log(SWAY_DEBUG, "Trying with mode %dx%d@%dmHz for: %s", - mode->width, mode->height, mode->refresh, backend_state->output->name); wlr_output_state_set_mode(state, mode); if (search_adaptive_sync(ctx, output_idx)) { return true; @@ -765,8 +787,6 @@ static bool search_render_format(struct search_context *ctx, size_t output_idx) // This is not a supported format for this output continue; } - sway_log(SWAY_DEBUG, "Trying with render format %d for: %s", fmts[idx], - wlr_output->name); wlr_output_state_set_render_format(state, fmts[idx]); if (search_mode(ctx, output_idx)) { return true; @@ -784,9 +804,7 @@ static bool search_valid_config(struct search_context *ctx, size_t output_idx) { struct matched_output_config *cfg = &ctx->configs[output_idx]; struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; struct wlr_output_state *state = &backend_state->base; - - sway_log(SWAY_DEBUG, "Finding valid config for: %s", - backend_state->output->name); + struct wlr_output *wlr_output = backend_state->output; if (!output_config_is_disabling(cfg->config)) { // Search through our possible configurations, doing a depth-first @@ -800,8 +818,8 @@ static bool search_valid_config(struct search_context *ctx, size_t output_idx) { } // We could not get anything to work, try to disable this output to see // if we can at least make the outputs before us work. - sway_log(SWAY_DEBUG, "Trying with disabled output for: %s", - backend_state->output->name); + sway_log(SWAY_DEBUG, "Unable to find valid config with output %s, disabling", + wlr_output->name); reset_output_state(state); } From b463957021db6c247d40de4059d4a31ad4e6d761 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 30 Apr 2024 20:05:11 -0400 Subject: [PATCH 023/361] sway_text_node: Allow 0 text width special case negative numbers instead. --- sway/sway_text_node.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c index 5eba53ba4..4b7ee999e 100644 --- a/sway/sway_text_node.c +++ b/sway/sway_text_node.c @@ -58,7 +58,7 @@ struct text_buffer { static int get_text_width(struct sway_text_node *props) { int width = props->width; - if (props->max_width) { + if (props->max_width >= 0) { width = MIN(width, props->max_width); } return MAX(width, 0); @@ -81,6 +81,11 @@ static void render_backing_buffer(struct text_buffer *buffer) { return; } + if (buffer->props.max_width == 0) { + wlr_scene_buffer_set_buffer(buffer->buffer_node, NULL); + return; + } + float scale = buffer->scale; int width = ceil(buffer->props.width * scale); int height = ceil(buffer->props.height * scale); @@ -236,6 +241,7 @@ struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent, buffer->buffer_node = node; buffer->props.node = &node->node; + buffer->props.max_width = -1; buffer->text = strdup(text); if (!buffer->text) { free(buffer); From 30f5c3a9117be3e4911cba02693f7b45a197da93 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 12 Apr 2024 19:20:36 +0200 Subject: [PATCH 024/361] tree/container: ensure pixman rect is valid in container_arrange_title_bar() Fixes "Invalid rectangle passed" errors printed by Pixman. --- sway/tree/container.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sway/tree/container.c b/sway/tree/container.c index 9224b4fb4..80ef34fe9 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -352,6 +352,8 @@ void container_arrange_title_bar(struct sway_container *con) { int alloc_width = MIN((int)node->width, width - h_padding - config->titlebar_h_padding); + alloc_width = MAX(alloc_width, 0); + sway_text_node_set_max_width(node, alloc_width); wlr_scene_node_set_position(node->node, h_padding, (height - node->height) >> 1); @@ -376,6 +378,8 @@ void container_arrange_title_bar(struct sway_container *con) { int alloc_width = MIN((int) node->width, width - h_padding - config->titlebar_h_padding); + alloc_width = MAX(alloc_width, 0); + sway_text_node_set_max_width(node, alloc_width); wlr_scene_node_set_position(node->node, h_padding, (height - node->height) >> 1); From dcdb72757a5ec591c692df5e96c57c51758dbd8f Mon Sep 17 00:00:00 2001 From: Manuel Stoeckl Date: Mon, 29 Apr 2024 21:01:44 -0400 Subject: [PATCH 025/361] desktop/layer_shell: provide fractional scale on creation Also, send a matching wl_surface.preferred_buffer_scale event. --- sway/desktop/layer_shell.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 4b2584b6b..6221b7b97 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -432,6 +433,12 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { surface->output = output; + // now that the surface's output is known, we can advertise its scale + wlr_fractional_scale_v1_notify_scale(surface->layer_surface->surface, + layer_surface->output->scale); + wlr_surface_set_preferred_buffer_scale(surface->layer_surface->surface, + ceil(layer_surface->output->scale)); + surface->surface_commit.notify = handle_surface_commit; wl_signal_add(&layer_surface->surface->events.commit, &surface->surface_commit); From 796898519be183adcffd09f1dca71d13c376488f Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 18 May 2024 13:48:17 +0200 Subject: [PATCH 026/361] build: disable wayland-protocols subproject tests by default --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 1043e4ba9..1d143110c 100644 --- a/meson.build +++ b/meson.build @@ -68,7 +68,7 @@ pcre2 = dependency('libpcre2-8') wayland_server = dependency('wayland-server', version: '>=1.21.0') wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') -wayland_protos = dependency('wayland-protocols', version: '>=1.24') +wayland_protos = dependency('wayland-protocols', version: '>=1.24', default_options: ['tests=false']) xkbcommon = dependency('xkbcommon', version: '>=1.5.0') cairo = dependency('cairo') pango = dependency('pango') From fd3b643d15618fe608c8ed0fed0956d1e2526574 Mon Sep 17 00:00:00 2001 From: "Anna (navi) Figueiredo Gomes" Date: Sat, 18 May 2024 16:13:08 +0200 Subject: [PATCH 027/361] sway/config/output.c: fix null deref on output config If there's no config for the output, oc is null, but some screens might have a default rotation, causing the log call to dereference a null pointer. Signed-off-by: Anna (navi) Figueiredo Gomes --- 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 fb1956df3..e72994594 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -448,7 +448,7 @@ static void queue_output_config(struct output_config *oc, #endif } if (wlr_output->transform != tr) { - sway_log(SWAY_DEBUG, "Set %s transform to %d", oc->name, tr); + sway_log(SWAY_DEBUG, "Set %s transform to %d", wlr_output->name, tr); wlr_output_state_set_transform(pending, tr); } From 970415241497ceccfb013b6f8cb2395abee74e5c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 18 May 2024 14:02:14 +0200 Subject: [PATCH 028/361] build: drop xwayland option Instead of having a build-time option to enable/disable xwayland support, just use the wlroots build config: enable xwayland in Sway if it was enabled when building wlroots. I don't see any use-case for disabling xwayland in Sway when enabled in wlroots: Sway doesn't pull in any additional dependency (just pulls in dependencies that wlroots already needs). We have a config command to disable xwayland at runtime anyways. This makes it so xwayland behaves the same way as other features such as libinput backend and session support. This also reduces the build matrix (less combinations of build options). I think we originally introduced the xwayland option when we didn't have a good way to figure out the wlroots build config from the Sway build system. --- .builds/alpine.yml | 7 ++++++- include/sway/criteria.h | 2 +- include/sway/server.h | 6 +++--- include/sway/tree/root.h | 4 ++-- include/sway/tree/view.h | 13 +++++++------ meson.build | 12 ++---------- meson_options.txt | 1 - sway/commands/swap.c | 4 ++-- sway/commands/xwayland.c | 2 +- sway/criteria.c | 18 +++++++++--------- sway/desktop/transaction.c | 2 +- sway/input/cursor.c | 2 +- sway/input/seat.c | 4 ++-- sway/input/seatop_default.c | 8 ++++---- sway/ipc-json.c | 4 ++-- sway/meson.build | 2 +- sway/server.c | 8 ++++---- sway/tree/root.c | 2 +- sway/tree/view.c | 14 +++++++------- 19 files changed, 56 insertions(+), 59 deletions(-) diff --git a/.builds/alpine.yml b/.builds/alpine.yml index 59df7737c..055e5ffab 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml @@ -38,9 +38,14 @@ tasks: cd sway ninja -C build - build-no-xwayland: | - cd sway + cd wlroots meson configure build -Dxwayland=disabled ninja -C build + sudo ninja -C build install + + cd ../sway + meson configure build --clearcache + ninja -C build - build-static: | cd sway mkdir subprojects diff --git a/include/sway/criteria.h b/include/sway/criteria.h index 8da345ea6..758d70ed2 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -36,7 +36,7 @@ struct criteria { struct pattern *app_id; struct pattern *con_mark; uint32_t con_id; // internal ID -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct pattern *class; uint32_t id; // X11 window ID struct pattern *instance; diff --git a/include/sway/server.h b/include/sway/server.h index c71851f68..90f187fd1 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -5,7 +5,7 @@ #include "config.h" #include "list.h" #include "sway/desktop/idle_inhibit_v1.h" -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND #include "sway/xwayland.h" #endif @@ -59,7 +59,7 @@ struct sway_server { struct wlr_tablet_manager_v2 *tablet_v2; -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct sway_xwayland xwayland; struct wl_listener xwayland_surface; struct wl_listener xwayland_ready; @@ -165,7 +165,7 @@ void sway_session_lock_add_output(struct sway_session_lock *lock, bool sway_session_lock_has_surface(struct sway_session_lock *lock, struct wlr_surface *surface); void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data); -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND void handle_xwayland_surface(struct wl_listener *listener, void *data); #endif void handle_server_decoration(struct wl_listener *listener, void *data); diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index 15df0f551..7de0abcdd 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -2,12 +2,12 @@ #define _SWAY_ROOT_H #include #include +#include #include #include #include #include "sway/tree/container.h" #include "sway/tree/node.h" -#include "config.h" #include "list.h" extern struct sway_root *root; @@ -47,7 +47,7 @@ struct sway_root { struct wlr_scene_tree *shell_top; struct wlr_scene_tree *fullscreen; struct wlr_scene_tree *fullscreen_global; -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct wlr_scene_tree *unmanaged; #endif struct wlr_scene_tree *shell_overlay; diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 7faacdcc2..3ae8cf224 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -1,10 +1,11 @@ #ifndef _SWAY_VIEW_H #define _SWAY_VIEW_H #include +#include #include #include #include "sway/config.h" -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND #include #endif #include "sway/input/input-manager.h" @@ -15,7 +16,7 @@ struct sway_xdg_decoration; enum sway_view_type { SWAY_VIEW_XDG_SHELL, -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND SWAY_VIEW_XWAYLAND, #endif }; @@ -27,7 +28,7 @@ enum sway_view_prop { VIEW_PROP_INSTANCE, VIEW_PROP_WINDOW_TYPE, VIEW_PROP_WINDOW_ROLE, -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND VIEW_PROP_X11_WINDOW_ID, VIEW_PROP_X11_PARENT_ID, #endif @@ -98,7 +99,7 @@ struct sway_view { union { struct wlr_xdg_toplevel *wlr_xdg_toplevel; -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct wlr_xwayland_surface *wlr_xwayland_surface; #endif }; @@ -127,7 +128,7 @@ struct sway_xdg_shell_view { struct wl_listener unmap; struct wl_listener destroy; }; -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct sway_xwayland_view { struct sway_view view; @@ -293,7 +294,7 @@ void view_center_and_clip_surface(struct sway_view *view); struct sway_view *view_from_wlr_xdg_surface( struct wlr_xdg_surface *xdg_surface); -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct sway_view *view_from_wlr_xwayland_surface( struct wlr_xwayland_surface *xsurface); #endif diff --git a/meson.build b/meson.build index 1d143110c..3c444e077 100644 --- a/meson.build +++ b/meson.build @@ -57,10 +57,6 @@ foreach name, _ : wlroots_features wlroots_features += { name: have } endforeach -if get_option('xwayland').enabled() and not wlroots_features['xwayland'] - error('Cannot enable Xwayland in sway: wlroots has been built without Xwayland support') -endif - null_dep = dependency('', required: false) jsonc = dependency('json-c', version: '>=0.13') @@ -77,16 +73,14 @@ gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf')) pixman = dependency('pixman-1') libevdev = dependency('libevdev') libinput = wlroots_features['libinput_backend'] ? dependency('libinput', version: '>=1.21.0') : null_dep -xcb = dependency('xcb', required: get_option('xwayland')) +xcb = wlroots_features['xwayland'] ? dependency('xcb') : null_dep drm = dependency('libdrm') libudev = wlroots_features['libinput_backend'] ? dependency('libudev') : null_dep math = cc.find_library('m') rt = cc.find_library('rt') -xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland')) +xcb_icccm = wlroots_features['xwayland'] ? dependency('xcb-icccm') : null_dep threads = dependency('threads') # for pthread_setschedparam -have_xwayland = xcb.found() and xcb_icccm.found() and wlroots_features['xwayland'] - if get_option('sd-bus-provider') == 'auto' if not get_option('tray').disabled() assert(get_option('auto_features').auto(), 'sd-bus-provider must not be set to auto since auto_features != auto') @@ -110,7 +104,6 @@ have_tray = (not get_option('tray').disabled()) and tray_deps_found conf_data = configuration_data() -conf_data.set10('HAVE_XWAYLAND', have_xwayland) conf_data.set10('HAVE_GDK_PIXBUF', gdk_pixbuf.found()) conf_data.set10('HAVE_LIBSYSTEMD', sdbus.found() and sdbus.name() == 'libsystemd') conf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind') @@ -271,7 +264,6 @@ endif subdir('completions') summary({ - 'xwayland': have_xwayland, 'gdk-pixbuf': gdk_pixbuf.found(), 'tray': have_tray, 'man-pages': scdoc.found(), diff --git a/meson_options.txt b/meson_options.txt index 8d0d6509c..506ecc9a6 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -4,7 +4,6 @@ option('bash-completions', type: 'boolean', value: true, description: 'Install b option('fish-completions', type: 'boolean', value: true, description: 'Install fish shell completions.') option('swaybar', type: 'boolean', value: true, description: 'Enable support for swaybar') option('swaynag', type: 'boolean', value: true, description: 'Enable support for swaynag') -option('xwayland', type: 'feature', value: 'auto', description: 'Enable support for X11 applications') option('tray', type: 'feature', value: 'auto', description: 'Enable support for swaybar tray') option('gdk-pixbuf', type: 'feature', value: 'auto', description: 'Enable support for more image formats in swaybar tray') option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages') diff --git a/sway/commands/swap.c b/sway/commands/swap.c index e142eede8..c0b0d0b9c 100644 --- a/sway/commands/swap.c +++ b/sway/commands/swap.c @@ -18,7 +18,7 @@ static bool test_con_id(struct sway_container *container, void *data) { return container->node.id == *con_id; } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND static bool test_id(struct sway_container *container, void *data) { xcb_window_t *wid = data; return (container->view && container->view->type == SWAY_VIEW_XWAYLAND @@ -53,7 +53,7 @@ struct cmd_results *cmd_swap(int argc, char **argv) { char *value = join_args(argv + 3, argc - 3); if (strcasecmp(argv[2], "id") == 0) { -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND xcb_window_t id = strtol(value, NULL, 0); other = root_find_container(test_id, &id); #endif diff --git a/sway/commands/xwayland.c b/sway/commands/xwayland.c index 584a8e3ae..c0b175fcf 100644 --- a/sway/commands/xwayland.c +++ b/sway/commands/xwayland.c @@ -10,7 +10,7 @@ struct cmd_results *cmd_xwayland(int argc, char **argv) { return error; } -#ifdef HAVE_XWAYLAND +#ifdef WLR_HAS_XWAYLAND enum xwayland_mode xwayland; if (strcmp(argv[0], "force") == 0) { xwayland = XWAYLAND_MODE_IMMEDIATE; diff --git a/sway/criteria.c b/sway/criteria.c index e16b4fa82..13f0530e0 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -22,7 +22,7 @@ bool criteria_is_empty(struct criteria *criteria) { && !criteria->app_id && !criteria->con_mark && !criteria->con_id -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND && !criteria->class && !criteria->id && !criteria->instance @@ -90,7 +90,7 @@ void criteria_destroy(struct criteria *criteria) { pattern_destroy(criteria->title); pattern_destroy(criteria->shell); pattern_destroy(criteria->app_id); -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND pattern_destroy(criteria->class); pattern_destroy(criteria->instance); pattern_destroy(criteria->window_role); @@ -110,7 +110,7 @@ static int regex_cmp(const char *item, const pcre2_code *regex) { return result; } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND static bool view_has_window_type(struct sway_view *view, enum atom_name name) { if (view->type != SWAY_VIEW_XWAYLAND) { return false; @@ -251,7 +251,7 @@ static bool criteria_matches_view(struct criteria *criteria, return false; } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND if (criteria->id) { // X11 window ID uint32_t x11_window_id = view_get_x11_window_id(view); if (!x11_window_id || x11_window_id != criteria->id) { @@ -428,7 +428,7 @@ list_t *criteria_get_containers(struct criteria *criteria) { return matches; } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND static enum atom_name parse_window_type(const char *type) { if (strcasecmp(type, "normal") == 0) { return NET_WM_WINDOW_TYPE_NORMAL; @@ -461,7 +461,7 @@ enum criteria_token { T_CON_ID, T_CON_MARK, T_FLOATING, -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND T_CLASS, T_ID, T_INSTANCE, @@ -487,7 +487,7 @@ static enum criteria_token token_from_name(char *name) { return T_CON_ID; } else if (strcmp(name, "con_mark") == 0) { return T_CON_MARK; -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND } else if (strcmp(name, "class") == 0) { return T_CLASS; } else if (strcmp(name, "id") == 0) { @@ -566,7 +566,7 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { case T_CON_MARK: pattern_create(&criteria->con_mark, value); break; -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND case T_CLASS: pattern_create(&criteria->class, value); break; @@ -674,7 +674,7 @@ struct criteria *criteria_parse(char *raw, char **error_arg) { ++head; struct criteria *criteria = calloc(1, sizeof(struct criteria)); -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND criteria->window_type = ATOM_LAST; // default value #endif char *name = NULL, *value = NULL; diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 042141ab2..e464ff1ac 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -761,7 +761,7 @@ static bool should_configure(struct sway_node *node, } struct sway_container_state *cstate = &node->sway_container->current; struct sway_container_state *istate = &instruction->container_state; -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND // Xwayland views are position-aware and need to be reconfigured // when their position changes. if (node->sway_container->view->type == SWAY_VIEW_XWAYLAND) { diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 3d04826cd..0b4a36d95 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -107,7 +107,7 @@ struct sway_node *node_at_coords( return NULL; } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND if (scene_descriptor_try_get(current, SWAY_SCENE_DESC_XWAYLAND_UNMANAGED)) { return NULL; } diff --git a/sway/input/seat.c b/sway/input/seat.c index 0c5672bca..da4bb12ac 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -190,7 +190,7 @@ static void seat_send_focus(struct sway_node *node, struct sway_seat *seat) { node->sway_container->view : NULL; if (view && seat_is_input_allowed(seat, view->surface)) { -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND if (view->type == SWAY_VIEW_XWAYLAND) { struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; wlr_xwayland_set_seat(xwayland, seat->wlr_seat); @@ -1002,7 +1002,7 @@ void seat_configure_xcursor(struct sway_seat *seat) { setenv("XCURSOR_THEME", cursor_theme, 1); } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND if (server.xwayland.wlr_xwayland && (!server.xwayland.xcursor_manager || !xcursor_manager_is_named(server.xwayland.xcursor_manager, cursor_theme) || diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 0c6f7c5e3..e01fa9336 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -15,7 +15,7 @@ #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "log.h" -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND #include "sway/xwayland.h" #endif @@ -234,7 +234,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, node->sway_container : NULL; struct wlr_layer_surface_v1 *layer; -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct wlr_xwayland_surface *xsurface; #endif if ((layer = wlr_layer_surface_v1_try_from_wlr_surface(surface)) && @@ -268,7 +268,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, seat_set_focus_container(seat, cont); seatop_begin_down(seat, node->sway_container, sx, sy); } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND // Handle tapping on an xwayland unmanaged view else if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && xsurface->override_redirect && @@ -514,7 +514,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, return; } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND // Handle clicking on xwayland unmanaged view struct wlr_xwayland_surface *xsurface; if (surface && diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 81ca34831..b7370aa6d 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -154,7 +154,7 @@ static json_object *ipc_json_output_mode_description( return mode_object; } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND static const char *ipc_json_xwindow_type_description(struct sway_view *view) { struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; struct sway_xwayland *xwayland = &server.xwayland; @@ -633,7 +633,7 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_new_string(ipc_json_content_type_description(content_type))); } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND if (c->view->type == SWAY_VIEW_XWAYLAND) { json_object_object_add(object, "window", json_object_new_int(view_get_x11_window_id(c->view))); diff --git a/sway/meson.build b/sway/meson.build index d937e4256..47b51d0cc 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -231,7 +231,7 @@ sway_deps = [ xcb_icccm, ] -if have_xwayland +if wlroots_features['xwayland'] sway_sources += 'desktop/xwayland.c' endif diff --git a/sway/server.c b/sway/server.c index 180d3a6bc..4b48e8e52 100644 --- a/sway/server.c +++ b/sway/server.c @@ -56,7 +56,7 @@ #include "sway/input/cursor.h" #include "sway/tree/root.h" -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND #include #include "sway/xwayland.h" #endif @@ -118,7 +118,7 @@ static bool is_privileged(const struct wl_global *global) { static bool filter_global(const struct wl_client *client, const struct wl_global *global, void *data) { -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; if (xwayland && global == xwayland->shell_v1->global) { return xwayland->server != NULL && client == xwayland->server->client; @@ -437,7 +437,7 @@ bool server_init(struct sway_server *server) { void server_fini(struct sway_server *server) { // TODO: free sway-specific resources -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND wlr_xwayland_destroy(server->xwayland.wlr_xwayland); #endif wl_display_destroy_clients(server->wl_display); @@ -447,7 +447,7 @@ void server_fini(struct sway_server *server) { } bool server_start(struct sway_server *server) { -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND if (config->xwayland != XWAYLAND_MODE_DISABLED) { sway_log(SWAY_DEBUG, "Initializing Xwayland (lazy=%d)", config->xwayland == XWAYLAND_MODE_LAZY); diff --git a/sway/tree/root.c b/sway/tree/root.c index ae3c3cb20..20fcfa595 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -53,7 +53,7 @@ struct sway_root *root_create(struct wl_display *wl_display) { root->layers.shell_top = alloc_scene_tree(root->layer_tree, &failed); root->layers.fullscreen = alloc_scene_tree(root->layer_tree, &failed); root->layers.fullscreen_global = alloc_scene_tree(root->layer_tree, &failed); -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND root->layers.unmanaged = alloc_scene_tree(root->layer_tree, &failed); #endif root->layers.shell_overlay = alloc_scene_tree(root->layer_tree, &failed); diff --git a/sway/tree/view.c b/sway/tree/view.c index 35b4b73f4..9a87d9e20 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -9,8 +10,7 @@ #include #include #include -#include "config.h" -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND #include #endif #include "list.h" @@ -126,7 +126,7 @@ const char *view_get_instance(struct sway_view *view) { } return NULL; } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND uint32_t view_get_x11_window_id(struct sway_view *view) { if (view->impl->get_int_prop) { return view->impl->get_int_prop(view, VIEW_PROP_X11_WINDOW_ID); @@ -159,7 +159,7 @@ const char *view_get_shell(struct sway_view *view) { switch(view->type) { case SWAY_VIEW_XDG_SHELL: return "xdg_shell"; -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND case SWAY_VIEW_XWAYLAND: return "xwayland"; #endif @@ -499,7 +499,7 @@ void view_execute_criteria(struct sway_view *view) { static void view_populate_pid(struct sway_view *view) { pid_t pid; switch (view->type) { -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND case SWAY_VIEW_XWAYLAND:; struct wlr_xwayland_surface *surf = wlr_xwayland_surface_try_from_wlr_surface(view->surface); @@ -838,7 +838,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, bool set_focus = should_focus(view); -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct wlr_xwayland_surface *xsurface; if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { set_focus &= wlr_xwayland_icccm_input_model(xsurface) != @@ -954,7 +954,7 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { if ((xdg_surface = wlr_xdg_surface_try_from_wlr_surface(wlr_surface))) { return view_from_wlr_xdg_surface(xdg_surface); } -#if HAVE_XWAYLAND +#if WLR_HAS_XWAYLAND struct wlr_xwayland_surface *xsurface; if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { return view_from_wlr_xwayland_surface(xsurface); From a168b2029932d2a76341d91b3c6d8463de568213 Mon Sep 17 00:00:00 2001 From: thal Date: Sat, 2 Mar 2024 17:17:51 -0600 Subject: [PATCH 029/361] tree/view: Do not clip to geometry if using CSD If a floating window is using CSD, the geometry should not be used to define the clipping region. Otherwise drop shadows and such may be clipped excessively. --- sway/tree/view.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index 9a87d9e20..1c1c8ee8a 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -927,11 +927,14 @@ void view_update_size(struct sway_view *view) { void view_center_and_clip_surface(struct sway_view *view) { struct sway_container *con = view->container; + bool clip_to_geometry = true; + if (container_is_floating(con)) { // We always center the current coordinates rather than the next, as the // geometry immediately affects the currently active rendering. int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2); int y = (int) fmax(0, (con->current.content_height - view->geometry.height) / 2); + clip_to_geometry = !view->using_csd; wlr_scene_node_set_position(&view->content_tree->node, x, y); } else { @@ -940,12 +943,16 @@ void view_center_and_clip_surface(struct sway_view *view) { // only make sure to clip the content if there is content to clip if (!wl_list_empty(&con->view->content_tree->children)) { - wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &(struct wlr_box){ - .x = con->view->geometry.x, - .y = con->view->geometry.y, - .width = con->current.content_width, - .height = con->current.content_height, - }); + struct wlr_box clip = {0}; + if (clip_to_geometry) { + clip = (struct wlr_box){ + .x = con->view->geometry.x, + .y = con->view->geometry.y, + .width = con->current.content_width, + .height = con->current.content_height, + }; + } + wlr_scene_subsurface_tree_set_clip(&con->view->content_tree->node, &clip); } } From 700f4805bc3ca86b3c2a46aa96bcee9d8e20f599 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 24 May 2024 23:49:32 +0200 Subject: [PATCH 030/361] server: hide xdg_output from unprivileged clients Regular Wayland clients shouldn't care about the position or size of outputs. Hide xdg_output from unprivileged clients to make sure they're not doing shenanigans with this information. --- include/sway/server.h | 2 ++ sway/server.c | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index 90f187fd1..3a63df342 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -81,6 +81,8 @@ struct sway_server { struct wlr_pointer_constraints_v1 *pointer_constraints; struct wl_listener pointer_constraint; + struct wlr_xdg_output_manager_v1 *xdg_output_manager_v1; + struct wlr_output_manager_v1 *output_manager_v1; struct wl_listener output_manager_apply; struct wl_listener output_manager_test; diff --git a/sway/server.c b/sway/server.c index 4b48e8e52..edbc1a4b1 100644 --- a/sway/server.c +++ b/sway/server.c @@ -113,7 +113,8 @@ static bool is_privileged(const struct wl_global *global) { global == server.input->keyboard_shortcuts_inhibit->global || global == server.input->virtual_keyboard->global || global == server.input->virtual_pointer->global || - global == server.input->transient_seat_manager->global; + global == server.input->transient_seat_manager->global || + global == server.xdg_output_manager_v1->global; } static bool filter_global(const struct wl_client *client, @@ -275,7 +276,8 @@ bool server_init(struct sway_server *server) { wl_signal_add(&root->output_layout->events.change, &server->output_layout_change); - wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); + server->xdg_output_manager_v1 = + wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display); sway_idle_inhibit_manager_v1_init(); From df69367d927e6d4fde70d61611c9317b70537261 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 24 May 2024 23:53:46 +0200 Subject: [PATCH 031/361] input/text_input: ensure keyboard is set before sending modifiers Clients get confused when modifier events are sent before the keymap. --- sway/input/text_input.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sway/input/text_input.c b/sway/input/text_input.c index c38a3bb20..ba8c0b997 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -66,11 +66,13 @@ static void handle_im_keyboard_grab_destroy(struct wl_listener *listener, void * struct sway_input_method_relay *relay = wl_container_of(listener, relay, input_method_keyboard_grab_destroy); struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; + struct wlr_seat *wlr_seat = keyboard_grab->input_method->seat; wl_list_remove(&relay->input_method_keyboard_grab_destroy.link); if (keyboard_grab->keyboard) { // send modifier state to original client - wlr_seat_keyboard_notify_modifiers(keyboard_grab->input_method->seat, + wlr_seat_set_keyboard(wlr_seat, keyboard_grab->keyboard); + wlr_seat_keyboard_notify_modifiers(wlr_seat, &keyboard_grab->keyboard->modifiers); } } From d0bd591ee70b706182b6bfff45a68bc5404ea89b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 25 May 2024 00:02:48 +0200 Subject: [PATCH 032/361] Drop server.h include from input/input-manager.h The only reason it's included there is for a declaration of struct sway_server, but we can just forward-declare it. This avoids rebuilding almost all of Sway when touching server.h. All other server.h includes are from source files, not headers. --- include/sway/criteria.h | 4 ++++ include/sway/input/input-manager.h | 3 ++- sway/commands/input/events.c | 1 + sway/commands/input/xkb_switch_layout.c | 1 + sway/commands/move.c | 1 + sway/commands/seat/cursor.c | 1 + sway/commands/seat/pointer_constraint.c | 1 + sway/commands/seat/shortcuts_inhibitor.c | 1 + sway/commands/shortcuts_inhibitor.c | 1 + sway/config.c | 1 + sway/config/bar.c | 1 + sway/config/input.c | 1 + sway/config/output.c | 1 + sway/criteria.c | 1 + sway/desktop/launcher.c | 1 + sway/desktop/transaction.c | 1 + sway/input/cursor.c | 1 + sway/input/keyboard.c | 1 + sway/input/seatop_default.c | 1 + sway/input/switch.c | 1 + sway/input/tablet.c | 1 + sway/input/text_input.c | 2 ++ sway/ipc-json.c | 1 + sway/tree/workspace.c | 1 + 24 files changed, 29 insertions(+), 1 deletion(-) diff --git a/include/sway/criteria.h b/include/sway/criteria.h index 758d70ed2..ae546821c 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -7,6 +7,10 @@ #include "list.h" #include "tree/view.h" +#if WLR_HAS_XWAYLAND +#include "sway/xwayland.h" +#endif + enum criteria_type { CT_COMMAND = 1 << 0, CT_ASSIGN_OUTPUT = 1 << 1, diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index 45c751994..b014e18f8 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h @@ -5,10 +5,11 @@ #include #include #include -#include "sway/server.h" #include "sway/config.h" #include "list.h" +struct sway_server; + struct sway_input_device { char *identifier; struct wlr_input_device *wlr_device; diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c index 08d99bf0b..3cea026ec 100644 --- a/sway/commands/input/events.c +++ b/sway/commands/input/events.c @@ -5,6 +5,7 @@ #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.h" +#include "sway/server.h" #include "log.h" #if WLR_HAS_LIBINPUT_BACKEND diff --git a/sway/commands/input/xkb_switch_layout.c b/sway/commands/input/xkb_switch_layout.c index ecac8e6c2..8d600fcad 100644 --- a/sway/commands/input/xkb_switch_layout.c +++ b/sway/commands/input/xkb_switch_layout.c @@ -3,6 +3,7 @@ #include "sway/config.h" #include "sway/commands.h" #include "sway/input/input-manager.h" +#include "sway/server.h" #include "log.h" struct xkb_switch_layout_action { diff --git a/sway/commands/move.c b/sway/commands/move.c index 8addf26ec..ff656cfbd 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -11,6 +11,7 @@ #include "sway/input/seat.h" #include "sway/ipc-server.h" #include "sway/output.h" +#include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/root.h" diff --git a/sway/commands/seat/cursor.c b/sway/commands/seat/cursor.c index df7c379d1..434e6bbb9 100644 --- a/sway/commands/seat/cursor.c +++ b/sway/commands/seat/cursor.c @@ -5,6 +5,7 @@ #include #include "sway/commands.h" #include "sway/input/cursor.h" +#include "sway/server.h" static struct cmd_results *press_or_release(struct sway_cursor *cursor, char *action, char *button_str); diff --git a/sway/commands/seat/pointer_constraint.c b/sway/commands/seat/pointer_constraint.c index 3890ebde0..38f85bcd6 100644 --- a/sway/commands/seat/pointer_constraint.c +++ b/sway/commands/seat/pointer_constraint.c @@ -4,6 +4,7 @@ #include "sway/config.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" +#include "sway/server.h" enum operation { OP_ENABLE, diff --git a/sway/commands/seat/shortcuts_inhibitor.c b/sway/commands/seat/shortcuts_inhibitor.c index 7c7f99cf0..df68618dc 100644 --- a/sway/commands/seat/shortcuts_inhibitor.c +++ b/sway/commands/seat/shortcuts_inhibitor.c @@ -2,6 +2,7 @@ #include "sway/commands.h" #include "sway/input/seat.h" #include "sway/input/input-manager.h" +#include "sway/server.h" #include "util.h" static struct cmd_results *handle_action(struct seat_config *sc, diff --git a/sway/commands/shortcuts_inhibitor.c b/sway/commands/shortcuts_inhibitor.c index ffa1a5c99..2dfd1b9f9 100644 --- a/sway/commands/shortcuts_inhibitor.c +++ b/sway/commands/shortcuts_inhibitor.c @@ -3,6 +3,7 @@ #include "sway/commands.h" #include "sway/config.h" #include "sway/input/seat.h" +#include "sway/server.h" #include "sway/tree/container.h" #include "sway/tree/view.h" diff --git a/sway/config.c b/sway/config.c index f9131e0f7..5058efcc0 100644 --- a/sway/config.c +++ b/sway/config.c @@ -23,6 +23,7 @@ #include "sway/config.h" #include "sway/criteria.h" #include "sway/desktop/transaction.h" +#include "sway/server.h" #include "sway/swaynag.h" #include "sway/tree/arrange.h" #include "sway/tree/root.h" diff --git a/sway/config/bar.c b/sway/config/bar.c index 908b28650..ecefb61af 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -12,6 +12,7 @@ #include "sway/config.h" #include "sway/input/keyboard.h" #include "sway/output.h" +#include "sway/server.h" #include "config.h" #include "list.h" #include "log.h" diff --git a/sway/config/input.c b/sway/config/input.c index de3b21ed4..613270dfb 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -3,6 +3,7 @@ #include #include "sway/config.h" #include "sway/input/keyboard.h" +#include "sway/server.h" #include "log.h" struct input_config *new_input_config(const char* identifier) { diff --git a/sway/config/output.c b/sway/config/output.c index e72994594..9a4473881 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -13,6 +13,7 @@ #include "sway/config.h" #include "sway/input/cursor.h" #include "sway/output.h" +#include "sway/server.h" #include "sway/tree/root.h" #include "log.h" #include "util.h" diff --git a/sway/criteria.c b/sway/criteria.c index 13f0530e0..2b7290c0e 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -7,6 +7,7 @@ #include "sway/criteria.h" #include "sway/tree/container.h" #include "sway/config.h" +#include "sway/server.h" #include "sway/tree/root.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c index 28043d192..2362e1ba0 100644 --- a/sway/desktop/launcher.c +++ b/sway/desktop/launcher.c @@ -4,6 +4,7 @@ #include "sway/input/seat.h" #include "sway/output.h" #include "sway/desktop/launcher.h" +#include "sway/server.h" #include "sway/tree/node.h" #include "sway/tree/container.h" #include "sway/tree/workspace.h" diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index e464ff1ac..d1898843d 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -10,6 +10,7 @@ #include "sway/input/cursor.h" #include "sway/input/input-manager.h" #include "sway/output.h" +#include "sway/server.h" #include "sway/tree/container.h" #include "sway/tree/node.h" #include "sway/tree/view.h" diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 0b4a36d95..235951d4c 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -25,6 +25,7 @@ #include "sway/layers.h" #include "sway/output.h" #include "sway/scene_descriptor.h" +#include "sway/server.h" #include "sway/tree/container.h" #include "sway/tree/root.h" #include "sway/tree/view.h" diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index f74d0658d..9ac216646 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -13,6 +13,7 @@ #include "sway/input/seat.h" #include "sway/input/cursor.h" #include "sway/ipc-server.h" +#include "sway/server.h" #include "log.h" #if WLR_HAS_SESSION diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index e01fa9336..f4a0f4634 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -11,6 +11,7 @@ #include "sway/input/tablet.h" #include "sway/layers.h" #include "sway/output.h" +#include "sway/server.h" #include "sway/scene_descriptor.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" diff --git a/sway/input/switch.c b/sway/input/switch.c index 831f4dbf6..6aab4ad0d 100644 --- a/sway/input/switch.c +++ b/sway/input/switch.c @@ -1,5 +1,6 @@ #include "sway/config.h" #include "sway/input/switch.h" +#include "sway/server.h" #include "log.h" struct sway_switch *sway_switch_create(struct sway_seat *seat, diff --git a/sway/input/tablet.c b/sway/input/tablet.c index 2863642a7..ec1e4f682 100644 --- a/sway/input/tablet.c +++ b/sway/input/tablet.c @@ -7,6 +7,7 @@ #include "sway/input/cursor.h" #include "sway/input/seat.h" #include "sway/input/tablet.h" +#include "sway/server.h" #if WLR_HAS_LIBINPUT_BACKEND #include diff --git a/sway/input/text_input.c b/sway/input/text_input.c index ba8c0b997..580a9f545 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -9,6 +9,8 @@ #include "sway/input/text_input.h" #include "sway/input/text_input_popup.h" #include "sway/layers.h" +#include "sway/server.h" + static void input_popup_update(struct sway_input_popup *popup); static struct sway_text_input *relay_get_focusable_text_input( diff --git a/sway/ipc-json.c b/sway/ipc-json.c index b7370aa6d..1ee391246 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -11,6 +11,7 @@ #include "log.h" #include "sway/config.h" #include "sway/ipc-json.h" +#include "sway/server.h" #include "sway/tree/container.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index a68dc9277..52e48ad58 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -10,6 +10,7 @@ #include "sway/input/seat.h" #include "sway/ipc-server.h" #include "sway/output.h" +#include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/container.h" #include "sway/tree/node.h" From 2e9139df664f1e2dbe14b5df4a9646411b924c66 Mon Sep 17 00:00:00 2001 From: Violet Purcell Date: Sun, 17 Mar 2024 13:27:34 -0400 Subject: [PATCH 033/361] Update for versioned wlroots files References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4614 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 3c444e077..eb66506ab 100644 --- a/meson.build +++ b/meson.build @@ -45,7 +45,7 @@ subproject( required: false, version: wlroots_version, ) -wlroots = dependency('wlroots', version: wlroots_version) +wlroots = dependency('wlroots-0.18', version: wlroots_version, fallback: 'wlroots') wlroots_features = { 'xwayland': false, 'libinput_backend': false, From 40ca4150b27a5b94938b6c3d744f74bb26d347f7 Mon Sep 17 00:00:00 2001 From: Manuel Stoeckl Date: Mon, 17 Jul 2023 21:40:28 -0400 Subject: [PATCH 034/361] sway/commands/output: Add command to set color profile This makes it possible to render output buffers in a different color space, by specifying an ICC profile for the output. --- .builds/alpine.yml | 1 + .builds/archlinux.yml | 1 + .builds/freebsd.yml | 1 + include/sway/commands.h | 1 + include/sway/config.h | 3 + include/sway/output.h | 2 + sway/commands/output.c | 1 + sway/commands/output/color_profile.c | 101 +++++++++++++++++++++++++++ sway/config/output.c | 18 +++++ sway/desktop/output.c | 5 +- sway/meson.build | 1 + sway/sway-output.5.scd | 12 ++++ sway/tree/output.c | 1 + 13 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 sway/commands/output/color_profile.c diff --git a/.builds/alpine.yml b/.builds/alpine.yml index 055e5ffab..7a1fa58e0 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml @@ -4,6 +4,7 @@ packages: - eudev-dev - gdk-pixbuf-dev - json-c-dev + - lcms2-dev - libdisplay-info-dev - libevdev-dev - libinput-dev diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index 9972c01ad..e249571ee 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -3,6 +3,7 @@ packages: - cairo - gdk-pixbuf2 - json-c + - lcms2 - libdisplay-info - libegl - libinput diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 29c6312af..8084574c2 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -8,6 +8,7 @@ packages: - devel/pkgconf - graphics/cairo - graphics/gdk-pixbuf2 +- graphics/lcms2 - graphics/wayland - graphics/wayland-protocols - textproc/scdoc diff --git a/include/sway/commands.h b/include/sway/commands.h index 270585870..0a9fdc705 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -283,6 +283,7 @@ sway_cmd input_cmd_xkb_variant; sway_cmd output_cmd_adaptive_sync; sway_cmd output_cmd_background; +sway_cmd output_cmd_color_profile; sway_cmd output_cmd_disable; sway_cmd output_cmd_dpms; sway_cmd output_cmd_enable; diff --git a/include/sway/config.h b/include/sway/config.h index 5ccc3e777..3e3a104ec 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include "../include/config.h" @@ -285,6 +286,8 @@ struct output_config { int max_render_time; // In milliseconds int adaptive_sync; enum render_bit_depth render_bit_depth; + bool set_color_transform; + struct wlr_color_transform *color_transform; char *background; char *background_option; diff --git a/include/sway/output.h b/include/sway/output.h index d546d4884..2189c6e87 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -66,6 +66,8 @@ struct sway_output { struct wl_signal disable; } events; + struct wlr_color_transform *color_transform; + struct timespec last_presentation; uint32_t refresh_nsec; int max_render_time; // In milliseconds diff --git a/sway/commands/output.c b/sway/commands/output.c index 5e5d31b3e..b822e770a 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -10,6 +10,7 @@ static const struct cmd_handler output_handlers[] = { { "adaptive_sync", output_cmd_adaptive_sync }, { "background", output_cmd_background }, { "bg", output_cmd_background }, + { "color_profile", output_cmd_color_profile }, { "disable", output_cmd_disable }, { "dpms", output_cmd_dpms }, { "enable", output_cmd_enable }, diff --git a/sway/commands/output/color_profile.c b/sway/commands/output/color_profile.c new file mode 100644 index 000000000..792bd55fb --- /dev/null +++ b/sway/commands/output/color_profile.c @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include +#include "sway/commands.h" +#include "sway/config.h" + +static bool read_file_into_buf(const char *path, void **buf, size_t *size) { + /* Why not use fopen/fread directly? glibc will succesfully open directories, + * not just files, and supports seeking on them. Instead, we directly + * work with file descriptors and use the more consistent open/fstat/read. */ + int fd = open(path, O_RDONLY | O_NOCTTY | O_CLOEXEC); + if (fd == -1) { + return false; + } + char *b = NULL; + struct stat info; + if (fstat(fd, &info) == -1) { + goto fail; + } + // only regular files, to avoid issues with e.g. opening pipes + if (!S_ISREG(info.st_mode)) { + goto fail; + } + off_t s = info.st_size; + if (s <= 0) { + goto fail; + } + b = calloc(1, s); + if (!b) { + goto fail; + } + size_t nread = 0; + while (nread < (size_t)s) { + size_t to_read = (size_t)s - nread; + ssize_t r = read(fd, b + nread, to_read); + if ((r == -1 && errno != EINTR) || r == 0) { + goto fail; + } + nread += (size_t)r; + } + close(fd); + *buf = b; + *size = (size_t)s; + return true; // success +fail: + free(b); + close(fd); + return false; +} + +struct cmd_results *output_cmd_color_profile(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 color_profile first argument."); + } + + if (strcmp(*argv, "srgb") == 0) { + wlr_color_transform_unref(config->handler_context.output_config->color_transform); + config->handler_context.output_config->color_transform = NULL; + config->handler_context.output_config->set_color_transform = true; + + config->handler_context.leftovers.argc = argc - 1; + config->handler_context.leftovers.argv = argv + 1; + } else if (strcmp(*argv, "icc") == 0) { + if (argc < 2) { + return cmd_results_new(CMD_INVALID, + "Invalid color profile specification: icc type requires a file"); + } + void *data = NULL; + size_t size = 0; + if (!read_file_into_buf(argv[1], &data, &size)) { + return cmd_results_new(CMD_FAILURE, + "Failed to load color profile: could not read ICC file"); + } + + struct wlr_color_transform *tmp = + wlr_color_transform_init_linear_to_icc(data, size); + if (!tmp) { + free(data); + return cmd_results_new(CMD_FAILURE, + "Failed to load color profile: failed to initialize transform from ICC"); + } + free(data); + + wlr_color_transform_unref(config->handler_context.output_config->color_transform); + config->handler_context.output_config->color_transform = tmp; + config->handler_context.output_config->set_color_transform = true; + + config->handler_context.leftovers.argc = argc - 2; + config->handler_context.leftovers.argv = argv + 2; + } else { + return cmd_results_new(CMD_INVALID, + "Invalid color profile specification: first argument should be icc|srgb"); + } + + return NULL; +} diff --git a/sway/config/output.c b/sway/config/output.c index 9a4473881..bcd21b9ba 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -76,6 +76,8 @@ struct output_config *new_output_config(const char *name) { oc->max_render_time = -1; oc->adaptive_sync = -1; oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; + oc->set_color_transform = false; + oc->color_transform = NULL; oc->power = -1; return oc; } @@ -191,6 +193,14 @@ static void merge_output_config(struct output_config *dst, struct output_config if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { dst->render_bit_depth = src->render_bit_depth; } + if (src->set_color_transform) { + if (src->color_transform) { + wlr_color_transform_ref(src->color_transform); + } + wlr_color_transform_unref(dst->color_transform); + dst->set_color_transform = true; + dst->color_transform = src->color_transform; + } if (src->background) { free(dst->background); dst->background = strdup(src->background); @@ -557,6 +567,13 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output output->max_render_time = oc->max_render_time; } + if (oc && oc->set_color_transform) { + if (oc->color_transform) { + wlr_color_transform_ref(oc->color_transform); + } + wlr_color_transform_unref(output->color_transform); + output->color_transform = oc->color_transform; + } return true; } @@ -997,6 +1014,7 @@ void free_output_config(struct output_config *oc) { free(oc->name); free(oc->background); free(oc->background_option); + wlr_color_transform_unref(oc->color_transform); free(oc); } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 2722e5567..cfa530211 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -269,7 +269,10 @@ static int output_repaint_timer_handler(void *data) { return 0; } - wlr_scene_output_commit(output->scene_output, NULL); + struct wlr_scene_output_state_options opts = { + .color_transform = output->color_transform, + }; + wlr_scene_output_commit(output->scene_output, &opts); return 0; } diff --git a/sway/meson.build b/sway/meson.build index 47b51d0cc..a189fe9a9 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -202,6 +202,7 @@ sway_sources = files( 'commands/output/toggle.c', 'commands/output/transform.c', 'commands/output/unplug.c', + 'commands/output/color_profile.c', 'tree/arrange.c', 'tree/container.c', diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index 7d088d5db..6d7c08604 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -178,6 +178,18 @@ must be separated by one space. For example: updated to work with different bit depths. This command is experimental, and may be removed or changed in the future. +*output* color_profile srgb|[icc ] + Sets the color profile for an output. The default is _srgb_. should be a + path to a display ICC profile. + + Not all renderers support this feature; currently it only works with the + the Vulkan renderer. Even where supported, the application of the color + profile may be inaccurate. + + This command is experimental, and may be removed or changed in the future. It + may have no effect or produce unexpected output when used together with future + HDR support features. + # SEE ALSO *sway*(5) *sway-input*(5) diff --git a/sway/tree/output.c b/sway/tree/output.c index 2d11195e4..6c8dd6dc9 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -279,6 +279,7 @@ void output_destroy(struct sway_output *output) { list_free(output->workspaces); list_free(output->current.workspaces); wl_event_source_remove(output->repaint_timer); + wlr_color_transform_unref(output->color_transform); free(output); } From cc342107690631cf1ff003fed0b1cdb072491c63 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 8 Jun 2024 11:28:42 +0200 Subject: [PATCH 035/361] Set color transform when calling wlr_scene_output_build_state() We were only passing the color transform when calling wlr_scene_output_commit(). However when modesetting or pushing a new gamma LUT we render via wlr_scene_output_build_state(). Pass the color transform there as well. --- sway/config/output.c | 1 + sway/desktop/output.c | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index bcd21b9ba..16be49c86 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -927,6 +927,7 @@ bool apply_output_configs(struct matched_output_config *configs, struct wlr_scene_output_state_options opts = { .swapchain = wlr_output_swapchain_manager_get_swapchain( &swapchain_mgr, backend_state->output), + .color_transform = cfg->output->color_transform, }; struct wlr_scene_output *scene_output = cfg->output->scene_output; struct wlr_output_state *state = &backend_state->base; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index cfa530211..6bf77d17b 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -243,10 +243,14 @@ static int output_repaint_timer_handler(void *data) { output_configure_scene(output, &root->root_scene->tree.node, 1.0f); + struct wlr_scene_output_state_options opts = { + .color_transform = output->color_transform, + }; + if (output->gamma_lut_changed) { struct wlr_output_state pending; wlr_output_state_init(&pending); - if (!wlr_scene_output_build_state(output->scene_output, &pending, NULL)) { + if (!wlr_scene_output_build_state(output->scene_output, &pending, &opts)) { return 0; } @@ -269,9 +273,6 @@ static int output_repaint_timer_handler(void *data) { return 0; } - struct wlr_scene_output_state_options opts = { - .color_transform = output->color_transform, - }; wlr_scene_output_commit(output->scene_output, &opts); return 0; } From 5f15c5e91defe8afc6c0f5105b7a51625676a685 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 24 Jun 2024 09:29:31 +0200 Subject: [PATCH 036/361] =?UTF-8?q?tree/view:=20set=20default=20min=20size?= =?UTF-8?q?=20to=201=C3=971?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's not possible to have a surface with a smaller size. --- sway/tree/view.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index 1c1c8ee8a..884beec84 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -173,9 +173,9 @@ void view_get_constraints(struct sway_view *view, double *min_width, view->impl->get_constraints(view, min_width, max_width, min_height, max_height); } else { - *min_width = DBL_MIN; + *min_width = 1; *max_width = DBL_MAX; - *min_height = DBL_MIN; + *min_height = 1; *max_height = DBL_MAX; } } From fce8de0f672e3c205dbbb0eb9a55d4bc05ff66ad Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 24 Jun 2024 09:29:59 +0200 Subject: [PATCH 037/361] tree/view: ensure content_{width,height} is positive The size computations may result in a zero or negative size, which are not valid wl_surface sizes. --- sway/tree/view.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index 884beec84..e9624094e 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -365,8 +365,8 @@ void view_autoconfigure(struct sway_view *view) { con->pending.content_x = x; con->pending.content_y = y; - con->pending.content_width = width; - con->pending.content_height = height; + con->pending.content_width = fmax(width, 1); + con->pending.content_height = fmax(height, 1); } void view_set_activated(struct sway_view *view, bool activated) { From 74cc02d60f221deeed800454ba3cf68a55001430 Mon Sep 17 00:00:00 2001 From: bretello Date: Thu, 27 Jun 2024 15:41:48 +0200 Subject: [PATCH 038/361] fix crash when setting urgency on an hidden scratchpad container --- sway/tree/view.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index e9624094e..086a8ff3e 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -1185,7 +1185,7 @@ void view_set_urgent(struct sway_view *view, bool enable) { ipc_event_window(view->container, "urgent"); - if (!container_is_scratchpad_hidden(view->container)) { + if (!container_is_scratchpad_hidden_or_child(view->container)) { workspace_detect_urgent(view->container->pending.workspace); } } From 8f6bc5bb36d1451dd51d91feabf2c91bf5bc1026 Mon Sep 17 00:00:00 2001 From: bretello Date: Thu, 27 Jun 2024 16:16:33 +0200 Subject: [PATCH 039/361] prevent workspace_find_container from crashing with NULL workspaces --- sway/tree/workspace.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 52e48ad58..f8709a4c7 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -708,6 +708,11 @@ void workspace_for_each_container(struct sway_workspace *ws, struct sway_container *workspace_find_container(struct sway_workspace *ws, bool (*test)(struct sway_container *con, void *data), void *data) { struct sway_container *result = NULL; + if (ws == NULL){ + sway_log(SWAY_ERROR, "Cannot find container with no workspace."); + return NULL; + } + // Tiling for (int i = 0; i < ws->tiling->length; ++i) { struct sway_container *child = ws->tiling->items[i]; From cdde0165dad94e4522f8f6f040e9d30145fbb14f Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Thu, 27 Jun 2024 21:01:19 +0300 Subject: [PATCH 040/361] protocols: use tablet-v2 xml from stable/ --- protocols/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/meson.build b/protocols/meson.build index 81edb5841..6eac8542c 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -7,10 +7,10 @@ wayland_scanner = find_program( ) protocols = [ + wl_protocol_dir / 'stable/tablet/tablet-v2.xml', wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml', wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml', - wl_protocol_dir / 'unstable/tablet/tablet-unstable-v2.xml', wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', wl_protocol_dir / 'staging/content-type/content-type-v1.xml', wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', From 5d237679f5d433b6a6c3b489298c42d40f217db4 Mon Sep 17 00:00:00 2001 From: Ronan Pigott Date: Mon, 31 Jul 2023 12:20:07 -0700 Subject: [PATCH 041/361] view: send scale notification when the output is known --- sway/tree/view.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sway/tree/view.c b/sway/tree/view.c index 086a8ff3e..4f757acf1 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -741,6 +742,14 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, ws = select_workspace(view); } + if (ws && ws->output) { + // Once the output is determined, we can notify the client early about + // scale to reduce startup jitter. + float scale = ws->output->wlr_output->scale; + wlr_fractional_scale_v1_notify_scale(wlr_surface, scale); + wlr_surface_set_preferred_buffer_scale(wlr_surface, ceil(scale)); + } + struct sway_seat *seat = input_manager_current_seat(); struct sway_node *node = seat_get_focus_inactive(seat, ws ? &ws->node : &root->node); From 1e0031781fc9283db7096aba34deca5503c2ab91 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 1 Jul 2024 09:21:53 +0200 Subject: [PATCH 042/361] desktop/output: unify page-flip codepath Instead of having a special codepath for applying gamma LUTs, have a single codepath for regular page-flips and gamma LUT updates. Should make it easier to add more logic on top e.g. for tearing page-flips. --- sway/desktop/output.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 6bf77d17b..4c9d0b631 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -247,13 +247,13 @@ static int output_repaint_timer_handler(void *data) { .color_transform = output->color_transform, }; - if (output->gamma_lut_changed) { - struct wlr_output_state pending; - wlr_output_state_init(&pending); - if (!wlr_scene_output_build_state(output->scene_output, &pending, &opts)) { - return 0; - } + struct wlr_output_state pending; + wlr_output_state_init(&pending); + if (!wlr_scene_output_build_state(output->scene_output, &pending, &opts)) { + return 0; + } + if (output->gamma_lut_changed) { output->gamma_lut_changed = false; struct wlr_gamma_control_v1 *gamma_control = wlr_gamma_control_manager_v1_get_control( @@ -263,17 +263,16 @@ static int output_repaint_timer_handler(void *data) { return 0; } - if (!wlr_output_commit_state(output->wlr_output, &pending)) { + if (!wlr_output_test_state(output->wlr_output, &pending)) { wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); - wlr_output_state_finish(&pending); - return 0; + wlr_output_state_set_gamma_lut(&pending, 0, NULL, NULL, NULL); } - - wlr_output_state_finish(&pending); - return 0; } - wlr_scene_output_commit(output->scene_output, &opts); + if (!wlr_output_commit_state(output->wlr_output, &pending)) { + sway_log(SWAY_ERROR, "Page-flip failed on output %s", output->wlr_output->name); + } + wlr_output_state_finish(&pending); return 0; } From 4e38f93f367dfb7f1ec66060e6262b806cecf3a7 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 2 Jul 2024 00:39:21 +0200 Subject: [PATCH 043/361] config/output: Skip VRR tests when not supported Adaptive sync is a "soft" setting which we degrade of off when not supported. Some outputs types do not support turning it off (Wayland, X11), which makes for an awkward three-way test where we first enable, disable and finally unset the setting. wlr_output.adaptive_sync_supported tells us whether the output definitely does not support making changes (backend without support, connector without the feature), or whether it might work. Use this to avoid wasting time on adaptive sync test that can never succeed, and to avoid the Wayland/X11-backend specific unset step. --- sway/config/output.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index 16be49c86..e64efb7ff 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -721,21 +721,18 @@ static bool search_adaptive_sync(struct search_context *ctx, size_t output_idx) struct wlr_backend_output_state *backend_state = &ctx->states[output_idx]; struct wlr_output_state *state = &backend_state->base; + if (!backend_state->output->adaptive_sync_supported) { + return search_finish(ctx, output_idx); + } + if (cfg->config && cfg->config->adaptive_sync == 1) { wlr_output_state_set_adaptive_sync_enabled(state, true); if (search_finish(ctx, output_idx)) { return true; } } - if (!cfg->config || cfg->config->adaptive_sync != -1) { - wlr_output_state_set_adaptive_sync_enabled(state, false); - if (search_finish(ctx, output_idx)) { - return true; - } - } - // If adaptive sync has not been set, or fallback in case we are on a - // backend that cannot disable adaptive sync such as the wayland backend. - state->committed &= ~WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED; + + wlr_output_state_set_adaptive_sync_enabled(state, false); return search_finish(ctx, output_idx); } From dfbcd1fbaa5148e537a1c29dc3cbe8ea21ee5eac Mon Sep 17 00:00:00 2001 From: llyyr Date: Mon, 27 May 2024 15:43:56 +0530 Subject: [PATCH 044/361] input/keyboard: don't send key release if we don't have focused surface "The compositor must not send this event if the wl_keyboard did not have an active surface immediately before this event. The compositor must not send this event if state is pressed (resp. released) and the key was already logically down (resp. was not logically down) immediately before this event." From https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/382 --- sway/input/keyboard.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 9ac216646..e33dbe0b7 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -509,12 +509,13 @@ static void handle_key_event(struct sway_keyboard *keyboard, } if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { - // If the pressed event was sent to a client, also send the released + // If the pressed event was sent to a client and we have a focused + // surface immediately before this event, also send the released // event. In particular, don't send the released event to the IM grab. bool pressed_sent = update_shortcut_state( &keyboard->state_pressed_sent, event->keycode, event->state, keyinfo.keycode, 0); - if (pressed_sent) { + if (pressed_sent && seat->wlr_seat->keyboard_state.focused_surface) { wlr_seat_set_keyboard(wlr_seat, keyboard->wlr); wlr_seat_keyboard_notify_key(wlr_seat, event->time_msec, event->keycode, event->state); From 0386b2afcb88f0ceba144c1f2bbb4b6fc6f13518 Mon Sep 17 00:00:00 2001 From: llyyr Date: Sat, 25 May 2024 22:30:20 +0530 Subject: [PATCH 045/361] input/seat: don't send redundant leave/enter on device creation Fixes: #8143 #8173 Upstream issue: https://github.com/fcitx/fcitx5/issues/1044 --- sway/input/seat.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sway/input/seat.c b/sway/input/seat.c index da4bb12ac..9a00a3e24 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -802,11 +802,10 @@ static void seat_configure_keyboard(struct sway_seat *seat, return; } - // force notify reenter to pick up the new configuration. This reuses + // Notify reenter to pick up the new configuration. This reuses // the current focused surface to avoid breaking input grabs. struct wlr_surface *surface = seat->wlr_seat->keyboard_state.focused_surface; if (surface) { - wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); seat_keyboard_notify_enter(seat, surface); } } From 4c3c0602116c12c2821e1e505e7248b3c642b4ca Mon Sep 17 00:00:00 2001 From: llyyr Date: Sun, 26 May 2024 00:59:44 +0530 Subject: [PATCH 046/361] input/keyboard: refactor into sway_keyboard_set_layout --- sway/input/keyboard.c | 80 ++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 36 deletions(-) diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index e33dbe0b7..91a4f8663 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -961,16 +961,8 @@ cleanup: free(sway_group); } -void sway_keyboard_configure(struct sway_keyboard *keyboard) { - struct input_config *input_config = - input_device_get_config(keyboard->seat_device->input_device); - - if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr), - "sway_keyboard_configure should not be called with a " - "keyboard group's keyboard")) { - return; - } - +static void sway_keyboard_set_layout(struct sway_keyboard *keyboard, + struct input_config *input_config) { struct xkb_keymap *keymap = sway_keyboard_compile_keymap(input_config, NULL); if (!keymap) { sway_log(SWAY_ERROR, "Failed to compile keymap. Attempting defaults"); @@ -986,31 +978,13 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { !wlr_keyboard_keymaps_match(keyboard->keymap, keymap) : true; bool effective_layout_changed = keyboard->effective_layout != 0; - 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; - } - - bool repeat_info_changed = keyboard->repeat_rate != repeat_rate || - keyboard->repeat_delay != repeat_delay; - - if (keymap_changed || repeat_info_changed || config->reloading) { + if (keymap_changed || config->reloading) { xkb_keymap_unref(keyboard->keymap); keyboard->keymap = keymap; keyboard->effective_layout = 0; - keyboard->repeat_rate = repeat_rate; - keyboard->repeat_delay = repeat_delay; sway_keyboard_group_remove_invalid(keyboard); - wlr_keyboard_set_keymap(keyboard->wlr, keyboard->keymap); - wlr_keyboard_set_repeat_info(keyboard->wlr, - keyboard->repeat_rate, keyboard->repeat_delay); - if (!keyboard->wlr->group) { sway_keyboard_group_add(keyboard); } @@ -1061,6 +1035,47 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { wlr_seat_set_keyboard(seat, keyboard->wlr); } + if (keymap_changed) { + ipc_event_input("xkb_keymap", + keyboard->seat_device->input_device); + } else if (effective_layout_changed) { + ipc_event_input("xkb_layout", + keyboard->seat_device->input_device); + } +} + +void sway_keyboard_configure(struct sway_keyboard *keyboard) { + struct input_config *input_config = + input_device_get_config(keyboard->seat_device->input_device); + + if (!sway_assert(!wlr_keyboard_group_from_wlr_keyboard(keyboard->wlr), + "sway_keyboard_configure should not be called with a " + "keyboard group's keyboard")) { + return; + } + + 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; + } + + bool repeat_info_changed = keyboard->repeat_rate != repeat_rate || + keyboard->repeat_delay != repeat_delay; + + if (repeat_info_changed || config->reloading) { + keyboard->repeat_rate = repeat_rate; + keyboard->repeat_delay = repeat_delay; + + wlr_keyboard_set_repeat_info(keyboard->wlr, + keyboard->repeat_rate, keyboard->repeat_delay); + } + + sway_keyboard_set_layout(keyboard, input_config); + wl_list_remove(&keyboard->keyboard_key.link); wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key); keyboard->keyboard_key.notify = handle_keyboard_key; @@ -1070,13 +1085,6 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { &keyboard->keyboard_modifiers); keyboard->keyboard_modifiers.notify = handle_keyboard_modifiers; - if (keymap_changed) { - ipc_event_input("xkb_keymap", - keyboard->seat_device->input_device); - } else if (effective_layout_changed) { - ipc_event_input("xkb_layout", - keyboard->seat_device->input_device); - } } void sway_keyboard_destroy(struct sway_keyboard *keyboard) { From e32bdaa7bead5052dd32c12917ea8f74a9b14405 Mon Sep 17 00:00:00 2001 From: llyyr Date: Sun, 26 May 2024 01:46:34 +0530 Subject: [PATCH 047/361] input/keyboard: don't set layout for virtual keyboard device This prevents us from recompiling keymap every time a virtual device is created by clients like fcitx5 --- sway/input/keyboard.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 91a4f8663..efb9ac394 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -1074,7 +1074,9 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { keyboard->repeat_rate, keyboard->repeat_delay); } - sway_keyboard_set_layout(keyboard, input_config); + if (!keyboard->seat_device->input_device->is_virtual) { + sway_keyboard_set_layout(keyboard, input_config); + } wl_list_remove(&keyboard->keyboard_key.link); wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key); From b04f4136bc6163246d7e24454b84a950c8137ffc Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 28 Mar 2024 00:34:30 +0100 Subject: [PATCH 048/361] desktop/output: Debounce modesets Output changes often happen in rapid succession. Instead of doing the modesets one by one, set a 10 millisecond debounce timer. --- include/sway/server.h | 2 ++ sway/desktop/output.c | 32 +++++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index 3a63df342..abf1b6b4e 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -135,6 +135,8 @@ struct sway_server { // Stores the nodes that have been marked as "dirty" and will be put into // the pending transaction. list_t *dirty_nodes; + + struct wl_event_source *delayed_modeset; }; extern struct sway_server server; diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 4c9d0b631..f936b2a8d 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -365,6 +365,26 @@ static void update_output_manager_config(struct sway_server *server) { ipc_event_output(); } +static int timer_modeset_handle(void *data) { + struct sway_server *server = data; + wl_event_source_remove(server->delayed_modeset); + server->delayed_modeset = NULL; + + apply_all_output_configs(); + transaction_commit_dirty(); + update_output_manager_config(server); + + return 0; +} + +static void request_modeset(struct sway_server *server) { + if (server->delayed_modeset == NULL) { + server->delayed_modeset = wl_event_loop_add_timer(server->wl_event_loop, + timer_modeset_handle, server); + wl_event_source_timer_update(server->delayed_modeset, 10); + } +} + static void begin_destroy(struct sway_output *output) { struct sway_server *server = output->server; @@ -388,9 +408,7 @@ static void begin_destroy(struct sway_output *output) { output->wlr_output->data = NULL; output->wlr_output = NULL; - transaction_commit_dirty(); - - update_output_manager_config(server); + request_modeset(server); } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -524,11 +542,7 @@ void handle_new_output(struct wl_listener *listener, void *data) { sway_session_lock_add_output(server->session_lock.lock, output); } - apply_all_output_configs(); - - transaction_commit_dirty(); - - update_output_manager_config(server); + request_modeset(server); } void handle_output_layout_change(struct wl_listener *listener, @@ -680,5 +694,5 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, break; } store_output_config(oc); - apply_all_output_configs(); + request_modeset(output->server); } From a0a078f75e977bff85ba6723fe54a4e982e9df52 Mon Sep 17 00:00:00 2001 From: novenary Date: Wed, 3 Jul 2024 16:37:49 +0300 Subject: [PATCH 049/361] transaction: fix missing top border with hide_lone_tab Regressed by scene graph. --- sway/desktop/transaction.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index d1898843d..7568990bf 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -314,7 +314,7 @@ static void arrange_children(enum sway_container_layout layout, list_t *children if (activated) { arrange_container(child, width, height - title_bar_height, - false, 0); + title_bar_height == 0, 0); } else { disable_container(child); } @@ -343,7 +343,7 @@ static void arrange_children(enum sway_container_layout layout, list_t *children if (activated) { arrange_container(child, width, height - title_height, - false, 0); + title_bar_height == 0, 0); } else { disable_container(child); } From 5233a0bd2ef84ab6d1eeb2ebcde3d0c73794cdb9 Mon Sep 17 00:00:00 2001 From: novenary Date: Thu, 28 Dec 2023 23:28:27 +0200 Subject: [PATCH 050/361] ipc: properly check for titlebars This fixes incorrect values for rect.y when using `hide_edge_borders --i3`. --- 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 1ee391246..91e31a298 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -578,9 +578,10 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object bool visible = view_is_visible(c->view); json_object_object_add(object, "visible", json_object_new_boolean(visible)); + bool has_titlebar = c->title_bar.tree->node.enabled; struct wlr_box window_box = { c->pending.content_x - c->pending.x, - (c->current.border == B_PIXEL) ? c->pending.content_y - c->pending.y : 0, + has_titlebar ? 0 : c->pending.content_y - c->pending.y, c->pending.content_width, c->pending.content_height }; From fdcfe00781d3ee31df99acb43ccd3873d2229809 Mon Sep 17 00:00:00 2001 From: llyyr Date: Thu, 4 Jul 2024 03:01:25 +0530 Subject: [PATCH 051/361] xdg_shell: don't send configure events to uninitialized surfaces the surface isn't initialized yet when we first handle it in `handle_xdg_shell_toplevel`, move setting WM capabilities to handle_commit instead. Fixes warnings from wlroots about a configure being scheduled for uninitialized surface --- sway/desktop/xdg_shell.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 7c4178910..fdfa7b652 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -289,6 +289,8 @@ static void handle_commit(struct wl_listener *listener, void *data) { } // XXX: https://github.com/swaywm/sway/issues/2176 wlr_xdg_surface_schedule_configure(xdg_surface); + wlr_xdg_toplevel_set_wm_capabilities(view->wlr_xdg_toplevel, + XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); // TODO: wlr_xdg_toplevel_set_bounds() return; } @@ -575,7 +577,4 @@ void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) { wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base); xdg_toplevel->base->data = xdg_shell_view; - - wlr_xdg_toplevel_set_wm_capabilities(xdg_toplevel, - XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); } From d8c4a2d5fe5c2fb5f3941a5daf9edb679242bb11 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 3 Jul 2024 23:48:03 +0200 Subject: [PATCH 052/361] tree/container: drop decl for container_update_textures_recursive() This function doesn't exist anymore. --- include/sway/tree/container.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 93f6bfbb1..8bf1955d7 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -175,8 +175,6 @@ struct sway_container *container_obstructing_fullscreen_container(struct sway_co bool container_has_ancestor(struct sway_container *container, struct sway_container *ancestor); -void container_update_textures_recursive(struct sway_container *con); - void container_reap_empty(struct sway_container *con); struct sway_container *container_flatten(struct sway_container *container); From 818ea17389326901093c0469a5225aab61ccc1ec Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 4 Jul 2024 00:06:21 +0200 Subject: [PATCH 053/361] sway_text_node: avoid unnecessary redraws When a floating container has a titlebar, render_backing_buffer() ends up being called each time the container is moved. Add some more checks for no-op changes in sway_text_node_set_max_width() and sway_text_node_set_background(). This makes the move smoother. --- sway/sway_text_node.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c index 4b7ee999e..7c7813559 100644 --- a/sway/sway_text_node.c +++ b/sway/sway_text_node.c @@ -294,6 +294,9 @@ void sway_text_node_set_text(struct sway_text_node *node, char *text) { void sway_text_node_set_max_width(struct sway_text_node *node, int max_width) { struct text_buffer *buffer = wl_container_of(node, buffer, props); + if (max_width == buffer->props.max_width) { + return; + } buffer->props.max_width = max_width; wlr_scene_buffer_set_dest_size(buffer->buffer_node, get_text_width(&buffer->props), buffer->props.height); @@ -303,6 +306,9 @@ void sway_text_node_set_max_width(struct sway_text_node *node, int max_width) { void sway_text_node_set_background(struct sway_text_node *node, float background[4]) { struct text_buffer *buffer = wl_container_of(node, buffer, props); + if (memcmp(&node->background, background, sizeof(*background) * 4) == 0) { + return; + } memcpy(&node->background, background, sizeof(*background) * 4); render_backing_buffer(buffer); } From 5be5a5005164d3ccff844f4c72836cb49cbf784a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarc=C3=ADsio=20Eduardo=20Moreira=20Crocomo?= Date: Wed, 10 Apr 2024 18:01:50 -0300 Subject: [PATCH 054/361] Implement clickfinger_button_map support. --- include/sway/commands.h | 1 + include/sway/config.h | 1 + sway/commands/input.c | 1 + sway/commands/input/clickfinger_button_map.c | 27 ++++++++++++++++++++ sway/config/input.c | 4 +++ sway/input/libinput.c | 15 +++++++++++ sway/ipc-json.c | 16 ++++++++++-- sway/meson.build | 1 + sway/sway-input.5.scd | 6 +++++ sway/sway-ipc.7.scd | 5 +++- 10 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 sway/commands/input/clickfinger_button_map.c diff --git a/include/sway/commands.h b/include/sway/commands.h index 0a9fdc705..15cd86982 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -249,6 +249,7 @@ sway_cmd input_cmd_seat; sway_cmd input_cmd_accel_profile; sway_cmd input_cmd_calibration_matrix; sway_cmd input_cmd_click_method; +sway_cmd input_cmd_clickfinger_button_map; sway_cmd input_cmd_drag; sway_cmd input_cmd_drag_lock; sway_cmd input_cmd_dwt; diff --git a/include/sway/config.h b/include/sway/config.h index 3e3a104ec..dfa3c1b7b 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -149,6 +149,7 @@ struct input_config { int accel_profile; struct calibration_matrix calibration_matrix; int click_method; + int clickfinger_button_map; int drag; int drag_lock; int dwt; diff --git a/sway/commands/input.c b/sway/commands/input.c index 306c40f74..35846b1cf 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -11,6 +11,7 @@ static const struct cmd_handler input_handlers[] = { { "accel_profile", input_cmd_accel_profile }, { "calibration_matrix", input_cmd_calibration_matrix }, { "click_method", input_cmd_click_method }, + { "clickfinger_button_map", input_cmd_clickfinger_button_map }, { "drag", input_cmd_drag }, { "drag_lock", input_cmd_drag_lock }, { "dwt", input_cmd_dwt }, diff --git a/sway/commands/input/clickfinger_button_map.c b/sway/commands/input/clickfinger_button_map.c new file mode 100644 index 000000000..57d6e39a6 --- /dev/null +++ b/sway/commands/input/clickfinger_button_map.c @@ -0,0 +1,27 @@ +#include +#include +#include "sway/config.h" +#include "sway/commands.h" +#include "sway/input/input-manager.h" + +struct cmd_results *input_cmd_clickfinger_button_map(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "clickfinger_button_map", EXPECTED_AT_LEAST, 1))) { + return error; + } + struct input_config *ic = config->handler_context.input_config; + if (!ic) { + return cmd_results_new(CMD_FAILURE, "No input device defined."); + } + + if (strcasecmp(argv[0], "lrm") == 0) { + ic->clickfinger_button_map = LIBINPUT_CONFIG_CLICKFINGER_MAP_LRM; + } else if (strcasecmp(argv[0], "lmr") == 0) { + ic->clickfinger_button_map = LIBINPUT_CONFIG_CLICKFINGER_MAP_LMR; + } else { + return cmd_results_new(CMD_INVALID, + "Expected 'clickfinger_button_map '"); + } + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/config/input.c b/sway/config/input.c index 613270dfb..e5694effc 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -28,6 +28,7 @@ struct input_config *new_input_config(const char* identifier) { input->dwtp = INT_MIN; input->send_events = INT_MIN; input->click_method = INT_MIN; + input->clickfinger_button_map = INT_MIN; input->middle_emulation = INT_MIN; input->natural_scroll = INT_MIN; input->accel_profile = INT_MIN; @@ -55,6 +56,9 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { if (src->click_method != INT_MIN) { dst->click_method = src->click_method; } + if (src->clickfinger_button_map != INT_MIN) { + dst->clickfinger_button_map = src->clickfinger_button_map; + } if (src->drag != INT_MIN) { dst->drag = src->drag; } diff --git a/sway/input/libinput.c b/sway/input/libinput.c index 0266c7a93..2fec290e7 100644 --- a/sway/input/libinput.c +++ b/sway/input/libinput.c @@ -132,6 +132,16 @@ static bool set_click_method(struct libinput_device *device, return true; } +static bool set_clickfinger_button_map(struct libinput_device *device, + enum libinput_config_clickfinger_button_map map) { + if (libinput_device_config_click_get_clickfinger_button_map(device) == map) { + return false; + } + sway_log(SWAY_DEBUG, "clickfinger_set_button_map(%d)", map); + log_status(libinput_device_config_click_set_clickfinger_button_map(device, map)); + return true; +} + static bool set_middle_emulation(struct libinput_device *dev, enum libinput_config_middle_emulation_state mid) { if (!libinput_device_config_middle_emulation_is_available(dev) || @@ -281,6 +291,9 @@ bool sway_input_configure_libinput_device(struct sway_input_device *input_device if (ic->click_method != INT_MIN) { changed |= set_click_method(device, ic->click_method); } + if (ic->clickfinger_button_map != INT_MIN) { + changed |= set_clickfinger_button_map(device, ic->clickfinger_button_map); + } if (ic->middle_emulation != INT_MIN) { changed |= set_middle_emulation(device, ic->middle_emulation); } @@ -356,6 +369,8 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) { libinput_device_config_left_handed_get_default(device)); changed |= set_click_method(device, libinput_device_config_click_get_default_method(device)); + changed |= set_clickfinger_button_map(device, + libinput_device_config_click_get_default_clickfinger_button_map(device)); changed |= set_middle_emulation(device, libinput_device_config_middle_emulation_get_default_enabled(device)); changed |= set_scroll_method(device, diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 91e31a298..e512a2239 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -992,6 +992,18 @@ static json_object *describe_libinput_device(struct libinput_device *device) { } json_object_object_add(object, "click_method", json_object_new_string(click_method)); + + const char *button_map = "unknown"; + switch (libinput_device_config_click_get_clickfinger_button_map(device)) { + case LIBINPUT_CONFIG_CLICKFINGER_MAP_LRM: + button_map = "lrm"; + break; + case LIBINPUT_CONFIG_CLICKFINGER_MAP_LMR: + button_map = "lmr"; + break; + } + json_object_object_add(object, "clickfinger_button_map", + json_object_new_string(button_map)); } if (libinput_device_config_middle_emulation_is_available(device)) { @@ -1109,9 +1121,9 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { struct xkb_keymap *keymap = keyboard->keymap; struct xkb_state *state = keyboard->xkb_state; - json_object_object_add(object, "repeat_delay", + json_object_object_add(object, "repeat_delay", json_object_new_int(keyboard->repeat_info.delay)); - json_object_object_add(object, "repeat_rate", + json_object_object_add(object, "repeat_rate", json_object_new_int(keyboard->repeat_info.rate)); json_object *layouts_arr = json_object_new_array(); diff --git a/sway/meson.build b/sway/meson.build index a189fe9a9..2f4406abb 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -154,6 +154,7 @@ sway_sources = files( 'commands/input/accel_profile.c', 'commands/input/calibration_matrix.c', 'commands/input/click_method.c', + 'commands/input/clickfinger_button_map.c', 'commands/input/drag.c', 'commands/input/drag_lock.c', 'commands/input/dwt.c', diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 442311bb9..fbef2a321 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -143,6 +143,12 @@ The following commands may only be used in the configuration file. *input* click_method none|button_areas|clickfinger Changes the click method for the specified device. +*input* clickfinger_button_map lrm|lmr + Specifies which button mapping to use for clickfinger. _lrm_ treats 1 finger as + left click, 2 fingers as right click, and 3 fingers as middle click. _lmr_ + treats 1 finger as left click, 2 fingers as middle click, and 3 fingers as + right click. + *input* drag enabled|disabled Enables or disables tap-and-drag for specified input device. diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index 2f6972489..e90abcbb8 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -1168,7 +1168,7 @@ following properties will be included for devices that support them: : 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_ +: The finger to button mapping in use for tapping. It can be _lmr_ or _lrm_ |- tap_drag : string : Whether tap-and-drag is enabled. It can be _enabled_ or _disabled_ @@ -1190,6 +1190,9 @@ following properties will be included for devices that support them: |- click_method : string : The click method in use. It can be _none_, _button_areas_, or _clickfinger_ +|- click_button_map +: string +: The finger to button mapping in use for clickfinger. It can be _lmr_ or _lrm_ |- middle_emulation : string : Whether middle emulation is enabled. It can be _enabled_ or _disabled_ From 3d0055203583d576ec1dec5a22f25ddf9a53b8ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarc=C3=ADsio=20Eduardo=20Moreira=20Crocomo?= Date: Thu, 4 Jul 2024 18:00:47 -0300 Subject: [PATCH 055/361] build: bump libinput version required --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index eb66506ab..c602d008b 100644 --- a/meson.build +++ b/meson.build @@ -72,7 +72,7 @@ pangocairo = dependency('pangocairo') gdk_pixbuf = dependency('gdk-pixbuf-2.0', required: get_option('gdk-pixbuf')) pixman = dependency('pixman-1') libevdev = dependency('libevdev') -libinput = wlroots_features['libinput_backend'] ? dependency('libinput', version: '>=1.21.0') : null_dep +libinput = wlroots_features['libinput_backend'] ? dependency('libinput', version: '>=1.26.0') : null_dep xcb = wlroots_features['xwayland'] ? dependency('xcb') : null_dep drm = dependency('libdrm') libudev = wlroots_features['libinput_backend'] ? dependency('libudev') : null_dep From 28fd73589df0e73e1d15e165acd90651a5f805d6 Mon Sep 17 00:00:00 2001 From: Ronan Pigott Date: Wed, 28 Feb 2024 14:22:09 -0700 Subject: [PATCH 056/361] xdg-activation: launcher tokens are activation requests If the launched client decides to pass it's token along as an activation request, allow that. This will make the behavior match tokens provided by an external launcher client. --- sway/xdg_activation_v1.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sway/xdg_activation_v1.c b/sway/xdg_activation_v1.c index b7c80dd45..fd6048746 100644 --- a/sway/xdg_activation_v1.c +++ b/sway/xdg_activation_v1.c @@ -38,14 +38,14 @@ void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, } // This is an activation request. If this context is internal we have ctx->seat. - struct sway_seat *seat = ctx->seat; - if (!seat) { - // Otherwise, use the seat indicated by the launcher client in set_serial - seat = ctx->token->seat ? ctx->token->seat->data : NULL; + if (ctx->seat) { + view_request_activate(view, ctx->seat); + return; } - if (seat && ctx->had_focused_surface) { - view_request_activate(view, seat); + // Otherwise, activate if passed from another focused client + if (ctx->token->seat && ctx->had_focused_surface) { + view_request_activate(view, ctx->token->seat->data); } else { // The token is valid, but cannot be used to activate a window view_request_urgent(view); From 8c5b23e592d2334b3324227dd9d1311e46c5fd69 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 11 Jul 2024 00:33:19 +0200 Subject: [PATCH 057/361] common/pango: Disable glyph position rounding Pango rounds glyph position and widths to nearest integer, which leads to font dimensions jumping around when rendering with a scale, causing text geometry to jump around when changing scale. This is disturbing when text buffers change scale, and also mean that the text geometry calculations in sway_text_node are incorrect. Disable this rounding to make the geometry stable. --- common/pango.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/pango.c b/common/pango.c index 288569b30..e52b52b93 100644 --- a/common/pango.c +++ b/common/pango.c @@ -53,6 +53,8 @@ size_t escape_markup_text(const char *src, char *dest) { PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc, const char *text, double scale, bool markup) { PangoLayout *layout = pango_cairo_create_layout(cairo); + pango_context_set_round_glyph_positions(pango_layout_get_context(layout), false); + PangoAttrList *attrs; if (markup) { char *buf; @@ -104,6 +106,7 @@ void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, void get_text_metrics(const PangoFontDescription *description, int *height, int *baseline) { cairo_t *cairo = cairo_create(NULL); PangoContext *pango = pango_cairo_create_context(cairo); + pango_context_set_round_glyph_positions(pango, false); // When passing NULL as a language, pango uses the current locale. PangoFontMetrics *metrics = pango_context_get_metrics(pango, description, NULL); From 274a5fcb73b1252e05543669c956a370516f052c Mon Sep 17 00:00:00 2001 From: Bill Li Date: Sat, 13 Jul 2024 15:09:07 +0800 Subject: [PATCH 058/361] build: Bump wlroots version --- meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index c602d008b..c5595a86a 100644 --- a/meson.build +++ b/meson.build @@ -38,14 +38,14 @@ if is_freebsd endif # Execute the wlroots subproject, if any -wlroots_version = ['>=0.18.0', '<0.19.0'] +wlroots_version = ['>=0.19.0', '<0.20.0'] subproject( 'wlroots', default_options: ['examples=false'], required: false, version: wlroots_version, ) -wlroots = dependency('wlroots-0.18', version: wlroots_version, fallback: 'wlroots') +wlroots = dependency('wlroots-0.19', version: wlroots_version, fallback: 'wlroots') wlroots_features = { 'xwayland': false, 'libinput_backend': false, From fc2796aee8e169f0d1d8ddcb4db2a0ee7cc7421b Mon Sep 17 00:00:00 2001 From: Bill Li Date: Sun, 14 Jul 2024 16:24:14 +0800 Subject: [PATCH 059/361] Chase wlroots!2434 References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/2434 --- sway/desktop/xwayland.c | 4 ++-- sway/input/cursor.c | 6 +++--- sway/input/seatop_default.c | 6 +++--- sway/input/tablet.c | 2 +- sway/tree/view.c | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 270cf08f8..0d45543a7 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -71,7 +71,7 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) { surface->set_geometry.notify = unmanaged_handle_set_geometry; } - if (wlr_xwayland_or_surface_wants_focus(xsurface)) { + if (wlr_xwayland_surface_override_redirect_wants_focus(xsurface)) { struct sway_seat *seat = input_manager_current_seat(); struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; wlr_xwayland_set_seat(xwayland, seat->wlr_seat); @@ -96,7 +96,7 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { // This simply returns focus to the parent surface if there's one available. // This seems to handle JetBrains issues. if (xsurface->parent && xsurface->parent->surface - && wlr_xwayland_or_surface_wants_focus(xsurface->parent)) { + && wlr_xwayland_surface_override_redirect_wants_focus(xsurface->parent)) { seat_set_focus_surface(seat, xsurface->parent->surface, false); return; } diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 235951d4c..bbd16717f 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -578,7 +578,7 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor, // tablet events until the drag is released, even if we are now over a // non-tablet surface. if (!cursor->simulating_pointer_from_tool_tip && - ((surface && wlr_surface_accepts_tablet_v2(tablet->tablet_v2, surface)) || + ((surface && wlr_surface_accepts_tablet_v2(surface, tablet->tablet_v2)) || wlr_tablet_tool_v2_has_implicit_grab(tool->tablet_v2_tool))) { seatop_tablet_tool_motion(seat, tool, time_msec); } else { @@ -664,7 +664,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); - } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { + } else if (!surface || !wlr_surface_accepts_tablet_v2(surface, tablet_v2)) { // If we started holding the tool tip down on a surface that accepts // tablet v2, we should notify that surface if it gets released over a // surface that doesn't support v2. @@ -749,7 +749,7 @@ static void handle_tool_button(struct wl_listener *listener, void *data) { bool mod_pressed = modifiers & config->floating_mod; bool surface_supports_tablet_events = - surface && wlr_surface_accepts_tablet_v2(tablet_v2, surface); + surface && wlr_surface_accepts_tablet_v2(surface, tablet_v2); // Simulate pointer when: // 1. The modifier key is pressed, OR diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index f4a0f4634..42ce333b8 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -273,7 +273,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, // Handle tapping on an xwayland unmanaged view else if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && xsurface->override_redirect && - wlr_xwayland_or_surface_wants_focus(xsurface)) { + wlr_xwayland_surface_override_redirect_wants_focus(xsurface)) { struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; wlr_xwayland_set_seat(xwayland, seat->wlr_seat); seat_set_focus_surface(seat, xsurface->surface, false); @@ -521,7 +521,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, if (surface && (xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && xsurface->override_redirect && - wlr_xwayland_or_surface_wants_focus(xsurface)) { + wlr_xwayland_surface_override_redirect_wants_focus(xsurface)) { struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; wlr_xwayland_set_seat(xwayland, seat->wlr_seat); seat_set_focus_surface(seat, xsurface->surface, false); @@ -667,7 +667,7 @@ static void handle_touch_down(struct sway_seat *seat, double sx, sy; node_at_coords(seat, seat->touch_x, seat->touch_y, &surface, &sx, &sy); - if (surface && wlr_surface_accepts_touch(wlr_seat, surface)) { + if (surface && wlr_surface_accepts_touch(surface, wlr_seat)) { if (seat_is_input_allowed(seat, surface)) { cursor->simulating_pointer_from_touch = false; seatop_begin_touch_down(seat, surface, event, sx, sy, lx, ly); diff --git a/sway/input/tablet.c b/sway/input/tablet.c index ec1e4f682..19d5debf9 100644 --- a/sway/input/tablet.c +++ b/sway/input/tablet.c @@ -363,7 +363,7 @@ void sway_tablet_pad_set_focus(struct sway_tablet_pad *tablet_pad, } if (surface == NULL || - !wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) { + !wlr_surface_accepts_tablet_v2(surface, tablet_pad->tablet->tablet_v2)) { return; } diff --git a/sway/tree/view.c b/sway/tree/view.c index 4f757acf1..229d765ea 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -850,7 +850,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, #if WLR_HAS_XWAYLAND struct wlr_xwayland_surface *xsurface; if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { - set_focus &= wlr_xwayland_icccm_input_model(xsurface) != + set_focus &= wlr_xwayland_surface_icccm_input_model(xsurface) != WLR_ICCCM_INPUT_MODEL_NONE; } #endif From 50073dc579fffffda8a4de903719b9bbb9d5ac3d Mon Sep 17 00:00:00 2001 From: Bill Li Date: Mon, 15 Jul 2024 05:16:40 +0800 Subject: [PATCH 060/361] ci: use package x11-servers/xwayland instead of x11-servers/xwayland-devel --- .builds/freebsd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 8084574c2..977fe467e 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -27,7 +27,7 @@ packages: - x11/libX11 - x11/pixman - x11/xcb-util-wm -- x11-servers/xwayland-devel +- x11-servers/xwayland - misc/hwdata sources: - https://github.com/swaywm/sway From a3a9ec1211fcf857aa2e047f9a1c1388d17194c3 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 14 Jul 2024 23:22:54 +0200 Subject: [PATCH 061/361] build: use fs.relative_to() instead of hand-rolled logic Meson has introduced a relative_to() function [1] in its fs module since version 1.3. [1]: https://mesonbuild.com/Fs-module.html#relative_to --- meson.build | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/meson.build b/meson.build index c5595a86a..0d5b0cc6c 100644 --- a/meson.build +++ b/meson.build @@ -3,7 +3,7 @@ project( 'c', version: '1.10-dev', license: 'MIT', - meson_version: '>=0.60.0', + meson_version: '>=1.3', default_options: [ 'c_std=c11', 'warning_level=2', @@ -172,31 +172,10 @@ if git.found() endif add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') -# Compute the relative path used by compiler invocations. -source_root = meson.current_source_dir().split('/') -build_root = meson.global_build_root().split('/') -relative_dir_parts = [] -i = 0 -in_prefix = true -foreach p : build_root - if i >= source_root.length() or not in_prefix or p != source_root[i] - in_prefix = false - relative_dir_parts += '..' - endif - i += 1 -endforeach -i = 0 -in_prefix = true -foreach p : source_root - if i >= build_root.length() or not in_prefix or build_root[i] != p - in_prefix = false - relative_dir_parts += p - endif - i += 1 -endforeach -relative_dir = join_paths(relative_dir_parts) + '/' +fs = import('fs') # Strip relative path prefixes from the code if possible, otherwise hide them. +relative_dir = fs.relative_to(meson.current_source_dir(), meson.global_build_root()) + '/' if cc.has_argument('-fmacro-prefix-map=/prefix/to/hide=') add_project_arguments( '-fmacro-prefix-map=@0@='.format(relative_dir), From 3f327b3db0c1fc6985c0ed3231e1bd6296584dad Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 15 Jul 2024 00:12:39 +0200 Subject: [PATCH 062/361] desktop/output: Stop repaint loop when not needed 1e0031781fc9 refactored repaint to accumulate all changes in a single wlr_output_state and commit them at the end of the repaint loop, replacing a call to wlr_scene_output_commit. wlr_scene_output_commit contains an early bail-out when no frame has been requested and no damage has accumulated, which was not replicated as part of this refactor, causing the repaint loop to never pause. Replicate the logic to stop the repaint loop as needed. Fixes: 1e0031781fc9 ("desktop/output: unify page-flip codepath") --- sway/desktop/output.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index f936b2a8d..27ede68ee 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -247,6 +247,13 @@ static int output_repaint_timer_handler(void *data) { .color_transform = output->color_transform, }; + struct wlr_output *wlr_output = output->wlr_output; + struct wlr_scene_output *scene_output = output->scene_output; + if (!wlr_output->needs_frame && !output->gamma_lut_changed && + !pixman_region32_not_empty(&scene_output->pending_commit_damage)) { + return 0; + } + struct wlr_output_state pending; wlr_output_state_init(&pending); if (!wlr_scene_output_build_state(output->scene_output, &pending, &opts)) { From 4d4c88f0a73f6ee3da1c99355f04362ef2ad68c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joan=20Bruguera=20Mic=C3=B3?= Date: Sat, 20 Jul 2024 22:34:01 +0000 Subject: [PATCH 063/361] layer-shell: Restore interactive layer focus code Commit 188811f80861 ("scene_graph: Port layer_shell") accidentally removed code in `arrange_layers` to handle focus on layer shell surfaces with keyboard interactivity. Due to this, layer shell surfaces requesting exclusive keyboard interactivity may not get automatically focused, and layer shell surfaces giving up exclusive keyboard interactivity can remain focused. Add the previous code back to fix the problem. Note the non-rename change included in b4d7e84d3852 ("desktop: Rename layers to shell_layers") is not included as it also seems accidental. Fixes: #7936 --- sway/desktop/layer_shell.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 6221b7b97..b136a24e7 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -90,6 +90,43 @@ void arrange_layers(struct sway_output *output) { } else { arrange_popups(root->layers.popup); } + + // Find topmost keyboard interactive layer, if such a layer exists + struct wlr_scene_tree *layers_above_shell[] = { + output->layers.shell_overlay, + output->layers.shell_top, + }; + size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]); + struct wlr_scene_node *node; + struct sway_layer_surface *topmost = NULL; + for (size_t i = 0; i < nlayers; ++i) { + wl_list_for_each_reverse(node, + &layers_above_shell[i]->children, link) { + struct sway_layer_surface *surface = scene_descriptor_try_get(node, + SWAY_SCENE_DESC_LAYER_SHELL); + if (surface && surface->layer_surface->current.keyboard_interactive + == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE && + surface->layer_surface->surface->mapped) { + topmost = surface; + break; + } + } + if (topmost != NULL) { + break; + } + } + + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + seat->has_exclusive_layer = false; + if (topmost != NULL) { + seat_set_focus_layer(seat, topmost->layer_surface); + } else if (seat->focused_layer && + seat->focused_layer->current.keyboard_interactive + != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) { + seat_set_focus_layer(seat, NULL); + } + } } static struct wlr_scene_tree *sway_layer_get_scene(struct sway_output *output, From 7e74a4914261cf32c45017521960adf7ff6dac8f Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 29 Jul 2024 20:14:18 +0200 Subject: [PATCH 064/361] desktop/xwayland: don't restack when marking window as inactive daaec72ac01f ("desktop/xwayland: restack surface upon activation") has updated Sway for wlroots commit bfc69decdd04 ("xwm: do not restack surfaces on activation"). However, it unconditionally restacks the window above all other windows even if marking the window as inactive. Closes: https://github.com/swaywm/sway/issues/7974 --- sway/desktop/xwayland.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 0d45543a7..e4d54ca6c 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -289,7 +289,9 @@ static void set_activated(struct sway_view *view, bool activated) { } wlr_xwayland_surface_activate(surface, activated); - wlr_xwayland_surface_restack(surface, NULL, XCB_STACK_MODE_ABOVE); + if (activated) { + wlr_xwayland_surface_restack(surface, NULL, XCB_STACK_MODE_ABOVE); + } } static void set_tiled(struct sway_view *view, bool tiled) { From 9bb45a403758c8606fe9a7f0b5b5316bae1a12dd Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 2 Aug 2024 17:49:44 +0300 Subject: [PATCH 065/361] xwayland: chase wlr_xwayland_surface_set_maximized() change See https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4670. --- sway/desktop/xwayland.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index e4d54ca6c..805c9b9d5 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -299,7 +299,7 @@ static void set_tiled(struct sway_view *view, bool tiled) { return; } struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; - wlr_xwayland_surface_set_maximized(surface, tiled); + wlr_xwayland_surface_set_maximized(surface, tiled, tiled); } static void set_fullscreen(struct sway_view *view, bool fullscreen) { From 6e4ccb99c3a2197468f8f34c290b7cd5612ff80b Mon Sep 17 00:00:00 2001 From: James Knight Date: Sat, 3 Aug 2024 12:30:17 -0400 Subject: [PATCH 066/361] build: avoid git repository discovery when determining version When attempting to use Git to populate commit/branch information in a version string, it is possible through repository discovery that it uses Git information not relevant to project. For example, if repository content is extract into an interim build location when using an embedded build framework (e.g. Buildroot), the project will not have its Git repository to refer to. When it cannot find its repository, it will look into its parent folders and may find the Git repository of another project and use its branch/commit information. This commit provides an explicit path to the project's Git repository when consider commit/branch information. This will prevent any repository discovery from occurring. Signed-off-by: James Knight --- meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 0d5b0cc6c..71e75fd9b 100644 --- a/meson.build +++ b/meson.build @@ -160,8 +160,8 @@ add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir version = '"@0@"'.format(meson.project_version()) git = find_program('git', native: true, required: false) if git.found() - git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false) - git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false) + git_commit = run_command([git, '--git-dir=.git', 'rev-parse', '--short', 'HEAD'], check: false) + git_branch = run_command([git, '--git-dir=.git', 'rev-parse', '--abbrev-ref', 'HEAD'], check: false) if git_commit.returncode() == 0 and git_branch.returncode() == 0 version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format( meson.project_version(), From b881c2e84c4be3c7b996f85200cfe391a7979267 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 10 Jul 2024 12:20:53 -0400 Subject: [PATCH 067/361] transaction: Reparent all container children when disabling for scratchpad Fixes: #8205 --- sway/desktop/transaction.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 7568990bf..2ee5a5dff 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -632,6 +632,15 @@ static void arrange_root(struct sway_root *root) { for (int i = 0; i < root->scratchpad->length; i++) { struct sway_container *con = root->scratchpad->items[i]; + // When a container is moved to a scratchpad, it's possible that it + // was moved into a floating container as part of the same transaction. + // In this case, we need to make sure we reparent all the container's + // children so that disabling the container will disable all descendants. + if (!con->view) for (int ii = 0; ii < con->current.children->length; ii++) { + struct sway_container *child = con->current.children->items[ii]; + wlr_scene_node_reparent(&child->scene_tree->node, con->content_tree); + } + wlr_scene_node_set_enabled(&con->scene_tree->node, false); } From 9a1c411abd8261c121dcd50dfe54132718768084 Mon Sep 17 00:00:00 2001 From: Ricardo Steijn <61013287+RicArch97@users.noreply.github.com> Date: Mon, 5 Aug 2024 02:13:49 +0200 Subject: [PATCH 068/361] Add support for tearing-control-v1 References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3871 Adds option to allow tearing per output, as well as an option to force enable or disable tearing for a specific application using a window rule. Only works with fullscreen applications. --- include/sway/commands.h | 2 + include/sway/config.h | 1 + include/sway/output.h | 1 + include/sway/server.h | 6 +++ include/sway/tree/view.h | 12 ++++++ protocols/meson.build | 1 + sway/commands.c | 1 + sway/commands/allow_tearing.c | 24 +++++++++++ sway/commands/output.c | 1 + sway/commands/output/allow_tearing.c | 23 +++++++++++ sway/config/output.c | 16 ++++++- sway/desktop/output.c | 28 +++++++++++++ sway/desktop/tearing.c | 62 ++++++++++++++++++++++++++++ sway/ipc-json.c | 4 ++ sway/meson.build | 3 ++ sway/server.c | 7 ++++ sway/sway-output.5.scd | 20 +++++++++ sway/sway.5.scd | 14 +++++++ sway/tree/view.c | 14 +++++++ swaymsg/main.c | 6 ++- 20 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 sway/commands/allow_tearing.c create mode 100644 sway/commands/output/allow_tearing.c create mode 100644 sway/desktop/tearing.c diff --git a/include/sway/commands.h b/include/sway/commands.h index 15cd86982..5210d3ba7 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -104,6 +104,7 @@ struct sway_container *container_find_resize_parent(struct sway_container *con, sway_cmd cmd_exec_validate; sway_cmd cmd_exec_process; +sway_cmd cmd_allow_tearing; sway_cmd cmd_assign; sway_cmd cmd_bar; sway_cmd cmd_bindcode; @@ -283,6 +284,7 @@ sway_cmd input_cmd_xkb_switch_layout; sway_cmd input_cmd_xkb_variant; sway_cmd output_cmd_adaptive_sync; +sway_cmd output_cmd_allow_tearing; sway_cmd output_cmd_background; sway_cmd output_cmd_color_profile; sway_cmd output_cmd_disable; diff --git a/include/sway/config.h b/include/sway/config.h index dfa3c1b7b..d9f561571 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -289,6 +289,7 @@ struct output_config { enum render_bit_depth render_bit_depth; bool set_color_transform; struct wlr_color_transform *color_transform; + int allow_tearing; char *background; char *background_option; diff --git a/include/sway/output.h b/include/sway/output.h index 2189c6e87..7e2d58927 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -73,6 +73,7 @@ struct sway_output { int max_render_time; // In milliseconds struct wl_event_source *repaint_timer; bool gamma_lut_changed; + bool allow_tearing; }; struct sway_output_non_desktop { diff --git a/include/sway/server.h b/include/sway/server.h index abf1b6b4e..460f9e17f 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -115,6 +115,10 @@ struct sway_server { struct wl_listener xdg_activation_v1_new_token; struct wl_listener request_set_cursor_shape; + + struct wlr_tearing_control_manager_v1 *tearing_control_v1; + struct wl_listener tearing_control_new_object; + struct wl_list tearing_controllers; // sway_tearing_controller::link struct wl_list pending_launcher_ctxs; // launcher_ctx::link @@ -182,4 +186,6 @@ void xdg_activation_v1_handle_new_token(struct wl_listener *listener, void set_rr_scheduling(void); +void handle_new_tearing_hint(struct wl_listener *listener, void *data); + #endif diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 3ae8cf224..14aad1a18 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "sway/config.h" #if WLR_HAS_XWAYLAND #include @@ -34,6 +35,12 @@ enum sway_view_prop { #endif }; +enum sway_view_tearing_mode { + TEARING_OVERRIDE_FALSE, + TEARING_OVERRIDE_TRUE, + TEARING_WINDOW_HINT, +}; + struct sway_view_impl { void (*get_constraints)(struct sway_view *view, double *min_width, double *max_width, double *min_height, double *max_height); @@ -111,6 +118,9 @@ struct sway_view { int max_render_time; // In milliseconds enum seat_config_shortcuts_inhibit shortcuts_inhibit; + + enum sway_view_tearing_mode tearing_mode; + enum wp_tearing_control_v1_presentation_hint tearing_hint; }; struct sway_xdg_shell_view { @@ -335,4 +345,6 @@ void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx); void view_send_frame_done(struct sway_view *view); +bool view_can_tear(struct sway_view *view); + #endif diff --git a/protocols/meson.build b/protocols/meson.build index 6eac8542c..d96f87575 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -14,6 +14,7 @@ protocols = [ wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', wl_protocol_dir / 'staging/content-type/content-type-v1.xml', wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', + wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml', 'wlr-layer-shell-unstable-v1.xml', 'idle.xml', 'wlr-output-power-management-unstable-v1.xml', diff --git a/sway/commands.c b/sway/commands.c index 8d003dfa6..c2c12ee65 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -112,6 +112,7 @@ static const struct cmd_handler config_handlers[] = { /* Runtime-only commands. Keep alphabetized */ static const struct cmd_handler command_handlers[] = { + { "allow_tearing", cmd_allow_tearing }, { "border", cmd_border }, { "create_output", cmd_create_output }, { "exit", cmd_exit }, diff --git a/sway/commands/allow_tearing.c b/sway/commands/allow_tearing.c new file mode 100644 index 000000000..ee5941381 --- /dev/null +++ b/sway/commands/allow_tearing.c @@ -0,0 +1,24 @@ +#include +#include "sway/config.h" +#include "sway/tree/view.h" +#include "util.h" + +struct cmd_results *cmd_allow_tearing(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "allow_tearing", EXPECTED_AT_LEAST, 1))) { + return error; + } + + struct sway_container *container = config->handler_context.container; + if (!container || !container->view) { + return cmd_results_new(CMD_INVALID, "Tearing can only be allowed on views"); + } + + bool wants_tearing = parse_boolean(argv[0], true); + + struct sway_view *view = container->view; + view->tearing_mode = wants_tearing ? TEARING_OVERRIDE_TRUE : + TEARING_OVERRIDE_FALSE; + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/output.c b/sway/commands/output.c index b822e770a..9478e0bad 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -8,6 +8,7 @@ // must be in order for the bsearch static const struct cmd_handler output_handlers[] = { { "adaptive_sync", output_cmd_adaptive_sync }, + { "allow_tearing", output_cmd_allow_tearing }, { "background", output_cmd_background }, { "bg", output_cmd_background }, { "color_profile", output_cmd_color_profile }, diff --git a/sway/commands/output/allow_tearing.c b/sway/commands/output/allow_tearing.c new file mode 100644 index 000000000..9a183b96c --- /dev/null +++ b/sway/commands/output/allow_tearing.c @@ -0,0 +1,23 @@ +#include "sway/commands.h" +#include "sway/config.h" +#include "util.h" + +struct cmd_results *output_cmd_allow_tearing(int argc, char **argv) { + if (!config->handler_context.output_config) { + return cmd_results_new(CMD_FAILURE, "Missing output config"); + } + if (argc == 0) { + return cmd_results_new(CMD_INVALID, "Missing allow_tearing argument"); + } + + if (parse_boolean(argv[0], + (config->handler_context.output_config->allow_tearing == 1))) { + config->handler_context.output_config->allow_tearing = 1; + } else { + config->handler_context.output_config->allow_tearing = 0; + } + + config->handler_context.leftovers.argc = argc - 1; + config->handler_context.leftovers.argv = argv + 1; + return NULL; +} diff --git a/sway/config/output.c b/sway/config/output.c index e64efb7ff..940c75fe3 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -79,6 +79,7 @@ struct output_config *new_output_config(const char *name) { oc->set_color_transform = false; oc->color_transform = NULL; oc->power = -1; + oc->allow_tearing = -1; return oc; } @@ -216,6 +217,9 @@ static void merge_output_config(struct output_config *dst, struct output_config if (src->power != -1) { dst->power = src->power; } + if (src->allow_tearing != -1) { + dst->allow_tearing = src->allow_tearing; + } } void store_output_config(struct output_config *oc) { @@ -258,11 +262,11 @@ void 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 subpixel %s transform %d) (bg %s %s) (power %d) " - "(max render time: %d)", + "(max render time: %d) (allow tearing: %d)", oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), oc->transform, oc->background, oc->background_option, oc->power, - oc->max_render_time); + oc->max_render_time, oc->allow_tearing); // If the configuration was not merged into an existing configuration, add // it to the list. Otherwise we're done with it and can free it. @@ -574,6 +578,13 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output wlr_color_transform_unref(output->color_transform); output->color_transform = oc->color_transform; } + + if (oc && oc->allow_tearing >= 0) { + sway_log(SWAY_DEBUG, "Set %s allow tearing to %d", + oc->name, oc->allow_tearing); + output->allow_tearing = oc->allow_tearing; + } + return true; } @@ -594,6 +605,7 @@ static void default_output_config(struct output_config *oc, oc->subpixel = output->detected_subpixel; oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; oc->max_render_time = 0; + oc->allow_tearing = 0; } // find_output_config returns a merged output_config containing all stored diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 27ede68ee..f1e08eff8 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -232,6 +232,23 @@ static void output_configure_scene(struct sway_output *output, } } +static bool output_can_tear(struct sway_output *output) { + struct sway_workspace *workspace = output->current.active_workspace; + if (!workspace) { + return false; + } + + struct sway_container *fullscreen_con = root->fullscreen_global; + if (!fullscreen_con) { + fullscreen_con = workspace->current.fullscreen; + } + if (fullscreen_con && fullscreen_con->view) { + return (output->allow_tearing && view_can_tear(fullscreen_con->view)); + } + + return false; +} + static int output_repaint_timer_handler(void *data) { struct sway_output *output = data; @@ -275,6 +292,17 @@ static int output_repaint_timer_handler(void *data) { wlr_output_state_set_gamma_lut(&pending, 0, NULL, NULL, NULL); } } + + if (output_can_tear(output)) { + pending.tearing_page_flip = true; + + if (!wlr_output_test_state(output->wlr_output, &pending)) { + sway_log(SWAY_DEBUG, "Output test failed on '%s', retrying without tearing page-flip", + output->wlr_output->name); + + pending.tearing_page_flip = false; + } + } if (!wlr_output_commit_state(output->wlr_output, &pending)) { sway_log(SWAY_ERROR, "Page-flip failed on output %s", output->wlr_output->name); diff --git a/sway/desktop/tearing.c b/sway/desktop/tearing.c new file mode 100644 index 000000000..36cc48bfe --- /dev/null +++ b/sway/desktop/tearing.c @@ -0,0 +1,62 @@ +#include +#include +#include "sway/server.h" +#include "sway/tree/view.h" +#include "sway/output.h" +#include "log.h" + +struct sway_tearing_controller { + struct wlr_tearing_control_v1 *tearing_control; + struct wl_listener set_hint; + struct wl_listener destroy; + + struct wl_list link; // sway_server::tearing_controllers +}; + +static void handle_tearing_controller_set_hint(struct wl_listener *listener, + void *data) { + struct sway_tearing_controller *controller = + wl_container_of(listener, controller, set_hint); + + struct sway_view *view = view_from_wlr_surface( + controller->tearing_control->surface); + if (view) { + view->tearing_hint = controller->tearing_control->current; + } +} + +static void handle_tearing_controller_destroy(struct wl_listener *listener, + void *data) { + struct sway_tearing_controller *controller = + wl_container_of(listener, controller, destroy); + wl_list_remove(&controller->link); + free(controller); +} + +void handle_new_tearing_hint(struct wl_listener *listener, + void *data) { + struct sway_server *server = + wl_container_of(listener, server, tearing_control_new_object); + struct wlr_tearing_control_v1 *tearing_control = data; + + enum wp_tearing_control_v1_presentation_hint hint = + wlr_tearing_control_manager_v1_surface_hint_from_surface( + server->tearing_control_v1, tearing_control->surface); + sway_log(SWAY_DEBUG, "New presentation hint %d received for surface %p", + hint, tearing_control->surface); + + struct sway_tearing_controller *controller = + calloc(1, sizeof(struct sway_tearing_controller)); + if (!controller) { + return; + } + + controller->tearing_control = tearing_control; + controller->set_hint.notify = handle_tearing_controller_set_hint; + wl_signal_add(&tearing_control->events.set_hint, &controller->set_hint); + controller->destroy.notify = handle_tearing_controller_destroy; + wl_signal_add(&tearing_control->events.destroy, &controller->destroy); + wl_list_init(&controller->link); + + wl_list_insert(&server->tearing_controllers, &controller->link); +} diff --git a/sway/ipc-json.c b/sway/ipc-json.c index e512a2239..8eaa8324a 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -399,6 +399,8 @@ static void ipc_json_describe_enabled_output(struct sway_output *output, } json_object_object_add(object, "max_render_time", json_object_new_int(output->max_render_time)); + + json_object_object_add(object, "allow_tearing", json_object_new_boolean(output->allow_tearing)); } json_object *ipc_json_describe_disabled_output(struct sway_output *output) { @@ -593,6 +595,8 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(object, "max_render_time", json_object_new_int(c->view->max_render_time)); + json_object_object_add(object, "allow_tearing", json_object_new_boolean(view_can_tear(c->view))); + json_object_object_add(object, "shell", json_object_new_string(view_get_shell(c->view))); json_object_object_add(object, "inhibit_idle", diff --git a/sway/meson.build b/sway/meson.build index 2f4406abb..8042c89be 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -18,6 +18,7 @@ sway_sources = files( 'desktop/idle_inhibit_v1.c', 'desktop/layer_shell.c', 'desktop/output.c', + 'desktop/tearing.c', 'desktop/transaction.c', 'desktop/xdg_shell.c', 'desktop/launcher.c', @@ -41,6 +42,7 @@ sway_sources = files( 'config/seat.c', 'config/input.c', + 'commands/allow_tearing.c', 'commands/assign.c', 'commands/bar.c', 'commands/bind.c', @@ -188,6 +190,7 @@ sway_sources = files( 'commands/input/xkb_variant.c', 'commands/output/adaptive_sync.c', + 'commands/output/allow_tearing.c', 'commands/output/background.c', 'commands/output/disable.c', 'commands/output/dpms.c', diff --git a/sway/server.c b/sway/server.c index edbc1a4b1..bb895377b 100644 --- a/sway/server.c +++ b/sway/server.c @@ -371,6 +371,13 @@ bool server_init(struct sway_server *server) { server->content_type_manager_v1 = wlr_content_type_manager_v1_create(server->wl_display, 1); wlr_fractional_scale_manager_v1_create(server->wl_display, 1); + + server->tearing_control_v1 = + wlr_tearing_control_manager_v1_create(server->wl_display, 1); + server->tearing_control_new_object.notify = handle_new_tearing_hint; + wl_signal_add(&server->tearing_control_v1->events.new_object, + &server->tearing_control_new_object); + wl_list_init(&server->tearing_controllers); struct wlr_xdg_foreign_registry *foreign_registry = wlr_xdg_foreign_registry_create(server->wl_display); diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index 6d7c08604..d9a28807a 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -190,6 +190,26 @@ must be separated by one space. For example: may have no effect or produce unexpected output when used together with future HDR support features. +*output* allow_tearing yes|no + Allows or disallows screen tearing as a result of immediate page flips, + and an immediate presentation mode from a client. The default is that no + screen tearing is allowed. + + With immediate page flips, frames from the client are presented as soon + as possible instead of synchronizing with the monitor's vblank interval + (VSync). + + It is recommended to set *max_render_time* to *off*. In that case a page flip + happens as soon as a client updates. Otherwise, tearing will only happen if + rendering takes longer than the configured milliseconds before the next + display refresh. + + To adjust whether tearing is allowed for specific applications, see + *allow_tearing* in *sway*(5). Note that tearing will only be enabled + when it's allowed for both the output and the application. + + This setting only has effect when a window is fullscreen on the output. + # SEE ALSO *sway*(5) *sway-input*(5) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 9f8239473..0b36a7572 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -215,6 +215,20 @@ set|plus|minus|toggle effect on the output the window is currently on. See *sway-output*(5) for further details. +*allow_tearing* yes|no + Allows or disallows screen tearing as a result of immediate page flips + for a fullscreen application. + + When this option is not set, the tearing hints provided by the application + determine whether tearing is allowed. When _yes_ is specified, + the application allows tearing regardless of the tearing hints. + When _no_ is specified, tearing will never be allowed on the application, + regardless of the tearing hints. + + This setting only has an effect if tearing is allowed on the output through + the per-output *allow_tearing* setting. See *sway-output*(5) + for further details. + *move* left|right|up|down [ px] Moves the focused container in the direction specified. The optional _px_ argument specifies how many pixels to move the container. If unspecified, diff --git a/sway/tree/view.c b/sway/tree/view.c index 229d765ea..423473879 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -58,6 +58,7 @@ bool view_init(struct sway_view *view, enum sway_view_type type, view->executed_criteria = create_list(); view->allow_request_urgent = true; view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; + view->tearing_mode = TEARING_WINDOW_HINT; wl_signal_init(&view->events.unmap); return true; } @@ -1260,6 +1261,19 @@ bool view_is_transient_for(struct sway_view *child, child->impl->is_transient_for(child, ancestor); } +bool view_can_tear(struct sway_view *view) { + switch (view->tearing_mode) { + case TEARING_OVERRIDE_FALSE: + return false; + case TEARING_OVERRIDE_TRUE: + return true; + case TEARING_WINDOW_HINT: + return view->tearing_hint == + WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC; + } + return false; +} + static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer, int x, int y, void *data) { struct timespec *when = data; diff --git a/swaymsg/main.c b/swaymsg/main.c index 573a7b166..5c57171e7 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -193,7 +193,7 @@ static void pretty_print_output(json_object *o) { json_object_object_get_ex(o, "current_workspace", &ws); json_object_object_get_ex(o, "non_desktop", &non_desktop); json_object *make, *model, *serial, *scale, *scale_filter, *subpixel, - *transform, *max_render_time, *adaptive_sync_status; + *transform, *max_render_time, *adaptive_sync_status, *allow_tearing; json_object_object_get_ex(o, "make", &make); json_object_object_get_ex(o, "model", &model); json_object_object_get_ex(o, "serial", &serial); @@ -203,6 +203,7 @@ static void pretty_print_output(json_object *o) { json_object_object_get_ex(o, "transform", &transform); json_object_object_get_ex(o, "max_render_time", &max_render_time); json_object_object_get_ex(o, "adaptive_sync_status", &adaptive_sync_status); + json_object_object_get_ex(o, "allow_tearing", &allow_tearing); json_object *x, *y; json_object_object_get_ex(rect, "x", &x); json_object_object_get_ex(rect, "y", &y); @@ -256,6 +257,9 @@ static void pretty_print_output(json_object *o) { printf(" Adaptive sync: %s\n", json_object_get_string(adaptive_sync_status)); + + printf(" Allow tearing: %s\n", + json_object_get_boolean(allow_tearing) ? "yes" : "no"); } else { printf( "Output %s '%s %s %s' (disabled)\n", From 05e895c4638293a6bfe594ff0cae4eaab63b740e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 10 May 2024 16:11:38 +0200 Subject: [PATCH 069/361] Add support for linux-drm-syncobj-v1 References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4262 --- sway/server.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sway/server.c b/sway/server.c index bb895377b..537febe83 100644 --- a/sway/server.c +++ b/sway/server.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -248,6 +249,11 @@ bool server_init(struct sway_server *server) { wlr_drm_create(server->wl_display, server->renderer); } } + if (wlr_renderer_get_drm_fd(server->renderer) >= 0 && + server->renderer->features.timeline) { + wlr_linux_drm_syncobj_manager_v1_create(server->wl_display, 1, + wlr_renderer_get_drm_fd(server->renderer)); + } server->allocator = wlr_allocator_autocreate(server->backend, server->renderer); From 3e956b922958b182912775497812cd42439b2955 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 7 Aug 2024 15:26:49 +0300 Subject: [PATCH 070/361] tearing: remove trailing whitespace --- sway/config/output.c | 4 ++-- sway/desktop/output.c | 5 ++--- sway/desktop/tearing.c | 16 ++++++++-------- sway/server.c | 4 ++-- sway/tree/view.c | 2 +- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index 940c75fe3..1b378078e 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -578,9 +578,9 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output wlr_color_transform_unref(output->color_transform); output->color_transform = oc->color_transform; } - + if (oc && oc->allow_tearing >= 0) { - sway_log(SWAY_DEBUG, "Set %s allow tearing to %d", + sway_log(SWAY_DEBUG, "Set %s allow tearing to %d", oc->name, oc->allow_tearing); output->allow_tearing = oc->allow_tearing; } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index f1e08eff8..c1aa4483e 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -292,14 +292,13 @@ static int output_repaint_timer_handler(void *data) { wlr_output_state_set_gamma_lut(&pending, 0, NULL, NULL, NULL); } } - + if (output_can_tear(output)) { pending.tearing_page_flip = true; - + if (!wlr_output_test_state(output->wlr_output, &pending)) { sway_log(SWAY_DEBUG, "Output test failed on '%s', retrying without tearing page-flip", output->wlr_output->name); - pending.tearing_page_flip = false; } } diff --git a/sway/desktop/tearing.c b/sway/desktop/tearing.c index 36cc48bfe..b74c74d25 100644 --- a/sway/desktop/tearing.c +++ b/sway/desktop/tearing.c @@ -17,7 +17,7 @@ static void handle_tearing_controller_set_hint(struct wl_listener *listener, void *data) { struct sway_tearing_controller *controller = wl_container_of(listener, controller, set_hint); - + struct sway_view *view = view_from_wlr_surface( controller->tearing_control->surface); if (view) { @@ -25,27 +25,27 @@ static void handle_tearing_controller_set_hint(struct wl_listener *listener, } } -static void handle_tearing_controller_destroy(struct wl_listener *listener, +static void handle_tearing_controller_destroy(struct wl_listener *listener, void *data) { - struct sway_tearing_controller *controller = + struct sway_tearing_controller *controller = wl_container_of(listener, controller, destroy); wl_list_remove(&controller->link); free(controller); } -void handle_new_tearing_hint(struct wl_listener *listener, +void handle_new_tearing_hint(struct wl_listener *listener, void *data) { - struct sway_server *server = + struct sway_server *server = wl_container_of(listener, server, tearing_control_new_object); struct wlr_tearing_control_v1 *tearing_control = data; - - enum wp_tearing_control_v1_presentation_hint hint = + + enum wp_tearing_control_v1_presentation_hint hint = wlr_tearing_control_manager_v1_surface_hint_from_surface( server->tearing_control_v1, tearing_control->surface); sway_log(SWAY_DEBUG, "New presentation hint %d received for surface %p", hint, tearing_control->surface); - struct sway_tearing_controller *controller = + struct sway_tearing_controller *controller = calloc(1, sizeof(struct sway_tearing_controller)); if (!controller) { return; diff --git a/sway/server.c b/sway/server.c index 537febe83..f89bd529b 100644 --- a/sway/server.c +++ b/sway/server.c @@ -377,8 +377,8 @@ bool server_init(struct sway_server *server) { server->content_type_manager_v1 = wlr_content_type_manager_v1_create(server->wl_display, 1); wlr_fractional_scale_manager_v1_create(server->wl_display, 1); - - server->tearing_control_v1 = + + server->tearing_control_v1 = wlr_tearing_control_manager_v1_create(server->wl_display, 1); server->tearing_control_new_object.notify = handle_new_tearing_hint; wl_signal_add(&server->tearing_control_v1->events.new_object, diff --git a/sway/tree/view.c b/sway/tree/view.c index 423473879..d25a09c2a 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -1268,7 +1268,7 @@ bool view_can_tear(struct sway_view *view) { case TEARING_OVERRIDE_TRUE: return true; case TEARING_WINDOW_HINT: - return view->tearing_hint == + return view->tearing_hint == WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC; } return false; From 32e5e5232d1b0b5a34b4296a79a4e8cfa32b5090 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 7 Aug 2024 15:27:02 +0300 Subject: [PATCH 071/361] tearing: fix UAF on destroy Fixes: 9a1c411abd8261c121dcd50dfe54132718768084 --- sway/desktop/tearing.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/desktop/tearing.c b/sway/desktop/tearing.c index b74c74d25..d8d276451 100644 --- a/sway/desktop/tearing.c +++ b/sway/desktop/tearing.c @@ -29,6 +29,8 @@ static void handle_tearing_controller_destroy(struct wl_listener *listener, void *data) { struct sway_tearing_controller *controller = wl_container_of(listener, controller, destroy); + wl_list_remove(&controller->set_hint.link); + wl_list_remove(&controller->destroy.link); wl_list_remove(&controller->link); free(controller); } From 951a22c2445f5c32b831bac0db86869627940402 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 7 Aug 2024 16:52:49 -0400 Subject: [PATCH 072/361] xwayland: Let scene restack --- sway/desktop/xwayland.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 805c9b9d5..b83537a0a 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -289,9 +289,6 @@ static void set_activated(struct sway_view *view, bool activated) { } wlr_xwayland_surface_activate(surface, activated); - if (activated) { - wlr_xwayland_surface_restack(surface, NULL, XCB_STACK_MODE_ABOVE); - } } static void set_tiled(struct sway_view *view, bool tiled) { From f344e9d5a5afe6ba1aeaf781be3bd18dbf8596f1 Mon Sep 17 00:00:00 2001 From: JingMatrix Date: Fri, 9 Aug 2024 00:26:03 +0200 Subject: [PATCH 073/361] Add null-safety check for virtual keyboard keymaps Note that in the `sway_keyboard_configure` function of sway/input/keyboard.c, we have skipped the `sway_keyboard_set_layout` function for virtual keyboards, which then have null keymaps. Hence, a null-safety check is needed at runtime. --- sway/commands/input/xkb_switch_layout.c | 10 +++++++++- sway/ipc-json.c | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/sway/commands/input/xkb_switch_layout.c b/sway/commands/input/xkb_switch_layout.c index 8d600fcad..623dfa186 100644 --- a/sway/commands/input/xkb_switch_layout.c +++ b/sway/commands/input/xkb_switch_layout.c @@ -95,10 +95,18 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { continue; } + struct wlr_keyboard *keyboard = + wlr_keyboard_from_input_device(dev->wlr_device); + if (keyboard->keymap == NULL && dev->is_virtual) { + // The `sway_keyboard_set_layout` function is by default skipped + // when configuring virtual keyboards. + continue; + } + struct xkb_switch_layout_action *action = &actions[actions_len++]; + action->keyboard = keyboard; - action->keyboard = wlr_keyboard_from_input_device(dev->wlr_device); if (relative) { action->layout = get_layout_relative(action->keyboard, relative); } else { diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 8eaa8324a..571338a4f 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -1133,7 +1133,9 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { json_object *layouts_arr = json_object_new_array(); json_object_object_add(object, "xkb_layout_names", layouts_arr); - xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(keymap); + xkb_layout_index_t num_layouts = + keymap ? xkb_keymap_num_layouts(keymap) : 0; + // Virtual keyboards might have null keymap xkb_layout_index_t layout_idx; for (layout_idx = 0; layout_idx < num_layouts; layout_idx++) { const char *layout = xkb_keymap_layout_get_name(keymap, layout_idx); From 9ba1beee580d07adfba903257ce8762b96ea3833 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 29 Feb 2024 01:03:11 +0100 Subject: [PATCH 074/361] Bind a few utilities to special keys in default config --- config.in | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/config.in b/config.in index a5173165b..811365e1a 100644 --- a/config.in +++ b/config.in @@ -195,6 +195,19 @@ mode "resize" { bindsym Escape mode "default" } bindsym $mod+r mode "resize" +# +# Utilities: +# + # Special keys to adjust volume via PulseAudio + bindsym --locked XF86AudioMute exec pactl set-sink-mute \@DEFAULT_SINK@ toggle + bindsym --locked XF86AudioLowerVolume exec pactl set-sink-volume \@DEFAULT_SINK@ -5% + bindsym --locked XF86AudioRaiseVolume exec pactl set-sink-volume \@DEFAULT_SINK@ +5% + bindsym --locked XF86AudioMicMute exec pactl set-source-mute \@DEFAULT_SOURCE@ toggle + # Special keys to adjust brightness via brightnessctl + bindsym --locked XF86MonBrightnessDown exec brightnessctl set 5%- + bindsym --locked XF86MonBrightnessUp exec brightnessctl set 5%+ + # Special key to take a screenshot with grim + bindsym Print exec grim # # Status Bar: From b44015578a3d53cdd9436850202d4405696c1f52 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 11 Aug 2024 19:03:19 +0200 Subject: [PATCH 075/361] Switch default config to wmenu-run This removes the last dependency bit on dmenu. No need for "swaymsg exec" anymore: wmenu-run handles the xdg-activation shenanigans. --- config.in | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/config.in b/config.in index 811365e1a..a2a01dda9 100644 --- a/config.in +++ b/config.in @@ -16,9 +16,7 @@ set $right l # Your preferred terminal emulator set $term foot # Your preferred application launcher -# Note: pass the final command to swaymsg so that the resulting window can be opened -# on the original workspace that the command was run on. -set $menu dmenu_path | wmenu | xargs swaymsg exec -- +set $menu wmenu-run ### Output configuration # From 6576b99c243e2b66d077db0a99ec9683747e3fe9 Mon Sep 17 00:00:00 2001 From: Felix Pehla <74104874+FelixPehla@users.noreply.github.com> Date: Wed, 7 Aug 2024 18:34:59 +0200 Subject: [PATCH 076/361] commands/output/color_profile: allows use of relative path for ICC profile --- sway/commands/output/color_profile.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sway/commands/output/color_profile.c b/sway/commands/output/color_profile.c index 792bd55fb..98481329b 100644 --- a/sway/commands/output/color_profile.c +++ b/sway/commands/output/color_profile.c @@ -5,6 +5,7 @@ #include #include "sway/commands.h" #include "sway/config.h" +#include "stringop.h" static bool read_file_into_buf(const char *path, void **buf, size_t *size) { /* Why not use fopen/fread directly? glibc will succesfully open directories, @@ -70,12 +71,23 @@ struct cmd_results *output_cmd_color_profile(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Invalid color profile specification: icc type requires a file"); } + + char *icc_path = strdup(argv[1]); + if (!expand_path(&icc_path)) { + struct cmd_results *cmd_res = cmd_results_new(CMD_INVALID, + "Invalid color profile specification: invalid file path"); + free(icc_path); + return cmd_res; + } + void *data = NULL; size_t size = 0; - if (!read_file_into_buf(argv[1], &data, &size)) { + if (!read_file_into_buf(icc_path, &data, &size)) { + free(icc_path); return cmd_results_new(CMD_FAILURE, "Failed to load color profile: could not read ICC file"); } + free(icc_path); struct wlr_color_transform *tmp = wlr_color_transform_init_linear_to_icc(data, size); From 5a3621460f193416605ad786e33687952527df21 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 14 Aug 2024 12:52:51 -0400 Subject: [PATCH 077/361] output: Use wlr_scene_output_needs_frame --- sway/desktop/output.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index c1aa4483e..7f5ec5f82 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -266,8 +266,7 @@ static int output_repaint_timer_handler(void *data) { struct wlr_output *wlr_output = output->wlr_output; struct wlr_scene_output *scene_output = output->scene_output; - if (!wlr_output->needs_frame && !output->gamma_lut_changed && - !pixman_region32_not_empty(&scene_output->pending_commit_damage)) { + if (!wlr_scene_output_needs_frame(scene_output)) { return 0; } From c3279944fb195a5169eb540ef8285533dc5edfba Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 14 Aug 2024 13:42:53 -0400 Subject: [PATCH 078/361] output: Use wlr_scene_set_gamma_control_manager_v1 --- include/sway/output.h | 3 --- sway/desktop/output.c | 37 ------------------------------------- sway/server.c | 5 ++--- 3 files changed, 2 insertions(+), 43 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 7e2d58927..1dcabc256 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -72,7 +72,6 @@ struct sway_output { uint32_t refresh_nsec; int max_render_time; // In milliseconds struct wl_event_source *repaint_timer; - bool gamma_lut_changed; bool allow_tearing; }; @@ -138,8 +137,6 @@ enum wlr_direction opposite_direction(enum wlr_direction d); void handle_output_layout_change(struct wl_listener *listener, void *data); -void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data); - void handle_output_manager_apply(struct wl_listener *listener, void *data); void handle_output_manager_test(struct wl_listener *listener, void *data); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 7f5ec5f82..72f753b02 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -264,7 +264,6 @@ static int output_repaint_timer_handler(void *data) { .color_transform = output->color_transform, }; - struct wlr_output *wlr_output = output->wlr_output; struct wlr_scene_output *scene_output = output->scene_output; if (!wlr_scene_output_needs_frame(scene_output)) { return 0; @@ -276,22 +275,6 @@ static int output_repaint_timer_handler(void *data) { return 0; } - if (output->gamma_lut_changed) { - output->gamma_lut_changed = false; - struct wlr_gamma_control_v1 *gamma_control = - wlr_gamma_control_manager_v1_get_control( - server.gamma_control_manager_v1, output->wlr_output); - if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) { - wlr_output_state_finish(&pending); - return 0; - } - - if (!wlr_output_test_state(output->wlr_output, &pending)) { - wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); - wlr_output_state_set_gamma_lut(&pending, 0, NULL, NULL, NULL); - } - } - if (output_can_tear(output)) { pending.tearing_page_flip = true; @@ -472,11 +455,6 @@ static void handle_commit(struct wl_listener *listener, void *data) { update_output_manager_config(output->server); } - - // Next time the output is enabled, try to re-apply the gamma LUT - if ((event->state->committed & WLR_OUTPUT_STATE_ENABLED) && !output->wlr_output->enabled) { - output->gamma_lut_changed = true; - } } static void handle_present(struct wl_listener *listener, void *data) { @@ -585,21 +563,6 @@ void handle_output_layout_change(struct wl_listener *listener, update_output_manager_config(server); } -void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) { - struct sway_server *server = - wl_container_of(listener, server, gamma_control_set_gamma); - const struct wlr_gamma_control_manager_v1_set_gamma_event *event = data; - - struct sway_output *output = event->output->data; - - if(!output) { - return; - } - - output->gamma_lut_changed = true; - wlr_output_schedule_frame(output->wlr_output); -} - static struct output_config *output_config_for_config_head( struct wlr_output_configuration_head_v1 *config_head, struct sway_output *output) { diff --git a/sway/server.c b/sway/server.c index f89bd529b..1ca560869 100644 --- a/sway/server.c +++ b/sway/server.c @@ -272,9 +272,8 @@ bool server_init(struct sway_server *server) { server->gamma_control_manager_v1 = wlr_gamma_control_manager_v1_create(server->wl_display); - server->gamma_control_set_gamma.notify = handle_gamma_control_set_gamma; - wl_signal_add(&server->gamma_control_manager_v1->events.set_gamma, - &server->gamma_control_set_gamma); + wlr_scene_set_gamma_control_manager_v1(root->root_scene, + server->gamma_control_manager_v1); server->new_output.notify = handle_new_output; wl_signal_add(&server->backend->events.new_output, &server->new_output); From c30c4519079e804c35e71810875c10f48097d230 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 14 Aug 2024 21:57:47 +0300 Subject: [PATCH 079/361] xdg-shell: chase xdg_surface geometry updates --- sway/desktop/xdg_shell.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index fdfa7b652..3aed4ec7e 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -299,18 +299,17 @@ static void handle_commit(struct wl_listener *listener, void *data) { return; } - struct wlr_box new_geo; - wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); - bool new_size = new_geo.width != view->geometry.width || - new_geo.height != view->geometry.height || - new_geo.x != view->geometry.x || - new_geo.y != view->geometry.y; + struct wlr_box *new_geo = &xdg_surface->geometry; + bool new_size = new_geo->width != view->geometry.width || + new_geo->height != view->geometry.height || + new_geo->x != view->geometry.x || + new_geo->y != view->geometry.y; if (new_size) { // The client changed its surface size in this commit. For floating // containers, we resize the container to match. For tiling containers, // we only recenter the surface. - memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); + memcpy(&view->geometry, new_geo, sizeof(struct wlr_box)); if (container_is_floating(view->container)) { view_update_size(view); // Only set the toplevel size the current container actually has a size. @@ -463,12 +462,8 @@ static void handle_map(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_view->view; struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; - view->natural_width = toplevel->base->current.geometry.width; - view->natural_height = toplevel->base->current.geometry.height; - if (!view->natural_width && !view->natural_height) { - view->natural_width = toplevel->base->surface->current.width; - view->natural_height = toplevel->base->surface->current.height; - } + view->natural_width = toplevel->base->geometry.width; + view->natural_height = toplevel->base->geometry.height; bool csd = false; From ae7c1b139a3c71d3e11fe2477d8b21c36de6770e Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sun, 18 Aug 2024 14:50:48 +0200 Subject: [PATCH 080/361] config/output: Do not set adaptive_sync if not supported After 4e38f93f367d ("config/output: Skip VRR tests when not supported"), the configuration search no longer touches VRR state for outputs that are known to not support it. This also means that it will not remove VRR if already set, which could cause output configuration to fail. Ensure that VRR state is never set for outputs that do not support it by adding the same test for support to queue_output_config. Fixes: 4e38f93f367d ("config/output: Skip VRR tests when not supported") Fixes: https://github.com/swaywm/sway/issues/8296 --- 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 1b378078e..cc3bf457f 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -491,7 +491,7 @@ static void queue_output_config(struct output_config *oc, wlr_output_state_set_scale(pending, scale); } - if (oc && oc->adaptive_sync != -1) { + if (oc && oc->adaptive_sync != -1 && wlr_output->adaptive_sync_supported) { sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, oc->adaptive_sync); wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); From f9c0f043e5ec39574c9d9b0fb3dece6169a0e67d Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 19 Aug 2024 13:02:55 +0200 Subject: [PATCH 081/361] config/output: Skip search if config has a mode When doing an output configuration search, the intent is to only look for modes if the output's configuration does not contain a specific mode. This was done by testing if config_has_auto_mode returned false. config_has_auto_mode had its return values backwards, leading to other modes being tested if the output configuration had specified modes or modelines, leading to unwanted modes being selected. Invert the function to config_has_manual_mode to give it a clearer name, and fix the return values in the process. --- sway/config/output.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index cc3bf457f..d33ea11a9 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -651,9 +651,9 @@ struct output_config *find_output_config(struct sway_output *sway_output) { return result; } -static bool config_has_auto_mode(struct output_config *oc) { +static bool config_has_manual_mode(struct output_config *oc) { if (!oc) { - return true; + return false; } if (oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t)-1) { return true; @@ -754,7 +754,8 @@ static bool search_mode(struct search_context *ctx, size_t output_idx) { struct wlr_output_state *state = &backend_state->base; struct wlr_output *wlr_output = backend_state->output; - if (!config_has_auto_mode(cfg->config)) { + // We only search for mode if one is not explicitly specified in the config + if (config_has_manual_mode(cfg->config)) { return search_adaptive_sync(ctx, output_idx); } From 7288f77bbe275825a0e0b011db873d9367782af0 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 21 Aug 2024 10:58:18 -0400 Subject: [PATCH 082/361] output: Chase wlroots!4803 --- sway/desktop/output.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 72f753b02..a71430fe5 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -465,7 +465,7 @@ static void handle_present(struct wl_listener *listener, void *data) { return; } - output->last_presentation = *output_event->when; + output->last_presentation = output_event->when; output->refresh_nsec = output_event->refresh; } From f00f964abf0eae36a1cce03c532115319499e570 Mon Sep 17 00:00:00 2001 From: "Anna (navi) Figueiredo Gomes" Date: Sat, 29 Jun 2024 11:00:09 +0200 Subject: [PATCH 083/361] sway/commands/move.c: arrange new workspace When moving a container to a new workspace, the workspace's dimension are left unset. Usually this doesn't matter, but when moving a floating container to a new workspace on a different output, this leads to the position of the container being calculated with 0, so the container ends up halfway offscreen on the leftmost topmost monitor. Signed-off-by: Anna (navi) Figueiredo Gomes --- sway/commands/move.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/commands/move.c b/sway/commands/move.c index ff656cfbd..ad106c648 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -510,6 +510,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, } } ws = workspace_create(NULL, ws_name); + arrange_workspace(ws); } free(ws_name); struct sway_container *dst = seat_get_focus_inactive_tiling(seat, ws); From 77b9ddabe2a97c5d04c30929b0f8cbde3470fdd7 Mon Sep 17 00:00:00 2001 From: llyyr Date: Fri, 23 Aug 2024 01:55:42 +0530 Subject: [PATCH 084/361] sway/tree/container: don't trunc coords in `floating_fix_coordinates` This can cause issues such as the window not being shown at the exact same coordinates when the old and new wlr_box aren't the same dimensions and the container is being moved back-and-forth between them. For example, in the case where a floating window gets moved from one output to another but the outputs aren't the same resolution. For e.g. have two displays that aren't the same resolution then: 1. Open a floating window and set it to pos 0,0 on output 2 2. Send it to scratchpad then `scratchpad show` on output 1 3. `scratchpad show` on output 2 again Observe that the window isn't at 0,0 on output 2 anymore. --- sway/tree/container.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 80ef34fe9..46c388b3e 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -773,11 +773,11 @@ void floating_fix_coordinates(struct sway_container *con, struct wlr_box *old, s // Fall back to centering on the workspace. container_floating_move_to_center(con); } else { - int rel_x = con->pending.x - old->x + (con->pending.width / 2); - int rel_y = con->pending.y - old->y + (con->pending.height / 2); + double rel_x = con->pending.x - old->x + (con->pending.width / 2); + double rel_y = con->pending.y - old->y + (con->pending.height / 2); - con->pending.x = new->x + (double)(rel_x * new->width) / old->width - (con->pending.width / 2); - con->pending.y = new->y + (double)(rel_y * new->height) / old->height - (con->pending.height / 2); + con->pending.x = new->x + (rel_x * new->width) / old->width - (con->pending.width / 2); + con->pending.y = new->y + (rel_y * new->height) / old->height - (con->pending.height / 2); sway_log(SWAY_DEBUG, "Transformed container %p to coords (%f, %f)", con, con->pending.x, con->pending.y); } From f2b2a8114900060f667d7ddd55ef9f8a1e74c1b4 Mon Sep 17 00:00:00 2001 From: Jon Wallace Date: Mon, 26 Aug 2024 22:15:55 -0700 Subject: [PATCH 085/361] Use heading markdown to demarcate sections of commands Its a little tought to notice that the COMMANDS section is actually 3 sections. Use markdown to make this easier to see for the user. --- sway/sway.5.scd | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 0b36a7572..c867c5f7d 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -52,7 +52,7 @@ Throughout the documentation, *|* is used to distinguish between arguments for which you may only select one. *[...]* is used for optional arguments, and *<...>* for arguments where you are expected to supply some value. -# COMMANDS +# COMMANDS - CONFIG ONLY This section only lists general commands. For input and output commands, refer to *sway-input*(5) and *sway-output*(5). @@ -98,6 +98,8 @@ The following commands may only be used in the configuration file. machines, it may be desirable to have Xwayland started immediately by using _force_ instead of _enable_. +# COMMANDS - RUNTIME ONLY + The following commands cannot be used directly in the configuration file. They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). @@ -385,6 +387,8 @@ set|plus|minus|toggle The default format is "%title". +# COMMANDS - CONFIG OR RUNTIME + The following commands may be used either in the configuration file or at runtime. From 980a4e02113789d0cca94aa023557c6f6e87ec73 Mon Sep 17 00:00:00 2001 From: Jon Wallace Date: Tue, 27 Aug 2024 10:42:58 -0700 Subject: [PATCH 086/361] use subheadings instead --- sway/sway.5.scd | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index c867c5f7d..bb958ebfd 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -52,11 +52,12 @@ Throughout the documentation, *|* is used to distinguish between arguments for which you may only select one. *[...]* is used for optional arguments, and *<...>* for arguments where you are expected to supply some value. -# COMMANDS - CONFIG ONLY +# COMMANDS This section only lists general commands. For input and output commands, refer to *sway-input*(5) and *sway-output*(5). +## Config only commands The following commands may only be used in the configuration file. *bar* [] @@ -98,8 +99,7 @@ The following commands may only be used in the configuration file. machines, it may be desirable to have Xwayland started immediately by using _force_ instead of _enable_. -# COMMANDS - RUNTIME ONLY - +## Runtime only commands The following commands cannot be used directly in the configuration file. They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). @@ -387,8 +387,7 @@ set|plus|minus|toggle The default format is "%title". -# COMMANDS - CONFIG OR RUNTIME - +## Config or runtime commands The following commands may be used either in the configuration file or at runtime. From be840f730e747a24106c8366ecb89e6b982cfa38 Mon Sep 17 00:00:00 2001 From: Norbert Bolanowski Date: Mon, 2 Sep 2024 20:02:42 +0200 Subject: [PATCH 087/361] move title_format to container --- include/sway/tree/container.h | 4 ++ include/sway/tree/view.h | 2 - sway/commands/title_format.c | 18 ++++--- sway/tree/container.c | 89 +++++++++++++++++++++++++++++++++-- sway/tree/view.c | 78 +----------------------------- 5 files changed, 102 insertions(+), 89 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 8bf1955d7..4608b8ac2 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -102,6 +102,8 @@ struct sway_container { char *title; // The view's title (unformatted) char *formatted_title; // The title displayed in the title bar int title_width; + + char *title_format; enum sway_container_layout prev_split_layout; @@ -183,6 +185,8 @@ void container_update_title_bar(struct sway_container *container); void container_update_marks(struct sway_container *container); +size_t parse_title_format(struct sway_container *container, char *buffer); + size_t container_build_representation(enum sway_container_layout layout, list_t *children, char *buffer); diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 14aad1a18..f60322212 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -80,8 +80,6 @@ struct sway_view { // Used when changing a view from tiled to floating. int natural_width, natural_height; - char *title_format; - bool using_csd; struct timespec urgent; diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c index 0b2ea2659..fbade4731 100644 --- a/sway/commands/title_format.c +++ b/sway/commands/title_format.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "sway/config.h" @@ -11,16 +12,19 @@ struct cmd_results *cmd_title_format(int argc, char **argv) { return error; } struct sway_container *container = config->handler_context.container; - if (!container || !container->view) { + if (!container) { return cmd_results_new(CMD_INVALID, - "Only views can have a title_format"); + "Only valid containers can have a title_format"); } - struct sway_view *view = container->view; char *format = join_args(argv, argc); - if (view->title_format) { - free(view->title_format); + if (container->title_format) { + free(container->title_format); + } + container->title_format = format; + if (container->view) { + view_update_title(container->view, true); + } else { + container_update_representation(container); } - view->title_format = format; - view_update_title(view, true); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/tree/container.c b/sway/tree/container.c index 46c388b3e..188d3223a 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -22,6 +22,7 @@ #include "sway/tree/workspace.h" #include "sway/xdg_decoration.h" #include "list.h" +#include "pango.h" #include "log.h" #include "stringop.h" @@ -499,6 +500,7 @@ void container_destroy(struct sway_container *con) { } free(con->title); free(con->formatted_title); + free(con->title_format); list_free(con->pending.children); list_free(con->current.children); @@ -645,6 +647,87 @@ bool container_has_ancestor(struct sway_container *descendant, return false; } +static char *escape_pango_markup(const char *buffer) { + size_t length = escape_markup_text(buffer, NULL); + char *escaped_title = calloc(length + 1, sizeof(char)); + escape_markup_text(buffer, escaped_title); + return escaped_title; +} + +static size_t append_prop(char *buffer, const char *value) { + if (!value) { + return 0; + } + // If using pango_markup in font, we need to escape all markup chars + // from values to make sure tags are not inserted by clients + if (config->pango_markup) { + char *escaped_value = escape_pango_markup(value); + lenient_strcat(buffer, escaped_value); + size_t len = strlen(escaped_value); + free(escaped_value); + return len; + } else { + lenient_strcat(buffer, value); + return strlen(value); + } +} + +/** + * Calculate and return the length of the formatted title. + * If buffer is not NULL, also populate the buffer with the formatted title. + */ +size_t parse_title_format(struct sway_container *container, char *buffer) { + if (!container->title_format || strcmp(container->title_format, "%title") == 0) { + if (container->view) { + return append_prop(buffer, view_get_title(container->view)); + } else { + return container_build_representation(container->pending.layout, container->pending.children, buffer); + } + } + + size_t len = 0; + char *format = container->title_format; + char *next = strchr(format, '%'); + while (next) { + // Copy everything up to the % + lenient_strncat(buffer, format, next - format); + len += next - format; + format = next; + + if (strncmp(next, "%title", 6) == 0) { + if (container->view) { + len += append_prop(buffer, view_get_title(container->view)); + } else { + len += container_build_representation(container->pending.layout, container->pending.children, buffer); + } + format += 6; + } else if (container->view) { + if (strncmp(next, "%app_id", 7) == 0) { + len += append_prop(buffer, view_get_app_id(container->view)); + format += 7; + } else if (strncmp(next, "%class", 6) == 0) { + len += append_prop(buffer, view_get_class(container->view)); + format += 6; + } else if (strncmp(next, "%instance", 9) == 0) { + len += append_prop(buffer, view_get_instance(container->view)); + format += 9; + } else if (strncmp(next, "%shell", 6) == 0) { + len += append_prop(buffer, view_get_shell(container->view)); + format += 6; + } + } else { + lenient_strcat(buffer, "%"); + ++format; + ++len; + } + next = strchr(format, '%'); + } + lenient_strcat(buffer, format); + len += strlen(format); + + return len; +} + /** * Calculate and return the length of the tree representation. * An example tree representation is: V[Terminal, Firefox] @@ -700,16 +783,14 @@ size_t container_build_representation(enum sway_container_layout layout, void container_update_representation(struct sway_container *con) { if (!con->view) { - size_t len = container_build_representation(con->pending.layout, - con->pending.children, NULL); + size_t len = parse_title_format(con, NULL); free(con->formatted_title); con->formatted_title = calloc(len + 1, sizeof(char)); if (!sway_assert(con->formatted_title, "Unable to allocate title string")) { return; } - container_build_representation(con->pending.layout, con->pending.children, - con->formatted_title); + parse_title_format(con, con->formatted_title); if (con->title_bar.title_text) { sway_text_node_set_text(con->title_bar.title_text, con->formatted_title); diff --git a/sway/tree/view.c b/sway/tree/view.c index d25a09c2a..492095b93 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -34,7 +34,6 @@ #include "sway/tree/workspace.h" #include "sway/config.h" #include "sway/xdg_decoration.h" -#include "pango.h" #include "stringop.h" bool view_init(struct sway_view *view, enum sway_view_type type, @@ -81,8 +80,6 @@ void view_destroy(struct sway_view *view) { view_assign_ctx(view, NULL); wlr_scene_node_destroy(&view->scene_tree->node); - free(view->title_format); - if (view->impl->destroy) { view->impl->destroy(view); } else { @@ -991,77 +988,6 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { return NULL; } -static char *escape_pango_markup(const char *buffer) { - size_t length = escape_markup_text(buffer, NULL); - char *escaped_title = calloc(length + 1, sizeof(char)); - escape_markup_text(buffer, escaped_title); - return escaped_title; -} - -static size_t append_prop(char *buffer, const char *value) { - if (!value) { - return 0; - } - // If using pango_markup in font, we need to escape all markup chars - // from values to make sure tags are not inserted by clients - if (config->pango_markup) { - char *escaped_value = escape_pango_markup(value); - lenient_strcat(buffer, escaped_value); - size_t len = strlen(escaped_value); - free(escaped_value); - return len; - } else { - lenient_strcat(buffer, value); - return strlen(value); - } -} - -/** - * Calculate and return the length of the formatted title. - * If buffer is not NULL, also populate the buffer with the formatted title. - */ -static size_t parse_title_format(struct sway_view *view, char *buffer) { - if (!view->title_format || strcmp(view->title_format, "%title") == 0) { - return append_prop(buffer, view_get_title(view)); - } - - size_t len = 0; - char *format = view->title_format; - char *next = strchr(format, '%'); - while (next) { - // Copy everything up to the % - lenient_strncat(buffer, format, next - format); - len += next - format; - format = next; - - if (strncmp(next, "%title", 6) == 0) { - len += append_prop(buffer, view_get_title(view)); - format += 6; - } else if (strncmp(next, "%app_id", 7) == 0) { - len += append_prop(buffer, view_get_app_id(view)); - format += 7; - } else if (strncmp(next, "%class", 6) == 0) { - len += append_prop(buffer, view_get_class(view)); - format += 6; - } else if (strncmp(next, "%instance", 9) == 0) { - len += append_prop(buffer, view_get_instance(view)); - format += 9; - } else if (strncmp(next, "%shell", 6) == 0) { - len += append_prop(buffer, view_get_shell(view)); - format += 6; - } else { - lenient_strcat(buffer, "%"); - ++format; - ++len; - } - next = strchr(format, '%'); - } - lenient_strcat(buffer, format); - len += strlen(format); - - return len; -} - void view_update_app_id(struct sway_view *view) { const char *app_id = view_get_app_id(view); @@ -1090,7 +1016,7 @@ void view_update_title(struct sway_view *view, bool force) { free(view->container->title); free(view->container->formatted_title); - size_t len = parse_title_format(view, NULL); + size_t len = parse_title_format(view->container, NULL); if (len) { char *buffer = calloc(len + 1, sizeof(char)); @@ -1098,7 +1024,7 @@ void view_update_title(struct sway_view *view, bool force) { return; } - parse_title_format(view, buffer); + parse_title_format(view->container, buffer); view->container->formatted_title = buffer; } else { view->container->formatted_title = NULL; From b83e5aaa546792336ecc60d2e61708f3cd6b1f5d Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 3 Sep 2024 15:28:29 +0200 Subject: [PATCH 088/361] desktop/output: Do not use commit listener to arrange The reasoning for using a commit handler is to ensure that all paths for output changes are correctly handled. With the centralized modeset infrastructure in place, we can move the logic there. This allows us to be smarter and avoid extraneous arranges, output manager updates and transaction commits. The side-effect is a minor duplication for the special-case request_state, but the shared path will be relied upon further in future commits to justify this duplication. --- include/sway/output.h | 3 ++- sway/config/output.c | 8 ++++++++ sway/desktop/output.c | 41 ++++++++++++++--------------------------- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 1dcabc256..990fa5062 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -57,7 +57,6 @@ struct sway_output { struct wl_listener layout_destroy; struct wl_listener destroy; - struct wl_listener commit; struct wl_listener present; struct wl_listener frame; struct wl_listener request_state; @@ -146,4 +145,6 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, struct sway_output_non_desktop *output_non_desktop_create(struct wlr_output *wlr_output); +void update_output_manager_config(struct sway_server *server); + #endif diff --git a/sway/config/output.c b/sway/config/output.c index d33ea11a9..cc6aedb77 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -11,9 +11,12 @@ #include #include #include "sway/config.h" +#include "sway/desktop/transaction.h" #include "sway/input/cursor.h" +#include "sway/layers.h" #include "sway/output.h" #include "sway/server.h" +#include "sway/tree/arrange.h" #include "sway/tree/root.h" #include "log.h" #include "util.h" @@ -963,8 +966,13 @@ bool apply_output_configs(struct matched_output_config *configs, sway_log(SWAY_DEBUG, "Finalizing config for %s", cfg->output->wlr_output->name); finalize_output_config(cfg->config, cfg->output); + arrange_layers(cfg->output); } + arrange_root(); + update_output_manager_config(&server); + transaction_commit_dirty(); + out: wlr_output_swapchain_manager_finish(&swapchain_mgr); for (size_t idx = 0; idx < configs_len; idx++) { diff --git a/sway/desktop/output.c b/sway/desktop/output.c index a71430fe5..f6a26b0e3 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -356,7 +356,7 @@ static void handle_frame(struct wl_listener *listener, void *user_data) { wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data); } -static void update_output_manager_config(struct sway_server *server) { +void update_output_manager_config(struct sway_server *server) { struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create(); @@ -387,9 +387,6 @@ static int timer_modeset_handle(void *data) { server->delayed_modeset = NULL; apply_all_output_configs(); - transaction_commit_dirty(); - update_output_manager_config(server); - return 0; } @@ -414,7 +411,6 @@ static void begin_destroy(struct sway_output *output) { wl_list_remove(&output->layout_destroy.link); wl_list_remove(&output->destroy.link); - wl_list_remove(&output->commit.link); wl_list_remove(&output->present.link); wl_list_remove(&output->frame.link); wl_list_remove(&output->request_state.link); @@ -437,26 +433,6 @@ static void handle_layout_destroy(struct wl_listener *listener, void *data) { begin_destroy(output); } -static void handle_commit(struct wl_listener *listener, void *data) { - struct sway_output *output = wl_container_of(listener, output, commit); - struct wlr_output_event_commit *event = data; - - if (!output->enabled) { - return; - } - - if (event->state->committed & ( - WLR_OUTPUT_STATE_MODE | - WLR_OUTPUT_STATE_TRANSFORM | - WLR_OUTPUT_STATE_SCALE)) { - arrange_layers(output); - arrange_output(output); - transaction_commit_dirty(); - - update_output_manager_config(output->server); - } -} - static void handle_present(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, present); struct wlr_output_event_present *output_event = data; @@ -473,7 +449,20 @@ static void handle_request_state(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, request_state); const struct wlr_output_event_request_state *event = data; + + uint32_t committed = event->state->committed; wlr_output_commit_state(output->wlr_output, event->state); + + if (committed & ( + WLR_OUTPUT_STATE_MODE | + WLR_OUTPUT_STATE_TRANSFORM | + WLR_OUTPUT_STATE_SCALE)) { + arrange_layers(output); + arrange_output(output); + transaction_commit_dirty(); + + update_output_manager_config(output->server); + } } static unsigned int last_headless_num = 0; @@ -537,8 +526,6 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->layout_destroy.notify = handle_layout_destroy; wl_signal_add(&wlr_output->events.destroy, &output->destroy); output->destroy.notify = handle_destroy; - wl_signal_add(&wlr_output->events.commit, &output->commit); - output->commit.notify = handle_commit; wl_signal_add(&wlr_output->events.present, &output->present); output->present.notify = handle_present; wl_signal_add(&wlr_output->events.frame, &output->frame); From 6045ad9a0224ea2aab2d488cd356b5557007f5d1 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 3 Sep 2024 15:36:47 +0200 Subject: [PATCH 089/361] tree/output: Rely on modeset arranging root output_enable/output_disable are only called from modeset, and from output destroy which requests modeset. As such, they can rely on the modeset handling arrange. --- sway/tree/output.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/sway/tree/output.c b/sway/tree/output.c index 6c8dd6dc9..f3b0b27a2 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -183,9 +183,6 @@ void output_enable(struct sway_output *output) { input_manager_configure_xcursor(); wl_signal_emit_mutable(&root->events.new_node, &output->node); - - arrange_layers(output); - arrange_root(); } static void evacuate_sticky(struct sway_workspace *old_ws, @@ -300,13 +297,6 @@ void output_disable(struct sway_output *output) { list_del(root->outputs, index); output->enabled = false; - - arrange_root(); - - // Reconfigure all devices, since devices with map_to_output directives for - // an output that goes offline should stop sending events as long as the - // output remains offline. - input_manager_configure_all_input_mappings(); } void output_begin_destroy(struct sway_output *output) { From af28ac04a4d523aecd74dacc94a91f7d9e537982 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 3 Sep 2024 15:39:39 +0200 Subject: [PATCH 090/361] (desktop|tree)/output: Do not use layout listener to arrange Output layout changes originate from the centralized modeset infrastructure and request_state which already takes care of arranging and updating outputs as needed. --- include/sway/output.h | 2 -- include/sway/server.h | 1 - include/sway/tree/root.h | 2 -- sway/desktop/output.c | 7 ------- sway/server.c | 3 --- sway/tree/root.c | 10 ---------- 6 files changed, 25 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 990fa5062..4584ea453 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -134,8 +134,6 @@ enum sway_container_layout output_get_default_layout( enum wlr_direction opposite_direction(enum wlr_direction d); -void handle_output_layout_change(struct wl_listener *listener, void *data); - void handle_output_manager_apply(struct wl_listener *listener, void *data); void handle_output_manager_test(struct wl_listener *listener, void *data); diff --git a/include/sway/server.h b/include/sway/server.h index 460f9e17f..ccf4a9cc2 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -45,7 +45,6 @@ struct sway_server { struct sway_input_manager *input; struct wl_listener new_output; - struct wl_listener output_layout_change; struct wl_listener renderer_lost; struct wlr_idle_notifier_v1 *idle_notifier_v1; diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index 7de0abcdd..10d9ea981 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -16,8 +16,6 @@ struct sway_root { struct sway_node node; struct wlr_output_layout *output_layout; - struct wl_listener output_layout_change; - // scene node layout: // - root // - staging diff --git a/sway/desktop/output.c b/sway/desktop/output.c index f6a26b0e3..9f4290cd6 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -543,13 +543,6 @@ void handle_new_output(struct wl_listener *listener, void *data) { request_modeset(server); } -void handle_output_layout_change(struct wl_listener *listener, - void *data) { - struct sway_server *server = - wl_container_of(listener, server, output_layout_change); - update_output_manager_config(server); -} - static struct output_config *output_config_for_config_head( struct wlr_output_configuration_head_v1 *config_head, struct sway_output *output) { diff --git a/sway/server.c b/sway/server.c index 1ca560869..30896baf6 100644 --- a/sway/server.c +++ b/sway/server.c @@ -277,9 +277,6 @@ bool server_init(struct sway_server *server) { server->new_output.notify = handle_new_output; wl_signal_add(&server->backend->events.new_output, &server->new_output); - server->output_layout_change.notify = handle_output_layout_change; - wl_signal_add(&root->output_layout->events.change, - &server->output_layout_change); server->xdg_output_manager_v1 = wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); diff --git a/sway/tree/root.c b/sway/tree/root.c index 20fcfa595..19d072b5c 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -19,12 +19,6 @@ struct sway_root *root; -static void output_layout_handle_change(struct wl_listener *listener, - void *data) { - arrange_root(); - transaction_commit_dirty(); -} - struct sway_root *root_create(struct wl_display *wl_display) { struct sway_root *root = calloc(1, sizeof(struct sway_root)); if (!root) { @@ -81,14 +75,10 @@ struct sway_root *root_create(struct wl_display *wl_display) { root->non_desktop_outputs = create_list(); root->scratchpad = create_list(); - root->output_layout_change.notify = output_layout_handle_change; - wl_signal_add(&root->output_layout->events.change, - &root->output_layout_change); return root; } void root_destroy(struct sway_root *root) { - wl_list_remove(&root->output_layout_change.link); list_free(root->scratchpad); list_free(root->non_desktop_outputs); list_free(root->outputs); From cfb292cca7cabfb42b27c82691bf47527d2237fb Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 5 Sep 2024 18:32:00 +0200 Subject: [PATCH 091/361] desktop/output: Avoid duplicate output manager update --- sway/desktop/output.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 9f4290cd6..8711a2481 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -633,10 +633,6 @@ static void output_manager_apply(struct sway_server *server, wlr_output_configuration_v1_send_failed(config); } wlr_output_configuration_v1_destroy(config); - - if (!test_only) { - update_output_manager_config(server); - } } void handle_output_manager_apply(struct wl_listener *listener, void *data) { From 4fe054c6db74401f4afc7453fc74665097b5261d Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 5 Sep 2024 18:32:51 +0200 Subject: [PATCH 092/361] tree/output: Avoid duplicate input mapping configure --- sway/tree/output.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sway/tree/output.c b/sway/tree/output.c index f3b0b27a2..44b941ca2 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -180,8 +180,6 @@ void output_enable(struct sway_output *output) { ws->layout = output_get_default_layout(output); } - input_manager_configure_xcursor(); - wl_signal_emit_mutable(&root->events.new_node, &output->node); } From fc6b8d6af2a8b5c68bbf49753b0e560ad2cff208 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Fri, 6 Sep 2024 19:09:41 -0400 Subject: [PATCH 093/361] container: Skip % char if it doesn't match a view property The else condition was missed here and we would never skip the % char if it didn't end up matching with any property. Since we fail to skip we would re-evaluate the % in an infinite loop never achieving any forward-progress. Fixes: https://github.com/swaywm/sway/issues/8333 --- sway/tree/container.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sway/tree/container.c b/sway/tree/container.c index 188d3223a..f482b06bc 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -714,6 +714,10 @@ size_t parse_title_format(struct sway_container *container, char *buffer) { } else if (strncmp(next, "%shell", 6) == 0) { len += append_prop(buffer, view_get_shell(container->view)); format += 6; + } else { + lenient_strcat(buffer, "%"); + ++format; + ++len; } } else { lenient_strcat(buffer, "%"); From 4f9ce4675cf428e8acd632de31482981e1bedcf8 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Fri, 6 Sep 2024 00:35:57 +0200 Subject: [PATCH 094/361] tree/arrange: Remove redundant output geometry update This is handled by apply_output_configs. --- sway/tree/arrange.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index d4003fe65..352ec0e57 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -314,14 +314,6 @@ void arrange_output(struct sway_output *output) { if (config->reloading) { return; } - struct wlr_box output_box; - wlr_output_layout_get_box(root->output_layout, - output->wlr_output, &output_box); - output->lx = output_box.x; - output->ly = output_box.y; - output->width = output_box.width; - output->height = output_box.height; - for (int i = 0; i < output->workspaces->length; ++i) { struct sway_workspace *workspace = output->workspaces->items[i]; arrange_workspace(workspace); From 14bff7b451d865f16e3fc82279dfba314b11269c Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Fri, 6 Sep 2024 00:36:29 +0200 Subject: [PATCH 095/361] desktop/transaction: Deactivate workspace on inactive outputs If the output is not active, it might not have a valid geometry to arrange for. Outputs do not gain a geometry until modeset, so if an output is connected with a configuration present to disable it, it will not have a geometry. If the output has a past workspace restored, this will be attemtped arranged to fit a 0x0 rectangle, which asserts when trying to sort out borders. Consider the workspace activated only if the output itself is active to get the scene nodes disabled. --- sway/desktop/transaction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 2ee5a5dff..931189891 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -559,7 +559,7 @@ static void arrange_output(struct sway_output *output, int width, int height) { for (int i = 0; i < output->current.workspaces->length; i++) { struct sway_workspace *child = output->current.workspaces->items[i]; - bool activated = output->current.active_workspace == child; + bool activated = output->current.active_workspace == child && output->wlr_output->enabled; wlr_scene_node_reparent(&child->layers.tiling->node, output->layers.tiling); wlr_scene_node_reparent(&child->layers.fullscreen->node, output->layers.fullscreen); From f4a6b0395f3fe38cb14bec1f5ac30445496e525c Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Fri, 6 Sep 2024 00:42:49 +0200 Subject: [PATCH 096/361] tree/arrange; Skip arranging disabled outputs Disabled outputs might not have a geometry to arrange for, so skip the arrange to avoid messing up the workspace geometry. --- sway/tree/arrange.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 352ec0e57..2b95a6dc9 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -314,6 +314,9 @@ void arrange_output(struct sway_output *output) { if (config->reloading) { return; } + if (!output->wlr_output->enabled) { + return; + } for (int i = 0; i < output->workspaces->length; ++i) { struct sway_workspace *workspace = output->workspaces->items[i]; arrange_workspace(workspace); From c5ba7f23a50cd43d39fbb45274484abbcaa4e157 Mon Sep 17 00:00:00 2001 From: llyyr Date: Sun, 8 Sep 2024 17:28:34 +0530 Subject: [PATCH 097/361] sway/input/keyboard: always set active keyboard if there is none Previously, we incorrectly only set active keyboard for non-virtual devices. 4c3c0602116c12c2821e1e505e7248b3c642b4ca incorrectly put unrelated code in `sway_keyboard_set_layout`. Fixes: 4c3c0602116c12c2821e1e505e7248b3c642b4ca --- sway/input/keyboard.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index efb9ac394..1a73df014 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -1028,13 +1028,6 @@ static void sway_keyboard_set_layout(struct sway_keyboard *keyboard, } } - // If the seat has no active keyboard, set this one - struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; - struct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard; - if (current_keyboard == NULL) { - wlr_seat_set_keyboard(seat, keyboard->wlr); - } - if (keymap_changed) { ipc_event_input("xkb_keymap", keyboard->seat_device->input_device); @@ -1078,6 +1071,13 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { sway_keyboard_set_layout(keyboard, input_config); } + // If the seat has no active keyboard, set this one + struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; + struct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard; + if (current_keyboard == NULL) { + wlr_seat_set_keyboard(seat, keyboard->wlr); + } + wl_list_remove(&keyboard->keyboard_key.link); wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key); keyboard->keyboard_key.notify = handle_keyboard_key; From fb5eadc363a7f8b9eeeb0ba562ecb3c40e0e6e5a Mon Sep 17 00:00:00 2001 From: Adam Chovanec Date: Sat, 7 Sep 2024 21:28:25 +0200 Subject: [PATCH 098/361] readme: update Czech translation --- README.cs.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.cs.md b/README.cs.md index 41efba54a..fbb23b34e 100644 --- a/README.cs.md +++ b/README.cs.md @@ -1,6 +1,6 @@ # sway -[English][en] - **[Česky][cs]** - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - [Svenska][sv] - [Ελληνικά][gr] - [हिन्दी][hi] - [Magyar][hu] - [فارسی][ir] - [Italiano][it] - [日本語][ja] - [한국어][ko] - [Nederlands][nl] - [Polski][pl] - [Português][pt] - [Română][ro] - [Русский][ru] - [Türkçe][tr] - [Українська][uk] - [中文-简体][zh-CN] - [中文-繁體][zh-TW] +[English][en] - [عربي][ar] - **[Česky][cs]** - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - [ქართული][ge] - [Ελληνικά][gr] - [हिन्दी][hi] - [Magyar][hu] - [فارسی][ir] - [Italiano][it] - [日本語][ja] - [한국어][ko] - [Nederlands][nl] - [Norsk][no] - [Polski][pl] - [Português][pt] - [Română][ro] - [Русский][ru] - [Svenska][sv] - [Türkçe][tr] - [Українська][uk] - [中文-简体][zh-CN] - [中文-繁體][zh-TW] sway je s [i3] kompatibilní [Wayland] kompozitor. Přečtěte si [FAQ]. Připojte se na [IRC kanál][IRC channel] \(#sway na irc.libera.chat). @@ -32,10 +32,11 @@ Nainstalujte závislosti: * pango * cairo * gdk-pixbuf2 (volitelné: oznamovací oblast) +* [swaybg] (volitelné: tapeta) * [scdoc] (volitelné: manuálové stránky) \* * git (volitelné: informace o verzi) \* -_\* Závislost pouze pro sestavení_ +_\* Závislost pouze pro kompilaci_ Spusťte tyto příkazy: @@ -56,12 +57,13 @@ Spusťte `sway` z TTY. Některé správce zobrazení mohou fungovat, ale nejsou podporovány sway (je známo, že gdm funguje docela dobře). [en]: https://github.com/swaywm/sway#readme +[ar]: README.ar.md [cs]: README.cs.md [de]: README.de.md [dk]: README.dk.md [es]: README.es.md [fr]: README.fr.md -[sv]: README.sv.md +[ge]: README.ge.md [gr]: README.gr.md [hi]: README.hi.md [hu]: README.hu.md @@ -70,10 +72,12 @@ podporovány sway (je známo, že gdm funguje docela dobře). [ja]: README.ja.md [ko]: README.ko.md [nl]: README.nl.md +[no]: README.no.md [pl]: README.pl.md [pt]: README.pt.md [ro]: README.ro.md [ru]: README.ru.md +[sv]: README.sv.md [tr]: README.tr.md [uk]: README.uk.md [zh-CN]: README.zh-CN.md @@ -86,4 +90,5 @@ podporovány sway (je známo, že gdm funguje docela dobře). [GitHub releases]: https://github.com/swaywm/sway/releases [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots +[swaybg]: https://github.com/swaywm/swaybg/ [scdoc]: https://git.sr.ht/~sircmpwn/scdoc From a0c03499348a4a3d4d2e9a387bf366ccbcf68186 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 5 Sep 2024 22:25:44 +0200 Subject: [PATCH 099/361] config/output: Support multiple matches in find_output_config Simplify find_output_config and inline the search through the output configs instead of using list_seq_find with a comparator function. The new implementation will merge any amount of matched configs in order, which will be relied upon in a future commit. --- sway/config/output.c | 39 +++++++++++++-------------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index cc6aedb77..aa8504ed2 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -25,13 +25,6 @@ #include #endif -int output_name_cmp(const void *item, const void *data) { - const struct output_config *output = item; - const char *name = data; - - return strcmp(output->name, name); -} - void output_get_identifier(char *identifier, size_t len, struct sway_output *output) { struct wlr_output *wlr_output = output->wlr_output; @@ -615,8 +608,6 @@ static void default_output_config(struct output_config *oc, // configuration that applies to the specified output. struct output_config *find_output_config(struct sway_output *sway_output) { const char *name = sway_output->wlr_output->name; - struct output_config *oc = NULL; - struct output_config *result = new_output_config(name); if (config->reloading) { default_output_config(result, sway_output->wlr_output); @@ -625,25 +616,21 @@ struct output_config *find_output_config(struct sway_output *sway_output) { char id[128]; output_get_identifier(id, sizeof(id), sway_output); - int i; - bool match = false; - if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) { - match = true; - oc = config->output_configs->items[i]; - merge_output_config(result, oc); - } - if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) { - match = true; - oc = config->output_configs->items[i]; - merge_output_config(result, oc); - } - if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) { - match = true; - oc = config->output_configs->items[i]; - merge_output_config(result, oc); + // We take a new config and merge on top, in order, the wildcard config, + // output config by name, and output config by identifier to form the final + // config. If there are multiple matches, they are merged in order. + struct output_config *oc = NULL; + const char *names[] = {"*", name, id, NULL}; + for (const char **name = &names[0]; *name; name++) { + for (int idx = 0; idx < config->output_configs->length; idx++) { + oc = config->output_configs->items[idx]; + if (strcmp(oc->name, *name) == 0) { + merge_output_config(result, oc); + } + } } - if (!match && !config->reloading) { + if (oc == NULL && !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 From 0496477f92e60d504c3938a54e823ad56c8b1868 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 9 Sep 2024 11:57:06 +0200 Subject: [PATCH 100/361] config/output: Always start with default in find_output_config We always need to start out with the default configuration, regardless of whether the config is reloading or not to ensure that config decisions are stable given a specific configuration. --- sway/config/output.c | 41 +++++++++++------------------------------ 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index aa8504ed2..46a0ffd3a 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -584,35 +584,24 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output return true; } -static void default_output_config(struct output_config *oc, - struct wlr_output *wlr_output) { - oc->enabled = 1; - oc->power = 1; - struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); - if (mode != NULL) { - oc->width = mode->width; - oc->height = mode->height; - oc->refresh_rate = mode->refresh / 1000.f; - } - oc->x = oc->y = -1; - oc->scale = 0; // auto - oc->scale_filter = SCALE_FILTER_DEFAULT; - struct sway_output *output = wlr_output->data; - oc->subpixel = output->detected_subpixel; - oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; - oc->max_render_time = 0; - oc->allow_tearing = 0; -} - // find_output_config returns a merged output_config containing all stored // configuration that applies to the specified output. struct output_config *find_output_config(struct sway_output *sway_output) { const char *name = sway_output->wlr_output->name; struct output_config *result = new_output_config(name); - if (config->reloading) { - default_output_config(result, sway_output->wlr_output); + if (result == NULL) { + return NULL; } + // Set output defaults for the "base" configuration + result->enabled = 1; + result->power = 1; + result->scale = 0; // auto + result->subpixel = sway_output->detected_subpixel; + result->transform = WL_OUTPUT_TRANSFORM_NORMAL; + result->max_render_time = 0; + result->allow_tearing = 0; + char id[128]; output_get_identifier(id, sizeof(id), sway_output); @@ -630,14 +619,6 @@ struct output_config *find_output_config(struct sway_output *sway_output) { } } - if (oc == NULL && !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); - return NULL; - } - return result; } From 29b3f00e6fd99296cde7e94b7063acfd075c559c Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 9 Sep 2024 15:28:22 +0200 Subject: [PATCH 101/361] config/output: Accept a list of output_configs to use Instead of using a single finalized output config per output, accept a regular list of output configs like the one ultimately stored for configuration purposes. This allows the output management code to test an augmented configuration while still using the same output config logic, without having to mutate the stored configuration. This in turn allows us to make a few APIs private. A bug note about an existing issue with derade to off is added as well. --- include/sway/config.h | 15 ++------ sway/config/output.c | 47 ++++++++++++++++++------- sway/desktop/output.c | 80 ++++++++++++++++--------------------------- 3 files changed, 67 insertions(+), 75 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index d9f561571..f8007c92e 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -296,14 +296,6 @@ struct output_config { char *background_fallback; }; -/** - * An output config pre-matched to an output - */ -struct matched_output_config { - struct sway_output *output; - struct output_config *config; -}; - /** * Stores size of gaps for each side */ @@ -693,14 +685,11 @@ const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filt struct output_config *new_output_config(const char *name); -bool apply_output_configs(struct matched_output_config *configs, - size_t configs_len, bool test_only, bool degrade_to_off); +bool apply_output_configs(struct output_config **ocs, size_t ocs_len, + bool test_only, bool degrade_to_off); void apply_all_output_configs(void); -void sort_output_configs_by_priority(struct matched_output_config *configs, - size_t configs_len); - /** * store_output_config stores a new output config. An output may be matched by * three different config types, in order of precedence: Identifier, name and diff --git a/sway/config/output.c b/sway/config/output.c index 46a0ffd3a..5006a385e 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -584,9 +584,11 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output return true; } -// find_output_config returns a merged output_config containing all stored -// configuration that applies to the specified output. -struct output_config *find_output_config(struct sway_output *sway_output) { +// find_output_config_from_list returns a merged output_config containing all +// stored configuration that applies to the specified output. +static struct output_config *find_output_config_from_list( + struct output_config **configs, size_t configs_len, + struct sway_output *sway_output) { const char *name = sway_output->wlr_output->name; struct output_config *result = new_output_config(name); if (result == NULL) { @@ -611,8 +613,8 @@ struct output_config *find_output_config(struct sway_output *sway_output) { struct output_config *oc = NULL; const char *names[] = {"*", name, id, NULL}; for (const char **name = &names[0]; *name; name++) { - for (int idx = 0; idx < config->output_configs->length; idx++) { - oc = config->output_configs->items[idx]; + for (size_t idx = 0; idx < configs_len; idx++) { + oc = configs[idx]; if (strcmp(oc->name, *name) == 0) { merge_output_config(result, oc); } @@ -622,6 +624,12 @@ struct output_config *find_output_config(struct sway_output *sway_output) { return result; } +struct output_config *find_output_config(struct sway_output *sway_output) { + return find_output_config_from_list( + (struct output_config **)config->output_configs->items, + config->output_configs->length, sway_output); +} + static bool config_has_manual_mode(struct output_config *oc) { if (!oc) { return false; @@ -634,6 +642,14 @@ static bool config_has_manual_mode(struct output_config *oc) { return false; } +/** + * An output config pre-matched to an output + */ +struct matched_output_config { + struct sway_output *output; + struct output_config *config; +}; + struct search_context { struct wlr_output_swapchain_manager *swapchain_mgr; struct wlr_backend_output_state *states; @@ -852,12 +868,12 @@ static int compare_matched_output_config_priority(const void *a, const void *b) return 0; } -void sort_output_configs_by_priority(struct matched_output_config *configs, - size_t configs_len) { +static void sort_output_configs_by_priority( + struct matched_output_config *configs, size_t configs_len) { qsort(configs, configs_len, sizeof(*configs), compare_matched_output_config_priority); } -bool apply_output_configs(struct matched_output_config *configs, +static bool apply_resolved_output_configs(struct matched_output_config *configs, size_t configs_len, bool test_only, bool degrade_to_off) { struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states)); if (!states) { @@ -965,11 +981,12 @@ out: return ok; } -void apply_all_output_configs(void) { +bool apply_output_configs(struct output_config **ocs, size_t ocs_len, + bool test_only, bool degrade_to_off) { size_t configs_len = wl_list_length(&root->all_outputs); struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); if (!configs) { - return; + return false; } int config_idx = 0; @@ -982,16 +999,22 @@ void apply_all_output_configs(void) { struct matched_output_config *config = &configs[config_idx++]; config->output = sway_output; - config->config = find_output_config(sway_output); + config->config = find_output_config_from_list(ocs, ocs_len, sway_output); } sort_output_configs_by_priority(configs, configs_len); - apply_output_configs(configs, configs_len, false, true); + bool ok = apply_resolved_output_configs(configs, configs_len, test_only, degrade_to_off); for (size_t idx = 0; idx < configs_len; idx++) { struct matched_output_config *cfg = &configs[idx]; free_output_config(cfg->config); } free(configs); + return ok; +} + +void apply_all_output_configs(void) { + apply_output_configs((struct output_config **)config->output_configs->items, + config->output_configs->length, false, true); } void free_output_config(struct output_config *oc) { diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 8711a2481..8ce31d859 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -544,9 +544,8 @@ void handle_new_output(struct wl_listener *listener, void *data) { } static struct output_config *output_config_for_config_head( - struct wlr_output_configuration_head_v1 *config_head, - struct sway_output *output) { - struct output_config *oc = new_output_config(output->wlr_output->name); + struct wlr_output_configuration_head_v1 *config_head) { + struct output_config *oc = new_output_config(config_head->state.output->name); oc->enabled = config_head->state.enabled; if (!oc->enabled) { return oc; @@ -572,67 +571,48 @@ static struct output_config *output_config_for_config_head( } static void output_manager_apply(struct sway_server *server, - struct wlr_output_configuration_v1 *config, bool test_only) { - size_t configs_len = wl_list_length(&root->all_outputs); - struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); + struct wlr_output_configuration_v1 *cfg, bool test_only) { + bool ok = false; + size_t configs_len = config->output_configs->length + wl_list_length(&cfg->heads); + struct output_config **configs = calloc(configs_len, sizeof(*configs)); if (!configs) { - return; + goto done; + } + size_t start_new_configs = config->output_configs->length; + for (size_t idx = 0; idx < start_new_configs; idx++) { + configs[idx] = config->output_configs->items[idx]; } - int config_idx = 0; - struct sway_output *sway_output; - wl_list_for_each(sway_output, &root->all_outputs, link) { - if (sway_output == root->fallback_output) { - configs_len--; - continue; - } - - struct matched_output_config *cfg = &configs[config_idx++]; - cfg->output = sway_output; - - struct wlr_output_configuration_head_v1 *config_head; - wl_list_for_each(config_head, &config->heads, link) { - if (config_head->state.output == sway_output->wlr_output) { - cfg->config = output_config_for_config_head(config_head, sway_output); - break; - } - } - if (!cfg->config) { - cfg->config = find_output_config(sway_output); - } + size_t config_idx = start_new_configs; + struct wlr_output_configuration_head_v1 *config_head; + wl_list_for_each(config_head, &cfg->heads, link) { + // Generate the configuration and store it as a temporary + // config. We keep a record of it so we can remove it later. + struct output_config *oc = output_config_for_config_head(config_head); + configs[config_idx++] = oc; } - sort_output_configs_by_priority(configs, configs_len); - bool ok = apply_output_configs(configs, configs_len, test_only, false); - for (size_t idx = 0; idx < configs_len; idx++) { - struct matched_output_config *cfg = &configs[idx]; - - // Only store new configs for successful non-test commits. Old configs, - // test-only and failed commits just get freed. - bool store_config = false; + // Try to commit without degrade to off enabled. Note that this will fail + // if any output configured for enablement fails to be enabled, even if it + // was not part of the config heads we were asked to configure. + ok = apply_output_configs(configs, configs_len, test_only, false); + for (size_t idx = start_new_configs; idx < configs_len; idx++) { + struct output_config *cfg = configs[idx]; if (!test_only && ok) { - struct wlr_output_configuration_head_v1 *config_head; - wl_list_for_each(config_head, &config->heads, link) { - if (config_head->state.output == cfg->output->wlr_output) { - store_config = true; - break; - } - } - } - if (store_config) { - store_output_config(cfg->config); + store_output_config(cfg); } else { - free_output_config(cfg->config); + free_output_config(cfg); } } free(configs); +done: if (ok) { - wlr_output_configuration_v1_send_succeeded(config); + wlr_output_configuration_v1_send_succeeded(cfg); } else { - wlr_output_configuration_v1_send_failed(config); + wlr_output_configuration_v1_send_failed(cfg); } - wlr_output_configuration_v1_destroy(config); + wlr_output_configuration_v1_destroy(cfg); } void handle_output_manager_apply(struct wl_listener *listener, void *data) { From d7a76d381bbe4321578bc3a95fbc82d76b67ef05 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 9 Sep 2024 20:04:17 +0200 Subject: [PATCH 102/361] config/output: Rename to apply_stored_output_configs --- include/sway/config.h | 2 +- sway/commands/output.c | 2 +- sway/config.c | 2 +- sway/config/output.c | 2 +- sway/desktop/output.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index f8007c92e..13f576b00 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -688,7 +688,7 @@ struct output_config *new_output_config(const char *name); bool apply_output_configs(struct output_config **ocs, size_t ocs_len, bool test_only, bool degrade_to_off); -void apply_all_output_configs(void); +void apply_stored_output_configs(void); /** * store_output_config stores a new output config. An output may be matched by diff --git a/sway/commands/output.c b/sway/commands/output.c index 9478e0bad..3f65b909c 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -111,7 +111,7 @@ struct cmd_results *cmd_output(int argc, char **argv) { // entire config and before the deferred commands so that an auto generated // workspace name is not given to re-enabled outputs. if (!config->reloading && !config->validating) { - apply_all_output_configs(); + apply_stored_output_configs(); if (background) { if (!spawn_swaybg()) { return cmd_results_new(CMD_FAILURE, diff --git a/sway/config.c b/sway/config.c index 5058efcc0..5fc414a14 100644 --- a/sway/config.c +++ b/sway/config.c @@ -533,7 +533,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { } sway_switch_retrigger_bindings_for_all(); - apply_all_output_configs(); + apply_stored_output_configs(); spawn_swaybg(); config->reloading = false; diff --git a/sway/config/output.c b/sway/config/output.c index 5006a385e..4a6ba1555 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1012,7 +1012,7 @@ bool apply_output_configs(struct output_config **ocs, size_t ocs_len, return ok; } -void apply_all_output_configs(void) { +void apply_stored_output_configs(void) { apply_output_configs((struct output_config **)config->output_configs->items, config->output_configs->length, false, true); } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 8ce31d859..0b0d5291e 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -386,7 +386,7 @@ static int timer_modeset_handle(void *data) { wl_event_source_remove(server->delayed_modeset); server->delayed_modeset = NULL; - apply_all_output_configs(); + apply_stored_output_configs(); return 0; } From f957c7e658871c27935f88f6e1d18b9db67f3808 Mon Sep 17 00:00:00 2001 From: Steffen Dirkwinkel Date: Fri, 13 Sep 2024 08:58:55 +0000 Subject: [PATCH 103/361] config/output: support DRM_FORMAT_ARGB8888 Some display output hardware [1] doesn't support any of the current formats, but works with ARGB8888. Fall back to it if available. [1] https://github.com/torvalds/linux/blob/196145c606d0f816fd3926483cb1ff87e09c2c0b/drivers/gpu/drm/xlnx/zynqmp_disp.c#L313 Signed-off-by: Steffen Dirkwinkel --- sway/config/output.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/config/output.c b/sway/config/output.c index 4a6ba1555..1ba72517f 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -783,6 +783,7 @@ static bool search_render_format(struct search_context *ctx, size_t output_idx) DRM_FORMAT_XRGB2101010, DRM_FORMAT_XBGR2101010, DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, DRM_FORMAT_INVALID, }; if (render_format_is_bgr(wlr_output->render_format)) { From 785a459a55d8b55b4bed1fdc55b04c32be5b450c Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Wed, 18 Sep 2024 00:46:29 +0200 Subject: [PATCH 104/361] ext-session-lock: Do not use commit listener to arrange Arranging lock surfaces rely on the sway_output width and height being updated, but these are only updated after the commit has been completed and all commit listeners have executed. This means that the lock surfaces will not be appropriately scaled to match a change in output dimensions, and may reveal what is under the lock background. Replace the implicit arrange through the output commit listener with an explicit arrange after the output configuration is finalized. This might have regressed by other transition away from output commit listeners for other arrange tasks, but even then it would have erroneously relied on signalling order. --- include/sway/lock.h | 6 ++++++ sway/config/output.c | 2 ++ sway/lock.c | 29 ++++++++++++----------------- 3 files changed, 20 insertions(+), 17 deletions(-) create mode 100644 include/sway/lock.h diff --git a/include/sway/lock.h b/include/sway/lock.h new file mode 100644 index 000000000..5be0f9695 --- /dev/null +++ b/include/sway/lock.h @@ -0,0 +1,6 @@ +#ifndef _SWAY_LOCK_H +#define _SWAY_LOCK_H + +void arrange_locks(void); + +#endif diff --git a/sway/config/output.c b/sway/config/output.c index 1ba72517f..25f51dc7f 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -14,6 +14,7 @@ #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/layers.h" +#include "sway/lock.h" #include "sway/output.h" #include "sway/server.h" #include "sway/tree/arrange.h" @@ -955,6 +956,7 @@ static bool apply_resolved_output_configs(struct matched_output_config *configs, } arrange_root(); + arrange_locks(); update_output_manager_config(&server); transaction_commit_dirty(); diff --git a/sway/lock.c b/sway/lock.c index 289e8ca46..43f313308 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -8,6 +8,7 @@ #include "sway/layers.h" #include "sway/output.h" #include "sway/server.h" +#include "sway/lock.h" struct sway_session_lock_output { struct wlr_scene_tree *tree; @@ -19,7 +20,6 @@ struct sway_session_lock_output { struct wl_list link; // sway_session_lock::outputs struct wl_listener destroy; - struct wl_listener commit; struct wlr_session_lock_surface_v1 *surface; @@ -89,6 +89,17 @@ static void lock_output_reconfigure(struct sway_session_lock_output *output) { } } +void arrange_locks(void) { + if (server.session_lock.lock == NULL) { + return; + } + + struct sway_session_lock_output *lock_output; + wl_list_for_each(lock_output, &server.session_lock.lock->outputs, link) { + lock_output_reconfigure(lock_output); + } +} + static void handle_new_surface(struct wl_listener *listener, void *data) { struct sway_session_lock *lock = wl_container_of(listener, lock, new_surface); struct wlr_session_lock_surface_v1 *lock_surface = data; @@ -125,7 +136,6 @@ static void sway_session_lock_output_destroy(struct sway_session_lock_output *ou wl_list_remove(&output->surface_map.link); } - wl_list_remove(&output->commit.link); wl_list_remove(&output->destroy.link); wl_list_remove(&output->link); @@ -138,18 +148,6 @@ static void lock_node_handle_destroy(struct wl_listener *listener, void *data) { sway_session_lock_output_destroy(output); } -static void lock_output_handle_commit(struct wl_listener *listener, void *data) { - struct wlr_output_event_commit *event = data; - struct sway_session_lock_output *output = - wl_container_of(listener, output, commit); - if (event->state->committed & ( - WLR_OUTPUT_STATE_MODE | - WLR_OUTPUT_STATE_SCALE | - WLR_OUTPUT_STATE_TRANSFORM)) { - lock_output_reconfigure(output); - } -} - static struct sway_session_lock_output *session_lock_output_create( struct sway_session_lock *lock, struct sway_output *output) { struct sway_session_lock_output *lock_output = calloc(1, sizeof(*lock_output)); @@ -186,9 +184,6 @@ static struct sway_session_lock_output *session_lock_output_create( lock_output->destroy.notify = lock_node_handle_destroy; wl_signal_add(&tree->node.events.destroy, &lock_output->destroy); - lock_output->commit.notify = lock_output_handle_commit; - wl_signal_add(&output->wlr_output->events.commit, &lock_output->commit); - lock_output_reconfigure(lock_output); wl_list_insert(&lock->outputs, &lock_output->link); From 034d02f8a5099ad1283ce3bd1ced524a17f8ba2f Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 29 Aug 2024 23:40:19 +0200 Subject: [PATCH 105/361] config/output: Add support for 6-bit render fmt GUD devices uses RGB565 by default for performance reasons. Allow specifying render_bit_depth 6 to pick this format. The definition works out if you consider the maximum number of bits per channel instead of the average. --- include/sway/config.h | 1 + sway/commands/output/render_bit_depth.c | 7 +++++-- sway/config/output.c | 25 +++++++++++++++++++------ sway/sway-output.5.scd | 6 +++--- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 13f576b00..3cd59722f 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -262,6 +262,7 @@ enum scale_filter_mode { enum render_bit_depth { RENDER_BIT_DEPTH_DEFAULT, // the default is currently 8 + RENDER_BIT_DEPTH_6, RENDER_BIT_DEPTH_8, RENDER_BIT_DEPTH_10, }; diff --git a/sway/commands/output/render_bit_depth.c b/sway/commands/output/render_bit_depth.c index c419321ea..3fa30e045 100644 --- a/sway/commands/output/render_bit_depth.c +++ b/sway/commands/output/render_bit_depth.c @@ -11,7 +11,10 @@ struct cmd_results *output_cmd_render_bit_depth(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Missing bit depth argument."); } - if (strcmp(*argv, "8") == 0) { + if (strcmp(*argv, "6") == 0) { + config->handler_context.output_config->render_bit_depth = + RENDER_BIT_DEPTH_6; + } else if (strcmp(*argv, "8") == 0) { config->handler_context.output_config->render_bit_depth = RENDER_BIT_DEPTH_8; } else if (strcmp(*argv, "10") == 0) { @@ -19,7 +22,7 @@ struct cmd_results *output_cmd_render_bit_depth(int argc, char **argv) { RENDER_BIT_DEPTH_10; } else { return cmd_results_new(CMD_INVALID, - "Invalid bit depth. Must be a value in (8|10)."); + "Invalid bit depth. Must be a value in (6|8|10)."); } config->handler_context.leftovers.argc = argc - 1; diff --git a/sway/config/output.c b/sway/config/output.c index 25f51dc7f..d72307f74 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -398,9 +398,15 @@ static int compute_default_scale(struct wlr_output *output, return 2; } -static bool render_format_is_10bit(uint32_t render_format) { - return render_format == DRM_FORMAT_XRGB2101010 || - render_format == DRM_FORMAT_XBGR2101010; +static enum render_bit_depth bit_depth_from_format(uint32_t render_format) { + if (render_format == DRM_FORMAT_XRGB2101010 || render_format == DRM_FORMAT_XBGR2101010) { + return RENDER_BIT_DEPTH_10; + } else if (render_format == DRM_FORMAT_XRGB8888 || render_format == DRM_FORMAT_ARGB8888) { + return RENDER_BIT_DEPTH_8; + } else if (render_format == DRM_FORMAT_RGB565) { + return RENDER_BIT_DEPTH_6; + } + return RENDER_BIT_DEPTH_DEFAULT; } static bool render_format_is_bgr(uint32_t fmt) { @@ -496,11 +502,13 @@ static void queue_output_config(struct output_config *oc, if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { if (oc->render_bit_depth == RENDER_BIT_DEPTH_10 && - render_format_is_10bit(output->wlr_output->render_format)) { + bit_depth_from_format(output->wlr_output->render_format) == oc->render_bit_depth) { // 10-bit was set successfully before, try to save some tests by reusing the format wlr_output_state_set_render_format(pending, output->wlr_output->render_format); } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_10) { wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010); + } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_6){ + wlr_output_state_set_render_format(pending, DRM_FORMAT_RGB565); } else { wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888); } @@ -785,6 +793,7 @@ static bool search_render_format(struct search_context *ctx, size_t output_idx) DRM_FORMAT_XBGR2101010, DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, + DRM_FORMAT_RGB565, DRM_FORMAT_INVALID, }; if (render_format_is_bgr(wlr_output->render_format)) { @@ -795,9 +804,13 @@ static bool search_render_format(struct search_context *ctx, size_t output_idx) const struct wlr_drm_format_set *primary_formats = wlr_output_get_primary_formats(wlr_output, WLR_BUFFER_CAP_DMABUF); - bool need_10bit = cfg->config && cfg->config->render_bit_depth == RENDER_BIT_DEPTH_10; + enum render_bit_depth needed_bits = RENDER_BIT_DEPTH_8; + if (cfg->config && cfg->config->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { + needed_bits = cfg->config->render_bit_depth; + } for (size_t idx = 0; fmts[idx] != DRM_FORMAT_INVALID; idx++) { - if (!need_10bit && render_format_is_10bit(fmts[idx])) { + enum render_bit_depth format_bits = bit_depth_from_format(fmts[idx]); + if (needed_bits < format_bits) { continue; } if (!wlr_drm_format_set_get(primary_formats, fmts[idx])) { diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index d9a28807a..c5087b1bd 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -163,9 +163,9 @@ must be separated by one space. For example: adaptive sync can improve latency, but can cause flickering on some hardware. -*output* render_bit_depth 8|10 - Controls the color channel bit depth at which frames are rendered; the - default is currently 8 bits per channel. +*output* render_bit_depth 6|8|10 + Controls the maximum color channel bit depth at which frames are + rendered; the default is currently 8 bits per channel. Setting higher values will not have an effect if hardware and software lack support for such bit depths. Successfully increasing the render bit depth From 9765c29be11f04f903a45f34b9142a865784fc46 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Fri, 30 Aug 2024 02:10:38 +0200 Subject: [PATCH 106/361] config/output: Stringify render format when logging it --- sway/config/output.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sway/config/output.c b/sway/config/output.c index d72307f74..8e2528c80 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "sway/config.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" @@ -673,7 +674,9 @@ static void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_s sway_log(SWAY_DEBUG, " enabled: %s", state->enabled ? "yes" : "no"); } if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { - sway_log(SWAY_DEBUG, " render_format: %d", state->render_format); + char *format_name = drmGetFormatName(state->render_format); + sway_log(SWAY_DEBUG, " render_format: %s", format_name); + free(format_name); } if (state->committed & WLR_OUTPUT_STATE_MODE) { if (state->mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM) { From e940acd3749a5af08d5c404cae242c8693784ddc Mon Sep 17 00:00:00 2001 From: Emil Engberg Date: Fri, 20 Sep 2024 15:36:58 +0200 Subject: [PATCH 107/361] Add toggle for output adaptive_sync --- sway/commands/output/adaptive_sync.c | 24 ++++++++++++++++++++---- sway/sway-output.5.scd | 2 +- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/sway/commands/output/adaptive_sync.c b/sway/commands/output/adaptive_sync.c index 7382e2ee3..4ce88f885 100644 --- a/sway/commands/output/adaptive_sync.c +++ b/sway/commands/output/adaptive_sync.c @@ -1,5 +1,7 @@ +#include #include "sway/commands.h" #include "sway/config.h" +#include "sway/output.h" #include "util.h" struct cmd_results *output_cmd_adaptive_sync(int argc, char **argv) { @@ -10,12 +12,26 @@ struct cmd_results *output_cmd_adaptive_sync(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Missing adaptive_sync argument"); } - if (parse_boolean(argv[0], true)) { - config->handler_context.output_config->adaptive_sync = 1; - } else { - config->handler_context.output_config->adaptive_sync = 0; + bool current_value = true; + if (strcasecmp(argv[0], "toggle") == 0) { + const char *oc_name = config->handler_context.output_config->name; + if (strcmp(oc_name, "*") == 0) { + return cmd_results_new(CMD_INVALID, + "Cannot apply toggle to all outputs"); + } + + struct sway_output *sway_output = all_output_by_name_or_id(oc_name); + if (!sway_output || !sway_output->wlr_output) { + return cmd_results_new(CMD_FAILURE, + "Cannot apply toggle to unknown output %s", oc_name); + } + + current_value = + sway_output->wlr_output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED; } + config->handler_context.output_config->adaptive_sync = parse_boolean(argv[0], current_value); + config->handler_context.leftovers.argc = argc - 1; config->handler_context.leftovers.argv = argv + 1; return NULL; diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index c5087b1bd..dc16c257b 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -154,7 +154,7 @@ must be separated by one space. For example: This setting only has an effect on Wayland and DRM backends, as support for presentation timestamps and predicted output refresh rate is required. -*output* adaptive_sync on|off +*output* adaptive_sync on|off|toggle Enables or disables adaptive synchronization (often referred to as Variable Refresh Rate, or by the vendor-specific names FreeSync/G-Sync). From 266cd4515a015b5684eaf6c0b48ce1afa9be2739 Mon Sep 17 00:00:00 2001 From: Scott Dubinsky Date: Fri, 20 Sep 2024 18:12:08 +0300 Subject: [PATCH 108/361] Remove unguarded double include --- sway/input/input-manager.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 248ca34ee..ddfe14689 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include From 48069097ea55021afa0af3c5148cb7caab724dcf Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 4 Jun 2024 20:05:58 -0400 Subject: [PATCH 109/361] text_input: Check for allocation failure --- sway/input/text_input.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sway/input/text_input.c b/sway/input/text_input.c index 580a9f545..4d52fedc4 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -448,6 +448,11 @@ static void handle_im_new_popup_surface(struct wl_listener *listener, struct sway_input_method_relay *relay = wl_container_of(listener, relay, input_method_new_popup_surface); struct sway_input_popup *popup = calloc(1, sizeof(*popup)); + if (!popup) { + sway_log(SWAY_ERROR, "Failed to allocate an input method popup"); + return; + } + popup->relay = relay; popup->popup_surface = data; popup->popup_surface->data = popup; From 1537c9dae53eebea4926321aa9f7fd982375859f Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sun, 4 Aug 2024 13:06:06 -0400 Subject: [PATCH 110/361] text_input: Move popup placement to own function --- include/sway/input/text_input_popup.h | 1 + sway/input/text_input.c | 152 ++++++++++++++------------ 2 files changed, 84 insertions(+), 69 deletions(-) diff --git a/include/sway/input/text_input_popup.h b/include/sway/input/text_input_popup.h index e5f6ab8b5..97dd6444a 100644 --- a/include/sway/input/text_input_popup.h +++ b/include/sway/input/text_input_popup.h @@ -9,6 +9,7 @@ struct sway_input_popup { struct wlr_scene_tree *scene_tree; struct sway_popup_desc desc; struct wlr_input_popup_surface_v2 *popup_surface; + struct wlr_output *fixed_output; struct wl_list link; diff --git a/sway/input/text_input.c b/sway/input/text_input.c index 4d52fedc4..9be418f56 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -128,6 +128,80 @@ static void handle_im_destroy(struct wl_listener *listener, void *data) { } } +static void constrain_popup(struct sway_input_popup *popup) { + struct sway_text_input *text_input = + relay_get_focused_text_input(popup->relay); + + + struct wlr_box parent = {0}; + wlr_scene_node_coords(&popup->desc.relative->parent->node, &parent.x, &parent.y); + + struct wlr_box geo = {0}; + struct wlr_output *output; + + if (popup->desc.view) { + struct sway_view *view = popup->desc.view; + output = wlr_output_layout_output_at(root->output_layout, + view->container->pending.content_x + view->geometry.x, + view->container->pending.content_y + view->geometry.y); + + parent.width = view->geometry.width; + parent.height = view->geometry.height; + geo = view->geometry; + } else { + output = popup->fixed_output; + } + + struct wlr_box output_box; + wlr_output_layout_get_box(root->output_layout, output, &output_box); + + bool cursor_rect = text_input->input->current.features & + WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE; + struct wlr_box cursor_area; + if (cursor_rect) { + cursor_area = text_input->input->current.cursor_rectangle; + } else { + cursor_area = (struct wlr_box) { + .width = parent.width, + .height = parent.height, + }; + } + + int popup_width = popup->popup_surface->surface->current.width; + int popup_height = popup->popup_surface->surface->current.height; + int x1 = parent.x + cursor_area.x; + int x2 = parent.x + cursor_area.x + cursor_area.width; + int y1 = parent.y + cursor_area.y; + int y2 = parent.y + cursor_area.y + cursor_area.height; + int x = x1; + int y = y2; + + int available_right = output_box.x + output_box.width - x1; + int available_left = x2 - output_box.x; + if (available_right < popup_width && available_left > available_right) { + x = x2 - popup_width; + } + + int available_down = output_box.y + output_box.height - y2; + int available_up = y1 - output_box.y; + if (available_down < popup_height && available_up > available_down) { + y = y1 - popup_height; + } + + wlr_scene_node_set_position(popup->desc.relative, x - parent.x - geo.x, y - parent.y - geo.y); + if (cursor_rect) { + struct wlr_box box = { + .x = x1 - x, + .y = y1 - y, + .width = cursor_area.width, + .height = cursor_area.height, + }; + wlr_input_popup_surface_v2_send_text_input_rectangle( + popup->popup_surface, &box); + } + wlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y); +} + static void relay_send_im_state(struct sway_input_method_relay *relay, struct wlr_text_input_v3 *input) { struct wlr_input_method_v2 *input_method = relay->input_method; @@ -150,8 +224,7 @@ static void relay_send_im_state(struct sway_input_method_relay *relay, } struct sway_input_popup *popup; wl_list_for_each(popup, &relay->input_popups, link) { - // send_text_input_rectangle is called in this function - input_popup_update(popup); + constrain_popup(popup); } wlr_input_method_v2_send_done(input_method); // TODO: pass intent, display popup size @@ -297,50 +370,30 @@ static void input_popup_update(struct sway_input_popup *popup) { return; } - bool cursor_rect = text_input->input->current.features - & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE; struct wlr_surface *focused_surface = text_input->input->focused_surface; - struct wlr_box cursor_area = text_input->input->current.cursor_rectangle; - struct wlr_box output_box; - struct wlr_box parent = {0}; struct wlr_layer_surface_v1 *layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(focused_surface); struct wlr_scene_tree *relative_parent; - struct wlr_box geo = {0}; - popup->scene_tree = wlr_scene_subsurface_tree_create(root->layers.popup, popup->popup_surface->surface); if (layer_surface != NULL) { - struct sway_layer_surface *layer = - layer_surface->data; + struct sway_layer_surface *layer = layer_surface->data; if (layer == NULL) { return; } relative_parent = layer->scene->tree; - struct wlr_output *output = layer->layer_surface->output; - wlr_output_layout_get_box(root->output_layout, output, &output_box); - int lx, ly; - wlr_scene_node_coords(&layer->tree->node, &lx, &ly); - parent.x = lx; - parent.y = ly; popup->desc.view = NULL; + + // we don't need to add an event here to NULL out this field because + // this field will only be initialized if the popup is part of a layer + // surface. Layer surfaces get destroyed as part of the output being + // destroyed, thus also trickling down to popups. + popup->fixed_output = layer->layer_surface->output; } else { struct sway_view *view = view_from_wlr_surface(focused_surface); relative_parent = view->scene_tree; - geo = view->geometry; - int lx, ly; - wlr_scene_node_coords(&view->scene_tree->node, &lx, &ly); - struct wlr_output *output = wlr_output_layout_output_at(root->output_layout, - view->container->pending.content_x + view->geometry.x, - view->container->pending.content_y + view->geometry.y); - wlr_output_layout_get_box(root->output_layout, output, &output_box); - parent.x = lx; - parent.y = ly; - - parent.width = view->geometry.width; - parent.height = view->geometry.height; popup->desc.view = view; } @@ -354,46 +407,7 @@ static void input_popup_update(struct sway_input_popup *popup) { return; } - if (!cursor_rect) { - cursor_area.x = 0; - cursor_area.y = 0; - cursor_area.width = parent.width; - cursor_area.height = parent.height; - } - - int popup_width = popup->popup_surface->surface->current.width; - int popup_height = popup->popup_surface->surface->current.height; - int x1 = parent.x + cursor_area.x; - int x2 = parent.x + cursor_area.x + cursor_area.width; - int y1 = parent.y + cursor_area.y; - int y2 = parent.y + cursor_area.y + cursor_area.height; - int x = x1; - int y = y2; - - int available_right = output_box.x + output_box.width - x1; - int available_left = x2 - output_box.x; - if (available_right < popup_width && available_left > available_right) { - x = x2 - popup_width; - } - - int available_down = output_box.y + output_box.height - y2; - int available_up = y1 - output_box.y; - if (available_down < popup_height && available_up > available_down) { - y = y1 - popup_height; - } - - wlr_scene_node_set_position(&relative->node, x - parent.x - geo.x, y - parent.y - geo.y); - if (cursor_rect) { - struct wlr_box box = { - .x = x1 - x, - .y = y1 - y, - .width = cursor_area.width, - .height = cursor_area.height, - }; - wlr_input_popup_surface_v2_send_text_input_rectangle( - popup->popup_surface, &box); - } - wlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y); + constrain_popup(popup); } static void input_popup_set_focus(struct sway_input_popup *popup, From 023f6b0a50dd4fe17a29d7f02922e18ef37df857 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sun, 4 Aug 2024 13:02:37 -0400 Subject: [PATCH 111/361] transaction: Allow no popup descriptor in popup list Input method popups in the future will destroy the scene descriptor when it isn't mapped and therefore shouldn't be tampered with here. --- sway/desktop/transaction.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 931189891..8f12832a0 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -612,9 +612,11 @@ void arrange_popups(struct wlr_scene_tree *popups) { struct sway_popup_desc *popup = scene_descriptor_try_get(node, SWAY_SCENE_DESC_POPUP); - int lx, ly; - wlr_scene_node_coords(popup->relative, &lx, &ly); - wlr_scene_node_set_position(node, lx, ly); + if (popup) { + int lx, ly; + wlr_scene_node_coords(popup->relative, &lx, &ly); + wlr_scene_node_set_position(node, lx, ly); + } } } From 74e507962e32ec8d6606d28383ac109fbf2370e4 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sun, 4 Aug 2024 13:03:59 -0400 Subject: [PATCH 112/361] text_input: Properly handle map/unmap events The last implementation would ignore these and get it could get into a bad state where it would start crashing sway. --- include/sway/input/text_input_popup.h | 2 + sway/input/text_input.c | 64 +++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/include/sway/input/text_input_popup.h b/include/sway/input/text_input_popup.h index 97dd6444a..7e838ed2a 100644 --- a/include/sway/input/text_input_popup.h +++ b/include/sway/input/text_input_popup.h @@ -15,6 +15,8 @@ struct sway_input_popup { struct wl_listener popup_destroy; struct wl_listener popup_surface_commit; + struct wl_listener popup_surface_map; + struct wl_listener popup_surface_unmap; struct wl_listener focused_surface_unmap; }; diff --git a/sway/input/text_input.c b/sway/input/text_input.c index 9be418f56..1414215bc 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -132,6 +132,9 @@ static void constrain_popup(struct sway_input_popup *popup) { struct sway_text_input *text_input = relay_get_focused_text_input(popup->relay); + if (!popup->desc.relative) { + return; + } struct wlr_box parent = {0}; wlr_scene_node_coords(&popup->desc.relative->parent->node, &parent.x, &parent.y); @@ -199,7 +202,10 @@ static void constrain_popup(struct sway_input_popup *popup) { wlr_input_popup_surface_v2_send_text_input_rectangle( popup->popup_surface, &box); } - wlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y); + + if (popup->scene_tree) { + wlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y); + } } static void relay_send_im_state(struct sway_input_method_relay *relay, @@ -376,7 +382,6 @@ static void input_popup_update(struct sway_input_popup *popup) { wlr_layer_surface_v1_try_from_wlr_surface(focused_surface); struct wlr_scene_tree *relative_parent; - popup->scene_tree = wlr_scene_subsurface_tree_create(root->layers.popup, popup->popup_surface->surface); if (layer_surface != NULL) { struct sway_layer_surface *layer = layer_surface->data; if (layer == NULL) { @@ -435,19 +440,43 @@ static void input_popup_set_focus(struct sway_input_popup *popup, static void handle_im_popup_destroy(struct wl_listener *listener, void *data) { struct sway_input_popup *popup = wl_container_of(listener, popup, popup_destroy); + wlr_scene_node_destroy(&popup->scene_tree->node); wl_list_remove(&popup->focused_surface_unmap.link); wl_list_remove(&popup->popup_surface_commit.link); + wl_list_remove(&popup->popup_surface_map.link); + wl_list_remove(&popup->popup_surface_unmap.link); wl_list_remove(&popup->popup_destroy.link); wl_list_remove(&popup->link); free(popup); } -static void handle_im_popup_surface_commit(struct wl_listener *listener, - void *data) { +static void handle_im_popup_surface_map(struct wl_listener *listener, void *data) { + struct sway_input_popup *popup = + wl_container_of(listener, popup, popup_surface_map); + struct sway_text_input *text_input = relay_get_focused_text_input(popup->relay); + if (text_input != NULL) { + input_popup_set_focus(popup, text_input->input->focused_surface); + } else { + input_popup_set_focus(popup, NULL); + } +} + +static void handle_im_popup_surface_unmap(struct wl_listener *listener, void *data) { + struct sway_input_popup *popup = + wl_container_of(listener, popup, popup_surface_unmap); + + // relative should already be freed as it should be a child of the just unmapped scene + popup->desc.relative = NULL; + + input_popup_set_focus(popup, NULL); +} + +static void handle_im_popup_surface_commit(struct wl_listener *listener, void *data) { struct sway_input_popup *popup = wl_container_of(listener, popup, popup_surface_commit); - input_popup_update(popup); + + constrain_popup(popup); } static void handle_im_focused_surface_unmap( @@ -471,12 +500,29 @@ static void handle_im_new_popup_surface(struct wl_listener *listener, popup->popup_surface = data; popup->popup_surface->data = popup; - wl_signal_add( - &popup->popup_surface->events.destroy, &popup->popup_destroy); + popup->scene_tree = wlr_scene_tree_create(root->layers.popup); + if (!popup->scene_tree) { + sway_log(SWAY_ERROR, "Failed to allocate scene tree"); + free(popup); + return; + } + + if (!wlr_scene_subsurface_tree_create(popup->scene_tree, + popup->popup_surface->surface)) { + sway_log(SWAY_ERROR, "Failed to allocate subsurface tree"); + wlr_scene_node_destroy(&popup->scene_tree->node); + free(popup); + return; + } + + wl_signal_add(&popup->popup_surface->events.destroy, &popup->popup_destroy); popup->popup_destroy.notify = handle_im_popup_destroy; - wl_signal_add(&popup->popup_surface->surface->events.commit, - &popup->popup_surface_commit); + wl_signal_add(&popup->popup_surface->surface->events.commit, &popup->popup_surface_commit); popup->popup_surface_commit.notify = handle_im_popup_surface_commit; + wl_signal_add(&popup->popup_surface->surface->events.map, &popup->popup_surface_map); + popup->popup_surface_map.notify = handle_im_popup_surface_map; + wl_signal_add(&popup->popup_surface->surface->events.unmap, &popup->popup_surface_unmap); + popup->popup_surface_unmap.notify = handle_im_popup_surface_unmap; wl_list_init(&popup->focused_surface_unmap.link); popup->focused_surface_unmap.notify = handle_im_focused_surface_unmap; From e9dd2182313e9a480e2b3d48162142414d1fee48 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 8 Jun 2024 15:58:56 -0400 Subject: [PATCH 113/361] text_input: Inline input_popup_update into input_popup_set_focus This seems to be the intention of input_popup_update in the first place: handle the scenario where the focus moves. --- sway/input/text_input.c | 75 ++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/sway/input/text_input.c b/sway/input/text_input.c index 1414215bc..6bcd02341 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -11,8 +11,6 @@ #include "sway/layers.h" #include "sway/server.h" -static void input_popup_update(struct sway_input_popup *popup); - static struct sway_text_input *relay_get_focusable_text_input( struct sway_input_method_relay *relay) { struct sway_text_input *text_input = NULL; @@ -208,6 +206,9 @@ static void constrain_popup(struct sway_input_popup *popup) { } } +static void input_popup_set_focus(struct sway_input_popup *popup, + struct wlr_surface *surface); + static void relay_send_im_state(struct sway_input_method_relay *relay, struct wlr_text_input_v3 *input) { struct wlr_input_method_v2 *input_method = relay->input_method; @@ -228,9 +229,16 @@ static void relay_send_im_state(struct sway_input_method_relay *relay, input->current.content_type.hint, input->current.content_type.purpose); } + + struct sway_text_input *text_input = relay_get_focused_text_input(relay); + struct sway_input_popup *popup; wl_list_for_each(popup, &relay->input_popups, link) { - constrain_popup(popup); + if (text_input != NULL) { + input_popup_set_focus(popup, text_input->input->focused_surface); + } else { + input_popup_set_focus(popup, NULL); + } } wlr_input_method_v2_send_done(input_method); // TODO: pass intent, display popup size @@ -354,35 +362,35 @@ static void relay_handle_text_input(struct wl_listener *listener, sway_text_input_create(relay, wlr_text_input); } -static void input_popup_update(struct sway_input_popup *popup) { - struct sway_text_input *text_input = - relay_get_focused_text_input(popup->relay); +static void input_popup_set_focus(struct sway_input_popup *popup, + struct wlr_surface *surface) { + wl_list_remove(&popup->focused_surface_unmap.link); - if (text_input == NULL || text_input->input->focused_surface == NULL) { + if (!popup->scene_tree) { + wl_list_init(&popup->focused_surface_unmap.link); return; } - if (popup->scene_tree != NULL) { - wlr_scene_node_destroy(&popup->scene_tree->node); - popup->scene_tree = NULL; - } - if (popup->desc.relative != NULL) { + if (popup->desc.relative) { + scene_descriptor_destroy(&popup->scene_tree->node, SWAY_SCENE_DESC_POPUP); wlr_scene_node_destroy(popup->desc.relative); popup->desc.relative = NULL; } - popup->desc.view = NULL; - if (!popup->popup_surface->surface->mapped) { + if (surface == NULL) { + wl_list_init(&popup->focused_surface_unmap.link); + wlr_scene_node_set_enabled(&popup->scene_tree->node, false); return; } - struct wlr_surface *focused_surface = text_input->input->focused_surface; - struct wlr_layer_surface_v1 *layer_surface = - wlr_layer_surface_v1_try_from_wlr_surface(focused_surface); - struct wlr_scene_tree *relative_parent; + wlr_layer_surface_v1_try_from_wlr_surface(surface); + + struct wlr_scene_tree *relative_parent; + if (layer_surface) { + wl_signal_add(&layer_surface->surface->events.unmap, + &popup->focused_surface_unmap); - if (layer_surface != NULL) { struct sway_layer_surface *layer = layer_surface->data; if (layer == NULL) { return; @@ -397,7 +405,8 @@ static void input_popup_update(struct sway_input_popup *popup) { // destroyed, thus also trickling down to popups. popup->fixed_output = layer->layer_surface->output; } else { - struct sway_view *view = view_from_wlr_surface(focused_surface); + struct sway_view *view = view_from_wlr_surface(surface); + wl_signal_add(&view->events.unmap, &popup->focused_surface_unmap); relative_parent = view->scene_tree; popup->desc.view = view; } @@ -413,28 +422,7 @@ static void input_popup_update(struct sway_input_popup *popup) { } constrain_popup(popup); -} - -static void input_popup_set_focus(struct sway_input_popup *popup, - struct wlr_surface *surface) { - wl_list_remove(&popup->focused_surface_unmap.link); - - if (surface == NULL) { - wl_list_init(&popup->focused_surface_unmap.link); - input_popup_update(popup); - return; - } - struct wlr_layer_surface_v1 *layer_surface = - wlr_layer_surface_v1_try_from_wlr_surface(surface); - if (layer_surface != NULL) { - wl_signal_add( - &layer_surface->surface->events.unmap, &popup->focused_surface_unmap); - input_popup_update(popup); - return; - } - - struct sway_view *view = view_from_wlr_surface(surface); - wl_signal_add(&view->events.unmap, &popup->focused_surface_unmap); + wlr_scene_node_set_enabled(&popup->scene_tree->node, true); } static void handle_im_popup_destroy(struct wl_listener *listener, void *data) { @@ -483,7 +471,8 @@ static void handle_im_focused_surface_unmap( struct wl_listener *listener, void *data) { struct sway_input_popup *popup = wl_container_of(listener, popup, focused_surface_unmap); - input_popup_update(popup); + + input_popup_set_focus(popup, NULL); } static void handle_im_new_popup_surface(struct wl_listener *listener, From 861dde100ab5536bea190b078c6c51adb6814be5 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 21 Sep 2024 00:53:26 +0200 Subject: [PATCH 114/361] commands/gaps: Check config->reading instead Checking if the config is not active or is reloading is just a convoluted way of checking if the config is being read. --- sway/commands/gaps.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c index 1deeb56e1..7ac6fcff5 100644 --- a/sway/commands/gaps.c +++ b/sway/commands/gaps.c @@ -215,15 +215,13 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { return error; } - bool config_loading = !config->active || config->reloading; - if (argc == 2) { return gaps_set_defaults(argc, argv); } - if (argc == 4 && !config_loading) { + if (argc == 4 && !config->reading) { return gaps_set_runtime(argc, argv); } - if (config_loading) { + if (config->reading) { return cmd_results_new(CMD_INVALID, "Expected %s", expected_defaults); } return cmd_results_new(CMD_INVALID, "Expected %s or %s", From b6da218974d2e0508e7816f707fe0b1f1c97f0d6 Mon Sep 17 00:00:00 2001 From: Olivia Taliesin Date: Tue, 15 Feb 2022 14:46:12 -0700 Subject: [PATCH 115/361] Removed destination-is-ancestor check from container_move_to_container to match i3 behaviour --- sway/commands/move.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sway/commands/move.c b/sway/commands/move.c index ad106c648..8891514ce 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -240,7 +240,6 @@ static void container_move_to_workspace(struct sway_container *container, static void container_move_to_container(struct sway_container *container, struct sway_container *destination) { if (container == destination - || container_has_ancestor(container, destination) || container_has_ancestor(destination, container)) { return; } From b73f54a966a30c2253818b89fefda16477531c14 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 21 Sep 2024 00:51:14 +0200 Subject: [PATCH 116/361] desktop/output: Expose request_modeset We remove the struct sway_server argument for consistency with the rest of our internal APIs which rely on the global server instance. --- include/sway/config.h | 2 ++ sway/desktop/output.c | 18 ++++++++---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 3cd59722f..71721393e 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -704,6 +704,8 @@ struct output_config *find_output_config(struct sway_output *output); void free_output_config(struct output_config *oc); +void request_modeset(void); + bool spawn_swaybg(void); int workspace_output_cmp_workspace(const void *a, const void *b); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 0b0d5291e..c919f1398 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -390,17 +390,15 @@ static int timer_modeset_handle(void *data) { return 0; } -static void request_modeset(struct sway_server *server) { - if (server->delayed_modeset == NULL) { - server->delayed_modeset = wl_event_loop_add_timer(server->wl_event_loop, - timer_modeset_handle, server); - wl_event_source_timer_update(server->delayed_modeset, 10); +void request_modeset(void) { + if (server.delayed_modeset == NULL) { + server.delayed_modeset = wl_event_loop_add_timer(server.wl_event_loop, + timer_modeset_handle, &server); + wl_event_source_timer_update(server.delayed_modeset, 10); } } static void begin_destroy(struct sway_output *output) { - struct sway_server *server = output->server; - if (output->enabled) { output_disable(output); } @@ -420,7 +418,7 @@ static void begin_destroy(struct sway_output *output) { output->wlr_output->data = NULL; output->wlr_output = NULL; - request_modeset(server); + request_modeset(); } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -540,7 +538,7 @@ void handle_new_output(struct wl_listener *listener, void *data) { sway_session_lock_add_output(server->session_lock.lock, output); } - request_modeset(server); + request_modeset(); } static struct output_config *output_config_for_config_head( @@ -646,5 +644,5 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, break; } store_output_config(oc); - request_modeset(output->server); + request_modeset(); } From cdff4f7c74b76e9141164b8c154621646140d8ec Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 21 Sep 2024 01:00:04 +0200 Subject: [PATCH 117/361] config: Batch input/output configuration on load We batch modesets and input configuration performed during config reload but commit for every command during the intial config load. There is no need to perform commits during the initial config load as outputs have not yet been created, but swaybg spawn should still be batched. At the same time, replace direct calls to apply output configuration with request_modeset to properly handle the modeset timer. --- sway/commands/input.c | 2 +- sway/commands/output.c | 21 ++++++++++----------- sway/config.c | 10 ++++++---- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/sway/commands/input.c b/sway/commands/input.c index 35846b1cf..310375a94 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -94,7 +94,7 @@ struct cmd_results *cmd_input(int argc, char **argv) { return res; } - if (!config->reloading) { + if (!config->reading) { input_manager_apply_input_config(ic); } } else { diff --git a/sway/commands/output.c b/sway/commands/output.c index 3f65b909c..9d58413f2 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -107,17 +107,16 @@ struct cmd_results *cmd_output(int argc, char **argv) { store_output_config(output); - // If reloading, the output configs will be applied after reading the - // entire config and before the deferred commands so that an auto generated - // workspace name is not given to re-enabled outputs. - if (!config->reloading && !config->validating) { - apply_stored_output_configs(); - if (background) { - if (!spawn_swaybg()) { - return cmd_results_new(CMD_FAILURE, - "Failed to apply background configuration"); - } - } + if (config->reading) { + // When reading the config file, we wait till the end to do a single + // modeset and swaybg spawn. + return cmd_results_new(CMD_SUCCESS, NULL); + } + request_modeset(); + + if (background && !spawn_swaybg()) { + return cmd_results_new(CMD_FAILURE, + "Failed to apply background configuration"); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/config.c b/sway/config.c index 5fc414a14..1090edc5c 100644 --- a/sway/config.c +++ b/sway/config.c @@ -516,7 +516,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { // Only really necessary if not explicitly `font` is set in the config. config_update_font_height(); - if (is_active && !validating) { + if (!validating) { input_manager_verify_fallback_seat(); for (int i = 0; i < config->input_configs->length; i++) { @@ -533,12 +533,14 @@ bool load_main_config(const char *file, bool is_active, bool validating) { } sway_switch_retrigger_bindings_for_all(); - apply_stored_output_configs(); spawn_swaybg(); config->reloading = false; - if (config->swaynag_config_errors.client != NULL) { - swaynag_show(&config->swaynag_config_errors); + if (is_active) { + request_modeset(); + if (config->swaynag_config_errors.client != NULL) { + swaynag_show(&config->swaynag_config_errors); + } } } From 63345977e2c411359a049c40cf2c1044a22b4f4a Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 21 Sep 2024 01:02:54 +0200 Subject: [PATCH 118/361] desktop/output: Clear modeset timer on output manager apply If a modeset timer exists at the time we apply an output manager config, clear it to avoid a useless double commit. --- sway/desktop/output.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index c919f1398..1f1f8d682 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -607,6 +607,10 @@ static void output_manager_apply(struct sway_server *server, done: if (ok) { wlr_output_configuration_v1_send_succeeded(cfg); + if (server->delayed_modeset != NULL) { + wl_event_source_remove(server->delayed_modeset); + server->delayed_modeset = NULL; + } } else { wlr_output_configuration_v1_send_failed(cfg); } From 00e9a941523baa4afa1f9c077235aa7aa5e8aeab Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Wed, 25 Sep 2024 06:35:30 -0500 Subject: [PATCH 119/361] swaybar: Fix 100% cpu usage if dbus dies. Currently, swaybar does not gracefully die if it detects that the dbus connection was lost. Although it's not recommended to restart dbus without restarting the compositor, it can very easily happen. In the case it does, compositor's tray should not consume 100% cpu until it has to be force killed. apply suggestions just setting the bar to not running will call teardown and unref the dbus. --- swaybar/bar.c | 2 +- swaybar/ipc.c | 3 +-- swaybar/tray/tray.c | 12 ++++++++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/swaybar/bar.c b/swaybar/bar.c index 5b1213a8d..4d20f20f0 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -508,7 +508,7 @@ void bar_run(struct swaybar *bar) { } #if HAVE_TRAY if (bar->tray) { - loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, bar->tray->bus); + loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, bar); } #endif while (bar->running) { diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 03500bdf3..71c9a4c5e 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -518,8 +518,7 @@ static bool handle_barconfig_update(struct swaybar *bar, const char *payload, #if HAVE_TRAY if (oldcfg->tray_hidden && !newcfg->tray_hidden) { bar->tray = create_tray(bar); - loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, - bar->tray->bus); + loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, bar); } else if (bar->tray && newcfg->tray_hidden) { loop_remove_fd(bar->eventloop, bar->tray->fd); destroy_tray(bar->tray); diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c index b0545f4a7..a4f382bfb 100644 --- a/swaybar/tray/tray.c +++ b/swaybar/tray/tray.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -90,9 +91,16 @@ void destroy_tray(struct swaybar_tray *tray) { } void tray_in(int fd, short mask, void *data) { - sd_bus *bus = data; + struct swaybar *bar = data; int ret; - while ((ret = sd_bus_process(bus, NULL)) > 0) { + + if (mask & (POLLHUP | POLLERR)) { + sway_log(SWAY_ERROR, "D-Bus connection closed unexpectedly"); + bar->running = false; + return; + } + + while ((ret = sd_bus_process(bar->tray->bus, NULL)) > 0) { // This space intentionally left blank } if (ret < 0) { From a0b3606f1725ee56e8dc15ae51ce62d042c0668a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 29 Sep 2024 16:57:52 +0200 Subject: [PATCH 120/361] Add support for alpha-modifier-v1 --- sway/server.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/server.c b/sway/server.c index 30896baf6..f16a55e20 100644 --- a/sway/server.c +++ b/sway/server.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -326,6 +327,7 @@ bool server_init(struct sway_server *server) { &server->pointer_constraint); wlr_presentation_create(server->wl_display, server->backend); + wlr_alpha_modifier_v1_create(server->wl_display); server->output_manager_v1 = wlr_output_manager_v1_create(server->wl_display); From a2757e5f165eae445ae550fd1d13f9ec0db44efc Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 29 Sep 2024 17:38:27 +0200 Subject: [PATCH 121/361] release: push tags before creating GitHub release Otherwise the GitHub release isn't attached to the Git tag. --- release.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/release.sh b/release.sh index 62baf4153..c5644cfd0 100755 --- a/release.sh +++ b/release.sh @@ -28,4 +28,5 @@ archive=$prefix.tar.gz git archive --prefix="$prefix/" -o "$archive" "$next" gpg --output "$archive".sig --detach-sig "$archive" +git push --follow-tags gh release create "sway $next" -t "$next" -n "" -d "$archive" "$archive.sig" From 9a9be01ad4130e4e19b437fd064f90982974971f Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sun, 29 Sep 2024 16:50:00 -0400 Subject: [PATCH 122/361] Fix alpha-modifier-v1 --- sway/desktop/output.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 1f1f8d682..a4eaa4b33 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -216,6 +217,15 @@ static void output_configure_scene(struct sway_output *output, if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); + struct wlr_scene_surface *surface = wlr_scene_surface_try_from_buffer(buffer); + + if (surface) { + const struct wlr_alpha_modifier_surface_v1_state *alpha_modifier_state = + wlr_alpha_modifier_v1_get_surface_state(surface->surface); + if (alpha_modifier_state != NULL) { + opacity *= (float)alpha_modifier_state->multiplier; + } + } // hack: don't call the scene setter because that will damage all outputs // We don't want to damage outputs that aren't our current output that From c90cb37b2a0861548461daa9b75d75317e01b679 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Wed, 2 Oct 2024 15:55:17 +0200 Subject: [PATCH 123/361] Re-init renderer for all outputs on lost context sway_root.outputs only include enabled outputs. We also need to re-init the renderer for any disabled outputs, so use sway_root.all_outputs instead. Resolves the following heap-use-after-free accessing the render formats when a disabled output is modeset after a GPU reset has occurred. --- sway/server.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/server.c b/sway/server.c index f16a55e20..941f4cb26 100644 --- a/sway/server.c +++ b/sway/server.c @@ -205,8 +205,8 @@ static void handle_renderer_lost(struct wl_listener *listener, void *data) { wlr_compositor_set_renderer(server->compositor, renderer); - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; + struct sway_output *output; + wl_list_for_each(output, &root->all_outputs, link) { wlr_output_init_render(output->wlr_output, server->allocator, server->renderer); } From f855b0898bf00285d5a7b840963b327230486632 Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Mon, 7 Oct 2024 18:49:44 +0900 Subject: [PATCH 124/361] fix: sway crashes if switch to another workspace with surface when IME popup is shown in pr https://github.com/swaywm/sway/pull/8196, when im_popup_surface is unmapped, author set the popup->relative to NULL, butt popup is still in popup groups, where assert the relative is not NULL, this cause the panic Take the suggestion of Nefsen402, remove the line where set relative to NULL, and add NULL check in scene_descriptor_destory --- sway/input/text_input.c | 1 + sway/scene_descriptor.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/sway/input/text_input.c b/sway/input/text_input.c index 6bcd02341..e16724671 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -454,6 +454,7 @@ static void handle_im_popup_surface_unmap(struct wl_listener *listener, void *da struct sway_input_popup *popup = wl_container_of(listener, popup, popup_surface_unmap); + scene_descriptor_destroy(&popup->scene_tree->node, SWAY_SCENE_DESC_POPUP); // relative should already be freed as it should be a child of the just unmapped scene popup->desc.relative = NULL; diff --git a/sway/scene_descriptor.c b/sway/scene_descriptor.c index a30d46646..92bdda00c 100644 --- a/sway/scene_descriptor.c +++ b/sway/scene_descriptor.c @@ -39,6 +39,9 @@ void *scene_descriptor_try_get(struct wlr_scene_node *node, void scene_descriptor_destroy(struct wlr_scene_node *node, enum sway_scene_descriptor_type type) { struct scene_descriptor *desc = scene_node_get_descriptor(node, type); + if (!desc) { + return; + } descriptor_destroy(desc); } From 7f1cd0b73ba3290f8ee5f81fdf7f1ffa4c642ea7 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Tue, 8 Oct 2024 11:09:57 -0500 Subject: [PATCH 125/361] input/mouse: bugfix button2 being interpreted as trying to move the container Man sway(5) specifies that when tiling_drag is enable, the floating_mod can be used to drag tiling, as well as floating containers. However the current code indiscriminately assumes any button press to be intended for moving the container, consequently causing an unintended call to `seatop_move_tilting:handle_button` rather than `seatop_default:handle_button` to pass `state=WL_POINTER_BUTTON_STATE_RELEASED` to `get_active_mouse_binding` My idea was to make 'Handle moving a tiling container' follow the same path as 'Handle moving a floating container' because the initial call to handle moving a floating correctly exits that branch and ends up passing the RELEASED state to `get_active_mouse_binding`. Fixes #8334 --- sway/input/seatop_default.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 42ce333b8..d8b3b5bff 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -491,7 +491,9 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, // Handle moving a tiling container if (config->tiling_drag && (mod_pressed || on_titlebar) && state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating_or_child && - cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { + cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE && + button == (config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT)) { + // If moving a container by its title bar, use a threshold for the drag if (!mod_pressed && config->tiling_drag_threshold > 0) { seatop_begin_move_tiling_threshold(seat, cont); From 17e2e52c6d1bf4bfebde8f6b2869702aacc3750a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 10 Oct 2024 16:32:59 +0200 Subject: [PATCH 126/361] server: check backend support for timelines References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4848 --- sway/server.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/server.c b/sway/server.c index 941f4cb26..8b122446b 100644 --- a/sway/server.c +++ b/sway/server.c @@ -251,7 +251,8 @@ bool server_init(struct sway_server *server) { } } if (wlr_renderer_get_drm_fd(server->renderer) >= 0 && - server->renderer->features.timeline) { + server->renderer->features.timeline && + server->backend->features.timeline) { wlr_linux_drm_syncobj_manager_v1_create(server->wl_display, 1, wlr_renderer_get_drm_fd(server->renderer)); } From dd063a0ef7941404d40578bfbdc8c11c70647baa Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 8 Aug 2024 23:39:30 +0200 Subject: [PATCH 127/361] input/keyboard: add support for pointer keys References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4775 --- sway/input/keyboard.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 1a73df014..651c44c5a 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -267,6 +268,7 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, const xkb_keysym_t *pressed_keysyms, uint32_t modifiers, size_t keysyms_len) { for (size_t i = 0; i < keysyms_len; ++i) { xkb_keysym_t keysym = pressed_keysyms[i]; + if (keysym >= XKB_KEY_XF86Switch_VT_1 && keysym <= XKB_KEY_XF86Switch_VT_12) { #if WLR_HAS_SESSION @@ -282,6 +284,36 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, return false; } +static bool keyboard_execute_pointer_keysyms(struct sway_keyboard *keyboard, + uint32_t time, const xkb_keysym_t *pressed_keysyms, size_t keysyms_len, + enum wl_keyboard_key_state state) { + struct sway_cursor *cursor = keyboard->seat_device->sway_seat->cursor; + + for (size_t i = 0; i < keysyms_len; ++i) { + xkb_keysym_t keysym = pressed_keysyms[i]; + + uint32_t button = wlr_keyboard_keysym_to_pointer_button(keysym); + if (button != 0) { + dispatch_cursor_button(cursor, &keyboard->wlr->base, time, button, + (enum wl_pointer_button_state)state); + wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); + return true; + } + + int dx, dy; + wlr_keyboard_keysym_to_pointer_motion(keysym, &dx, &dy); + if (state == WL_KEYBOARD_KEY_STATE_PRESSED && (dx != 0 || dy != 0)) { + dx *= 10; + dy *= 10; + pointer_motion(cursor, time, &keyboard->wlr->base, dx, dy, dx, dy); + wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); + return true; + } + } + + return false; +} + /** * Get keysyms and modifiers from the keyboard as xkb sees them. * @@ -507,6 +539,11 @@ static void handle_key_event(struct sway_keyboard *keyboard, keyboard, keyinfo.raw_keysyms, keyinfo.raw_modifiers, keyinfo.raw_keysyms_len); } + if (!handled) { + handled = keyboard_execute_pointer_keysyms(keyboard, event->time_msec, + keyinfo.translated_keysyms, keyinfo.translated_keysyms_len, + event->state); + } if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { // If the pressed event was sent to a client and we have a focused From db76fefd0c61d2c85f448eeb43ca3a97c10770a5 Mon Sep 17 00:00:00 2001 From: Jan Palus Date: Wed, 16 Oct 2024 19:47:54 +0200 Subject: [PATCH 128/361] trigger container update after disabling urgent in timer switching workspace directly to urgent window creates timer which delays reset of urgent state so user is able to notice it. make sure state change is reflected visually as well (border change) by triggering container update Fixes: #8377 --- sway/input/seat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/input/seat.c b/sway/input/seat.c index 9a00a3e24..0dd26290e 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1094,6 +1094,7 @@ static void seat_send_unfocus(struct sway_node *node, struct sway_seat *seat) { static int handle_urgent_timeout(void *data) { struct sway_view *view = data; view_set_urgent(view, false); + container_update_itself_and_parents(view->container); return 0; } From ce6b2db0f2e9c71dda496d1aaaafcdcb9dade150 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 14 Oct 2024 16:03:55 -0400 Subject: [PATCH 129/361] layer_shell: Arrange exclusive zone clients first This makes layer_shell more stable against the order of clients. --- sway/desktop/layer_shell.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index b136a24e7..62c6a5118 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -54,7 +54,7 @@ struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( } static void arrange_surface(struct sway_output *output, const struct wlr_box *full_area, - struct wlr_box *usable_area, struct wlr_scene_tree *tree) { + struct wlr_box *usable_area, struct wlr_scene_tree *tree, bool exclusive) { struct wlr_scene_node *node; wl_list_for_each(node, &tree->children, link) { struct sway_layer_surface *surface = scene_descriptor_try_get(node, @@ -68,6 +68,10 @@ static void arrange_surface(struct sway_output *output, const struct wlr_box *fu continue; } + if ((surface->scene->layer_surface->current.exclusive_zone > 0) != exclusive) { + continue; + } + wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area); } } @@ -78,10 +82,14 @@ void arrange_layers(struct sway_output *output) { &usable_area.width, &usable_area.height); const struct wlr_box full_area = usable_area; - arrange_surface(output, &full_area, &usable_area, output->layers.shell_background); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_top); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, false); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, false); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, false); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, false); if (!wlr_box_equal(&usable_area, &output->usable_area)) { sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); From 8363699f145fca844772643ceedcdaa7c6b90982 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 17 Oct 2024 10:10:02 -0400 Subject: [PATCH 130/361] layer_shell: Restore sway 1.9 ordering --- sway/desktop/layer_shell.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 62c6a5118..333c09b49 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -82,14 +82,15 @@ void arrange_layers(struct sway_output *output) { &usable_area.width, &usable_area.height); const struct wlr_box full_area = usable_area; - arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, true); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, false); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, true); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, false); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, true); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, false); arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, false); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, false); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, false); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, false); if (!wlr_box_equal(&usable_area, &output->usable_area)) { sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); From 35d8adefc456735a1487e5feb103f30199688c7d Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 13 Oct 2024 13:25:47 +0200 Subject: [PATCH 131/361] input/seatop_default: refactor move/resize button logic Make it so config->floating_mod_inverse only applies when pressing mod, not when clicking on titlebars. Centralize logic into shared variables. --- sway/input/seatop_default.c | 71 +++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index d8b3b5bff..6a7dddd9a 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -355,6 +355,13 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + bool mod_pressed = modifiers & config->floating_mod; + uint32_t mod_move_btn = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; + uint32_t mod_resize_btn = config->floating_mod_inverse ? BTN_LEFT : BTN_RIGHT; + bool mod_move_btn_pressed = mod_pressed && button == mod_move_btn; + bool mod_resize_btn_pressed = mod_pressed && button == mod_resize_btn; + bool titlebar_left_btn_pressed = on_titlebar && button == BTN_LEFT; + // Handle mouse bindings if (trigger_pointer_button_binding(seat, device, button, state, modifiers, on_titlebar, on_border, on_contents, on_workspace)) { @@ -403,33 +410,28 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, } // Handle tiling resize via mod - bool mod_pressed = modifiers & config->floating_mod; - if (cont && !is_floating_or_child && mod_pressed && + if (cont && !is_floating_or_child && mod_pressed && mod_resize_btn_pressed && state == WL_POINTER_BUTTON_STATE_PRESSED) { - uint32_t btn_resize = config->floating_mod_inverse ? - BTN_LEFT : BTN_RIGHT; - if (button == btn_resize) { - edge = 0; - edge |= cursor->cursor->x > cont->pending.x + cont->pending.width / 2 ? - WLR_EDGE_RIGHT : WLR_EDGE_LEFT; - edge |= cursor->cursor->y > cont->pending.y + cont->pending.height / 2 ? - WLR_EDGE_BOTTOM : WLR_EDGE_TOP; + edge = 0; + edge |= cursor->cursor->x > cont->pending.x + cont->pending.width / 2 ? + WLR_EDGE_RIGHT : WLR_EDGE_LEFT; + edge |= cursor->cursor->y > cont->pending.y + cont->pending.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; + 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 changing focus when clicking on a container @@ -454,12 +456,10 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, // Handle beginning floating move if (cont && is_floating_or_child && !is_fullscreen_or_child && - state == WL_POINTER_BUTTON_STATE_PRESSED) { - uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; - if (button == btn_move && (mod_pressed || on_titlebar)) { - seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); - return; - } + state == WL_POINTER_BUTTON_STATE_PRESSED && + (mod_move_btn_pressed || titlebar_left_btn_pressed)) { + seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); + return; } // Handle beginning floating resize @@ -473,9 +473,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, } // Via mod+click - uint32_t btn_resize = config->floating_mod_inverse ? - BTN_LEFT : BTN_RIGHT; - if (mod_pressed && button == btn_resize) { + if (mod_resize_btn_pressed) { struct sway_container *floater = container_toplevel_ancestor(cont); edge = 0; edge |= cursor->cursor->x > floater->pending.x + floater->pending.width / 2 ? @@ -489,18 +487,15 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, } // Handle moving a tiling container - if (config->tiling_drag && (mod_pressed || on_titlebar) && + if (config->tiling_drag && (mod_move_btn_pressed || titlebar_left_btn_pressed) && state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating_or_child && - cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE && - button == (config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT)) { - + cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { // If moving a container by its 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; } From 7d93652105c370518f1e5856f8586b2297cab772 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Wed, 16 Oct 2024 21:55:57 +0200 Subject: [PATCH 132/361] config/output: Improve modeset state logging Include scale and subpixel in the output state log, and log the output state on first commit attempt instead of just during fallback search. --- sway/config/output.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index 8e2528c80..d774d17eb 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -692,6 +692,13 @@ static void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_s sway_log(SWAY_DEBUG, " adaptive_sync: %s", state->adaptive_sync_enabled ? "enabled": "disabled"); } + if (state->committed & WLR_OUTPUT_STATE_SCALE) { + sway_log(SWAY_DEBUG, " scale: %f", state->scale); + } + if (state->committed & WLR_OUTPUT_STATE_SUBPIXEL) { + sway_log(SWAY_DEBUG, " subpixel: %s", + sway_wl_output_subpixel_to_string(state->subpixel)); + } } static bool search_valid_config(struct search_context *ctx, size_t output_idx); @@ -906,9 +913,8 @@ static bool apply_resolved_output_configs(struct matched_output_config *configs, backend_state->output = cfg->output->wlr_output; wlr_output_state_init(&backend_state->base); - sway_log(SWAY_DEBUG, "Preparing config for %s", - cfg->output->wlr_output->name); queue_output_config(cfg->config, cfg->output, &backend_state->base); + dump_output_state(cfg->output->wlr_output, &backend_state->base); } struct wlr_output_swapchain_manager swapchain_mgr; From 7e0c0dda42183cf3f6a64bace230252cbeadbbd6 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 17 Oct 2024 00:19:29 +0200 Subject: [PATCH 133/361] config/output: Always set output states from config queue_output_config had some remaining logic that would avoid setting output states if they already appeared to be in effect. That is not what most of the states did nor what is currently expected, so clean that up. --- sway/config/output.c | 53 ++++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index d774d17eb..b9fb773a0 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -451,54 +451,41 @@ static void queue_output_config(struct output_config *oc, wlr_output_state_set_mode(pending, preferred_mode); } - 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)); + if (oc && oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) { wlr_output_state_set_subpixel(pending, oc->subpixel); + } else { + wlr_output_state_set_subpixel(pending, output->detected_subpixel); } - enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; if (oc && oc->transform >= 0) { - tr = oc->transform; + wlr_output_state_set_transform(pending, oc->transform); #if WLR_HAS_DRM_BACKEND } else if (wlr_output_is_drm(wlr_output)) { - tr = wlr_drm_connector_get_panel_orientation(wlr_output); - sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr); + wlr_output_state_set_transform(pending, + wlr_drm_connector_get_panel_orientation(wlr_output)); #endif - } - if (wlr_output->transform != tr) { - sway_log(SWAY_DEBUG, "Set %s transform to %d", wlr_output->name, tr); - wlr_output_state_set_transform(pending, tr); + } else { + wlr_output_state_set_transform(pending, WL_OUTPUT_TRANSFORM_NORMAL); } - // Apply the scale last before the commit, because the scale auto-detection - // reads the pending output size - float scale; + // Apply the scale after sorting out the mode, because the scale + // auto-detection reads the pending output size if (oc && oc->scale > 0) { - scale = oc->scale; - // The factional-scale-v1 protocol uses increments of 120ths to send // the scale factor to the client. Adjust the scale so that we use the // same value as the clients'. - float adjusted_scale = round(scale * 120) / 120; - if (scale != adjusted_scale) { - sway_log(SWAY_INFO, "Adjusting output scale from %f to %f", - scale, adjusted_scale); - scale = adjusted_scale; - } + wlr_output_state_set_scale(pending, round(oc->scale * 120) / 120); } else { - scale = compute_default_scale(wlr_output, pending); - sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); - } - if (scale != wlr_output->scale) { - sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale); - wlr_output_state_set_scale(pending, scale); + wlr_output_state_set_scale(pending, + compute_default_scale(wlr_output, pending)); } - if (oc && oc->adaptive_sync != -1 && wlr_output->adaptive_sync_supported) { - sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, - oc->adaptive_sync); - wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); + if (wlr_output->adaptive_sync_supported) { + if (oc && oc->adaptive_sync != -1) { + wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); + } else { + wlr_output_state_set_adaptive_sync_enabled(pending, false); + } } if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { @@ -513,6 +500,8 @@ static void queue_output_config(struct output_config *oc, } else { wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888); } + } else { + wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888); } } From af0d4a048a38847769fda4898a07a72401ee40be Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 17 Oct 2024 01:08:54 +0200 Subject: [PATCH 134/361] config/output: Always set all output fields on finalize --- sway/config/output.c | 51 +++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index b9fb773a0..f39082d04 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -520,24 +520,23 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output return true; } - if (oc) { - enum scale_filter_mode scale_filter_old = output->scale_filter; - switch (oc->scale_filter) { - case SCALE_FILTER_DEFAULT: - case SCALE_FILTER_SMART: - output->scale_filter = ceilf(wlr_output->scale) == wlr_output->scale ? - SCALE_FILTER_NEAREST : SCALE_FILTER_LINEAR; - break; - case SCALE_FILTER_LINEAR: - case SCALE_FILTER_NEAREST: - output->scale_filter = oc->scale_filter; - break; - } - if (scale_filter_old != output->scale_filter) { - sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, - sway_output_scale_filter_to_string(output->scale_filter)); - wlr_damage_ring_add_whole(&output->scene_output->damage_ring); - } + enum scale_filter_mode scale_filter_old = output->scale_filter; + enum scale_filter_mode scale_filter_new = oc ? oc->scale_filter : SCALE_FILTER_DEFAULT; + switch (scale_filter_new) { + case SCALE_FILTER_DEFAULT: + case SCALE_FILTER_SMART: + output->scale_filter = ceilf(wlr_output->scale) == wlr_output->scale ? + SCALE_FILTER_NEAREST : SCALE_FILTER_LINEAR; + break; + case SCALE_FILTER_LINEAR: + case SCALE_FILTER_NEAREST: + output->scale_filter = scale_filter_new; + break; + } + if (scale_filter_old != output->scale_filter) { + sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, + sway_output_scale_filter_to_string(output->scale_filter)); + wlr_damage_ring_add_whole(&output->scene_output->damage_ring); } // Find position for it @@ -560,25 +559,19 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output output_enable(output); } - if (oc && oc->max_render_time >= 0) { - sway_log(SWAY_DEBUG, "Set %s max render time to %d", - oc->name, oc->max_render_time); - output->max_render_time = oc->max_render_time; - } - if (oc && oc->set_color_transform) { if (oc->color_transform) { wlr_color_transform_ref(oc->color_transform); } wlr_color_transform_unref(output->color_transform); output->color_transform = oc->color_transform; + } else { + wlr_color_transform_unref(output->color_transform); + output->color_transform = NULL; } - if (oc && oc->allow_tearing >= 0) { - sway_log(SWAY_DEBUG, "Set %s allow tearing to %d", - oc->name, oc->allow_tearing); - output->allow_tearing = oc->allow_tearing; - } + output->max_render_time = oc && oc->max_render_time > 0 ? oc->max_render_time : 0; + output->allow_tearing = oc && oc->allow_tearing > 0; return true; } From 17ecb9eb1d355c677dc9cf772d372982b7b9541b Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 17 Oct 2024 01:19:28 +0200 Subject: [PATCH 135/361] config/output: Remove initial values in find_output_config Starting by setting some special initial output config values settings was something sway used to do when the config was initially being processed. This was later changed to always happen, as there shouldn't be differences in how output config is calculated during config load and after. Most of these values are redundant, as they are either the zero value or a value that would be selected if the unset (-1) value was found. For output transforms, the automatic panel orientation code would only trigger if the final output config has an unset transform, which the initial values set in find_output_config made impossible. Remove these initial values and instead use a fresh output config as is. --- sway/config/output.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index f39082d04..6db10b076 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -587,15 +587,6 @@ static struct output_config *find_output_config_from_list( return NULL; } - // Set output defaults for the "base" configuration - result->enabled = 1; - result->power = 1; - result->scale = 0; // auto - result->subpixel = sway_output->detected_subpixel; - result->transform = WL_OUTPUT_TRANSFORM_NORMAL; - result->max_render_time = 0; - result->allow_tearing = 0; - char id[128]; output_get_identifier(id, sizeof(id), sway_output); From a63027245a6805bb952e47c5751ecdd7d1063d2f Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 17 Oct 2024 01:28:38 +0200 Subject: [PATCH 136/361] config/output: Remove remaining logs from queue_output_config The job of queue_output_config is now just to fill out a wlr_output_state according to the output configuration, but it still has a lot of logging from before we had wlr_output_state or the new modeset logic, when queue_output_state instead touched the implicit pending state of wlr_output. Whatever debug logs it had would already be covered by the output state debug logs or the command debug logs, so let's just remove it. --- sway/config/output.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index 6db10b076..5fb282d4d 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -285,7 +285,6 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending mhz = mhz <= 0 ? INT_MAX : mhz; if (wl_list_empty(&output->modes) || custom) { - sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); wlr_output_state_set_custom_mode(pending, width, height, refresh_rate > 0 ? mhz : 0); return; @@ -305,10 +304,7 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending } } } - if (best) { - sway_log(SWAY_INFO, "Assigning configured mode (%dx%d@%.3fHz) to %s", - best->width, best->height, best->refresh / 1000.f, output->name); - } else { + if (!best) { best = wlr_output_preferred_mode(output); sway_log(SWAY_INFO, "Configured mode (%dx%d@%.3fHz) not available, " "applying preferred mode (%dx%d@%.3fHz)", @@ -325,7 +321,6 @@ static void set_modeline(struct wlr_output *output, sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); return; } - sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name); struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode); if (mode) { wlr_output_state_set_mode(pending, mode); @@ -391,7 +386,6 @@ static int compute_default_scale(struct wlr_output *output, double dpi_x = (double) width / (output->phys_width / MM_PER_INCH); double dpi_y = (double) height / (output->phys_height / MM_PER_INCH); - sway_log(SWAY_DEBUG, "Output DPI: %fx%f", dpi_x, dpi_y); if (dpi_x <= HIDPI_DPI_LIMIT || dpi_y <= HIDPI_DPI_LIMIT) { return 1; } @@ -427,25 +421,17 @@ static void queue_output_config(struct output_config *oc, struct wlr_output *wlr_output = output->wlr_output; if (output_config_is_disabling(oc)) { - sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); wlr_output_state_set_enabled(pending, false); return; } - - sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); wlr_output_state_set_enabled(pending, true); if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) { - sway_log(SWAY_DEBUG, "Set %s modeline", - wlr_output->name); set_modeline(wlr_output, pending, &oc->drm_mode); } else if (oc && oc->width > 0 && oc->height > 0) { - sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", - wlr_output->name, oc->width, oc->height, oc->refresh_rate); set_mode(wlr_output, pending, oc->width, oc->height, oc->refresh_rate, oc->custom_mode == 1); } else if (!wl_list_empty(&wlr_output->modes)) { - sway_log(SWAY_DEBUG, "Set preferred mode"); struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output); wlr_output_state_set_mode(pending, preferred_mode); From 015e357fce8250cdde03844ca81e9ae982ef4bfb Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 25 Oct 2024 18:38:55 +0300 Subject: [PATCH 137/361] desktop/output: chase wlroots private fields update This will be replaced with a proper solution later. --- sway/desktop/output.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index a4eaa4b33..18a04c090 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -188,8 +188,8 @@ static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output, struct wlr_scene_buffer *buffer) { // if we are scaling down, we should always choose linear if (buffer->dst_width > 0 && buffer->dst_height > 0 && ( - buffer->dst_width < buffer->buffer_width || - buffer->dst_height < buffer->buffer_height)) { + buffer->dst_width < buffer->WLR_PRIVATE.buffer_width || + buffer->dst_height < buffer->WLR_PRIVATE.buffer_height)) { return WLR_SCALE_FILTER_BILINEAR; } From 839434abc0438b4ea8d9bc497dcd2c33c5d77037 Mon Sep 17 00:00:00 2001 From: llyyr Date: Tue, 15 Oct 2024 23:07:46 +0530 Subject: [PATCH 138/361] sway/server: bind to presentation-time-v2 Depends on: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4858 --- sway/server.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/server.c b/sway/server.c index 8b122446b..e091d9464 100644 --- a/sway/server.c +++ b/sway/server.c @@ -70,6 +70,7 @@ #define SWAY_XDG_SHELL_VERSION 5 #define SWAY_LAYER_SHELL_VERSION 4 #define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1 +#define SWAY_PRESENTATION_VERSION 2 bool allow_unsupported_gpu = false; @@ -327,7 +328,7 @@ bool server_init(struct sway_server *server) { wl_signal_add(&server->pointer_constraints->events.new_constraint, &server->pointer_constraint); - wlr_presentation_create(server->wl_display, server->backend); + wlr_presentation_create(server->wl_display, server->backend, SWAY_PRESENTATION_VERSION); wlr_alpha_modifier_v1_create(server->wl_display); server->output_manager_v1 = From e7c972b04a109c59dae1dd73da4d9bd7cbc6800c Mon Sep 17 00:00:00 2001 From: AsciiWolf Date: Mon, 28 Oct 2024 12:36:08 +0100 Subject: [PATCH 139/361] Remove language bars from remaining non-English README files --- README.cs.md | 2 -- README.sv.md | 2 -- 2 files changed, 4 deletions(-) diff --git a/README.cs.md b/README.cs.md index fbb23b34e..14320a390 100644 --- a/README.cs.md +++ b/README.cs.md @@ -1,7 +1,5 @@ # sway -[English][en] - [عربي][ar] - **[Česky][cs]** - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - [ქართული][ge] - [Ελληνικά][gr] - [हिन्दी][hi] - [Magyar][hu] - [فارسی][ir] - [Italiano][it] - [日本語][ja] - [한국어][ko] - [Nederlands][nl] - [Norsk][no] - [Polski][pl] - [Português][pt] - [Română][ro] - [Русский][ru] - [Svenska][sv] - [Türkçe][tr] - [Українська][uk] - [中文-简体][zh-CN] - [中文-繁體][zh-TW] - sway je s [i3] kompatibilní [Wayland] kompozitor. Přečtěte si [FAQ]. Připojte se na [IRC kanál][IRC channel] \(#sway na irc.libera.chat). diff --git a/README.sv.md b/README.sv.md index c50ca068c..49caf9285 100644 --- a/README.sv.md +++ b/README.sv.md @@ -1,7 +1,5 @@ # sway -[English][en] - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - **[Svenska][sv]** - [Ελληνικά][gr] - [Magyar][hu] - [فارسی][ir] - [Italiano][it] - [日本語][ja] - [한국어][ko] - [Nederlands][nl] - [Polski][pl] - [Português][pt] - [Română][ro] - [Русский][ru] - [Türkçe][tr] - [Українська][uk] - [中文-简体][zh-CN] - [中文-繁體][zh-TW] - sway är en [i3]-kompatibel [Wayland] compositor. Läs våran [FAQ]-sida. Gå med i vår [IRC-kanal] \(#sway på irc.libera.chat). From 1e53007bc33c1afa2c3ea580050f94f29c5ce8e4 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 28 Oct 2024 11:47:10 +0100 Subject: [PATCH 140/361] desktop/output: Store output config on request_state An output backend might request any change to an output state at any time, although currently only this is currently only used for changing window size on the wayland and x11 backend. Applying the configuration directly means that the current output state becomes inconsistent with the configured state, which can cause the new state to be reverted later if apply_stored_output_configs is called. Before 4f9ce4675cf4. the output geometry would be updated by arrange_outputs, but this is only done by the modeset logic now, resulting in the stored geometry never being updated on wayland backend window resize. This was not discovered as the stored geometry is not used particularly often. Solve both by storing a new output configuration and relying on the modeset logic to apply a new state. Fixes: 4f9ce4675cf4 ("tree/arrange: Remove redundant output geometry update") --- sway/desktop/output.c | 48 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 18a04c090..394c545de 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -457,19 +457,47 @@ static void handle_request_state(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, request_state); const struct wlr_output_event_request_state *event = data; + const struct wlr_output_state *state = event->state; - uint32_t committed = event->state->committed; - wlr_output_commit_state(output->wlr_output, event->state); + // Store the requested changes so that the active configuration is + // consistent with the current state, and to avoid duplicate logic to apply + // the changes. + struct output_config *oc = new_output_config(output->wlr_output->name); + if (!oc) { + sway_log(SWAY_ERROR, "Allocation failed"); + return; + } - if (committed & ( - WLR_OUTPUT_STATE_MODE | - WLR_OUTPUT_STATE_TRANSFORM | - WLR_OUTPUT_STATE_SCALE)) { - arrange_layers(output); - arrange_output(output); - transaction_commit_dirty(); + int committed = state->committed; + if (committed & WLR_OUTPUT_STATE_MODE) { + if (state->mode != NULL) { + oc->width = state->mode->width; + oc->height = state->mode->height; + oc->refresh_rate = state->mode->refresh / 1000.f; + } else { + oc->width = state->custom_mode.width; + oc->height = state->custom_mode.height; + oc->refresh_rate = state->custom_mode.refresh / 1000.f; + } + committed &= ~WLR_OUTPUT_STATE_MODE; + } + if (committed & WLR_OUTPUT_STATE_SCALE) { + oc->scale = state->scale; + committed &= ~WLR_OUTPUT_STATE_SCALE; + } + if (committed & WLR_OUTPUT_STATE_TRANSFORM) { + oc->transform = state->transform; + committed &= ~WLR_OUTPUT_STATE_TRANSFORM; + } - update_output_manager_config(output->server); + // We do not expect or support any other changes here + assert(committed == 0); + store_output_config(oc); + apply_stored_output_configs(); + + if (server.delayed_modeset != NULL) { + wl_event_source_remove(server.delayed_modeset); + server.delayed_modeset = NULL; } } From f38719f575ab88e9d38bf4cfe3cb744071772bf2 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 28 Oct 2024 12:07:18 +0100 Subject: [PATCH 141/361] desktop/output: Add missing output config allocation checks --- sway/desktop/output.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 394c545de..064b5449b 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -582,6 +582,10 @@ void handle_new_output(struct wl_listener *listener, void *data) { static struct output_config *output_config_for_config_head( struct wlr_output_configuration_head_v1 *config_head) { struct output_config *oc = new_output_config(config_head->state.output->name); + if (!oc) { + return NULL; + } + oc->enabled = config_head->state.enabled; if (!oc->enabled) { return oc; @@ -612,7 +616,8 @@ static void output_manager_apply(struct sway_server *server, size_t configs_len = config->output_configs->length + wl_list_length(&cfg->heads); struct output_config **configs = calloc(configs_len, sizeof(*configs)); if (!configs) { - goto done; + sway_log(SWAY_ERROR, "Allocation failed"); + goto error; } size_t start_new_configs = config->output_configs->length; for (size_t idx = 0; idx < start_new_configs; idx++) { @@ -625,6 +630,10 @@ static void output_manager_apply(struct sway_server *server, // Generate the configuration and store it as a temporary // config. We keep a record of it so we can remove it later. struct output_config *oc = output_config_for_config_head(config_head); + if (!oc) { + sway_log(SWAY_ERROR, "Allocation failed"); + goto error_config; + } configs[config_idx++] = oc; } @@ -632,6 +641,8 @@ static void output_manager_apply(struct sway_server *server, // if any output configured for enablement fails to be enabled, even if it // was not part of the config heads we were asked to configure. ok = apply_output_configs(configs, configs_len, test_only, false); + +error_config: for (size_t idx = start_new_configs; idx < configs_len; idx++) { struct output_config *cfg = configs[idx]; if (!test_only && ok) { @@ -642,7 +653,7 @@ static void output_manager_apply(struct sway_server *server, } free(configs); -done: +error: if (ok) { wlr_output_configuration_v1_send_succeeded(cfg); if (server->delayed_modeset != NULL) { @@ -677,6 +688,11 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, struct sway_output *output = event->output->data; struct output_config *oc = new_output_config(output->wlr_output->name); + if (!oc) { + sway_log(SWAY_ERROR, "Allocation failed"); + return; + } + switch (event->mode) { case ZWLR_OUTPUT_POWER_V1_MODE_OFF: oc->power = 0; From d417a8fcd0a701395c2029adade261d19800f763 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 27 Oct 2024 21:55:24 +0100 Subject: [PATCH 142/361] release.sh: read meson-rewrite output from stdout Since version 1.6, Meson now uses stdout: https://github.com/mesonbuild/meson/commit/3f4957c713f70d708f066fff119088040bb1d287 --- release.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release.sh b/release.sh index c5644cfd0..6ad8f2c5e 100755 --- a/release.sh +++ b/release.sh @@ -1,7 +1,7 @@ #!/bin/sh -eu prev=$(git describe --tags --abbrev=0) -next=$(meson rewrite kwargs info project / 2>&1 >/dev/null | jq -r '.kwargs["project#/"].version') +next=$(meson rewrite kwargs info project / | jq -r '.kwargs["project#/"].version') case "$next" in *-dev) From 4cfcb3643bc2123c65c3748761d624c86df0002e Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sun, 3 Nov 2024 16:01:09 -0500 Subject: [PATCH 143/361] container: Properly constrain title bar padding Important for centered titles --- sway/tree/container.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index f482b06bc..62bff1ea5 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -349,7 +349,7 @@ void container_arrange_title_bar(struct sway_container *con) { h_padding = width - config->titlebar_h_padding - marks_buffer_width; } - h_padding = MAX(h_padding, 0); + h_padding = MAX(h_padding, config->titlebar_h_padding); int alloc_width = MIN((int)node->width, width - h_padding - config->titlebar_h_padding); @@ -375,7 +375,7 @@ void container_arrange_title_bar(struct sway_container *con) { h_padding = config->titlebar_h_padding; } - h_padding = MAX(h_padding, 0); + h_padding = MAX(h_padding, config->titlebar_h_padding); int alloc_width = MIN((int) node->width, width - h_padding - config->titlebar_h_padding); From 78fa4e985618209a289f892dc54d67c83494a9d2 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 5 Nov 2024 15:31:13 +0100 Subject: [PATCH 144/361] config/output: Update output position in two passes The modeset logic iterates over all outputs at the end, sets their new position in the layout and takes a copy of its geometry that is later referenced by layout and scene management code. If one output is auto configured, then a later output that is manually configured can lead to the first output being moved without the stored geometry being updated. Split this into two passes: The first pass finalizes the output config and makes updates to the layout, while the second pass updates the copy of the geometry and arranges things as a result of it. --- sway/config/output.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index 5fb282d4d..f8922ea52 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -533,14 +533,6 @@ static bool finalize_output_config(struct output_config *oc, struct sway_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_box); - output->lx = output_box.x; - output->ly = output_box.y; - output->width = output_box.width; - output->height = output_box.height; - if (!output->enabled) { output_enable(output); } @@ -562,6 +554,15 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output return true; } +static void output_update_position(struct sway_output *output) { + struct wlr_box output_box; + wlr_output_layout_get_box(root->output_layout, output->wlr_output, &output_box); + output->lx = output_box.x; + output->ly = output_box.y; + output->width = output_box.width; + output->height = output_box.height; +} + // find_output_config_from_list returns a merged output_config containing all // stored configuration that applies to the specified output. static struct output_config *find_output_config_from_list( @@ -933,6 +934,13 @@ static bool apply_resolved_output_configs(struct matched_output_config *configs, sway_log(SWAY_DEBUG, "Finalizing config for %s", cfg->output->wlr_output->name); finalize_output_config(cfg->config, cfg->output); + } + + // Output layout being applied in finalize_output_config can shift outputs + // around, so we do a second pass to update positions and arrange. + for (size_t idx = 0; idx < configs_len; idx++) { + struct matched_output_config *cfg = &configs[idx]; + output_update_position(cfg->output); arrange_layers(cfg->output); } From 62fd8c4d011ea4dc360831723a3844c0e4439f32 Mon Sep 17 00:00:00 2001 From: mtvare6 Date: Tue, 29 Oct 2024 14:56:33 +0530 Subject: [PATCH 145/361] desktop/transaction: clamp vertical border length to 0 Fixes #8120 --- sway/desktop/transaction.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 8f12832a0..50a597a66 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -424,13 +424,14 @@ static void arrange_container(struct sway_container *con, int border_bottom = con->current.border_bottom ? border_width : 0; int border_left = con->current.border_left ? border_width : 0; int border_right = con->current.border_right ? border_width : 0; + int vert_border_height = MAX(0, height - border_top - border_bottom); wlr_scene_rect_set_size(con->border.top, width, border_top); wlr_scene_rect_set_size(con->border.bottom, width, border_bottom); wlr_scene_rect_set_size(con->border.left, - border_left, height - border_top - border_bottom); + border_left, vert_border_height); wlr_scene_rect_set_size(con->border.right, - border_right, height - border_top - border_bottom); + border_right, vert_border_height); wlr_scene_node_set_position(&con->border.top->node, 0, 0); wlr_scene_node_set_position(&con->border.bottom->node, From 03483ff3707a358d935e451d39748e58c205ce8a Mon Sep 17 00:00:00 2001 From: Manuel Stoeckl Date: Fri, 8 Nov 2024 19:44:30 -0500 Subject: [PATCH 146/361] swaynag: fix null dereference on scale change If cursor-shape-v1 is available, the old wl_cursor_theme path should not be used. --- swaynag/swaynag.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c index 50eea1483..da32eeb76 100644 --- a/swaynag/swaynag.c +++ b/swaynag/swaynag.c @@ -324,7 +324,9 @@ static void output_scale(void *data, struct wl_output *output, swaynag_output->scale = factor; if (swaynag_output->swaynag->output == swaynag_output) { swaynag_output->swaynag->scale = swaynag_output->scale; - update_all_cursors(swaynag_output->swaynag); + if (!swaynag_output->swaynag->cursor_shape_manager) { + update_all_cursors(swaynag_output->swaynag); + } render_frame(swaynag_output->swaynag); } } From f23d10074739de31e9339796dc06348fd919c515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joan=20Bruguera=20Mic=C3=B3?= Date: Sun, 10 Nov 2024 15:24:15 +0000 Subject: [PATCH 147/361] swaybar: Emit property changes for SNI watcher Emit property change signals for the IsStatusNotifierHostRegistered and RegisteredStatusNotifierItems properties in StatusNotifierWatcher, so code relying on the PropertiesChanged signal, instead of signals such as StatusNotifierHostRegistered, can work properly. A library that is affected by this is the libappindicator-gtk3* library and it can cause tray icons to be missing after starting swaybar due to a race condition, as follows: * An application using libappindicator-gtk3 starts, e.g. nm-applet. * Some time later, swaybar starts. * swaybar creates the StatusNotifierWatcher. * libappindicator-gtk3 observes the new watcher, but it sees that IsStatusNotifierHostRegistered=false, so it falls back to the Freedesktop System tray protocol. * swaybar creates the StatusNotifierHost. At this point, libappindicator-gtk3 should "un-fallback" back to SNI. However, since swaybar does not emit the PropertiesChange signal on IsStatusNotifierHostRegistered, libappindicator-gtk3 doesn't get notified, and stays in fallback state forever. * As a result, nm-applet will not show in the swaybar tray. This race can be made reliable by inserting a 1-second long sleep here: https://github.com/swaywm/sway/blob/03483ff3707a358d935e451d39748e58c205ce8a/swaybar/tray/tray.c#L57 (*) Note that the libappindicator-gtk3 library has been mostly replaced by libayatana-appindicator, which is not affected by this. The affected version is still used by Arch Linux, source code at: https://bazaar.launchpad.net/~indicator-applet-developers/libappindicator/trunk/files/298 --- swaybar/tray/watcher.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c index 3cfea8d8e..54f000bd6 100644 --- a/swaybar/tray/watcher.c +++ b/swaybar/tray/watcher.c @@ -38,6 +38,8 @@ static int handle_lost_service(sd_bus_message *msg, list_del(watcher->items, idx--); sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, "StatusNotifierItemUnregistered", "s", id); + sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface, + "RegisteredStatusNotifierItems", NULL); free(id); if (using_standard_protocol(watcher)) { break; @@ -50,6 +52,10 @@ static int handle_lost_service(sd_bus_message *msg, sway_log(SWAY_DEBUG, "Unregistering Status Notifier Host '%s'", service); free(watcher->hosts->items[idx]); list_del(watcher->hosts, idx); + if (watcher->hosts->length == 0) { + sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface, + "IsStatusNotifierHostRegistered", NULL); + } } } @@ -82,6 +88,8 @@ static int register_sni(sd_bus_message *msg, void *data, sd_bus_error *error) { if (list_seq_find(watcher->items, cmp_id, id) == -1) { sway_log(SWAY_DEBUG, "Registering Status Notifier Item '%s'", id); list_add(watcher->items, id); + sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface, + "RegisteredStatusNotifierItems", NULL); sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, "StatusNotifierItemRegistered", "s", id); } else { @@ -104,6 +112,10 @@ static int register_host(sd_bus_message *msg, void *data, sd_bus_error *error) { if (list_seq_find(watcher->hosts, cmp_id, service) == -1) { sway_log(SWAY_DEBUG, "Registering Status Notifier Host '%s'", service); list_add(watcher->hosts, strdup(service)); + if (watcher->hosts->length == 1) { + sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface, + "IsStatusNotifierHostRegistered", NULL); + } sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, "StatusNotifierHostRegistered", ""); } else { From 463c4c9369dc551c51c0888b411d49f8d9660a85 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 11 Nov 2024 12:47:52 +0100 Subject: [PATCH 148/361] desktop/output: Clean up output state if build_state fails wlr_scene_output_build_state can fail for various reasons. Ensure that the pending output state is cleaned up in that case. --- sway/desktop/output.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 064b5449b..9b98e29a4 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -282,6 +282,7 @@ static int output_repaint_timer_handler(void *data) { struct wlr_output_state pending; wlr_output_state_init(&pending); if (!wlr_scene_output_build_state(output->scene_output, &pending, &opts)) { + wlr_output_state_finish(&pending); return 0; } From fdc4318ac66d257d21e8f3b953e341d5e80a1ddc Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 11 Nov 2024 12:48:50 +0100 Subject: [PATCH 149/361] desktop/output: Clear frame_pending even output is disabled frame_pending should always be cleared once the repaint callback is fired to ensure that future frame scheduling is not accidentally held back. --- sway/desktop/output.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 9b98e29a4..8682cc656 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -262,12 +262,11 @@ static bool output_can_tear(struct sway_output *output) { static int output_repaint_timer_handler(void *data) { struct sway_output *output = data; + output->wlr_output->frame_pending = false; if (!output->enabled) { return 0; } - output->wlr_output->frame_pending = false; - output_configure_scene(output, &root->root_scene->tree.node, 1.0f); struct wlr_scene_output_state_options opts = { From 96db66abf0622c8590d60ad6638b35d90e185f60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Solt=20Budav=C3=A1ri?= <3123434+solt87@users.noreply.github.com> Date: Sat, 16 Nov 2024 13:47:50 +0000 Subject: [PATCH 150/361] Fix orthographic mistakes in Hungarian README Fix a few mistakes so the text conforms to Hungarian orthography. The following rules were applied (the whole linked page is in Hungarian): - - - - - --- README.hu.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.hu.md b/README.hu.md index 82ca6785b..2fcc36dd5 100644 --- a/README.hu.md +++ b/README.hu.md @@ -1,10 +1,10 @@ # sway -A Sway egy [i3]-kompatibilis [Wayland] kompozitor. Olvasd el a [Gyarkan Ismételt Kérdéseket][FAQ]. Csatlakozz az [IRC csatornához][IRC channel] \(`#sway` az `irc.libera.chat`-en). +A Sway egy [i3]-kompatibilis [Wayland]-kompozitor. Olvasd el a [Gyarkan Ismételt Kérdéseket][FAQ]. Csatlakozz az [IRC-csatornához][IRC channel] \(`#sway` az `irc.libera.chat`-en). -## Csomag aláírások +## Csomagaláírások -A kiadott csomagok az [E88F5E48] kulccsal vannak aláírva és [GitHub-on][GitHub releases] publikálva. +A kiadott csomagok az [E88F5E48] kulccsal vannak aláírva, és [GitHubon][GitHub releases] publikálva. ## Telepítés @@ -13,12 +13,12 @@ A kiadott csomagok az [E88F5E48] kulccsal vannak aláírva és [GitHub-on][GitHu A Sway sok disztribúció csomagkezelőjéből elérhető, próbáld meg a "sway" csomagot telepíteni az általad használt eszközzel. -Ha szeretnél csomagot készíteni a saját disztribúciódhoz, ugorj be az IRC +Ha szeretnél csomagot készíteni a saját disztribúciódhoz, ugorj be az IRC- csatornára, vagy küldj levelet a sir@cmpwn.com címre tanácsokért. ### Fordítás forráskódból -Olvasd el [ezt a wiki oldalt][Development setup], ha szeretnéd tesztelési vagy +Olvasd el [ezt a wikioldalt][Development setup], ha szeretnéd tesztelési vagy fejlesztési célokból lefordítani az aktuális (HEAD) állapotát a `sway`-nek és a `wlroots`-nak. @@ -46,7 +46,7 @@ Futtasd ezeket a parancsokat: ## Konfiguráció -Ha előzőleg i3-mat használtál, akkor átmásolhatod az i3 beállításaidat a +Ha előzőleg i3-at használtál, akkor átmásolhatod az i3-beállításaidat a `~/.config/sway/config` file-ba és ugyanúgy működni fognak. Egyéb esetben másold le kiindulási alapnak a mintát, ami általában az `etc/sway/config` elérési útvonalon található. @@ -55,7 +55,7 @@ kapcsolatban. ## Futtatás -Futtasd a `sway` parancsot egy TTY felületről. Néhány bejelentkezéskezelő +Futtasd a `sway` parancsot egy TTY-felületről. Néhány bejelentkezéskezelő (display manager) működhet, de alapvetően nem támogatottak a sway által. (A gdm-ről ismeretes, hogy egész jól működik.) From 6111297d91faf5f2820acaa14d76d6389b469b77 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 11 Nov 2024 12:16:44 +0100 Subject: [PATCH 151/361] config: Force modeset before running deferred configs Some commands require outputs to be enabled. These commands are deferred to allow outputs to be discovered, but the delayed modeset might only run some time later. Force a modeset to occur before running deferred commands. Fixes: https://github.com/swaywm/sway/issues/8433 --- include/sway/config.h | 1 + sway/desktop/output.c | 14 +++++++++----- sway/main.c | 1 + 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 71721393e..013853c85 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -705,6 +705,7 @@ struct output_config *find_output_config(struct sway_output *output); void free_output_config(struct output_config *oc); void request_modeset(void); +void force_modeset(void); bool spawn_swaybg(void); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 8682cc656..5c2332ceb 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -408,6 +408,14 @@ void request_modeset(void) { } } +void force_modeset(void) { + if (server.delayed_modeset != NULL) { + wl_event_source_remove(server.delayed_modeset); + server.delayed_modeset = NULL; + } + apply_stored_output_configs(); +} + static void begin_destroy(struct sway_output *output) { if (output->enabled) { output_disable(output); @@ -493,12 +501,8 @@ static void handle_request_state(struct wl_listener *listener, void *data) { // We do not expect or support any other changes here assert(committed == 0); store_output_config(oc); - apply_stored_output_configs(); - if (server.delayed_modeset != NULL) { - wl_event_source_remove(server.delayed_modeset); - server.delayed_modeset = NULL; - } + force_modeset(); } static unsigned int last_headless_num = 0; diff --git a/sway/main.c b/sway/main.c index 1c4939aa0..165ccc095 100644 --- a/sway/main.c +++ b/sway/main.c @@ -361,6 +361,7 @@ int main(int argc, char **argv) { } config->active = true; + force_modeset(); load_swaybars(); run_deferred_commands(); run_deferred_bindings(); From a2c73c9b8b426417e0253ea0420b98298af1c963 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sun, 17 Nov 2024 12:38:32 +0100 Subject: [PATCH 152/361] ipc-server: Force modeset if needed after executing commands IPC clients generally expect executed commands to have taken effect when the command completes, while delayed modeset means that it can take several milliseconds more before e.g. an output is enabled. However, modesetting on every output command in the IPC call could on systems with already slow modesetting behavior lead to an unresponsive system for a not insignificant period of time. To strike a balance, force modeset once all the commands of this IPC call have executed if a modeset is pending. --- include/sway/config.h | 1 + sway/desktop/output.c | 4 ++++ sway/ipc-server.c | 6 ++++++ 3 files changed, 11 insertions(+) diff --git a/include/sway/config.h b/include/sway/config.h index 013853c85..bb770c6f7 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -706,6 +706,7 @@ void free_output_config(struct output_config *oc); void request_modeset(void); void force_modeset(void); +bool modeset_is_pending(void); bool spawn_swaybg(void); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 5c2332ceb..c7ad8f937 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -408,6 +408,10 @@ void request_modeset(void) { } } +bool modeset_is_pending(void) { + return server.delayed_modeset != NULL; +} + void force_modeset(void) { if (server.delayed_modeset != NULL) { wl_event_source_remove(server.delayed_modeset); diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 7f353c0ec..b934bb568 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -648,6 +648,12 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt } list_t *res_list = execute_command(buf, NULL, NULL); + if (modeset_is_pending()) { + // IPC expects commands to have taken immediate effect, so we need + // to force a modeset after output commands. We do a single modeset + // here to avoid modesetting for every output command in sequence. + force_modeset(); + } transaction_commit_dirty(); char *json = cmd_results_to_json(res_list); int length = strlen(json); From fec3da7d58c06421355e4b3dd1dcf291fd20627c Mon Sep 17 00:00:00 2001 From: mtvare6 Date: Wed, 13 Nov 2024 22:32:00 +0530 Subject: [PATCH 153/361] commands/include: handle many files in single line i3 supports including multiple files in a single line, whereas sway limits to single file per line. --- sway/commands/include.c | 6 +++--- sway/sway.5.scd | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sway/commands/include.c b/sway/commands/include.c index d4c14c35f..e0d0c0640 100644 --- a/sway/commands/include.c +++ b/sway/commands/include.c @@ -3,12 +3,12 @@ struct cmd_results *cmd_include(int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, "include", EXPECTED_EQUAL_TO, 1))) { + if ((error = checkarg(argc, "include", EXPECTED_AT_LEAST, 1))) { return error; } - + char *files = join_args(argv, argc); // We don't care if the included config(s) fails to load. - load_include_configs(argv[0], config, &config->swaynag_config_errors); + load_include_configs(files, config, &config->swaynag_config_errors); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/sway.5.scd b/sway/sway.5.scd index bb958ebfd..50de552f0 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -66,8 +66,8 @@ The following commands may only be used in the configuration file. *default_orientation* horizontal|vertical|auto Sets the default container layout for tiled containers. -*include* - Includes another file from _path_. _path_ can be either a full path or a +*include* + Include files from _paths_. _paths_ can include either a full path or a path relative to the parent config, and expands shell syntax (see *wordexp*(3) for details). The same include file can only be included once; subsequent attempts will be ignored. From 5312376077254d6431bb92ba22de3840b9933f67 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sun, 17 Nov 2024 21:43:31 +0100 Subject: [PATCH 154/361] desktop/output: Clear repaint timer earlier in destroy The teardown of a sway_output is split in two: begin_destroy and output_destroy. The former clears some state such as NULL'ing the reference to wlr_output, while the latter frees the struct and its remaining resources. If an output is destroyed while a repaint timer is pending, future frame callbacks will no longer occur as the listener is torn down in begin_destroy, but the repaint timer is not torn down and may still fire until output_destroy is hit. As begin_destroy cleared the reference to wlr_output, this leads to a NULL-pointer dereference. Tear down the repaint timer in begin_destroy as there is no need for it. Fixes: fdc4318ac66d ("desktop/output: Clear frame_pending even output is disabled") --- sway/desktop/output.c | 3 +++ sway/tree/output.c | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index c7ad8f937..607eca2c3 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -440,6 +440,9 @@ static void begin_destroy(struct sway_output *output) { output->wlr_output->data = NULL; output->wlr_output = NULL; + wl_event_source_remove(output->repaint_timer); + output->repaint_timer = NULL; + request_modeset(); } diff --git a/sway/tree/output.c b/sway/tree/output.c index 44b941ca2..65d9e3e71 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -273,7 +273,6 @@ void output_destroy(struct sway_output *output) { destroy_scene_layers(output); list_free(output->workspaces); list_free(output->current.workspaces); - wl_event_source_remove(output->repaint_timer); wlr_color_transform_unref(output->color_transform); free(output); } From e2409aa49611bee1e1b99033461bfab0a7550c48 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 20 Nov 2024 20:41:16 +0100 Subject: [PATCH 155/361] ipc-json: handle LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY New entry introduced in libinput 1.27.0. --- meson.build | 8 +++----- sway/ipc-json.c | 5 +++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index 71e75fd9b..9ce5723e7 100644 --- a/meson.build +++ b/meson.build @@ -109,11 +109,9 @@ conf_data.set10('HAVE_LIBSYSTEMD', sdbus.found() and sdbus.name() == 'libsystemd conf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind') conf_data.set10('HAVE_BASU', sdbus.found() and sdbus.name() == 'basu') conf_data.set10('HAVE_TRAY', have_tray) -conf_data.set10('HAVE_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', cc.has_header_symbol( - 'libinput.h', - 'LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', - dependencies: libinput, -)) +foreach sym : ['LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', 'LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY'] + conf_data.set10('HAVE_' + sym, cc.has_header_symbol('libinput.h', sym, dependencies: libinput)) +endforeach scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) if scdoc.found() diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 571338a4f..fc1df2ac6 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -931,6 +931,11 @@ static json_object *describe_libinput_device(struct libinput_device *device) { case LIBINPUT_CONFIG_DRAG_LOCK_DISABLED: drag_lock = "disabled"; break; +#if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY + case LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY: + drag_lock = "enabled_sticky"; + break; +#endif } json_object_object_add(object, "tap_drag_lock", json_object_new_string(drag_lock)); From bbadf9b8b10d171a6d5196da7716ea50ee7a6062 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 20 Nov 2024 20:50:27 +0100 Subject: [PATCH 156/361] Add support for LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY Use it as the default, as recommended by the libinput release notes: https://lists.freedesktop.org/archives/wayland-devel/2024-November/043860.html --- sway/commands/input/drag_lock.c | 5 +++++ sway/input/libinput.c | 8 ++++++++ sway/sway-input.5.scd | 5 +++-- sway/sway-ipc.7.scd | 3 ++- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/sway/commands/input/drag_lock.c b/sway/commands/input/drag_lock.c index 24c548e2d..55a39a64a 100644 --- a/sway/commands/input/drag_lock.c +++ b/sway/commands/input/drag_lock.c @@ -15,6 +15,11 @@ struct cmd_results *input_cmd_drag_lock(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "No input device defined."); } +#if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY + if (strcmp(argv[0], "enabled_sticky")) { + ic->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY; + } else +#endif if (parse_boolean(argv[0], true)) { ic->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; } else { diff --git a/sway/input/libinput.c b/sway/input/libinput.c index 2fec290e7..ad8592703 100644 --- a/sway/input/libinput.c +++ b/sway/input/libinput.c @@ -272,6 +272,10 @@ bool sway_input_configure_libinput_device(struct sway_input_device *input_device } if (ic->drag_lock != INT_MIN) { changed |= set_tap_drag_lock(device, ic->drag_lock); + } else { +#if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY + changed |= set_tap_drag_lock(device, LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY); +#endif } if (ic->pointer_accel != FLT_MIN) { changed |= set_accel_speed(device, ic->pointer_accel); @@ -354,8 +358,12 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) { libinput_device_config_tap_get_default_button_map(device)); changed |= set_tap_drag(device, libinput_device_config_tap_get_default_drag_enabled(device)); +#if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY + changed |= set_tap_drag_lock(device, LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY); +#else changed |= set_tap_drag_lock(device, libinput_device_config_tap_get_default_drag_lock_enabled(device)); +#endif changed |= set_accel_speed(device, libinput_device_config_accel_get_default_speed(device)); changed |= set_rotation_angle(device, diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index fbef2a321..18744a22a 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -152,8 +152,9 @@ The following commands may only be used in the configuration file. *input* drag enabled|disabled Enables or disables tap-and-drag for specified input device. -*input* drag_lock enabled|disabled - Enables or disables drag lock for specified input device. +*input* drag_lock enabled|disabled|enabled_sticky + Enables or disables drag lock for specified input device. The default is + _enabled_sticky_. *input* dwt enabled|disabled Enables or disables disable-while-typing for the specified input device. diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index e90abcbb8..fe6bb92e2 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -1174,7 +1174,8 @@ following properties will be included for devices that support them: : 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_ +: Whether drag-lock is enabled. It can be _enabled_, _disabled_ or + _enabled_sticky_ |- accel_speed : double : The pointer-acceleration in use From 4faf0f90988b854d354ce1c8d152884294269de0 Mon Sep 17 00:00:00 2001 From: Violet Purcell Date: Fri, 22 Nov 2024 17:31:15 -0500 Subject: [PATCH 157/361] tree/container: remove output_{enter,leave} listeners in destroy https://gitlab.freedesktop.org/wlroots/wlroots/-/commit/0d6cc471e95c1632b234fc9152659d24fe5982f1 added an assert that all signals are clear when destroying a wlr_scene_buffer, which is currently triggering due to sway not removing the output_enter and output_leave listeners on the container before calling wlr_scene_node_destroy on output_handler. Remove the listeners before wlr_scene_node_destroy is called. --- sway/tree/container.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/tree/container.c b/sway/tree/container.c index 62bff1ea5..a04c4c36b 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -508,6 +508,8 @@ void container_destroy(struct sway_container *con) { if (con->view && con->view->container == con) { con->view->container = NULL; + wl_list_remove(&con->output_enter.link); + wl_list_remove(&con->output_leave.link); wlr_scene_node_destroy(&con->output_handler->node); if (con->view->destroying) { view_destroy(con->view); From 1d783794b508e529bdc665296d690057c93997df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gy=C3=B6rgy=20Kurucz?= Date: Wed, 20 Nov 2024 19:15:31 +0100 Subject: [PATCH 158/361] input/libinput: fix builtin device detection logic Fixes: #8468 --- sway/input/libinput.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/input/libinput.c b/sway/input/libinput.c index ad8592703..b9df8d0bf 100644 --- a/sway/input/libinput.c +++ b/sway/input/libinput.c @@ -418,8 +418,8 @@ bool sway_libinput_device_is_builtin(struct sway_input_device *sway_device) { } const char prefix_platform[] = "platform-"; - if (strncmp(id_path, prefix_platform, strlen(prefix_platform)) != 0) { - return false; + if (strncmp(id_path, prefix_platform, strlen(prefix_platform)) == 0) { + return true; } const char prefix_pci[] = "pci-"; From 4eb86fce07dfe8cb9073739c84eb334cc0d262a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Baltaz=C3=A1r=20Radics?= Date: Sun, 8 Dec 2024 16:05:42 +0100 Subject: [PATCH 159/361] input/libinput: fix parsing input drag_lock command Regression introduced by by b160fac9f7a --- sway/commands/input/drag_lock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/commands/input/drag_lock.c b/sway/commands/input/drag_lock.c index 55a39a64a..915a7ada1 100644 --- a/sway/commands/input/drag_lock.c +++ b/sway/commands/input/drag_lock.c @@ -16,7 +16,7 @@ struct cmd_results *input_cmd_drag_lock(int argc, char **argv) { } #if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY - if (strcmp(argv[0], "enabled_sticky")) { + if (strcmp(argv[0], "enabled_sticky") == 0) { ic->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY; } else #endif From f293418d9d8a9e71fc52bc75a83d24ec2cc934c9 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sun, 8 Dec 2024 11:53:11 -0500 Subject: [PATCH 160/361] swaybar: Handle opaque region properly The background color can be set individually for the different elements of the bar. If any of the backgrounds have transparency, we have to bail out from advertising an opaque surface. --- swaybar/render.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/swaybar/render.c b/swaybar/render.c index 879a4e42a..13cfdc97a 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -29,6 +29,7 @@ struct render_context { cairo_font_options_t *textaa_sharp; cairo_font_options_t *textaa_safe; uint32_t background_color; + bool has_transparency; }; static void choose_text_aa_mode(struct render_context *ctx, uint32_t fontcolor) { @@ -265,6 +266,7 @@ static uint32_t render_status_block(struct render_context *ctx, uint32_t bg_color = block->urgent ? config->colors.urgent_workspace.background : block->background; + ctx->has_transparency |= (bg_color & 0xFF) != 0xFF; if (bg_color) { render_sharp_rectangle(cairo, bg_color, x_pos, y_pos, block_width, render_height); @@ -574,6 +576,7 @@ static uint32_t render_binding_mode_indicator(struct render_context *ctx, cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); cairo_set_source_u32(cairo, config->colors.binding_mode.background); ctx->background_color = config->colors.binding_mode.background; + ctx->has_transparency |= (config->colors.binding_mode.background & 0xFF) != 0xFF; cairo_rectangle(cairo, x, 0, width, height); cairo_fill(cairo); @@ -653,6 +656,7 @@ static uint32_t render_workspace_button(struct render_context *ctx, cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); cairo_set_source_u32(cairo, box_colors.background); ctx->background_color = box_colors.background; + ctx->has_transparency |= (box_colors.background & 0xFF) != 0xFF; cairo_rectangle(cairo, *x, 0, width, height); cairo_fill(cairo); @@ -760,10 +764,12 @@ void render_frame(struct swaybar_output *output) { background_color = output->bar->config->colors.background; } - struct render_context ctx = { 0 }; - ctx.output = output; - // initial background color used for deciding the best way to antialias text - ctx.background_color = background_color; + struct render_context ctx = { + .output = output, + // initial background color used for deciding the best way to antialias text + .background_color = background_color, + .has_transparency = (background_color & 0xFF) != 0xFF, + }; cairo_surface_t *recorder = cairo_recording_surface_create( CAIRO_CONTENT_COLOR_ALPHA, NULL); @@ -834,8 +840,7 @@ void render_frame(struct swaybar_output *output) { wl_surface_damage(output->surface, 0, 0, output->width, output->height); - uint32_t bg_alpha = background_color & 0xFF; - if (bg_alpha == 0xFF) { + if (!ctx.has_transparency) { struct wl_region *region = wl_compositor_create_region(output->bar->compositor); wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); From 801bc76ce3ab2f620e30d1d0ad979caa5b03a164 Mon Sep 17 00:00:00 2001 From: Hong Xu Date: Mon, 16 Dec 2024 21:17:05 -0800 Subject: [PATCH 161/361] Explain that the title bar always shows --- 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 50de552f0..5e4df0dc7 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -106,9 +106,9 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). *border* none|normal|csd|pixel [] Set border style for focused window. _normal_ includes a border of thickness _n_ and a title bar. _pixel_ is a border without title bar _n_ - pixels thick. Default is _normal_ with border thickness 2. _csd_ is short - for client-side-decorations, which allows the client to draw its own - decorations. + pixels thick. The title bar always shows in stacking or tabbed layouts. + _csd_ is short for client-side-decorations, which allows the client to draw + its own decorations. Default is _normal_ with border thickness 2. *border* toggle Cycles through the available border styles. From c55dff95bcf2875ff26a0505c6eda44947ed927d Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 7 Jan 2025 13:21:37 +0100 Subject: [PATCH 162/361] stringop: move over has_prefix() --- common/stringop.c | 4 ++++ include/stringop.h | 2 ++ sway/input/seat.c | 4 ---- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/common/stringop.c b/common/stringop.c index 16d04917e..dcbd16564 100644 --- a/common/stringop.c +++ b/common/stringop.c @@ -360,3 +360,7 @@ char *format_str(const char *fmt, ...) { va_end(args); return str; } + +bool has_prefix(const char *str, const char *prefix) { + return strncmp(str, prefix, strlen(prefix)) == 0; +} diff --git a/include/stringop.h b/include/stringop.h index 19a50f237..ffc355cf0 100644 --- a/include/stringop.h +++ b/include/stringop.h @@ -40,4 +40,6 @@ bool expand_path(char **path); char *vformat_str(const char *fmt, va_list args) _SWAY_ATTRIB_PRINTF(1, 0); char *format_str(const char *fmt, ...) _SWAY_ATTRIB_PRINTF(1, 2); +bool has_prefix(const char *str, const char *prefix); + #endif diff --git a/sway/input/seat.c b/sway/input/seat.c index 0dd26290e..1b63f625b 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -647,10 +647,6 @@ static void seat_reset_input_config(struct sway_seat *seat, sway_device->input_device->wlr_device, NULL); } -static bool has_prefix(const char *str, const char *prefix) { - return strncmp(str, prefix, strlen(prefix)) == 0; -} - /** * Get the name of the built-in output, if any. Returns NULL if there isn't * exactly one built-in output. From 0c60d1581f7b12ae472c786b7dfe27a1c6ec9a47 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 7 Jan 2025 13:21:56 +0100 Subject: [PATCH 163/361] Use has_prefix() instead of strncmp() throughout This is safer than hardcoded string lengths. --- sway/commands/assign.c | 2 +- sway/commands/bar/font.c | 2 +- sway/commands/bind.c | 5 ++--- sway/commands/font.c | 4 ++-- sway/commands/gesture.c | 3 +-- sway/commands/input/events.c | 5 ++--- sway/commands/mark.c | 2 +- sway/config.c | 4 ++-- sway/config/input.c | 2 +- sway/input/cursor.c | 4 ++-- sway/input/input-manager.c | 2 +- sway/input/libinput.c | 8 ++------ sway/main.c | 4 ++-- sway/tree/container.c | 22 +++++++++++----------- sway/tree/workspace.c | 2 +- swaybar/ipc.c | 5 +++-- swaybar/render.c | 6 +++--- swaybar/tray/item.c | 5 +++-- swaybar/tray/watcher.c | 6 +++--- 19 files changed, 44 insertions(+), 49 deletions(-) diff --git a/sway/commands/assign.c b/sway/commands/assign.c index bf95cf002..5bcbb1644 100644 --- a/sway/commands/assign.c +++ b/sway/commands/assign.c @@ -23,7 +23,7 @@ struct cmd_results *cmd_assign(int argc, char **argv) { --argc; ++argv; - if (strncmp(*argv, "→", strlen("→")) == 0) { + if (has_prefix(*argv, "→")) { if (argc < 2) { free(criteria); return cmd_results_new(CMD_INVALID, "Missing workspace"); diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c index 0c074679a..51ca20ed5 100644 --- a/sway/commands/bar/font.c +++ b/sway/commands/bar/font.c @@ -11,7 +11,7 @@ struct cmd_results *bar_cmd_font(int argc, char **argv) { char *font = join_args(argv, argc); free(config->current_bar->font); - if (strncmp(font, "pango:", 6) == 0) { + if (has_prefix(font, "pango:")) { if (config->current_bar->pango_markup == PANGO_MARKUP_DEFAULT) { config->current_bar->pango_markup = true; } diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 268f28553..0d9347007 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -367,8 +367,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, } } else if (strcmp("--exclude-titlebar", argv[0]) == 0) { exclude_titlebar = true; - } else if (strncmp("--input-device=", argv[0], - strlen("--input-device=")) == 0) { + } else if (has_prefix("--input-device=", argv[0])) { free(binding->input); binding->input = strdup(argv[0] + strlen("--input-device=")); strip_quotes(binding->input); @@ -399,7 +398,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, list_t *split = split_string(argv[0], "+"); for (int i = 0; i < split->length; ++i) { // Check for group - if (strncmp(split->items[i], "Group", strlen("Group")) == 0) { + if (has_prefix(split->items[i], "Group") == 0) { if (binding->group != XKB_LAYOUT_INVALID) { free_sway_binding(binding); list_free_items_and_destroy(split); diff --git a/sway/commands/font.c b/sway/commands/font.c index 9920d03eb..842e8ae61 100644 --- a/sway/commands/font.c +++ b/sway/commands/font.c @@ -13,9 +13,9 @@ struct cmd_results *cmd_font(int argc, char **argv) { char *font = join_args(argv, argc); free(config->font); - if (strncmp(font, "pango:", 6) == 0) { + if (has_prefix(font, "pango:")) { config->pango_markup = true; - config->font = strdup(font + 6); + config->font = strdup(font + strlen("pango:")); free(font); } else { config->pango_markup = false; diff --git a/sway/commands/gesture.c b/sway/commands/gesture.c index 90a20716d..e63b7ac6e 100644 --- a/sway/commands/gesture.c +++ b/sway/commands/gesture.c @@ -121,8 +121,7 @@ static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, boo binding->flags |= BINDING_EXACT; } else if (strcmp("--no-warn", argv[0]) == 0) { warn = false; - } else if (strncmp("--input-device=", argv[0], - strlen("--input-device=")) == 0) { + } else if (has_prefix("--input-device=", argv[0])) { free(binding->input); binding->input = strdup(argv[0] + strlen("--input-device=")); } else { diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c index 3cea026ec..4650108a1 100644 --- a/sway/commands/input/events.c +++ b/sway/commands/input/events.c @@ -86,7 +86,7 @@ static void toggle_select_send_events_for_device(struct input_config *ic, static void toggle_send_events(int argc, char **argv) { struct input_config *ic = config->handler_context.input_config; bool wildcard = strcmp(ic->identifier, "*") == 0; - const char *type = strncmp(ic->identifier, "type:", strlen("type:")) == 0 + const char *type = has_prefix(ic->identifier, "type:") ? ic->identifier + strlen("type:") : NULL; struct sway_input_device *device = NULL; wl_list_for_each(device, &server.input->devices, link) { @@ -146,8 +146,7 @@ struct cmd_results *input_cmd_events(int argc, char **argv) { toggle_send_events(argc - 1, argv + 1); - if (strcmp(ic->identifier, "*") == 0 || - strncmp(ic->identifier, "type:", strlen("type:")) == 0) { + if (strcmp(ic->identifier, "*") == 0 || has_prefix(ic->identifier, "type:")) { // Update the device input configs and then reset the type/wildcard // config send events mode so that is does not override the device // ones. The device ones will be applied when attempting to apply diff --git a/sway/commands/mark.c b/sway/commands/mark.c index 2bfc86b30..81bf0e38b 100644 --- a/sway/commands/mark.c +++ b/sway/commands/mark.c @@ -23,7 +23,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) { } bool add = false, toggle = false; - while (argc > 0 && strncmp(*argv, "--", 2) == 0) { + while (argc > 0 && has_prefix(*argv, "--") == 0) { if (strcmp(*argv, "--add") == 0) { add = true; } else if (strcmp(*argv, "--replace") == 0) { diff --git a/sway/config.c b/sway/config.c index 1090edc5c..ec7059687 100644 --- a/sway/config.c +++ b/sway/config.c @@ -925,8 +925,8 @@ char *do_var_replacement(char *str) { // Find matching variable for (i = 0; i < config->symbols->length; ++i) { struct sway_variable *var = config->symbols->items[i]; - int vnlen = strlen(var->name); - if (strncmp(find, var->name, vnlen) == 0) { + if (has_prefix(find, var->name)) { + int vnlen = strlen(var->name); int vvlen = strlen(var->value); char *newstr = malloc(strlen(str) - vnlen + vvlen + 1); if (!newstr) { diff --git a/sway/config/input.c b/sway/config/input.c index e5694effc..1fb737b65 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -300,7 +300,7 @@ struct input_config *store_input_config(struct input_config *ic, return NULL; } - bool type = strncmp(ic->identifier, "type:", strlen("type:")) == 0; + bool type = has_prefix(ic->identifier, "type:"); if (type && error && !validate_type_on_existing(ic, error)) { return NULL; } diff --git a/sway/input/cursor.c b/sway/input/cursor.c index bbd16717f..e8c451183 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -1212,7 +1212,7 @@ uint32_t get_mouse_bindsym(const char *name, char **error) { SWAY_SCROLL_UP, SWAY_SCROLL_DOWN, SWAY_SCROLL_LEFT, SWAY_SCROLL_RIGHT, BTN_SIDE, BTN_EXTRA}; return buttons[number - 1]; - } else if (strncmp(name, "BTN_", strlen("BTN_")) == 0) { + } else if (has_prefix(name, "BTN_")) { // Get event code from name int code = libevdev_event_code_from_name(EV_KEY, name); if (code == -1) { @@ -1237,7 +1237,7 @@ uint32_t get_mouse_bindcode(const char *name, char **error) { return 0; } const char *event = libevdev_event_code_get_name(EV_KEY, code); - if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { + if (!event || !has_prefix(event, "BTN_")) { *error = format_str("Event code %d (%s) is not a button", code, event ? event : "(null)"); return 0; diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index ddfe14689..99af4c691 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -577,7 +577,7 @@ void input_manager_configure_all_input_mappings(void) { 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; + bool type_wildcard = has_prefix(input_config->identifier, "type:"); 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; diff --git a/sway/input/libinput.c b/sway/input/libinput.c index b9df8d0bf..f76c6505c 100644 --- a/sway/input/libinput.c +++ b/sway/input/libinput.c @@ -417,13 +417,9 @@ bool sway_libinput_device_is_builtin(struct sway_input_device *sway_device) { return false; } - const char prefix_platform[] = "platform-"; - if (strncmp(id_path, prefix_platform, strlen(prefix_platform)) == 0) { + if (has_prefix(id_path, "platform-")) { return true; } - const char prefix_pci[] = "pci-"; - const char infix_platform[] = "-platform-"; - return (strncmp(id_path, prefix_pci, strlen(prefix_pci)) == 0) && - strstr(id_path, infix_platform); + return has_prefix(id_path, "pci-") && strstr(id_path, "-platform-"); } diff --git a/sway/main.c b/sway/main.c index 165ccc095..accffc71f 100644 --- a/sway/main.c +++ b/sway/main.c @@ -159,8 +159,8 @@ void enable_debug_flag(const char *flag) { debug.txn_wait = true; } else if (strcmp(flag, "txn-timings") == 0) { debug.txn_timings = true; - } else if (strncmp(flag, "txn-timeout=", 12) == 0) { - server.txn_timeout_ms = atoi(&flag[12]); + } else if (has_prefix(flag, "txn-timeout=")) { + server.txn_timeout_ms = atoi(&flag[strlen("txn-timeout=")]); } else if (strcmp(flag, "legacy-wl-drm") == 0) { debug.legacy_wl_drm = true; } else { diff --git a/sway/tree/container.c b/sway/tree/container.c index a04c4c36b..0288beacb 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -696,26 +696,26 @@ size_t parse_title_format(struct sway_container *container, char *buffer) { len += next - format; format = next; - if (strncmp(next, "%title", 6) == 0) { + if (has_prefix(next, "%title")) { if (container->view) { len += append_prop(buffer, view_get_title(container->view)); } else { len += container_build_representation(container->pending.layout, container->pending.children, buffer); } - format += 6; + format += strlen("%title"); } else if (container->view) { - if (strncmp(next, "%app_id", 7) == 0) { + if (has_prefix(next, "%app_id")) { len += append_prop(buffer, view_get_app_id(container->view)); - format += 7; - } else if (strncmp(next, "%class", 6) == 0) { + format += strlen("%app_id"); + } else if (has_prefix(next, "%class")) { len += append_prop(buffer, view_get_class(container->view)); - format += 6; - } else if (strncmp(next, "%instance", 9) == 0) { + format += strlen("%class"); + } else if (has_prefix(next, "%instance")) { len += append_prop(buffer, view_get_instance(container->view)); - format += 9; - } else if (strncmp(next, "%shell", 6) == 0) { + format += strlen("%instance"); + } else if (has_prefix(next, "%shell")) { len += append_prop(buffer, view_get_shell(container->view)); - format += 6; + format += strlen("%shell"); } else { lenient_strcat(buffer, "%"); ++format; @@ -778,7 +778,7 @@ size_t container_build_representation(enum sway_container_layout layout, len += strlen(identifier); lenient_strcat(buffer, identifier); } else { - len += 6; + len += strlen("(null)"); lenient_strcat(buffer, "(null)"); } } diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index f8709a4c7..a09dc3a45 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -246,7 +246,7 @@ static void workspace_name_from_binding(const struct sway_binding * binding, } // If the command is workspace number , isolate the name - if (strncmp(_target, "number ", strlen("number ")) == 0) { + if (has_prefix(_target, "number ")) { size_t length = strlen(_target) - strlen("number ") + 1; char *temp = malloc(length); strncpy(temp, _target + strlen("number "), length - 1); diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 71c9a4c5e..f651f0359 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -15,6 +15,7 @@ #include "list.h" #include "log.h" #include "loop.h" +#include "stringop.h" #include "util.h" void ipc_send_workspace_command(struct swaybar *bar, const char *ws) { @@ -45,8 +46,8 @@ void ipc_send_workspace_command(struct swaybar *bar, const char *ws) { char *parse_font(const char *font) { char *new_font = NULL; - if (strncmp("pango:", font, 6) == 0) { - font += 6; + if (has_prefix("pango:", font)) { + font += strlen("pango:"); } new_font = strdup(font); return new_font; diff --git a/swaybar/render.c b/swaybar/render.c index 13cfdc97a..45faefa97 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -293,11 +293,11 @@ static uint32_t render_status_block(struct render_context *ctx, } double offset = 0; - if (strncmp(block->align, "left", 4) == 0) { + if (has_prefix(block->align, "left")) { offset = x_pos; - } else if (strncmp(block->align, "right", 5) == 0) { + } else if (has_prefix(block->align, "right")) { offset = x_pos + width - text_width; - } else if (strncmp(block->align, "center", 6) == 0) { + } else if (has_prefix(block->align, "center")) { offset = x_pos + (width - text_width) / 2; } double text_y = height / 2.0 - text_height / 2.0; diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index ca6c03ad5..12929743b 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -15,6 +15,7 @@ #include "cairo_util.h" #include "list.h" #include "log.h" +#include "stringop.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" // TODO menu @@ -161,7 +162,7 @@ static int get_property_callback(sd_bus_message *msg, void *data, } if (strcmp(prop, "Status") == 0 || (sni->status && (sni->status[0] == 'N' ? - prop[0] == 'A' : strncmp(prop, "Icon", 4) == 0))) { + prop[0] == 'A' : has_prefix(prop, "Icon")))) { set_sni_dirty(sni); } cleanup: @@ -364,7 +365,7 @@ static void handle_click(struct swaybar_sni *sni, int x, int y, method = "ContextMenu"; } - if (strncmp(method, "Scroll", strlen("Scroll")) == 0) { + if (has_prefix(method, "Scroll")) { char dir = method[strlen("Scroll")]; char *orientation = (dir == 'U' || dir == 'D') ? "vertical" : "horizontal"; int sign = (dir == 'U' || dir == 'L') ? -1 : 1; diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c index 54f000bd6..284964030 100644 --- a/swaybar/tray/watcher.c +++ b/swaybar/tray/watcher.c @@ -31,9 +31,9 @@ static int handle_lost_service(sd_bus_message *msg, struct swaybar_watcher *watcher = data; for (int idx = 0; idx < watcher->items->length; ++idx) { char *id = watcher->items->items[idx]; - int cmp_res = using_standard_protocol(watcher) ? - cmp_id(id, service) : strncmp(id, service, strlen(service)); - if (cmp_res == 0) { + bool cmp_res = using_standard_protocol(watcher) ? + cmp_id(id, service) == 0 : has_prefix(id, service); + if (cmp_res) { 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, From a1838c5522e4c386245f17823d247dc76ad91d71 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 7 Jan 2025 18:50:11 +0100 Subject: [PATCH 164/361] Fix has_prefix() comparisons with 0 has_prefix() returns a bool, unlike strncmp() which returns an int. Fixes: 0c60d1581f7b ("Use has_prefix() instead of strncmp() throughout") Closes: https://github.com/swaywm/sway/issues/8527 --- sway/commands/bind.c | 2 +- sway/commands/mark.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 0d9347007..32d4f8916 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -398,7 +398,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, list_t *split = split_string(argv[0], "+"); for (int i = 0; i < split->length; ++i) { // Check for group - if (has_prefix(split->items[i], "Group") == 0) { + if (has_prefix(split->items[i], "Group")) { if (binding->group != XKB_LAYOUT_INVALID) { free_sway_binding(binding); list_free_items_and_destroy(split); diff --git a/sway/commands/mark.c b/sway/commands/mark.c index 81bf0e38b..77c8d2394 100644 --- a/sway/commands/mark.c +++ b/sway/commands/mark.c @@ -23,7 +23,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) { } bool add = false, toggle = false; - while (argc > 0 && has_prefix(*argv, "--") == 0) { + while (argc > 0 && has_prefix(*argv, "--")) { if (strcmp(*argv, "--add") == 0) { add = true; } else if (strcmp(*argv, "--replace") == 0) { From c7c0a5a1b36a5ac93baacdf0098e121817d0d642 Mon Sep 17 00:00:00 2001 From: mtvare6 Date: Wed, 8 Jan 2025 09:40:09 +0530 Subject: [PATCH 165/361] config/output: skip format checks if all are supported Fixes #8496 --- sway/config/output.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/config/output.c b/sway/config/output.c index f8922ea52..9ae2ee6a9 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -783,7 +783,8 @@ static bool search_render_format(struct search_context *ctx, size_t output_idx) if (needed_bits < format_bits) { continue; } - if (!wlr_drm_format_set_get(primary_formats, fmts[idx])) { + // If primary_formats is NULL, all formats are supported + if (primary_formats && !wlr_drm_format_set_get(primary_formats, fmts[idx])) { // This is not a supported format for this output continue; } From a6c0441ee060c7045b24aff943c7d0bf0f47412b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 8 Jan 2025 08:21:58 +0100 Subject: [PATCH 166/361] config/output: don't hardcode DMA-BUF in search_render_format() We could be running with a backend which doesn't support DMA-BUFs, e.g. inside a parent Wayland compositor without GPU acceleration. --- sway/config/output.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/config/output.c b/sway/config/output.c index 9ae2ee6a9..277084762 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -773,7 +774,7 @@ static bool search_render_format(struct search_context *ctx, size_t output_idx) } const struct wlr_drm_format_set *primary_formats = - wlr_output_get_primary_formats(wlr_output, WLR_BUFFER_CAP_DMABUF); + wlr_output_get_primary_formats(wlr_output, server.allocator->buffer_caps); enum render_bit_depth needed_bits = RENDER_BIT_DEPTH_8; if (cfg->config && cfg->config->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { needed_bits = cfg->config->render_bit_depth; From 3629a832e5b3a73ccd4e42eca79943af121866da Mon Sep 17 00:00:00 2001 From: llyyr Date: Sat, 11 Jan 2025 18:49:20 +0530 Subject: [PATCH 167/361] layer_shell: cleanup new_popup listener when destroying node --- sway/desktop/layer_shell.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 333c09b49..05faa4656 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -256,6 +256,7 @@ static void handle_node_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&layer->unmap.link); wl_list_remove(&layer->surface_commit.link); wl_list_remove(&layer->node_destroy.link); + wl_list_remove(&layer->new_popup.link); wl_list_remove(&layer->output_destroy.link); layer->layer_surface->data = NULL; From e3f0ba4cd9ca709cac115ade54958885614d889c Mon Sep 17 00:00:00 2001 From: Jim Date: Sat, 11 Jan 2025 16:36:07 +0100 Subject: [PATCH 168/361] Increase max default buffer size to 1 MiB Increasing the max default buffer size prevents clients from crashing when they need more than 4096 bytes. This can happen when the GUI thread of the application is blocked, especially when moving your mouse over it with high mouse sensitivity. --- sway/server.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/server.c b/sway/server.c index e091d9464..cbc0d2593 100644 --- a/sway/server.c +++ b/sway/server.c @@ -222,6 +222,7 @@ bool server_init(struct sway_server *server) { server->wl_event_loop = wl_display_get_event_loop(server->wl_display); wl_display_set_global_filter(server->wl_display, filter_global, NULL); + wl_display_set_default_max_buffer_size(server->wl_display, 1024 * 1024); root = root_create(server->wl_display); From cff16d32f9435592d0fe1f5794213e50341d9bed Mon Sep 17 00:00:00 2001 From: Jacob McNamee Date: Thu, 26 Dec 2024 21:38:05 -0800 Subject: [PATCH 169/361] tree/view: add getters for sandbox properties --- include/sway/tree/view.h | 6 ++++++ sway/tree/view.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index f60322212..9f084eeb4 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -221,6 +221,12 @@ const char *view_get_window_role(struct sway_view *view); uint32_t view_get_window_type(struct sway_view *view); +const char *view_get_sandbox_engine(struct sway_view *view); + +const char *view_get_sandbox_app_id(struct sway_view *view); + +const char *view_get_sandbox_instance_id(struct sway_view *view); + const char *view_get_shell(struct sway_view *view); void view_get_constraints(struct sway_view *view, double *min_width, diff --git a/sway/tree/view.c b/sway/tree/view.c index 492095b93..33161cc53 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -154,6 +155,34 @@ uint32_t view_get_window_type(struct sway_view *view) { return 0; } +static const struct wlr_security_context_v1_state *security_context_from_view( + struct sway_view *view) { + const struct wl_client *client = + wl_resource_get_client(view->surface->resource); + const struct wlr_security_context_v1_state *security_context = + wlr_security_context_manager_v1_lookup_client( + server.security_context_manager_v1, client); + return security_context; +} + +const char *view_get_sandbox_engine(struct sway_view *view) { + const struct wlr_security_context_v1_state *security_context = + security_context_from_view(view); + return security_context ? security_context->sandbox_engine : NULL; +} + +const char *view_get_sandbox_app_id(struct sway_view *view) { + const struct wlr_security_context_v1_state *security_context = + security_context_from_view(view); + return security_context ? security_context->app_id : NULL; +} + +const char *view_get_sandbox_instance_id(struct sway_view *view) { + const struct wlr_security_context_v1_state *security_context = + security_context_from_view(view); + return security_context ? security_context->instance_id : NULL; +} + const char *view_get_shell(struct sway_view *view) { switch(view->type) { case SWAY_VIEW_XDG_SHELL: From 60f06fc4f1590e1213893177ebf00ea780178562 Mon Sep 17 00:00:00 2001 From: Puck Meerburg Date: Mon, 26 Sep 2022 18:59:46 +0000 Subject: [PATCH 170/361] ipc-json: add sandbox properties to view JSON --- sway/ipc-json.c | 12 ++++++++++++ sway/sway-ipc.7.scd | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index fc1df2ac6..142512eb2 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -602,6 +602,18 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(object, "inhibit_idle", json_object_new_boolean(view_inhibit_idle(c->view))); + const char *sandbox_engine = view_get_sandbox_engine(c->view); + json_object_object_add(object, "sandbox_engine", + sandbox_engine ? json_object_new_string(sandbox_engine) : NULL); + + const char *sandbox_app_id = view_get_sandbox_app_id(c->view); + json_object_object_add(object, "sandbox_app_id", + sandbox_app_id ? json_object_new_string(sandbox_app_id) : NULL); + + const char *sandbox_instance_id = view_get_sandbox_instance_id(c->view); + json_object_object_add(object, "sandbox_instance_id", + sandbox_instance_id ? json_object_new_string(sandbox_instance_id) : NULL); + json_object *idle_inhibitors = json_object_new_object(); struct sway_idle_inhibitor_v1 *user_inhibitor = diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index fe6bb92e2..f39e35180 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -403,6 +403,16 @@ node and will have the following properties: : (Only views) An object containing the state of the _application_ and _user_ idle inhibitors. _application_ can be _enabled_ or _none_. _user_ can be _focus_, _fullscreen_, _open_, _visible_ or _none_. +|- sandbox_engine +: string +: (Only views) The associated sandbox engine (or _null_) +|- sandbox_app_id +: string +: (Only views) The app ID provided by the associated sandbox engine (or _null_) +|- sandbox_instance_id +: string +: (Only views) The instance ID provided by the associated sandbox engine (or + _null_) |- window : integer : (Only xwayland views) The X11 window ID for the xwayland view From 3ab1f0ca3d4f7701c7219f9401f5f372bedc244b Mon Sep 17 00:00:00 2001 From: Puck Meerburg Date: Sun, 7 Aug 2022 10:12:51 +0000 Subject: [PATCH 171/361] criteria: add sandbox properties --- include/sway/criteria.h | 3 ++ sway/criteria.c | 86 ++++++++++++++++++++++++++++++++++++++++- sway/sway.5.scd | 16 ++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/include/sway/criteria.h b/include/sway/criteria.h index ae546821c..8ba8c9989 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -53,6 +53,9 @@ struct criteria { char urgent; // 'l' for latest or 'o' for oldest struct pattern *workspace; pid_t pid; + struct pattern *sandbox_engine; + struct pattern *sandbox_app_id; + struct pattern *sandbox_instance_id; }; bool criteria_is_empty(struct criteria *criteria); diff --git a/sway/criteria.c b/sway/criteria.c index 2b7290c0e..0aefa0080 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -34,7 +34,10 @@ bool criteria_is_empty(struct criteria *criteria) { && !criteria->tiling && !criteria->urgent && !criteria->workspace - && !criteria->pid; + && !criteria->pid + && !criteria->sandbox_engine + && !criteria->sandbox_app_id + && !criteria->sandbox_instance_id; } // The error pointer is used for parsing functions, and saves having to pass it @@ -98,6 +101,9 @@ void criteria_destroy(struct criteria *criteria) { #endif pattern_destroy(criteria->con_mark); pattern_destroy(criteria->workspace); + pattern_destroy(criteria->sandbox_engine); + pattern_destroy(criteria->sandbox_app_id); + pattern_destroy(criteria->sandbox_instance_id); free(criteria->target); free(criteria->cmdlist); free(criteria->raw); @@ -248,6 +254,66 @@ static bool criteria_matches_view(struct criteria *criteria, } } + if (criteria->sandbox_engine) { + const char *sandbox_engine = view_get_sandbox_engine(view); + if (!sandbox_engine) { + return false; + } + + switch (criteria->sandbox_engine->match_type) { + case PATTERN_FOCUSED: + if (focused && lenient_strcmp(sandbox_engine, view_get_sandbox_engine(focused))) { + return false; + } + break; + case PATTERN_PCRE2: + if (regex_cmp(sandbox_engine, criteria->sandbox_engine->regex) < 0) { + return false; + } + break; + } + } + + if (criteria->sandbox_app_id) { + const char *sandbox_app_id = view_get_sandbox_app_id(view); + if (!sandbox_app_id) { + return false; + } + + switch (criteria->sandbox_app_id->match_type) { + case PATTERN_FOCUSED: + if (focused && lenient_strcmp(sandbox_app_id, view_get_sandbox_app_id(focused))) { + return false; + } + break; + case PATTERN_PCRE2: + if (regex_cmp(sandbox_app_id, criteria->sandbox_app_id->regex) < 0) { + return false; + } + break; + } + } + + if (criteria->sandbox_instance_id) { + const char *sandbox_instance_id = view_get_sandbox_instance_id(view); + if (!sandbox_instance_id) { + return false; + } + + switch (criteria->sandbox_instance_id->match_type) { + case PATTERN_FOCUSED: + if (focused && lenient_strcmp(sandbox_instance_id, view_get_sandbox_instance_id(focused))) { + return false; + } + break; + case PATTERN_PCRE2: + if (regex_cmp(sandbox_instance_id, criteria->sandbox_instance_id->regex) < 0) { + return false; + } + break; + } + } + if (!criteria_matches_container(criteria, view->container)) { return false; } @@ -475,6 +541,9 @@ enum criteria_token { T_URGENT, T_WORKSPACE, T_PID, + T_SANDBOX_ENGINE, + T_SANDBOX_APP_ID, + T_SANDBOX_INSTANCE_ID, T_INVALID, }; @@ -514,6 +583,12 @@ static enum criteria_token token_from_name(char *name) { return T_FLOATING; } else if (strcmp(name, "pid") == 0) { return T_PID; + } else if (strcmp(name, "sandbox_engine") == 0) { + return T_SANDBOX_ENGINE; + } else if (strcmp(name, "sandbox_app_id") == 0) { + return T_SANDBOX_APP_ID; + } else if (strcmp(name, "sandbox_instance_id") == 0) { + return T_SANDBOX_INSTANCE_ID; } return T_INVALID; } @@ -617,6 +692,15 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { error = strdup("The value for 'pid' should be numeric"); } break; + case T_SANDBOX_ENGINE: + pattern_create(&criteria->sandbox_engine, value); + break; + case T_SANDBOX_APP_ID: + pattern_create(&criteria->sandbox_app_id, value); + break; + case T_SANDBOX_INSTANCE_ID: + pattern_create(&criteria->sandbox_instance_id, value); + break; case T_INVALID: break; } diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 5e4df0dc7..4def2cbb1 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -1058,6 +1058,22 @@ The following attributes may be matched with: expression. If the value is \_\_focused\_\_, then all the views on the currently focused workspace matches. +*sandbox_engine* + Compare against the associated sandbox engine. Can be a regular expression. + If the value is \_\_focused\_\_, then the sandbox engine must be the same as + that of the currently focused window. + +*sandbox_app_id* + Compare against the app ID provided by the associated sandbox engine. Can be + a regular expression. If the value is \_\_focused\_\_, then the sandbox app + ID must be the same as that of the currently focused window. + +*sandbox_instance_id* + Compare against the instance ID provided by the associated sandbox engine. + Can be a regular expression. If the value is \_\_focused\_\_, then the + sandbox instance ID must be the same as that of the currently focused + window. + # SEE ALSO *sway*(1) *sway-input*(5) *sway-output*(5) *sway-bar*(5) *sway-ipc*(7) From f177d0544108d2fbf567ee0db646efc99e72d47f Mon Sep 17 00:00:00 2001 From: Jacob McNamee Date: Fri, 27 Dec 2024 00:03:06 -0800 Subject: [PATCH 172/361] tree/container: support sandbox properties in title format --- sway/sway.5.scd | 29 +++++++++++++++++++++++------ sway/tree/container.c | 9 +++++++++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 4def2cbb1..11468d777 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -372,12 +372,29 @@ set|plus|minus|toggle *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) + *%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) + + *%sandbox_engine* + The associated sandbox engine + + *%sandbox_app_id* + The app ID provided by the associated sandbox engine + + *%sandbox_instance_id* + The instance ID provided by the associated sandbox engine This command is typically used with *for_window* criteria. For example: diff --git a/sway/tree/container.c b/sway/tree/container.c index 0288beacb..6ff4036fa 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -716,6 +716,15 @@ size_t parse_title_format(struct sway_container *container, char *buffer) { } else if (has_prefix(next, "%shell")) { len += append_prop(buffer, view_get_shell(container->view)); format += strlen("%shell"); + } else if (has_prefix(next, "%sandbox_engine")) { + len += append_prop(buffer, view_get_sandbox_engine(container->view)); + format += strlen("%sandbox_engine"); + } else if (has_prefix(next, "%sandbox_app_id")) { + len += append_prop(buffer, view_get_sandbox_app_id(container->view)); + format += strlen("%sandbox_app_id"); + } else if (has_prefix(next, "%sandbox_instance_id")) { + len += append_prop(buffer, view_get_sandbox_instance_id(container->view)); + format += strlen("%sandbox_instance_id"); } else { lenient_strcat(buffer, "%"); ++format; From 0b08dce08cbcf515103d8a7fd8c390ed04c93428 Mon Sep 17 00:00:00 2001 From: Jacob McNamee Date: Sat, 4 Jan 2025 00:03:14 -0800 Subject: [PATCH 173/361] swaymsg: pretty-print sandbox properties --- swaymsg/main.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/swaymsg/main.c b/swaymsg/main.c index 5c57171e7..0ef6eb8a5 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -330,6 +330,9 @@ static void pretty_print_tree(json_object *obj, int indent) { const char *instance = json_object_get_string(json_object_object_get(window_props_obj, "instance")); const char *class = json_object_get_string(json_object_object_get(window_props_obj, "class")); int x11_id = json_object_get_int(json_object_object_get(obj, "window")); + const char *sandbox_engine = json_object_get_string(json_object_object_get(obj, "sandbox_engine")); + const char *sandbox_app_id = json_object_get_string(json_object_object_get(obj, "sandbox_app_id")); + const char *sandbox_instance_id = json_object_get_string(json_object_object_get(obj, "sandbox_instance_id")); printf(" (%s, pid: %d", shell, pid); if (app_id != NULL) { @@ -344,6 +347,15 @@ static void pretty_print_tree(json_object *obj, int indent) { if (x11_id != 0) { printf(", X11 window: 0x%X", x11_id); } + if (sandbox_engine != NULL) { + printf(", sandbox_engine: \"%s\"", sandbox_engine); + } + if (sandbox_app_id != NULL) { + printf(", sandbox_app_id: \"%s\"", sandbox_app_id); + } + if (sandbox_instance_id != NULL) { + printf(", sandbox_instance_id: \"%s\"", sandbox_instance_id); + } printf(")"); } From 30c858423dcdfb82d768a5025b5f00c19b250322 Mon Sep 17 00:00:00 2001 From: llyyr Date: Thu, 16 Jan 2025 19:11:36 +0530 Subject: [PATCH 174/361] config/output: don't leak background_fallback --- sway/config/output.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/config/output.c b/sway/config/output.c index 277084762..9fb3a12ac 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1018,6 +1018,7 @@ void free_output_config(struct output_config *oc) { free(oc->name); free(oc->background); free(oc->background_option); + free(oc->background_fallback); wlr_color_transform_unref(oc->color_transform); free(oc); } From 8acb0482da68af69d52ab168f9e30e2464b9c7a3 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 12 Feb 2024 21:13:35 +0100 Subject: [PATCH 175/361] Add ext-image-copy-capture-v1 and ext-image-capture-source-v1 References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4545 --- include/sway/server.h | 1 + protocols/meson.build | 9 ++++++--- sway/server.c | 5 +++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index ccf4a9cc2..7b76eb906 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -106,6 +106,7 @@ struct sway_server { struct wlr_content_type_manager_v1 *content_type_manager_v1; struct wlr_data_control_manager_v1 *data_control_manager_v1; struct wlr_screencopy_manager_v1 *screencopy_manager_v1; + struct wlr_ext_image_copy_capture_manager_v1 *ext_image_copy_capture_manager_v1; struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager_v1; struct wlr_security_context_manager_v1 *security_context_manager_v1; diff --git a/protocols/meson.build b/protocols/meson.build index d96f87575..4d24e7071 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -9,12 +9,15 @@ wayland_scanner = find_program( protocols = [ wl_protocol_dir / 'stable/tablet/tablet-v2.xml', wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', - wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml', - wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml', - wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', wl_protocol_dir / 'staging/content-type/content-type-v1.xml', wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', + wl_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml', + wl_protocol_dir / 'staging/ext-image-capture-source/ext-image-capture-source-v1.xml', + wl_protocol_dir / 'staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml', wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml', + wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', + wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml', + wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml', 'wlr-layer-shell-unstable-v1.xml', 'idle.xml', 'wlr-output-power-management-unstable-v1.xml', diff --git a/sway/server.c b/sway/server.c index cbc0d2593..c7fc2a6df 100644 --- a/sway/server.c +++ b/sway/server.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include #include @@ -108,6 +110,7 @@ static bool is_privileged(const struct wl_global *global) { global == server.foreign_toplevel_manager->global || global == server.data_control_manager_v1->global || global == server.screencopy_manager_v1->global || + global == server.ext_image_copy_capture_manager_v1->global || global == server.export_dmabuf_manager_v1->global || global == server.security_context_manager_v1->global || global == server.gamma_control_manager_v1->global || @@ -371,6 +374,8 @@ bool server_init(struct sway_server *server) { server->export_dmabuf_manager_v1 = wlr_export_dmabuf_manager_v1_create(server->wl_display); server->screencopy_manager_v1 = wlr_screencopy_manager_v1_create(server->wl_display); + server->ext_image_copy_capture_manager_v1 = wlr_ext_image_copy_capture_manager_v1_create(server->wl_display, 1); + wlr_ext_output_image_capture_source_manager_v1_create(server->wl_display, 1); server->data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display); server->security_context_manager_v1 = wlr_security_context_manager_v1_create(server->wl_display); wlr_viewporter_create(server->wl_display); From 3ff60987f3a1f8707e0d095a1dd822fda7aa1fe0 Mon Sep 17 00:00:00 2001 From: Bill Li Date: Mon, 27 Jan 2025 07:31:19 +0800 Subject: [PATCH 176/361] Drop wlr_matrix.h include from sway/desktop/output.c wlr_matrix is now private API. Fixes #8549 --- sway/desktop/output.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 607eca2c3..d1facdea6 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include From d093c2e3583e785cb308bec74c08a61e8808faf6 Mon Sep 17 00:00:00 2001 From: Attila Fidan Date: Mon, 27 Jan 2025 05:13:24 +0000 Subject: [PATCH 177/361] input/cursor: remove tool_proximity listener in destroy --- sway/input/cursor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index e8c451183..1fd57ec4e 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -1047,6 +1047,7 @@ void sway_cursor_destroy(struct sway_cursor *cursor) { wl_list_remove(&cursor->touch_frame.link); wl_list_remove(&cursor->tool_axis.link); wl_list_remove(&cursor->tool_tip.link); + wl_list_remove(&cursor->tool_proximity.link); wl_list_remove(&cursor->tool_button.link); wl_list_remove(&cursor->request_set_cursor.link); From 851b8c6fb624a8056df16fa9db2b3dc05021e519 Mon Sep 17 00:00:00 2001 From: Dan Baterisna Date: Fri, 7 Feb 2025 00:18:50 +0800 Subject: [PATCH 178/361] man: Document bar mode toggle command Functionality for switching swaybar's current mode between hidden and docked currently exists, but is absent from the relevant manpage. --- sway/sway-bar.5.scd | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd index 42e59d57d..a1733aa2b 100644 --- a/sway/sway-bar.5.scd +++ b/sway/sway-bar.5.scd @@ -143,6 +143,12 @@ runtime. This setting also applies to the current binding mode indicator. +The following commands may only be used at runtime. + +*mode* toggle [] + Toggles the current mode between _hide_ and _dock_. Any other mode + is treated as _hide_. + ## TRAY Swaybar provides a system tray where third-party applications may place icons. From 4852087e6166062d33372bd2869fce4033232e08 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Mon, 3 Feb 2025 13:13:48 -0500 Subject: [PATCH 179/361] output/background: fix config ignoring fallback color currently, the output background command handler prematurely returns with an error if the background file cannot be accessed. It should only error if user did not provide fallback color. closes #8556 Changes - Introduce variables to avoid uneccessary writing on output members - Log a debug message when fallback is being used over inaccessible file - Always parse the background color and swaynag warn if it is incorrect - when updating output member variables, free previous values - add cleanup label and goto it if `strdup` fails - Move output->member initializations to before parsing fallback, Also free and init output->background as well --- sway/commands/output/background.c | 90 +++++++++++++++---------------- 1 file changed, 42 insertions(+), 48 deletions(-) diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 55bd7671f..fe1621626 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -3,10 +3,8 @@ #include #include #include -#include #include "sway/commands.h" #include "sway/config.h" -#include "sway/swaynag.h" #include "log.h" #include "stringop.h" @@ -42,14 +40,14 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { } struct output_config *output = config->handler_context.output_config; - + char *src = NULL; 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"); + if (!(output->background = strdup(argv[0]))) goto cleanup; + if (!(output->background_option = strdup("solid_color"))) goto cleanup; output->background_fallback = NULL; argc -= 2; argv += 2; } else { @@ -77,37 +75,25 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Missing background file"); } - char *src = join_args(argv, j); + if (!(src = join_args(argv, j))) goto cleanup; if (!expand_path(&src)) { struct cmd_results *cmd_res = cmd_results_new(CMD_INVALID, "Invalid syntax (%s)", src); free(src); return cmd_res; } - if (!src) { - sway_log(SWAY_ERROR, "Failed to allocate expanded path"); - return cmd_results_new(CMD_FAILURE, "Unable to allocate resource"); - } if (config->reading && *src != '/') { // src file is inside configuration dir char *conf = strdup(config->current_config_path); - if (!conf) { - sway_log(SWAY_ERROR, "Failed to duplicate string"); - free(src); - return cmd_results_new(CMD_FAILURE, - "Unable to allocate resources"); - } + if (!conf) goto cleanup; char *conf_path = dirname(conf); char *real_src = malloc(strlen(conf_path) + strlen(src) + 2); if (!real_src) { - free(src); free(conf); - sway_log(SWAY_ERROR, "Unable to allocate memory"); - return cmd_results_new(CMD_FAILURE, - "Unable to allocate resources"); + goto cleanup; } snprintf(real_src, strlen(conf_path) + strlen(src) + 2, "%s/%s", conf_path, src); @@ -117,40 +103,48 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { } bool can_access = access(src, F_OK) != -1; + argc -= j + 1; argv += j + 1; + free(output->background_option); + free(output->background_fallback); + free(output->background); + output->background = output->background_option = output->background_fallback = NULL; + char *fallback = NULL; + + if (argc && *argv[0] == '#') { + if (validate_color(argv[0])) { + if (!(fallback = strdup(argv[0]))) goto cleanup; + output->background_fallback = fallback; + } else { + sway_log(SWAY_ERROR, "fallback '%s' should be of the form #RRGGBB", argv[0]); + config_add_swaynag_warning("fallback '%s' should be of the form #RRGGBB\n", argv[0]); + } + argc--; argv++; + } + if (!can_access) { - sway_log_errno(SWAY_ERROR, "Unable to access background file '%s'", - src); - config_add_swaynag_warning("Unable to access background file '%s'", - src); - struct cmd_results *result = cmd_results_new(CMD_FAILURE, - "unable to access background file '%s'", src); - free(src); - return result; + if (!fallback) { + sway_log(SWAY_ERROR, "Unable to access background file '%s' " + "and no valid fallback provided", src); + struct cmd_results *res = cmd_results_new(CMD_FAILURE, "Unable to access " + "background file '%s' and no valid fallback provided", src); + free(src); + return res; + } + sway_log(SWAY_DEBUG, "Cannot access file '%s', using fallback '%s'", src, fallback); + output->background = fallback; + if (!(output->background_option = strdup("solid_color"))) goto cleanup; + output->background_fallback = NULL; } else { output->background = src; - output->background_option = strdup(mode); - } - argc -= j + 1; argv += j + 1; - - 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++; - - if (!can_access) { - output->background = output->background_fallback; - output->background_option = strdup("solid_color"); - output->background_fallback = NULL; - } + if (!(output->background_option = strdup(mode))) goto cleanup; } } - config->handler_context.leftovers.argc = argc; config->handler_context.leftovers.argv = argv; return NULL; + +cleanup: + free(src); + sway_log(SWAY_ERROR, "Failed to allocate resources"); + return cmd_results_new(CMD_FAILURE, "Unable to allocate resources"); } From c1031d84655d2658dfeb8f4b93f099adee0494be Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Sun, 16 Feb 2025 18:37:25 +0100 Subject: [PATCH 180/361] sway/ipc-json: add ext-foreign-toplevel-handle identifier to get_tree ipc output Fixes #8291 --- sway/ipc-json.c | 5 +++++ sway/sway-ipc.7.scd | 3 +++ swaymsg/main.c | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 142512eb2..d3adedd4e 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "config.h" #include "log.h" @@ -577,6 +578,10 @@ 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); + json_object_object_add(object, "foreign_toplevel_identifier", + c->view->ext_foreign_toplevel ? + json_object_new_string(c->view->ext_foreign_toplevel->identifier) : NULL); + bool visible = view_is_visible(c->view); json_object_object_add(object, "visible", json_object_new_boolean(visible)); diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index f39e35180..ab85cf66c 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -389,6 +389,9 @@ node and will have the following properties: |- pid : integer : (Only views) The PID of the application that owns the view +|- foreign_toplevel_identifier +: string +: (Only views) The ext-foreign-toplevel-list-v1 toplevel identifier of this node. |- visible : boolean : (Only views) Whether the node is visible diff --git a/swaymsg/main.c b/swaymsg/main.c index 0ef6eb8a5..7534ea6db 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -330,6 +330,7 @@ static void pretty_print_tree(json_object *obj, int indent) { const char *instance = json_object_get_string(json_object_object_get(window_props_obj, "instance")); const char *class = json_object_get_string(json_object_object_get(window_props_obj, "class")); int x11_id = json_object_get_int(json_object_object_get(obj, "window")); + const char *foreign_toplevel_id = json_object_get_string(json_object_object_get(obj, "foreign_toplevel_identifier")); const char *sandbox_engine = json_object_get_string(json_object_object_get(obj, "sandbox_engine")); const char *sandbox_app_id = json_object_get_string(json_object_object_get(obj, "sandbox_app_id")); const char *sandbox_instance_id = json_object_get_string(json_object_object_get(obj, "sandbox_instance_id")); @@ -347,6 +348,9 @@ static void pretty_print_tree(json_object *obj, int indent) { if (x11_id != 0) { printf(", X11 window: 0x%X", x11_id); } + if (foreign_toplevel_id != NULL) { + printf(", foreign_toplevel_id: \"%s\"", foreign_toplevel_id); + } if (sandbox_engine != NULL) { printf(", sandbox_engine: \"%s\"", sandbox_engine); } From 10e50e6bf9b63b205c141f97a5709fd4d405542f Mon Sep 17 00:00:00 2001 From: Mark Stosberg Date: Sun, 16 Feb 2025 16:04:17 -0500 Subject: [PATCH 181/361] docs: use "window" instead of "view" throughout. "view" is an internal term, while the commonly understood user-facing term is "window" Ref: #7323 --- sway/sway-ipc.7.scd | 64 +++++++++++++++++++++---------------------- sway/sway.5.scd | 66 ++++++++++++++++++++++----------------------- 2 files changed, 65 insertions(+), 65 deletions(-) diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index ab85cf66c..833db0ef8 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -138,7 +138,7 @@ has the following properties: : Whether the workspace is currently focused by the default seat (_seat0_) |- urgent : boolean -: Whether a view on the workspace has the urgent flag set +: Whether a window on the workspace has the urgent flag set |- rect : object : The bounds of the workspace. It consists of _x_, _y_, _width_, and _height_ @@ -374,7 +374,7 @@ node and will have the following properties: that can be used as an aid in submitting reproduction steps for bug reports |- fullscreen_mode : integer -: (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means +: (Only containers and windows) The fullscreen mode of the node. 0 means none, 1 means full workspace, and 2 means global fullscreen |- floating : string @@ -384,45 +384,45 @@ node and will have the following properties: : Whether the window is in the scratchpad. Can be either "none" or "fresh" |- app_id : string -: (Only views) For an xdg-shell view, the name of the application, if set. +: (Only windows) For an xdg-shell window, the name of the application, if set. Otherwise, _null_ |- pid : integer -: (Only views) The PID of the application that owns the view +: (Only windows) The PID of the application that owns the window |- foreign_toplevel_identifier : string -: (Only views) The ext-foreign-toplevel-list-v1 toplevel identifier of this node. +: (Only windows) The ext-foreign-toplevel-list-v1 toplevel identifier of this node. |- visible : boolean -: (Only views) Whether the node is visible +: (Only windows) Whether the node is visible |- shell : string -: (Only views) The shell of the view, such as _xdg\_shell_ or _xwayland_ +: (Only windows) The shell of the window, such as _xdg\_shell_ or _xwayland_ |- inhibit_idle : boolean -: (Only views) Whether the view is inhibiting the idle state +: (Only windows) Whether the window is inhibiting the idle state |- idle_inhibitors : object -: (Only views) An object containing the state of the _application_ and _user_ idle inhibitors. +: (Only windows) An object containing the state of the _application_ and _user_ idle inhibitors. _application_ can be _enabled_ or _none_. _user_ can be _focus_, _fullscreen_, _open_, _visible_ or _none_. |- sandbox_engine : string -: (Only views) The associated sandbox engine (or _null_) +: (Only windows) The associated sandbox engine (or _null_) |- sandbox_app_id : string -: (Only views) The app ID provided by the associated sandbox engine (or _null_) +: (Only windows) The app ID provided by the associated sandbox engine (or _null_) |- sandbox_instance_id : string -: (Only views) The instance ID provided by the associated sandbox engine (or +: (Only windows) The instance ID provided by the associated sandbox engine (or _null_) |- window : integer -: (Only xwayland views) The X11 window ID for the xwayland view +: (Only xwayland windows) The X11 window ID for the xwayland window |- window_properties : object -: (Only xwayland views) An object containing the _title_, _class_, _instance_, - _window\_role_, _window\_type_, and _transient\_for_ for the view +: (Only xwayland windows) An object containing the _title_, _class_, _instance_, + _window\_role_, _window\_type_, and _transient\_for_ for the window *Example Reply:* @@ -927,13 +927,13 @@ containing the _#RRGGBBAA_ representation of the color: 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 + contain an urgent window |- urgent_workspace_bg : The color to use for the background of the workspace buttons for workspaces - that contain an urgent view + that contain an urgent window |- urgent_workspace_border : The color to use for the border of the workspace buttons for workspaces that - contain an urgent view + contain an urgent window |- binding_mode_text : The color to use for the text of the binding mode indicator |- binding_mode_bg @@ -1480,7 +1480,7 @@ available: : Sent whenever the binding mode changes |- 0x80000003 : window -: Sent whenever an event involving a view occurs such as being reparented, +: Sent whenever an event involving a window occurs such as being reparented, focused, or closed |- 0x80000004 : barconfig_update @@ -1536,8 +1536,8 @@ The following change types are currently available: |- 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 +: A window on the workspace has had their urgency hint set or all urgency hints + for windows on the workspace have been cleared |- reload : The configuration file has been reloaded @@ -1635,7 +1635,7 @@ with the following properties: ## 0x80000003. WINDOW -Sent whenever a change involving a view occurs. The event consists of a single +Sent whenever a change involving a window occurs. The event consists of a single object with the following properties: [- *PROPERTY* @@ -1646,30 +1646,30 @@ object with the following properties: :[ The type of change that occurred. See below for more information |- container : object -: An object representing the view effected +: An object representing the window effected The following change types are currently available: [- *TYPE* :- *DESCRIPTION* |- new -:[ The view was created +:[ The window was created |- close -: The view was closed +: The window was closed |- focus -: The view was focused +: The window was focused |- title -: The view's title has changed +: The window's title has changed |- fullscreen_mode -: The view's fullscreen mode has changed +: The window's fullscreen mode has changed |- move -: The view has been reparented in the tree +: The window has been reparented in the tree |- floating -: The view has become floating or is no longer floating +: The window has become floating or is no longer floating |- urgent -: The view's urgency hint has changed status +: The window's urgency hint has changed status |- mark -: A mark has been added or removed from the view +: A mark has been added or removed from the window *Example Event:* diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 11468d777..f580b2af3 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -117,7 +117,7 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). Exit sway and end your Wayland session. *floating* enable|disable|toggle - Make focused view floating, non-floating, or the opposite of what it is now. + Make focused window floating, non-floating, or the opposite of what it is now. *focus* Moves focus to the container that matches the specified criteria. @@ -152,9 +152,9 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). Moves focus between the floating and tiled layers. *fullscreen* [enable|disable|toggle] [global] - Makes focused view fullscreen, non-fullscreen, or the opposite of what it + Makes focused window fullscreen, non-fullscreen, or the opposite of what it is now. If no argument is given, it does the same as _toggle_. If _global_ - is specified, the view will be fullscreen across all outputs. + is specified, the window will be fullscreen across all outputs. *gaps* inner|outer|horizontal|vertical|top|right|bottom|left all|current set|plus|minus|toggle @@ -164,16 +164,16 @@ set|plus|minus|toggle _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 + Set/unset an idle inhibitor for the window. _focus_ will inhibit idle when + the window 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. _open_ will inhibit idle until the window is closed (or the + inhibitor is unset/changed). _visible_ will inhibit idle when the window is visible on any output. _none_ will remove any existing idle inhibitor for - the view. + the window. 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. + existing window or with _for_window_ to set idle inhibitors for future windows. *layout* default|splith|splitv|stacking|tabbed Sets the layout mode of the focused container. @@ -331,12 +331,12 @@ set|plus|minus|toggle *shortcuts_inhibitor* enable|disable Enables or disables the ability of clients to inhibit keyboard - shortcuts for a view. This is primarily useful for virtualization and - remote desktop software. It affects either the currently focused view - or a set of views selected by criteria. Subcommand _disable_ - additionally deactivates any active inhibitors for the given view(s). + shortcuts for a window. This is primarily useful for virtualization and + remote desktop software. It affects either the currently focused window + or a set of windows selected by criteria. Subcommand _disable_ + additionally deactivates any active inhibitors for the given window(s). Criteria are particularly useful with the *for_window* command to - configure a class of views differently from the per-seat defaults + configure a class of windows differently from the per-seat defaults established by the *seat* subcommand of the same name. See *sway-input*(5) for more ways to affect inhibitors. @@ -364,7 +364,7 @@ set|plus|minus|toggle Swaps the position, geometry, and fullscreen status of two containers. The first container can be selected either by criteria or focus. The second container can be selected by _id_, _con_id_, or _mark_. _id_ can only be - used with xwayland views. If the first container has focus, it will retain + used with xwayland windows. If the first container has focus, it will retain focus unless it is moved to a different workspace or the second container becomes fullscreen on the same workspace as the first container. In either of those cases, the second container will gain focus. @@ -409,14 +409,14 @@ The following commands may be used either in the configuration file or at runtime. *assign* [→] [workspace] [number] - Assigns views matching _criteria_ (see *CRITERIA* for details) to + Assigns windows matching _criteria_ (see *CRITERIA* for details) to _workspace_. The → (U+2192) is optional and cosmetic. This command is equivalent to: for_window move container to workspace *assign* [→] output left|right|up|down| - Assigns views matching _criteria_ (see *CRITERIA* for details) to the + Assigns windows matching _criteria_ (see *CRITERIA* for details) to the specified output. The → (U+2192) is optional and cosmetic. This command is equivalent to: @@ -599,10 +599,10 @@ runtime. The window that has focus. *client.focused_inactive* - The most recently focused view within a container which is not focused. + The most recently focused window within a container which is not focused. *client.focused_tab_title* - A view that has focused descendant container. + A window that has focused descendant container. Tab or stack container title that is the parent of the focused container but is not directly focused. Defaults to focused_inactive if not specified and does not use the indicator and child_border colors. @@ -611,10 +611,10 @@ runtime. Ignored (present for i3 compatibility). *client.unfocused* - A view that does not have focus. + A window that does not have focus. *client.urgent* - A view with an urgency hint. *Note*: Native Wayland windows do not + A window with an urgency hint. *Note*: Native Wayland windows do not support urgency. Urgency only works for Xwayland windows. The meaning of each color is: @@ -629,12 +629,12 @@ runtime. The text color of the title bar. _indicator_ - The color used to indicate where a new view will open. In a tiled - container, this would paint the right border of the current view if a - new view would be opened to the right. + The color used to indicate where a new window will open. In a tiled + container, this would paint the right border of the current window if a + new window would be opened to the right. _child_border_ - The border around the view itself. + The border around the window itself. The default colors are: @@ -772,7 +772,7 @@ The default colors are: *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 + affects spacing around each window and outer affects the spacing around each workspace. Outer gaps are in addition to inner gaps. To reduce or remove outer gaps, outer gaps can be set to a negative value. _outer_ gaps can also be specified per side with _top_, _right_, _bottom_, and _left_ or @@ -844,9 +844,9 @@ The default colors are: A list of output names may be obtained via *swaymsg -t get_outputs*. *popup_during_fullscreen* smart|ignore|leave_fullscreen - Determines what to do when a fullscreen view opens a dialog. + Determines what to do when a fullscreen window opens a dialog. If _smart_ (the default), the dialog will be displayed. If _ignore_, the - dialog will not be rendered. If _leave_fullscreen_, the view will exit + dialog will not be rendered. If _leave_fullscreen_, the window will exit fullscreen mode and the dialog will be rendered. *primary_selection* enabled|disabled @@ -976,14 +976,14 @@ A criteria is a string in the form of, for example: ``` The string contains one or more (space separated) attribute/value pairs. They -are used by some commands to choose which views to execute actions on. All +are used by some commands to choose which windows to execute actions on. All attributes must match for the criteria to match. Criteria is retained across commands separated by a *,*, but will be reset (and allow for new criteria, if desired) for commands separated by a *;*. Criteria may be used with either the *for_window* or *assign* commands to -specify operations to perform on new views. A criteria may also be used to -perform specific commands (ones that normally act upon one window) on all views +specify operations to perform on new windows. A criteria may also be used to +perform specific commands (ones that normally act upon one window) on all windows that match that criteria. For example: Focus on a window with the mark "IRC": @@ -1071,8 +1071,8 @@ The following attributes may be matched with: applications and requires XWayland. *workspace* - Compare against the workspace name for this view. Can be a regular - expression. If the value is \_\_focused\_\_, then all the views on the + Compare against the workspace name for this window. Can be a regular + expression. If the value is \_\_focused\_\_, then all the windows on the currently focused workspace matches. *sandbox_engine* From 38005bd85461d1e65295aacbf7f4d00e54295032 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 18 Feb 2025 12:38:44 -0500 Subject: [PATCH 182/361] output: Expose output_configure_scene to header --- include/sway/output.h | 3 +++ sway/desktop/output.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/sway/output.h b/include/sway/output.h index 4584ea453..f6354e0ed 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -91,6 +91,9 @@ struct sway_output *output_from_wlr_output(struct wlr_output *output); struct sway_output *output_get_in_direction(struct sway_output *reference, enum wlr_direction direction); +void output_configure_scene(struct sway_output *output, + struct wlr_scene_node *node, float opacity); + void output_add_workspace(struct sway_output *output, struct sway_workspace *workspace); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index d1facdea6..d6a182527 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -202,7 +202,7 @@ static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output, } } -static void output_configure_scene(struct sway_output *output, +void output_configure_scene(struct sway_output *output, struct wlr_scene_node *node, float opacity) { if (!node->enabled) { return; From 0da0d37f3dcbf66befb198fcd221d8345b9814b5 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 18 Feb 2025 12:39:18 -0500 Subject: [PATCH 183/361] output: Allow configuring scene without an output --- sway/desktop/output.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index d6a182527..e24193175 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -229,7 +229,9 @@ void output_configure_scene(struct sway_output *output, // hack: don't call the scene setter because that will damage all outputs // We don't want to damage outputs that aren't our current output that // we're configuring - buffer->filter_mode = get_scale_filter(output, buffer); + if (output) { + buffer->filter_mode = get_scale_filter(output, buffer); + } wlr_scene_buffer_set_opacity(buffer, opacity); } else if (node->type == WLR_SCENE_NODE_TREE) { From 7fab75a7a6d9b2cccfec7741cc59c705b3c40f15 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 18 Feb 2025 12:41:22 -0500 Subject: [PATCH 184/361] commands/opacity: Call output_configure_scene on updated container Calling container_update() wasn't enough: If there is no visible window decorations (title bar, borders) container_update would basically no-op and the scene wouldn't repaint with the update alpha. By also calling output_configure_scene() we force a call to wlr_scene_buffer_set_opacity() thus ensuring we update the scene. Closes: #8580 --- sway/commands/opacity.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c index 610cecc6d..de058c6b8 100644 --- a/sway/commands/opacity.c +++ b/sway/commands/opacity.c @@ -2,7 +2,8 @@ #include #include #include "sway/commands.h" -#include "sway/tree/view.h" +#include "sway/tree/container.h" +#include "sway/output.h" #include "log.h" struct cmd_results *cmd_opacity(int argc, char **argv) { @@ -37,6 +38,7 @@ struct cmd_results *cmd_opacity(int argc, char **argv) { } con->alpha = val; + output_configure_scene(NULL, &con->scene_tree->node, 1); container_update(con); return cmd_results_new(CMD_SUCCESS, NULL); From 8a60f30423813f9c0938e1605939db71310c7b50 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 25 Feb 2025 10:52:52 +0100 Subject: [PATCH 185/361] sway_text_node: Apply max_width when rendering max_width was applied to the source box, but not to the cairo surface. The cairo surface would therefore take on arbitrarily large dimensions according to the required dimensions to fit the text input, which if large enough would cause failures during output rendering and leave a black hole in the titlebar. --- sway/sway_text_node.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c index 7c7813559..4bc8d996c 100644 --- a/sway/sway_text_node.c +++ b/sway/sway_text_node.c @@ -87,7 +87,7 @@ static void render_backing_buffer(struct text_buffer *buffer) { } float scale = buffer->scale; - int width = ceil(buffer->props.width * scale); + int width = ceil(get_text_width(&buffer->props) * scale); int height = ceil(buffer->props.height * scale); float *color = (float *)&buffer->props.color; float *background = (float *)&buffer->props.background; @@ -153,7 +153,7 @@ static void render_backing_buffer(struct text_buffer *buffer) { pixman_region32_init(&opaque); if (background[3] == 1) { pixman_region32_union_rect(&opaque, &opaque, 0, 0, - buffer->props.width, buffer->props.height); + get_text_width(&buffer->props), buffer->props.height); } wlr_scene_buffer_set_opaque_region(buffer->buffer_node, &opaque); pixman_region32_fini(&opaque); From 962e1e70a60e9f39d2fdb6fa1810017682fd1f7b Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 25 Feb 2025 13:44:54 +0100 Subject: [PATCH 186/361] sway_text_node: Remove use of source box The source box is always set to the full buffer dimensions, making it ineffective. Remove it. --- sway/sway_text_node.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c index 4bc8d996c..89ece91e2 100644 --- a/sway/sway_text_node.c +++ b/sway/sway_text_node.c @@ -64,18 +64,6 @@ static int get_text_width(struct sway_text_node *props) { return MAX(width, 0); } -static void update_source_box(struct text_buffer *buffer) { - struct sway_text_node *props = &buffer->props; - struct wlr_fbox source_box = { - .x = 0, - .y = 0, - .width = ceil(get_text_width(props) * buffer->scale), - .height = ceil(props->height * buffer->scale), - }; - - wlr_scene_buffer_set_source_box(buffer->buffer_node, &source_box); -} - static void render_backing_buffer(struct text_buffer *buffer) { if (!buffer->visible) { return; @@ -147,7 +135,6 @@ static void render_backing_buffer(struct text_buffer *buffer) { wlr_scene_buffer_set_buffer(buffer->buffer_node, &cairo_buffer->base); wlr_buffer_drop(&cairo_buffer->base); - update_source_box(buffer); pixman_region32_t opaque; pixman_region32_init(&opaque); @@ -300,7 +287,6 @@ void sway_text_node_set_max_width(struct sway_text_node *node, int max_width) { buffer->props.max_width = max_width; wlr_scene_buffer_set_dest_size(buffer->buffer_node, get_text_width(&buffer->props), buffer->props.height); - update_source_box(buffer); render_backing_buffer(buffer); } From e3d9cc2aa5f1c298fd956b64e5e20f50aaac72fe Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 11 Feb 2025 12:41:59 +0100 Subject: [PATCH 187/361] Rework fork/exec strategy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cmd_exec_process is used whenever sway is meant to execute a child process on behalf of the user, and had a lot of complexity. In order to avoid having to wait on the user's process, a double-fork was used, which in turn required us to wait on the outer process. In order to track the child PID for launcher purposes, a pipe was used to transmit the PID back to sway. This resulted in sway blocking for 5-6 ms per exec on my system, which is quite significant. The error handling was also quite lacking - the read loop did not handle errors at all for example. Instead, teach sway to handle SIGCHLD and do away with the double-fork. This in turn allows us to get rid of the pipe as we can record the child's PID directly. This reduces the time we block to just 1.5 ms on my system. We'd be able to get down to just 150 µs if we could use posix_spawn(3), but posix_spawn(3) cannot reset NOFILE. clone(2) or vfork(2) would be alternatives, but that presents portability issues. This change is replicated for swaybar, swaybg and swaynag handling, which had similar albeit less complicated implementations. --- sway/commands/exec_always.c | 75 ++++++++++--------------------------- sway/config/bar.c | 39 +++++++------------ sway/config/output.c | 40 +++++++------------- sway/main.c | 8 ++++ sway/swaynag.c | 51 ++++++++++--------------- 5 files changed, 73 insertions(+), 140 deletions(-) diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index 8bc1048cd..14566fc43 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -25,16 +25,6 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) { return error; } -static void export_xdga_token(struct launcher_ctx *ctx) { - const char *token = launcher_ctx_get_token_name(ctx); - setenv("XDG_ACTIVATION_TOKEN", token, 1); -} - -static void export_startup_id(struct launcher_ctx *ctx) { - const char *token = launcher_ctx_get_token_name(ctx); - setenv("DESKTOP_STARTUP_ID", token, 1); -} - struct cmd_results *cmd_exec_process(int argc, char **argv) { struct cmd_results *error = NULL; char *cmd = NULL; @@ -56,67 +46,42 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { sway_log(SWAY_DEBUG, "Executing %s", cmd); - int fd[2]; - if (pipe(fd) != 0) { - sway_log(SWAY_ERROR, "Unable to create pipe for fork"); - } - - pid_t pid, child; struct launcher_ctx *ctx = launcher_ctx_create_internal(); + // Fork process - if ((pid = fork()) == 0) { - // Fork child process again + pid_t child = fork(); + if (child == 0) { restore_nofile_limit(); setsid(); sigset_t set; sigemptyset(&set); sigprocmask(SIG_SETMASK, &set, NULL); signal(SIGPIPE, SIG_DFL); - close(fd[0]); - if ((child = fork()) == 0) { - close(fd[1]); - if (ctx) { - export_xdga_token(ctx); + + if (ctx) { + const char *token = launcher_ctx_get_token_name(ctx); + setenv("XDG_ACTIVATION_TOKEN", token, 1); + if (!no_startup_id) { + setenv("DESKTOP_STARTUP_ID", token, 1); } - if (ctx && !no_startup_id) { - export_startup_id(ctx); - } - execlp("sh", "sh", "-c", cmd, (void *)NULL); - sway_log_errno(SWAY_ERROR, "execlp failed"); - _exit(1); } - ssize_t s = 0; - while ((size_t)s < sizeof(pid_t)) { - s += write(fd[1], ((uint8_t *)&child) + s, sizeof(pid_t) - s); - } - close(fd[1]); + + execlp("sh", "sh", "-c", cmd, (void*)NULL); + sway_log_errno(SWAY_ERROR, "execve failed"); _exit(0); // Close child process - } else if (pid < 0) { + } else if (child < 0) { + launcher_ctx_destroy(ctx); free(cmd); - close(fd[0]); - close(fd[1]); return cmd_results_new(CMD_FAILURE, "fork() failed"); } - free(cmd); - close(fd[1]); // close write - ssize_t s = 0; - while ((size_t)s < sizeof(pid_t)) { - s += read(fd[0], ((uint8_t *)&child) + s, sizeof(pid_t) - s); - } - close(fd[0]); - // cleanup child process - waitpid(pid, NULL, 0); - if (child > 0) { - sway_log(SWAY_DEBUG, "Child process created with pid %d", child); - if (ctx != NULL) { - sway_log(SWAY_DEBUG, "Recording workspace for process %d", child); - ctx->pid = child; - } - } else { - launcher_ctx_destroy(ctx); - return cmd_results_new(CMD_FAILURE, "Second fork() failed"); + + sway_log(SWAY_DEBUG, "Child process created with pid %d", child); + if (ctx != NULL) { + sway_log(SWAY_DEBUG, "Recording workspace for process %d", child); + ctx->pid = child; } + free(cmd); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/config/bar.c b/sway/config/bar.c index ecefb61af..f7eddfbb1 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -220,29 +220,21 @@ static void invoke_swaybar(struct bar_config *bar) { signal(SIGPIPE, SIG_DFL); restore_nofile_limit(); - - pid = fork(); - if (pid < 0) { - sway_log_errno(SWAY_ERROR, "fork failed"); - _exit(EXIT_FAILURE); - } else if (pid == 0) { - if (!sway_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); - - // run custom swaybar - char *const cmd[] = { - bar->swaybar_command ? bar->swaybar_command : "swaybar", - "-b", bar->id, NULL}; - execvp(cmd[0], cmd); + if (!sway_set_cloexec(sockets[1], false)) { _exit(EXIT_FAILURE); } - _exit(EXIT_SUCCESS); + + char wayland_socket_str[16]; + snprintf(wayland_socket_str, sizeof(wayland_socket_str), + "%d", sockets[1]); + setenv("WAYLAND_SOCKET", wayland_socket_str, true); + + // run custom swaybar + char *const cmd[] = { + bar->swaybar_command ? bar->swaybar_command : "swaybar", + "-b", bar->id, NULL}; + execvp(cmd[0], cmd); + _exit(EXIT_FAILURE); } if (close(sockets[1]) != 0) { @@ -250,11 +242,6 @@ static void invoke_swaybar(struct bar_config *bar) { return; } - if (waitpid(pid, NULL, 0) < 0) { - sway_log_errno(SWAY_ERROR, "waitpid failed"); - return; - } - sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); } diff --git a/sway/config/output.c b/sway/config/output.c index 9fb3a12ac..4d4a47b46 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1061,41 +1061,27 @@ static bool _spawn_swaybg(char **command) { return false; } else if (pid == 0) { restore_nofile_limit(); - - pid = fork(); - if (pid < 0) { - sway_log_errno(SWAY_ERROR, "fork failed"); - _exit(EXIT_FAILURE); - } else if (pid == 0) { - if (!sway_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, "failed to execute '%s' " - "(background configuration probably not applied)", - command[0]); + if (!sway_set_cloexec(sockets[1], false)) { _exit(EXIT_FAILURE); } - _exit(EXIT_SUCCESS); + + 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, "failed to execute '%s' " + "(background configuration probably not applied)", + command[0]); + _exit(EXIT_FAILURE); } if (close(sockets[1]) != 0) { sway_log_errno(SWAY_ERROR, "close failed"); return false; } - int fork_status = 0; - if (waitpid(pid, &fork_status, 0) < 0) { - sway_log_errno(SWAY_ERROR, "waitpid failed"); - return false; - } - - return WIFEXITED(fork_status) && WEXITSTATUS(fork_status) == EXIT_SUCCESS; + return true; } bool spawn_swaybg(void) { diff --git a/sway/main.c b/sway/main.c index accffc71f..0cc7623d8 100644 --- a/sway/main.c +++ b/sway/main.c @@ -48,6 +48,13 @@ void sig_handler(int signal) { sway_terminate(EXIT_SUCCESS); } +void sigchld_handler(int signal) { + pid_t pid; + do { + pid = waitpid(-1, NULL, WNOHANG); + } while (pid > 0); +} + void run_as_ipc_client(char *command, char *socket_path) { int socketfd = ipc_open_socket(socket_path); uint32_t len = strlen(command); @@ -325,6 +332,7 @@ int main(int argc, char **argv) { // handle SIGTERM signals signal(SIGTERM, sig_handler); signal(SIGINT, sig_handler); + signal(SIGCHLD, sigchld_handler); // prevent ipc from crashing sway signal(SIGPIPE, SIG_IGN); diff --git a/sway/swaynag.c b/sway/swaynag.c index bc5e23ea4..f0a31218a 100644 --- a/sway/swaynag.c +++ b/sway/swaynag.c @@ -64,35 +64,27 @@ bool swaynag_spawn(const char *swaynag_command, goto failed; } else if (pid == 0) { restore_nofile_limit(); - - pid = fork(); - if (pid < 0) { - sway_log_errno(SWAY_ERROR, "fork failed"); - _exit(EXIT_FAILURE); - } else if (pid == 0) { - if (!sway_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); - execlp("sh", "sh", "-c", cmd, NULL); - sway_log_errno(SWAY_ERROR, "execlp failed"); + if (!sway_set_cloexec(sockets[1], false)) { _exit(EXIT_FAILURE); } - _exit(EXIT_SUCCESS); + + 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); + execlp("sh", "sh", "-c", cmd, NULL); + sway_log_errno(SWAY_ERROR, "execlp failed"); + _exit(EXIT_FAILURE); } if (swaynag->detailed) { @@ -107,11 +99,6 @@ bool swaynag_spawn(const char *swaynag_command, return false; } - if (waitpid(pid, NULL, 0) < 0) { - sway_log_errno(SWAY_ERROR, "waitpid failed"); - return false; - } - return true; failed: From 048e304b8a54fc31b53b716538971c0c2315d4c2 Mon Sep 17 00:00:00 2001 From: Chris Perl Date: Fri, 7 Mar 2025 10:55:52 -0500 Subject: [PATCH 188/361] Remove constraint that con->view != NULL to use __focused__ criteria To use something like: [con_id=__focused__] mark --add --toggle foo The container must currently have a view. However, it is possible to focus parent containers that do not have a view. For example, via: focus parent Since containers without views can be the focused (meaning the container is marked "focused": true in the output of: swaymsg -t get_tree), it seems reasonable that a view is not required to target a container via __focused__. --- sway/criteria.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sway/criteria.c b/sway/criteria.c index 0aefa0080..29f73f697 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -630,8 +630,7 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { if (strcmp(value, "__focused__") == 0) { struct sway_seat *seat = input_manager_current_seat(); struct sway_container *focus = seat_get_focused_container(seat); - struct sway_view *view = focus ? focus->view : NULL; - criteria->con_id = view ? view->container->node.id : 0; + criteria->con_id = focus ? focus->node.id : 0; } else { criteria->con_id = strtoul(value, &endptr, 10); if (*endptr != 0) { From 9dcccf784bf89e54e5a6f65f72c116943f3b30ea Mon Sep 17 00:00:00 2001 From: nilninull Date: Sun, 4 Feb 2024 17:20:45 +0900 Subject: [PATCH 189/361] Add the DesktopNames key to the sway.desktop session file According to https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html, to set the value of XDG_CURRENT_DESKTOP, a DesktopNames key is required in the session file. And the value of XDG_CURRENT_DESKTOP is used to run xdg-desktop-portal*. If this value is not set, xdg-desktop-portal-wlr will not run at login. --- sway.desktop | 1 + 1 file changed, 1 insertion(+) diff --git a/sway.desktop b/sway.desktop index 420db5aa1..cc7fcee73 100644 --- a/sway.desktop +++ b/sway.desktop @@ -3,3 +3,4 @@ Name=Sway Comment=An i3-compatible Wayland compositor Exec=sway Type=Application +DesktopNames=sway;wlroots From 5d7b9a8320f8999059f287734c1df76289b01a27 Mon Sep 17 00:00:00 2001 From: llyyr Date: Sun, 9 Mar 2025 02:08:43 +0530 Subject: [PATCH 190/361] sway/server: create ext-data-control manager --- include/sway/server.h | 3 ++- sway/server.c | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index 7b76eb906..95c9c45b0 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -104,7 +104,8 @@ struct sway_server { struct wlr_ext_foreign_toplevel_list_v1 *foreign_toplevel_list; struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; struct wlr_content_type_manager_v1 *content_type_manager_v1; - struct wlr_data_control_manager_v1 *data_control_manager_v1; + struct wlr_data_control_manager_v1 *wlr_data_control_manager_v1; + struct wlr_ext_data_control_manager_v1 *ext_data_control_manager_v1; struct wlr_screencopy_manager_v1 *screencopy_manager_v1; struct wlr_ext_image_copy_capture_manager_v1 *ext_image_copy_capture_manager_v1; struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager_v1; diff --git a/sway/server.c b/sway/server.c index c7fc2a6df..5de6648f6 100644 --- a/sway/server.c +++ b/sway/server.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -108,7 +109,8 @@ static bool is_privileged(const struct wl_global *global) { global == server.input_method->global || global == server.foreign_toplevel_list->global || global == server.foreign_toplevel_manager->global || - global == server.data_control_manager_v1->global || + global == server.wlr_data_control_manager_v1->global || + global == server.ext_data_control_manager_v1->global || global == server.screencopy_manager_v1->global || global == server.ext_image_copy_capture_manager_v1->global || global == server.export_dmabuf_manager_v1->global || @@ -376,7 +378,8 @@ bool server_init(struct sway_server *server) { server->screencopy_manager_v1 = wlr_screencopy_manager_v1_create(server->wl_display); server->ext_image_copy_capture_manager_v1 = wlr_ext_image_copy_capture_manager_v1_create(server->wl_display, 1); wlr_ext_output_image_capture_source_manager_v1_create(server->wl_display, 1); - server->data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display); + server->wlr_data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display); + server->ext_data_control_manager_v1 = wlr_ext_data_control_manager_v1_create(server->wl_display, 1); server->security_context_manager_v1 = wlr_security_context_manager_v1_create(server->wl_display); wlr_viewporter_create(server->wl_display); wlr_single_pixel_buffer_manager_v1_create(server->wl_display); From 8238e5242bdbbc4c3b7cba0651c620a89b872a27 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sun, 9 Mar 2025 12:07:11 +0100 Subject: [PATCH 191/361] Use SIG_IGN for SIGCHLD instead of our own handler The behavior of handlers registered with signal(3p) is not well-defined for signals delivered more than once, as laid out in the man page. We should replace our use of signal with sigaction, but for SIGCHLD specifically we can also just skip the signals altogether by setting the handler to SIG_IGN which causes child reaping to not be required. Fixes: https://github.com/swaywm/sway/pull/8567 --- include/sway/server.h | 1 + sway/commands/exec_always.c | 5 +---- sway/config/bar.c | 7 +------ sway/main.c | 19 +++++++++++-------- 4 files changed, 14 insertions(+), 18 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index 95c9c45b0..feb516c5b 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -163,6 +163,7 @@ bool server_start(struct sway_server *server); void server_run(struct sway_server *server); void restore_nofile_limit(void); +void restore_signals(void); void handle_new_output(struct wl_listener *listener, void *data); diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index 14566fc43..8f02bbdc7 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -52,11 +52,8 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { pid_t child = fork(); if (child == 0) { restore_nofile_limit(); + restore_signals(); setsid(); - sigset_t set; - sigemptyset(&set); - sigprocmask(SIG_SETMASK, &set, NULL); - signal(SIGPIPE, SIG_DFL); if (ctx) { const char *token = launcher_ctx_get_token_name(ctx); diff --git a/sway/config/bar.c b/sway/config/bar.c index f7eddfbb1..6cace0da2 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -213,13 +213,8 @@ static void invoke_swaybar(struct bar_config *bar) { sway_log(SWAY_ERROR, "Failed to create fork for swaybar"); return; } else if (pid == 0) { - // Remove the SIGUSR1 handler that wlroots adds for xwayland - sigset_t set; - sigemptyset(&set); - sigprocmask(SIG_SETMASK, &set, NULL); - signal(SIGPIPE, SIG_DFL); - restore_nofile_limit(); + restore_signals(); if (!sway_set_cloexec(sockets[1], false)) { _exit(EXIT_FAILURE); } diff --git a/sway/main.c b/sway/main.c index 0cc7623d8..cabdd3aa0 100644 --- a/sway/main.c +++ b/sway/main.c @@ -48,13 +48,6 @@ void sig_handler(int signal) { sway_terminate(EXIT_SUCCESS); } -void sigchld_handler(int signal) { - pid_t pid; - do { - pid = waitpid(-1, NULL, WNOHANG); - } while (pid > 0); -} - void run_as_ipc_client(char *command, char *socket_path) { int socketfd = ipc_open_socket(socket_path); uint32_t len = strlen(command); @@ -159,6 +152,14 @@ void restore_nofile_limit(void) { } } +void restore_signals(void) { + sigset_t set; + sigemptyset(&set); + sigprocmask(SIG_SETMASK, &set, NULL); + signal(SIGCHLD, SIG_DFL); + signal(SIGPIPE, SIG_DFL); +} + void enable_debug_flag(const char *flag) { if (strcmp(flag, "noatomic") == 0) { debug.noatomic = true; @@ -332,7 +333,9 @@ int main(int argc, char **argv) { // handle SIGTERM signals signal(SIGTERM, sig_handler); signal(SIGINT, sig_handler); - signal(SIGCHLD, sigchld_handler); + + // avoid need to reap children + signal(SIGCHLD, SIG_IGN); // prevent ipc from crashing sway signal(SIGPIPE, SIG_IGN); From 61cc08cf3c49b0a5785b50c070ef3c33f1bbacab Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sun, 9 Mar 2025 12:17:42 +0100 Subject: [PATCH 192/361] config/output: Reset everything before swaybg exec swaybar and the exec command reset signal masks, signal handlers and NOFILE limit before exec, but swaybg was missing all that. Reset it for swaybg as well. --- sway/config/output.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/config/output.c b/sway/config/output.c index 4d4a47b46..b8a613cc5 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1061,6 +1061,7 @@ static bool _spawn_swaybg(char **command) { return false; } else if (pid == 0) { restore_nofile_limit(); + restore_signals(); if (!sway_set_cloexec(sockets[1], false)) { _exit(EXIT_FAILURE); } From 2f5b3c099965b72f4c14110fb42afc1a9d537029 Mon Sep 17 00:00:00 2001 From: melvinm1 <108258563+melvinm1@users.noreply.github.com> Date: Sun, 16 Mar 2025 00:29:15 +0100 Subject: [PATCH 193/361] Fix output repositioning in global fullscreen Call wlr_scene_output_set_position when in global fullscreen to correctly set output positions when repositioning outputs (using swaymsg output or similar). --- sway/desktop/transaction.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 50a597a66..52c03b18a 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -652,6 +652,8 @@ static void arrange_root(struct sway_root *root) { struct sway_output *output = root->outputs->items[i]; struct sway_workspace *ws = output->current.active_workspace; + wlr_scene_output_set_position(output->scene_output, output->lx, output->ly); + if (ws) { arrange_workspace_floating(ws); } From 3a49409dae9b8d579a4b92370ff5ba4a48cb5156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Piwo=C5=84ski?= Date: Wed, 12 Mar 2025 11:56:52 +0000 Subject: [PATCH 194/361] sway/commands: Return error if container is not in scratchpad --- sway/commands/scratchpad.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c index c995f2f08..8a63740c3 100644 --- a/sway/commands/scratchpad.c +++ b/sway/commands/scratchpad.c @@ -118,10 +118,10 @@ struct cmd_results *cmd_scratchpad(int argc, char **argv) { // If using criteria, this command is executed for every container which // matches the criteria. If this container isn't in the scratchpad, - // we'll just silently return a success. The same is true if the + // we'll return an error. The same is true if the // overridden node is not a container. if (!con || !con->scratchpad) { - return cmd_results_new(CMD_SUCCESS, NULL); + return cmd_results_new(CMD_INVALID, "Container is not in scratchpad."); } scratchpad_toggle_container(con); } else { From 30434b2beb0c621015452775011426da8d5e4705 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Wed, 19 Mar 2025 00:15:01 +0100 Subject: [PATCH 195/361] desktop/output: Skip repaint if wlr_output is disabled When the repaint timer fires, we check if the sway_output is disabled, and if so, skip the output commit after having reset frame_pending. The sway_output enable flag is only updated if the output is disabled and removed from the layout, not if the power is disabled for e.g. idle. This can lead to situations where a commit is attempted on a disabled output, which will lead to an attempted and failed primary swapchain allocation. Use the wlr_output.enabled state to check if the output is active. --- sway/desktop/output.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index e24193175..aec1d0a6b 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -264,7 +264,7 @@ static int output_repaint_timer_handler(void *data) { struct sway_output *output = data; output->wlr_output->frame_pending = false; - if (!output->enabled) { + if (!output->wlr_output->enabled) { return 0; } From d148560f50ce81bd5ca0e0f0d52c65c21f8b751d Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Thu, 20 Mar 2025 21:58:21 +0900 Subject: [PATCH 196/361] text_input: Fix ime panic in ext-session-lock in the origin text_input.c, we only check the sway_view and layershell, but now we have the third shell named sessionlock, so we need to modify both text_input.c and view.c to handle the new type of shell --- sway/input/text_input.c | 25 +++++++++++++++++++++++++ sway/tree/view.c | 4 ++++ 2 files changed, 29 insertions(+) diff --git a/sway/input/text_input.c b/sway/input/text_input.c index e16724671..a46f833c9 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -10,6 +10,7 @@ #include "sway/input/text_input_popup.h" #include "sway/layers.h" #include "sway/server.h" +#include static struct sway_text_input *relay_get_focusable_text_input( struct sway_input_method_relay *relay) { @@ -385,6 +386,8 @@ static void input_popup_set_focus(struct sway_input_popup *popup, struct wlr_layer_surface_v1 *layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(surface); + struct wlr_session_lock_surface_v1 *lock_surface = + wlr_session_lock_surface_v1_try_from_wlr_surface(surface); struct wlr_scene_tree *relative_parent; if (layer_surface) { @@ -404,8 +407,30 @@ static void input_popup_set_focus(struct sway_input_popup *popup, // surface. Layer surfaces get destroyed as part of the output being // destroyed, thus also trickling down to popups. popup->fixed_output = layer->layer_surface->output; + } else if (lock_surface) { + wl_signal_add(&lock_surface->surface->events.unmap, + &popup->focused_surface_unmap); + + struct sway_layer_surface *lock = lock_surface->data; + if (lock == NULL) { + return; + } + + relative_parent = lock->scene->tree; + popup->desc.view = NULL; + + // we don't need to add an event here to NULL out this field because + // this field will only be initialized if the popup is part of a layer + // surface. Layer surfaces get destroyed as part of the output being + // destroyed, thus also trickling down to popups. + popup->fixed_output = lock->layer_surface->output; } else { struct sway_view *view = view_from_wlr_surface(surface); + // In the future there may be other shells been added, so we also need to check here. + if (view == NULL) { + sway_log(SWAY_DEBUG, "Unsupported IME focus surface"); + return; + } wl_signal_add(&view->events.unmap, &popup->focused_surface_unmap); relative_parent = view->scene_tree; popup->desc.view = view; diff --git a/sway/tree/view.c b/sway/tree/view.c index 33161cc53..a5617fb4c 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -12,6 +12,7 @@ #include #include #include +#include #if WLR_HAS_XWAYLAND #include #endif @@ -1010,6 +1011,9 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) { return NULL; } + if (wlr_session_lock_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) { + return NULL; + } const char *role = wlr_surface->role ? wlr_surface->role->name : NULL; sway_log(SWAY_DEBUG, "Surface of unknown type (role %s): %p", From 4b185a0fe0031455d5ceab1eda2b9d9ffe0c81de Mon Sep 17 00:00:00 2001 From: Paul Riou Date: Thu, 20 Mar 2025 20:01:36 +0000 Subject: [PATCH 197/361] stringop: fix has_prefix() arg order in config parsing has_prefix() expects the prefix to be the 2nd argument, not the first. The config parsing was broken when using `--input-device=`. Introduced by: 0c60d1581f7b12ae472c786b7dfe27a1c6ec9a47 "Use has_prefix() instead of strncmp() throughout" --- sway/commands/bind.c | 2 +- sway/commands/gesture.c | 2 +- swaybar/ipc.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 32d4f8916..15373d5a8 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -367,7 +367,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, } } else if (strcmp("--exclude-titlebar", argv[0]) == 0) { exclude_titlebar = true; - } else if (has_prefix("--input-device=", argv[0])) { + } else if (has_prefix(argv[0], "--input-device=")) { free(binding->input); binding->input = strdup(argv[0] + strlen("--input-device=")); strip_quotes(binding->input); diff --git a/sway/commands/gesture.c b/sway/commands/gesture.c index e63b7ac6e..8dff29a34 100644 --- a/sway/commands/gesture.c +++ b/sway/commands/gesture.c @@ -121,7 +121,7 @@ static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, boo binding->flags |= BINDING_EXACT; } else if (strcmp("--no-warn", argv[0]) == 0) { warn = false; - } else if (has_prefix("--input-device=", argv[0])) { + } else if (has_prefix(argv[0], "--input-device=")) { free(binding->input); binding->input = strdup(argv[0] + strlen("--input-device=")); } else { diff --git a/swaybar/ipc.c b/swaybar/ipc.c index f651f0359..68d8dd32d 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -46,7 +46,7 @@ void ipc_send_workspace_command(struct swaybar *bar, const char *ws) { char *parse_font(const char *font) { char *new_font = NULL; - if (has_prefix("pango:", font)) { + if (has_prefix(font, "pango:")) { font += strlen("pango:"); } new_font = strdup(font); From c2d6aff64c1e265c8f1d95b780b54193defae18a Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Wed, 12 Mar 2025 16:35:41 +0100 Subject: [PATCH 198/361] Avoid crashing on too many containers If far too many containers are created, they can become so small that their size calculations come out negative, leading to crashes on asserts. Instead, set a lower bound for sizes and disable the container entirely if it goes below it, giving whatever space it used to the last container. The splits are not recalculated, so currently the effect is that if all containers have the same width fraction, they keep getting narrower until at some point they all round to zero and the last container will be given all the available space. A better behavior would have been if the additional container did not contribute to size and fraction calculations at all, but it's an extreme edge-case, anything is better than crashing, and this is easier to implement. --- sway/desktop/transaction.c | 27 ++++++++++++++++++--------- sway/tree/arrange.c | 18 +++++++++++++++--- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 52c03b18a..16ce8e2ed 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -312,9 +312,9 @@ static void arrange_children(enum sway_container_layout layout, list_t *children wlr_scene_node_set_position(&child->scene_tree->node, 0, title_bar_height); wlr_scene_node_reparent(&child->scene_tree->node, content); - if (activated) { - arrange_container(child, width, height - title_bar_height, - title_bar_height == 0, 0); + height -= title_bar_height; + if (activated && width > 0 && height > 0) { + arrange_container(child, width, height, title_bar_height == 0, 0); } else { disable_container(child); } @@ -341,9 +341,9 @@ static void arrange_children(enum sway_container_layout layout, list_t *children wlr_scene_node_set_position(&child->scene_tree->node, 0, title_height); wlr_scene_node_reparent(&child->scene_tree->node, content); - if (activated) { - arrange_container(child, width, height - title_height, - title_bar_height == 0, 0); + height -= title_bar_height; + if (activated && width > 0 && height > 0) { + arrange_container(child, width, height, title_bar_height == 0, 0); } else { disable_container(child); } @@ -359,8 +359,12 @@ static void arrange_children(enum sway_container_layout layout, list_t *children wlr_scene_node_set_enabled(&child->border.tree->node, true); wlr_scene_node_set_position(&child->scene_tree->node, 0, off); wlr_scene_node_reparent(&child->scene_tree->node, content); - arrange_container(child, width, cheight, true, gaps); - off += cheight + gaps; + if (width > 0 && cheight > 0) { + arrange_container(child, width, cheight, true, gaps); + off += cheight + gaps; + } else { + disable_container(child); + } } } else if (layout == L_HORIZ) { int off = 0; @@ -372,7 +376,12 @@ static void arrange_children(enum sway_container_layout layout, list_t *children wlr_scene_node_set_position(&child->scene_tree->node, off, 0); wlr_scene_node_reparent(&child->scene_tree->node, content); arrange_container(child, cwidth, height, true, gaps); - off += cwidth + gaps; + if (cwidth > 0 && height > 0) { + arrange_container(child, cwidth, height, true, gaps); + off += cwidth + gaps; + } else { + disable_container(child); + } } } else { sway_assert(false, "unreachable"); diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 2b95a6dc9..faf54d02a 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -29,7 +29,7 @@ static void apply_horiz_layout(list_t *children, struct wlr_box *parent) { } } - // Calculate each height fraction + // Calculate each width fraction double total_width_fraction = 0; for (int i = 0; i < children->length; ++i) { struct sway_container *child = children->items[i]; @@ -82,12 +82,18 @@ static void apply_horiz_layout(list_t *children, struct wlr_box *parent) { child->pending.y = parent->y; child->pending.width = round(child->width_fraction * child_total_width); child->pending.height = parent->height; - child_x += child->pending.width + inner_gap; // Make last child use remaining width of parent if (i == children->length - 1) { child->pending.width = parent->x + parent->width - child->pending.x; } + + // Arbitrary lower bound for window size + if (child->pending.width < 10 || child->pending.height < 10) { + child->pending.width = 0; + child->pending.height = 0; + } + child_x += child->pending.width + inner_gap; } } @@ -161,12 +167,18 @@ static void apply_vert_layout(list_t *children, struct wlr_box *parent) { child->pending.y = child_y; child->pending.width = parent->width; child->pending.height = round(child->height_fraction * child_total_height); - child_y += child->pending.height + inner_gap; // Make last child use remaining height of parent if (i == children->length - 1) { child->pending.height = parent->y + parent->height - child->pending.y; } + + // Arbitrary lower bound for window size + if (child->pending.width < 10 || child->pending.height < 10) { + child->pending.width = 0; + child->pending.height = 0; + } + child_y += child->pending.height + inner_gap; } } From ab455bbadae5f115262161c165fdd46d1cc4295d Mon Sep 17 00:00:00 2001 From: Dennis Baurichter Date: Tue, 11 Feb 2025 18:06:19 +0100 Subject: [PATCH 199/361] man: clarify criteria (incl. PCRE2 usage) Replace the XWayland-only class attribute in the examples: - The first example given should work for Wayland-native windows. - The example 'Kill all windows with the title "Emacs"' should use title, not class. Also, it's a substring (regex) match. There are many different implementations of regular expressions with incompatible syntax. For example, GNU grep alone provides three different ones. Clarify the use of PCRE2 by sway criteria. --- sway/sway.5.scd | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index f580b2af3..9bc03c9de 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -972,7 +972,7 @@ The default colors are: A criteria is a string in the form of, for example: ``` -[class="[Rr]egex.*" title="some title"] +[app_id="some-application" title="[Rr]egex.*"] ``` The string contains one or more (space separated) attribute/value pairs. They @@ -992,10 +992,19 @@ Focus on a window with the mark "IRC": [con_mark="IRC"] focus ``` -Kill all windows with the title "Emacs": +Kill all windows where the title contains "Emacs": ``` -[class="Emacs"] kill +[title="Emacs"] kill +``` + +Several attributes allow regular expressions. These use Perl-compatible regular +expressions (PCRE2), which are documented in *pcre2pattern*(3) and summarized in +*pcre2syntax*(3). For example, this moves all windows with titles ending in +"sway" or "Sway" to workspace 1: + +``` +[title="[Ss]way$"] move workspace 1 ``` You may like to use swaymsg -t get_tree for finding the values of these @@ -1094,3 +1103,4 @@ The following attributes may be matched with: # SEE ALSO *sway*(1) *sway-input*(5) *sway-output*(5) *sway-bar*(5) *sway-ipc*(7) +*pcre2pattern*(3) *pcre2syntax*(3) From a25645a5a6e205fccee1ca5abc00f8ee31b3bf44 Mon Sep 17 00:00:00 2001 From: Claudia Date: Wed, 26 Mar 2025 19:34:44 +0100 Subject: [PATCH 200/361] Fix tabbed/stacking container height regression Commit c2d6aff added a bounds check on `height - title_bar_height`, repurposing the local variable `height` in an attempt to DRY out the expression. However, because re-assignment occurs inside the loop body, its result would leak across loop iterations, compounding its effect and leading to the artifact reported in issue #8625, where each child except the first in a tabbed container would acquire a visible waterline. Introduce a second variable and reset it in each loop iteration to get rid of the waterline. Fixes #8625. --- sway/desktop/transaction.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 16ce8e2ed..01fe31289 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -312,9 +312,9 @@ static void arrange_children(enum sway_container_layout layout, list_t *children wlr_scene_node_set_position(&child->scene_tree->node, 0, title_bar_height); wlr_scene_node_reparent(&child->scene_tree->node, content); - height -= title_bar_height; - if (activated && width > 0 && height > 0) { - arrange_container(child, width, height, title_bar_height == 0, 0); + int net_height = height - title_bar_height; + if (activated && width > 0 && net_height > 0) { + arrange_container(child, width, net_height, title_bar_height == 0, 0); } else { disable_container(child); } @@ -341,9 +341,9 @@ static void arrange_children(enum sway_container_layout layout, list_t *children wlr_scene_node_set_position(&child->scene_tree->node, 0, title_height); wlr_scene_node_reparent(&child->scene_tree->node, content); - height -= title_bar_height; - if (activated && width > 0 && height > 0) { - arrange_container(child, width, height, title_bar_height == 0, 0); + int net_height = height - title_bar_height; + if (activated && width > 0 && net_height > 0) { + arrange_container(child, width, net_height, title_bar_height == 0, 0); } else { disable_container(child); } From cb246cb9c2c1440e05998d82eada21eaba107d59 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Tue, 8 Apr 2025 17:13:03 -0400 Subject: [PATCH 201/361] ipc: standardize pretty print with raw print `swaymsg -t get_inputs --raw` calls it a pointer but `--pretty` calls it a Mouse. Previous commit 6737b90cb that set this to pointer probably forgo to update the pretty one. closes #8584 --- swaymsg/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swaymsg/main.c b/swaymsg/main.c index 7534ea6db..dc1c74072 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -99,7 +99,7 @@ static const char *pretty_type_name(const char *name) { const char *b; } type_names[] = { { "keyboard", "Keyboard" }, - { "pointer", "Mouse" }, + { "pointer", "Pointer" }, { "touchpad", "Touchpad" }, { "tablet_pad", "Tablet pad" }, { "tablet_tool", "Tablet tool" }, From 8f089f0229eabbcd3f68d641d5b826220f1edb0c Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Sun, 9 Mar 2025 22:51:48 -0400 Subject: [PATCH 202/361] Use wl_event_loop_add_signal for exit signals This avoids calling non-async-signal-safe functions from within a signal handler. --- sway/main.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/sway/main.c b/sway/main.c index cabdd3aa0..0af977d4d 100644 --- a/sway/main.c +++ b/sway/main.c @@ -44,10 +44,6 @@ void sway_terminate(int exit_code) { } } -void sig_handler(int signal) { - sway_terminate(EXIT_SUCCESS); -} - void run_as_ipc_client(char *command, char *socket_path) { int socketfd = ipc_open_socket(socket_path); uint32_t len = strlen(command); @@ -152,6 +148,22 @@ void restore_nofile_limit(void) { } } +static int term_signal(int signal, void *data) { + sway_terminate(EXIT_SUCCESS); + return 0; +} + +static void init_signals(void) { + wl_event_loop_add_signal(server.wl_event_loop, SIGTERM, term_signal, NULL); + wl_event_loop_add_signal(server.wl_event_loop, SIGINT, term_signal, NULL); + + // avoid need to reap children + signal(SIGCHLD, SIG_IGN); + + // prevent ipc write errors from crashing sway + signal(SIGPIPE, SIG_IGN); +} + void restore_signals(void) { sigset_t set; sigemptyset(&set); @@ -330,22 +342,14 @@ int main(int argc, char **argv) { increase_nofile_limit(); - // handle SIGTERM signals - signal(SIGTERM, sig_handler); - signal(SIGINT, sig_handler); - - // avoid need to reap children - signal(SIGCHLD, SIG_IGN); - - // prevent ipc from crashing sway - signal(SIGPIPE, SIG_IGN); - sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION); if (!server_init(&server)) { return 1; } + init_signals(); + if (server.linux_dmabuf_v1) { wlr_scene_set_linux_dmabuf_v1(root->root_scene, server.linux_dmabuf_v1); } From 5e6a6ea3404330f99d2425d20cd9e298164d5988 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 23 May 2024 14:34:24 +0200 Subject: [PATCH 203/361] idle_inhibit: Ignore inhibitors when locked When a session is locked, no views are visible and there is no way to interact with the user session. This means that all inhibitors based on visibility - with the exception being inhibitors on the session lock surfaces themselves - become inert, allowing the session to go idle. The only inhibitor type on normal views that one could argue should remain active is INHIBIT_IDLE_OPEN, but for now we disable all view inhibitors regardless of type. --- sway/desktop/idle_inhibit_v1.c | 14 ++++++++++++++ sway/lock.c | 7 +++++++ sway/tree/view.c | 4 ++++ 3 files changed, 25 insertions(+) diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index f3af7aa1b..d241cdaf4 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -1,5 +1,6 @@ #include #include +#include #include "log.h" #include "sway/desktop/idle_inhibit_v1.h" #include "sway/input/seat.h" @@ -103,6 +104,19 @@ void sway_idle_inhibit_v1_user_inhibitor_destroy( } bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { + if (server.session_lock.lock) { + // A session lock is active. In this case, only application inhibitors + // on the session lock surface can have any effect. + if (inhibitor->mode != INHIBIT_IDLE_APPLICATION) { + return false; + } + struct wlr_surface *wlr_surface = inhibitor->wlr_inhibitor->surface; + if (!wlr_session_lock_surface_v1_try_from_wlr_surface(wlr_surface)) { + return false; + } + return wlr_surface->mapped; + } + switch (inhibitor->mode) { case INHIBIT_IDLE_APPLICATION:; // If there is no view associated with the inhibitor, assume visible diff --git a/sway/lock.c b/sway/lock.c index 43f313308..c8975c747 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -234,6 +234,9 @@ static void handle_unlock(struct wl_listener *listener, void *data) { struct sway_output *output = root->outputs->items[i]; arrange_layers(output); } + + // Views are now visible, so check if we need to activate inhibition again. + sway_idle_inhibit_v1_check_active(); } static void handle_abandon(struct wl_listener *listener, void *data) { @@ -297,6 +300,10 @@ static void handle_session_lock(struct wl_listener *listener, void *data) { wlr_session_lock_v1_send_locked(lock); server.session_lock.lock = sway_lock; + + // The lock screen covers everything, so check if any active inhibition got + // deactivated due to lost visibility. + sway_idle_inhibit_v1_check_active(); } static void handle_session_lock_destroy(struct wl_listener *listener, void *data) { diff --git a/sway/tree/view.c b/sway/tree/view.c index a5617fb4c..16080a2f9 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -218,6 +218,10 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, } bool view_inhibit_idle(struct sway_view *view) { + if (server.session_lock.lock) { + return false; + } + struct sway_idle_inhibitor_v1 *user_inhibitor = sway_idle_inhibit_v1_user_inhibitor_for_view(view); From 541183b3228b986b5d09f12513506c386456df7e Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 23 May 2024 15:01:46 +0200 Subject: [PATCH 204/361] idle_inhibit: Explicitly handle layer surfaces Layer surfaces do not have a view, and while they can be occluded they are always visible on their associated output - assuming it is enabled. --- sway/desktop/idle_inhibit_v1.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index d241cdaf4..0fc79989a 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -119,8 +119,15 @@ bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { switch (inhibitor->mode) { case INHIBIT_IDLE_APPLICATION:; + struct wlr_surface *wlr_surface = inhibitor->wlr_inhibitor->surface; + if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface)) { + // Layer surfaces can be occluded but are always on screen after + // they have been mapped. + return wlr_surface->mapped; + } + // If there is no view associated with the inhibitor, assume visible - struct sway_view *view = view_from_wlr_surface(inhibitor->wlr_inhibitor->surface); + struct sway_view *view = view_from_wlr_surface(wlr_surface); return !view || !view->container || view_is_visible(view); case INHIBIT_IDLE_FOCUS:; struct sway_seat *seat = NULL; From cc482228a41e9e3e31b6b27143d5ed24e7dc5069 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 23 May 2024 15:03:44 +0200 Subject: [PATCH 205/361] idle_inhibit: Assume view is invisible by default We have historically considered surfaces without a view visible. This made sense in case of layer surfaces which do not have a view, but it also allows unmapped surfaces to act as global inhibitors irrespective of the current view state, which is not the intention fo the protocol. As we now explicitly handle layer surfaces, assume that views are only visible if they can be found and their visibility checked. --- sway/desktop/idle_inhibit_v1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index 0fc79989a..9fc223d06 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -126,9 +126,9 @@ bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { return wlr_surface->mapped; } - // If there is no view associated with the inhibitor, assume visible + // If there is no view associated with the inhibitor, assume invisible struct sway_view *view = view_from_wlr_surface(wlr_surface); - return !view || !view->container || view_is_visible(view); + return view && view->container && view_is_visible(view); case INHIBIT_IDLE_FOCUS:; struct sway_seat *seat = NULL; wl_list_for_each(seat, &server.input->seats, link) { From 583862e6d11c44bb7a66dbc4dbdf029b517df18b Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 11 Jun 2024 00:12:02 +0200 Subject: [PATCH 206/361] idle_inhibit: Check if layer surface output is enabled While we we cannot easily check for true visibility of layer surfaces as easily as for views, we can check at least check that the output associated with the surface is enabled. --- sway/desktop/idle_inhibit_v1.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index 9fc223d06..b495c204c 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -120,10 +120,13 @@ bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { switch (inhibitor->mode) { case INHIBIT_IDLE_APPLICATION:; struct wlr_surface *wlr_surface = inhibitor->wlr_inhibitor->surface; - if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface)) { + struct wlr_layer_surface_v1 *layer_surface = + wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface); + if (layer_surface) { // Layer surfaces can be occluded but are always on screen after // they have been mapped. - return wlr_surface->mapped; + return layer_surface->output && layer_surface->output->enabled && + wlr_surface->mapped; } // If there is no view associated with the inhibitor, assume invisible From 0a9b0b83ebfffc3b5b456b49d8cfe76736fe011b Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Tue, 18 Feb 2025 01:51:49 +0100 Subject: [PATCH 207/361] server: remove event listeners on fini This fixes a crash in wlroots listener checks. See #8509. --- sway/server.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sway/server.c b/sway/server.c index 5de6648f6..fc2ba8191 100644 --- a/sway/server.c +++ b/sway/server.c @@ -460,8 +460,29 @@ bool server_init(struct sway_server *server) { } void server_fini(struct sway_server *server) { + // remove listeners + wl_list_remove(&server->renderer_lost.link); + wl_list_remove(&server->new_output.link); + wl_list_remove(&server->layer_shell_surface.link); + wl_list_remove(&server->xdg_shell_toplevel.link); + wl_list_remove(&server->server_decoration.link); + wl_list_remove(&server->xdg_decoration.link); + wl_list_remove(&server->pointer_constraint.link); + wl_list_remove(&server->output_manager_apply.link); + wl_list_remove(&server->output_manager_test.link); + wl_list_remove(&server->output_power_manager_set_mode.link); +#if WLR_HAS_DRM_BACKEND + wl_list_remove(&server->drm_lease_request.link); +#endif + wl_list_remove(&server->tearing_control_new_object.link); + wl_list_remove(&server->xdg_activation_v1_request_activate.link); + wl_list_remove(&server->xdg_activation_v1_new_token.link); + wl_list_remove(&server->request_set_cursor_shape.link); + // TODO: free sway-specific resources #if WLR_HAS_XWAYLAND + wl_list_remove(&server->xwayland_surface.link); + wl_list_remove(&server->xwayland_ready.link); wlr_xwayland_destroy(server->xwayland.wlr_xwayland); #endif wl_display_destroy_clients(server->wl_display); From e51ecf71aa0afdcce4c9768f0a614222ee79cee2 Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Tue, 18 Feb 2025 01:52:25 +0100 Subject: [PATCH 208/361] input/input-manager: remove event listeners on fini This fixes a crash in wlroots listener checks. See #8509. --- include/sway/input/input-manager.h | 2 ++ sway/input/input-manager.c | 8 ++++++++ sway/server.c | 1 + 3 files changed, 11 insertions(+) diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index b014e18f8..5113844d8 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h @@ -39,6 +39,8 @@ struct sway_input_manager { struct sway_input_manager *input_manager_create(struct sway_server *server); +void input_manager_finish(struct sway_input_manager *input); + bool input_manager_has_focus(struct sway_node *node); void input_manager_set_focus(struct sway_node *node); diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 99af4c691..ffcf8fc52 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -493,6 +493,14 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) { return input; } +void input_manager_finish(struct sway_input_manager *input) { + wl_list_remove(&input->new_input.link); + wl_list_remove(&input->virtual_keyboard_new.link); + wl_list_remove(&input->virtual_pointer_new.link); + wl_list_remove(&input->keyboard_shortcuts_inhibit_new_inhibitor.link); + wl_list_remove(&input->transient_seat_create.link); +} + bool input_manager_has_focus(struct sway_node *node) { struct sway_seat *seat = NULL; wl_list_for_each(seat, &server.input->seats, link) { diff --git a/sway/server.c b/sway/server.c index fc2ba8191..ff7ed42e1 100644 --- a/sway/server.c +++ b/sway/server.c @@ -478,6 +478,7 @@ void server_fini(struct sway_server *server) { wl_list_remove(&server->xdg_activation_v1_request_activate.link); wl_list_remove(&server->xdg_activation_v1_new_token.link); wl_list_remove(&server->request_set_cursor_shape.link); + input_manager_finish(server->input); // TODO: free sway-specific resources #if WLR_HAS_XWAYLAND From 92c82e6952394b714d7818deb1d46d616a39dfc0 Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Tue, 18 Feb 2025 01:52:57 +0100 Subject: [PATCH 209/361] desktop/idle_inhibit: remove event listeners on destroy This fixes a crash in wlroots listener checks. See #8509. --- include/sway/desktop/idle_inhibit_v1.h | 1 + sway/desktop/idle_inhibit_v1.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/include/sway/desktop/idle_inhibit_v1.h b/include/sway/desktop/idle_inhibit_v1.h index 84cc666d0..447ac8708 100644 --- a/include/sway/desktop/idle_inhibit_v1.h +++ b/include/sway/desktop/idle_inhibit_v1.h @@ -13,6 +13,7 @@ enum sway_idle_inhibit_mode { struct sway_idle_inhibit_manager_v1 { struct wlr_idle_inhibit_manager_v1 *wlr_manager; struct wl_listener new_idle_inhibitor_v1; + struct wl_listener manager_destroy; struct wl_list inhibitors; }; diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index b495c204c..6b2761fcd 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -45,6 +45,14 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { sway_idle_inhibit_v1_check_active(); } +void handle_manager_destroy(struct wl_listener *listener, void *data) { + struct sway_idle_inhibit_manager_v1 *manager = + wl_container_of(listener, manager, manager_destroy); + + wl_list_remove(&manager->manager_destroy.link); + wl_list_remove(&manager->new_idle_inhibitor_v1.link); +} + void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, enum sway_idle_inhibit_mode mode) { struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; @@ -177,6 +185,9 @@ bool sway_idle_inhibit_manager_v1_init(void) { wl_signal_add(&manager->wlr_manager->events.new_inhibitor, &manager->new_idle_inhibitor_v1); manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1; + wl_signal_add(&manager->wlr_manager->events.destroy, + &manager->manager_destroy); + manager->manager_destroy.notify = handle_manager_destroy; wl_list_init(&manager->inhibitors); return true; From 53126cdceb6dbf6ee163ca0db960cf3900870075 Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Tue, 18 Feb 2025 22:45:53 +0100 Subject: [PATCH 210/361] input/text_input: remove event listeners on destroy sway_input_method_relay can be destroyed from two sources, either the seat is destroyed or the manager protocol objects are destroyed due compositor exit. This fixes a crash in wlroots listener checks. See #8509. --- include/sway/input/text_input.h | 2 ++ sway/input/text_input.c | 38 +++++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/include/sway/input/text_input.h b/include/sway/input/text_input.h index 1993f928f..1818749ad 100644 --- a/include/sway/input/text_input.h +++ b/include/sway/input/text_input.h @@ -25,8 +25,10 @@ struct sway_input_method_relay { struct wlr_input_method_v2 *input_method; // doesn't have to be present struct wl_listener text_input_new; + struct wl_listener text_input_manager_destroy; struct wl_listener input_method_new; + struct wl_listener input_method_manager_destroy; struct wl_listener input_method_commit; struct wl_listener input_method_new_popup_surface; struct wl_listener input_method_grab_keyboard; diff --git a/sway/input/text_input.c b/sway/input/text_input.c index a46f833c9..c84fac8f6 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -597,6 +597,34 @@ static void relay_handle_input_method(struct wl_listener *listener, } } +static void sway_input_method_relay_finish_text_input(struct sway_input_method_relay *relay) { + wl_list_remove(&relay->text_input_new.link); + wl_list_remove(&relay->text_input_manager_destroy.link); + wl_list_init(&relay->text_input_new.link); + wl_list_init(&relay->text_input_manager_destroy.link); +} + +static void relay_handle_text_input_manager_destroy(struct wl_listener *listener, void *data) { + struct sway_input_method_relay *relay = wl_container_of(listener, relay, + text_input_manager_destroy); + + sway_input_method_relay_finish_text_input(relay); +} + +static void sway_input_method_relay_finish_input_method(struct sway_input_method_relay *relay) { + wl_list_remove(&relay->input_method_new.link); + wl_list_remove(&relay->input_method_manager_destroy.link); + wl_list_init(&relay->input_method_new.link); + wl_list_init(&relay->input_method_manager_destroy.link); +} + +static void relay_handle_input_method_manager_destroy(struct wl_listener *listener, void *data) { + struct sway_input_method_relay *relay = wl_container_of(listener, relay, + input_method_manager_destroy); + + sway_input_method_relay_finish_input_method(relay); +} + void sway_input_method_relay_init(struct sway_seat *seat, struct sway_input_method_relay *relay) { relay->seat = seat; @@ -606,16 +634,22 @@ void sway_input_method_relay_init(struct sway_seat *seat, relay->text_input_new.notify = relay_handle_text_input; wl_signal_add(&server.text_input->events.text_input, &relay->text_input_new); + relay->text_input_manager_destroy.notify = relay_handle_text_input_manager_destroy; + wl_signal_add(&server.text_input->events.destroy, + &relay->text_input_manager_destroy); relay->input_method_new.notify = relay_handle_input_method; wl_signal_add( &server.input_method->events.input_method, &relay->input_method_new); + relay->input_method_manager_destroy.notify = relay_handle_input_method_manager_destroy; + wl_signal_add(&server.input_method->events.destroy, + &relay->input_method_manager_destroy); } void sway_input_method_relay_finish(struct sway_input_method_relay *relay) { - wl_list_remove(&relay->input_method_new.link); - wl_list_remove(&relay->text_input_new.link); + sway_input_method_relay_finish_text_input(relay); + sway_input_method_relay_finish_input_method(relay); } void sway_input_method_relay_set_focus(struct sway_input_method_relay *relay, From ab2e1f5817a8024366fcb02285c978c5fef7dae1 Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Tue, 18 Feb 2025 23:14:06 +0100 Subject: [PATCH 211/361] tree/container: remove event listeners on destroy Change begin_destroy to remove event listeners before the final destroy, since otherwise event listeners would be removed twice, which crashes. This fixes a crash in wlroots listener checks. See #8509. --- include/sway/tree/container.h | 1 + sway/tree/container.c | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 4608b8ac2..4fb2d7208 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -95,6 +95,7 @@ struct sway_container { struct wl_listener output_enter; struct wl_listener output_leave; + struct wl_listener output_handler_destroy; struct sway_container_state current; struct sway_container_state pending; diff --git a/sway/tree/container.c b/sway/tree/container.c index 6ff4036fa..0385d7c17 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -50,6 +50,14 @@ static void handle_output_leave( } } +static void handle_destroy( + struct wl_listener *listener, void *data) { + struct sway_container *con = wl_container_of( + listener, con, output_handler_destroy); + + container_begin_destroy(con); +} + static bool handle_point_accepts_input( struct wlr_scene_buffer *buffer, double *x, double *y) { return false; @@ -135,6 +143,9 @@ struct sway_container *container_create(struct sway_view *view) { c->output_leave.notify = handle_output_leave; wl_signal_add(&c->output_handler->events.output_leave, &c->output_leave); + c->output_handler_destroy.notify = handle_destroy; + wl_signal_add(&c->output_handler->node.events.destroy, + &c->output_handler_destroy); c->output_handler->point_accepts_input = handle_point_accepts_input; } } @@ -508,8 +519,6 @@ void container_destroy(struct sway_container *con) { if (con->view && con->view->container == con) { con->view->container = NULL; - wl_list_remove(&con->output_enter.link); - wl_list_remove(&con->output_leave.link); wlr_scene_node_destroy(&con->output_handler->node); if (con->view->destroying) { view_destroy(con->view); @@ -552,6 +561,12 @@ void container_begin_destroy(struct sway_container *con) { if (con->pending.parent || con->pending.workspace) { container_detach(con); } + + if (con->view && con->view->container == con) { + wl_list_remove(&con->output_enter.link); + wl_list_remove(&con->output_leave.link); + wl_list_remove(&con->output_handler_destroy.link); + } } void container_reap_empty(struct sway_container *con) { From 240a69ad63ad36893132ab1187035654d9478436 Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Fri, 21 Mar 2025 18:35:36 +0100 Subject: [PATCH 212/361] server: recreate renderer in idle callback to avoid UAF Destroying the wlr_renderer in a callback to its own renderer_lost event is unsafe due to wl_signal_emit*() still accessing it after it was destroyed. Delegate recreation of renderer to an idle callback and ensure that only one such idle callback is scheduled at a time by storing the returned event source. --- include/sway/server.h | 1 + sway/server.c | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index feb516c5b..b1d7523ca 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -46,6 +46,7 @@ struct sway_server { struct wl_listener new_output; struct wl_listener renderer_lost; + struct wl_event_source *recreating_renderer; struct wlr_idle_notifier_v1 *idle_notifier_v1; struct sway_idle_inhibit_manager_v1 idle_inhibit_manager_v1; diff --git a/sway/server.c b/sway/server.c index ff7ed42e1..9d882862a 100644 --- a/sway/server.c +++ b/sway/server.c @@ -182,11 +182,11 @@ static void detect_proprietary(struct wlr_backend *backend, void *data) { drmFreeVersion(version); } -static void handle_renderer_lost(struct wl_listener *listener, void *data) { - struct sway_server *server = wl_container_of(listener, server, renderer_lost); +static void do_renderer_recreate(void *data) { + struct sway_server *server = data; + server->recreating_renderer = NULL; sway_log(SWAY_INFO, "Re-creating renderer after GPU reset"); - struct wlr_renderer *renderer = wlr_renderer_autocreate(server->backend); if (renderer == NULL) { sway_log(SWAY_ERROR, "Unable to create renderer"); @@ -221,6 +221,18 @@ static void handle_renderer_lost(struct wl_listener *listener, void *data) { wlr_renderer_destroy(old_renderer); } +static void handle_renderer_lost(struct wl_listener *listener, void *data) { + struct sway_server *server = wl_container_of(listener, server, renderer_lost); + + if (server->recreating_renderer != NULL) { + sway_log(SWAY_DEBUG, "Re-creation of renderer already scheduled"); + return; + } + + sway_log(SWAY_INFO, "Scheduling re-creation of renderer after GPU reset"); + server->recreating_renderer = wl_event_loop_add_idle(server->wl_event_loop, do_renderer_recreate, server); +} + bool server_init(struct sway_server *server) { sway_log(SWAY_DEBUG, "Initializing Wayland server"); server->wl_display = wl_display_create(); From 4943534929dfd3f0ea55e26241544354f8365e60 Mon Sep 17 00:00:00 2001 From: Loukas Agorgianitis Date: Mon, 14 Apr 2025 08:57:40 +0200 Subject: [PATCH 213/361] server: fix shutdown crash when running on x11 backend Signed-off-by: Loukas Agorgianitis --- sway/server.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sway/server.c b/sway/server.c index 9d882862a..f7155a77d 100644 --- a/sway/server.c +++ b/sway/server.c @@ -484,7 +484,9 @@ void server_fini(struct sway_server *server) { wl_list_remove(&server->output_manager_test.link); wl_list_remove(&server->output_power_manager_set_mode.link); #if WLR_HAS_DRM_BACKEND - wl_list_remove(&server->drm_lease_request.link); + if (server->drm_lease_manager) { + wl_list_remove(&server->drm_lease_request.link); + } #endif wl_list_remove(&server->tearing_control_new_object.link); wl_list_remove(&server->xdg_activation_v1_request_activate.link); From 3f0b3f8f9b3b737fd0e6d36e2a2c469b07268ec2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 14 Apr 2025 09:29:45 +0200 Subject: [PATCH 214/361] Fix crash on shutdown when Xwayland is disabled --- sway/server.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sway/server.c b/sway/server.c index f7155a77d..5aa5f4f99 100644 --- a/sway/server.c +++ b/sway/server.c @@ -496,9 +496,11 @@ void server_fini(struct sway_server *server) { // TODO: free sway-specific resources #if WLR_HAS_XWAYLAND - wl_list_remove(&server->xwayland_surface.link); - wl_list_remove(&server->xwayland_ready.link); - wlr_xwayland_destroy(server->xwayland.wlr_xwayland); + if (server->xwayland.wlr_xwayland != NULL) { + wl_list_remove(&server->xwayland_surface.link); + wl_list_remove(&server->xwayland_ready.link); + wlr_xwayland_destroy(server->xwayland.wlr_xwayland); + } #endif wl_display_destroy_clients(server->wl_display); wlr_backend_destroy(server->backend); From 7733bf9963d6a18df548164642e6637c2e71f1f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Vuji=C4=8Di=C4=87?= Date: Mon, 14 Apr 2025 20:58:20 +1200 Subject: [PATCH 215/361] Remove duplicate arrange_container --- sway/desktop/transaction.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 01fe31289..0b3cbfb4d 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -375,7 +375,6 @@ static void arrange_children(enum sway_container_layout layout, list_t *children wlr_scene_node_set_enabled(&child->border.tree->node, true); wlr_scene_node_set_position(&child->scene_tree->node, off, 0); wlr_scene_node_reparent(&child->scene_tree->node, content); - arrange_container(child, cwidth, height, true, gaps); if (cwidth > 0 && height > 0) { arrange_container(child, cwidth, height, true, gaps); off += cwidth + gaps; From 8a8c78deacd388dabbe82a4a5055458494cd6258 Mon Sep 17 00:00:00 2001 From: llyyr Date: Wed, 16 Apr 2025 17:32:47 +0530 Subject: [PATCH 216/361] layer_shell: destroy layer_surface on assigned output destruction According to the spec, the closed event should be sent when the surface is no longer shown, because the output may have been destroyed or the user may have asked for it to be removed. In such cases, the clients should destroy the resource. This fixes mako not being able to show notifications if the assigned output was destroyed while a notificataion was still visible Fixes: 188811f80861 ("scene_graph: Port layer_shell") --- 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 05faa4656..b14a9a4b1 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -221,7 +221,7 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) { wl_container_of(listener, layer, output_destroy); layer->output = NULL; - wlr_scene_node_destroy(&layer->scene->tree->node); + wlr_layer_surface_v1_destroy(layer->layer_surface); } static void handle_node_destroy(struct wl_listener *listener, void *data) { From d3e1c13e1f40d38a454fa0236975c4d5196bd77e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 17 Apr 2025 19:11:10 +0200 Subject: [PATCH 217/361] swaymsg, swaynag: drop sway_terminate() definitions These are unused. --- swaymsg/main.c | 4 ---- swaynag/main.c | 5 ----- 2 files changed, 9 deletions(-) diff --git a/swaymsg/main.c b/swaymsg/main.c index dc1c74072..6a9eb1987 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -16,10 +16,6 @@ #include "ipc-client.h" #include "log.h" -void sway_terminate(int exit_code) { - exit(exit_code); -} - static bool success_object(json_object *result) { json_object *success; diff --git a/swaynag/main.c b/swaynag/main.c index 634bddbfc..54317dce2 100644 --- a/swaynag/main.c +++ b/swaynag/main.c @@ -13,11 +13,6 @@ void sig_handler(int signal) { exit(EXIT_FAILURE); } -void sway_terminate(int code) { - swaynag_destroy(&swaynag); - exit(code); -} - int main(int argc, char **argv) { int status = EXIT_SUCCESS; From 0153bc92abb4974c1a3421a79e976dcf9938e50a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 17 Apr 2025 19:11:37 +0200 Subject: [PATCH 218/361] server: move sway_terminate() definition to header --- include/sway/server.h | 2 ++ sway/commands/exit.c | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index b1d7523ca..6152651e6 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -158,6 +158,8 @@ extern struct sway_debug debug; extern bool allow_unsupported_gpu; +void sway_terminate(int exit_code); + bool server_init(struct sway_server *server); void server_fini(struct sway_server *server); bool server_start(struct sway_server *server); diff --git a/sway/commands/exit.c b/sway/commands/exit.c index 10cde640c..0f326cea6 100644 --- a/sway/commands/exit.c +++ b/sway/commands/exit.c @@ -1,8 +1,7 @@ #include #include "sway/commands.h" #include "sway/config.h" - -void sway_terminate(int exit_code); +#include "sway/server.h" struct cmd_results *cmd_exit(int argc, char **argv) { struct cmd_results *error = NULL; From 1d4632f97fb6ee61abe350ae9f76270562396553 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 13 Jul 2024 13:13:08 +0200 Subject: [PATCH 219/361] Drop wl_drm again In [1] we re-introduced a debug flag to enable wl_drm. Time has passed and Xwayland + VA-API + amdvlk now all support linux-dmabuf-v1. [1]: https://github.com/swaywm/sway/pull/7916 --- include/sway/server.h | 1 - sway/main.c | 2 -- sway/server.c | 4 ---- 3 files changed, 7 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index 6152651e6..66f0967ce 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -151,7 +151,6 @@ 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 - bool legacy_wl_drm; // Enable the legacy wl_drm interface }; extern struct sway_debug debug; diff --git a/sway/main.c b/sway/main.c index 0af977d4d..bebb5e1b5 100644 --- a/sway/main.c +++ b/sway/main.c @@ -181,8 +181,6 @@ void enable_debug_flag(const char *flag) { debug.txn_timings = true; } else if (has_prefix(flag, "txn-timeout=")) { server.txn_timeout_ms = atoi(&flag[strlen("txn-timeout=")]); - } else if (strcmp(flag, "legacy-wl-drm") == 0) { - debug.legacy_wl_drm = true; } else { sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); } diff --git a/sway/server.c b/sway/server.c index 5aa5f4f99..979761486 100644 --- a/sway/server.c +++ b/sway/server.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -265,9 +264,6 @@ bool server_init(struct sway_server *server) { if (wlr_renderer_get_texture_formats(server->renderer, WLR_BUFFER_CAP_DMABUF) != NULL) { server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( server->wl_display, 4, server->renderer); - if (debug.legacy_wl_drm) { - wlr_drm_create(server->wl_display, server->renderer); - } } if (wlr_renderer_get_drm_fd(server->renderer) >= 0 && server->renderer->features.timeline && From 86ff19fadeaa45f7f6398d62be1ee6149a0889a8 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 20 Apr 2025 13:31:10 +0200 Subject: [PATCH 220/361] build: bump version to 1.11-rc1 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 9ce5723e7..2d406dec0 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'sway', 'c', - version: '1.10-dev', + version: '1.11-rc1', license: 'MIT', meson_version: '>=1.3', default_options: [ From 0e19d85d37e556721f982c3f63d4e2927f306b18 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 20 Apr 2025 21:09:57 +0200 Subject: [PATCH 221/361] Use pthread_atfork() to restore signals and NOFILE limit This ensures these functions are always called (even when a library such as wlroots or libc perform the fork) and removes the need to manually call them. --- include/sway/server.h | 3 --- meson.build | 2 +- sway/commands/exec_always.c | 2 -- sway/config/bar.c | 2 -- sway/config/output.c | 2 -- sway/main.c | 38 +++++++++++++++++++++---------------- sway/swaynag.c | 2 -- 7 files changed, 23 insertions(+), 28 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index 66f0967ce..e7d7094f1 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -164,9 +164,6 @@ void server_fini(struct sway_server *server); bool server_start(struct sway_server *server); void server_run(struct sway_server *server); -void restore_nofile_limit(void); -void restore_signals(void); - void handle_new_output(struct wl_listener *listener, void *data); void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); diff --git a/meson.build b/meson.build index 2d406dec0..9e8e798e1 100644 --- a/meson.build +++ b/meson.build @@ -79,7 +79,7 @@ libudev = wlroots_features['libinput_backend'] ? dependency('libudev') : null_de math = cc.find_library('m') rt = cc.find_library('rt') xcb_icccm = wlroots_features['xwayland'] ? dependency('xcb-icccm') : null_dep -threads = dependency('threads') # for pthread_setschedparam +threads = dependency('threads') # for pthread_setschedparam and pthread_atfork if get_option('sd-bus-provider') == 'auto' if not get_option('tray').disabled() diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index 8f02bbdc7..a966696c5 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -51,8 +51,6 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { // Fork process pid_t child = fork(); if (child == 0) { - restore_nofile_limit(); - restore_signals(); setsid(); if (ctx) { diff --git a/sway/config/bar.c b/sway/config/bar.c index 6cace0da2..f4efb276c 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -213,8 +213,6 @@ static void invoke_swaybar(struct bar_config *bar) { sway_log(SWAY_ERROR, "Failed to create fork for swaybar"); return; } else if (pid == 0) { - restore_nofile_limit(); - restore_signals(); if (!sway_set_cloexec(sockets[1], false)) { _exit(EXIT_FAILURE); } diff --git a/sway/config/output.c b/sway/config/output.c index b8a613cc5..5ed518bf4 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1060,8 +1060,6 @@ static bool _spawn_swaybg(char **command) { sway_log_errno(SWAY_ERROR, "fork failed"); return false; } else if (pid == 0) { - restore_nofile_limit(); - restore_signals(); if (!sway_set_cloexec(sockets[1], false)) { _exit(EXIT_FAILURE); } diff --git a/sway/main.c b/sway/main.c index bebb5e1b5..56f09b7e7 100644 --- a/sway/main.c +++ b/sway/main.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -121,6 +122,16 @@ static bool detect_suid(void) { return true; } +static void restore_nofile_limit(void) { + if (original_nofile_rlimit.rlim_cur == 0) { + return; + } + if (setrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) { + sway_log_errno(SWAY_ERROR, "Failed to restore max open files limit: " + "setrlimit(NOFILE) failed"); + } +} + static void increase_nofile_limit(void) { if (getrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) { sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: " @@ -135,17 +146,10 @@ static void increase_nofile_limit(void) { "setrlimit(NOFILE) failed"); sway_log(SWAY_INFO, "Running with %d max open files", (int)original_nofile_rlimit.rlim_cur); - } -} - -void restore_nofile_limit(void) { - if (original_nofile_rlimit.rlim_cur == 0) { return; } - if (setrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) { - sway_log_errno(SWAY_ERROR, "Failed to restore max open files limit: " - "setrlimit(NOFILE) failed"); - } + + pthread_atfork(NULL, NULL, restore_nofile_limit); } static int term_signal(int signal, void *data) { @@ -153,6 +157,14 @@ static int term_signal(int signal, void *data) { return 0; } +static void restore_signals(void) { + sigset_t set; + sigemptyset(&set); + sigprocmask(SIG_SETMASK, &set, NULL); + signal(SIGCHLD, SIG_DFL); + signal(SIGPIPE, SIG_DFL); +} + static void init_signals(void) { wl_event_loop_add_signal(server.wl_event_loop, SIGTERM, term_signal, NULL); wl_event_loop_add_signal(server.wl_event_loop, SIGINT, term_signal, NULL); @@ -162,14 +174,8 @@ static void init_signals(void) { // prevent ipc write errors from crashing sway signal(SIGPIPE, SIG_IGN); -} -void restore_signals(void) { - sigset_t set; - sigemptyset(&set); - sigprocmask(SIG_SETMASK, &set, NULL); - signal(SIGCHLD, SIG_DFL); - signal(SIGPIPE, SIG_DFL); + pthread_atfork(NULL, NULL, restore_signals); } void enable_debug_flag(const char *flag) { diff --git a/sway/swaynag.c b/sway/swaynag.c index f0a31218a..204a5791a 100644 --- a/sway/swaynag.c +++ b/sway/swaynag.c @@ -63,7 +63,6 @@ bool swaynag_spawn(const char *swaynag_command, sway_log(SWAY_ERROR, "Failed to create fork for swaynag"); goto failed; } else if (pid == 0) { - restore_nofile_limit(); if (!sway_set_cloexec(sockets[1], false)) { _exit(EXIT_FAILURE); } @@ -148,4 +147,3 @@ void swaynag_show(struct swaynag_instance *swaynag) { close(swaynag->fd[1]); } } - From 38a42f97d46931e97693610c999ea51834b71352 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 27 Apr 2025 18:36:18 +0200 Subject: [PATCH 222/361] Replace signal() with sigaction() The man page for signal(3) reads: > new applications should use sigaction() rather than signal() --- sway/main.c | 12 +++++++----- swaybar/main.c | 5 +++-- swaynag/main.c | 3 ++- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/sway/main.c b/sway/main.c index 56f09b7e7..69efd6cb4 100644 --- a/sway/main.c +++ b/sway/main.c @@ -161,19 +161,21 @@ static void restore_signals(void) { sigset_t set; sigemptyset(&set); sigprocmask(SIG_SETMASK, &set, NULL); - signal(SIGCHLD, SIG_DFL); - signal(SIGPIPE, SIG_DFL); + + struct sigaction sa_dfl = { .sa_handler = SIG_DFL }; + sigaction(SIGCHLD, &sa_dfl, NULL); + sigaction(SIGPIPE, &sa_dfl, NULL); } static void init_signals(void) { wl_event_loop_add_signal(server.wl_event_loop, SIGTERM, term_signal, NULL); wl_event_loop_add_signal(server.wl_event_loop, SIGINT, term_signal, NULL); + struct sigaction sa_ign = { .sa_handler = SIG_IGN }; // avoid need to reap children - signal(SIGCHLD, SIG_IGN); - + sigaction(SIGCHLD, &sa_ign, NULL); // prevent ipc write errors from crashing sway - signal(SIGPIPE, SIG_IGN); + sigaction(SIGPIPE, &sa_ign, NULL); pthread_atfork(NULL, NULL, restore_signals); } diff --git a/swaybar/main.c b/swaybar/main.c index 3dc672334..e1b0cecac 100644 --- a/swaybar/main.c +++ b/swaybar/main.c @@ -93,8 +93,9 @@ int main(int argc, char **argv) { free(socket_path); - signal(SIGINT, sig_handler); - signal(SIGTERM, sig_handler); + struct sigaction sa = { .sa_handler = sig_handler }; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); swaybar.running = true; bar_run(&swaybar); diff --git a/swaynag/main.c b/swaynag/main.c index 54317dce2..b68157ff2 100644 --- a/swaynag/main.c +++ b/swaynag/main.c @@ -102,7 +102,8 @@ int main(int argc, char **argv) { sway_log(SWAY_DEBUG, "\t[%s] `%s`", button->text, button->action); } - signal(SIGTERM, sig_handler); + struct sigaction sa = { .sa_handler = sig_handler }; + sigaction(SIGTERM, &sa, NULL); swaynag_setup(&swaynag); swaynag_run(&swaynag); From 5b8874e3f428b54aaa2676346954dc712320d219 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Thu, 24 Apr 2025 15:41:20 -0400 Subject: [PATCH 223/361] sway/commands: Handle incorrect resize unit problem: an invalid usage of the command resize set will cause sway to crash because it doesn't check for an invalid height. solution: validate height along with width. --- sway/commands/resize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/commands/resize.c b/sway/commands/resize.c index 32b746eaf..49731a641 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -457,7 +457,7 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) { if (argc > num_consumed_args) { return cmd_results_new(CMD_INVALID, "%s", usage); } - if (width.unit == MOVEMENT_UNIT_INVALID) { + if (height.unit == MOVEMENT_UNIT_INVALID) { return cmd_results_new(CMD_INVALID, "%s", usage); } } From 6894b498a889d809093c920d397a42db7f5e4970 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 27 Apr 2025 22:52:48 +0200 Subject: [PATCH 224/361] build: bump version to 1.11-rc2 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 9e8e798e1..646f694c2 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'sway', 'c', - version: '1.11-rc1', + version: '1.11-rc2', license: 'MIT', meson_version: '>=1.3', default_options: [ From 6cac61b6b96c4a48a69f8ec3c06c2df560b01827 Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Fri, 3 May 2024 09:09:14 +0200 Subject: [PATCH 225/361] Fix includes with relative paths The function `load_include_configs` already changes the directory to the one containing the parent config. Therefore, `load_include_config` trying to assemble the "full" path leads to repetition of path segments, making the `realpath` call fail with ENOENT. Just calling `realpath` on the path itself from the directory with the parent configuration is sufficient, so there is no point in passing `parent_dir` to `load_include_config`. --- sway/config.c | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/sway/config.c b/sway/config.c index ec7059687..d579022d3 100644 --- a/sway/config.c +++ b/sway/config.c @@ -552,28 +552,12 @@ bool load_main_config(const char *file, bool is_active, bool validating) { return success; } -static bool load_include_config(const char *path, const char *parent_dir, - struct sway_config *config, struct swaynag_instance *swaynag) { +static bool load_include_config(const char *path, struct sway_config *config, + struct swaynag_instance *swaynag) { // save parent config const char *parent_config = config->current_config_path; - char *full_path; - int len = strlen(path); - if (len >= 1 && path[0] != '/') { - len = len + strlen(parent_dir) + 2; - full_path = malloc(len * sizeof(char)); - if (!full_path) { - sway_log(SWAY_ERROR, - "Unable to allocate full path to included config"); - return false; - } - snprintf(full_path, len, "%s/%s", parent_dir, path); - } else { - full_path = strdup(path); - } - - char *real_path = realpath(full_path, NULL); - free(full_path); + char *real_path = realpath(path, NULL); if (real_path == NULL) { sway_log(SWAY_DEBUG, "%s not found.", path); @@ -625,7 +609,7 @@ void 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); + load_include_config(w[i], config, swaynag); } wordfree(&p); } From 8ac1f72c9ef88b2919457e3598059f963df69305 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Wed, 30 Apr 2025 13:34:31 +0200 Subject: [PATCH 226/361] config/output: Use INT_MAX as x/y unset value We oftne use -1 to indicate unset values. In case of output (x, y), we would consider the fields set if they are not both -1. This means that (0, -1) and (-1, 0) are valid coordinates, but (-1, -1) is not. We support negative output positioning, so we cannot use -1 to mean unset. Zero is also not an option as that would disallow reverting a set position back to (0, 0). INT_MAX is an unreasonable output position, so use it to indicate unset values, and only use the value when both are set. --- sway/config/output.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index 5ed518bf4..e061e25bd 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -67,7 +67,7 @@ struct output_config *new_output_config(const char *name) { oc->refresh_rate = -1; oc->custom_mode = -1; oc->drm_mode.type = -1; - oc->x = oc->y = -1; + oc->x = oc->y = INT_MAX; oc->scale = -1; oc->scale_filter = SCALE_FILTER_DEFAULT; oc->transform = -1; @@ -93,11 +93,11 @@ static void supersede_output_config(struct output_config *dst, struct output_con if (src->height != -1) { dst->height = -1; } - if (src->x != -1) { - dst->x = -1; + if (src->x != INT_MAX) { + dst->x = INT_MAX; } - if (src->y != -1) { - dst->y = -1; + if (src->y != INT_MAX) { + dst->y = INT_MAX; } if (src->scale != -1) { dst->scale = -1; @@ -157,10 +157,10 @@ static void merge_output_config(struct output_config *dst, struct output_config if (src->height != -1) { dst->height = src->height; } - if (src->x != -1) { + if (src->x != INT_MAX) { dst->x = src->x; } - if (src->y != -1) { + if (src->y != INT_MAX) { dst->y = src->y; } if (src->scale != -1) { @@ -527,7 +527,7 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output } // Find position for it - if (oc && (oc->x != -1 || oc->y != -1)) { + if (oc && oc->x != INT_MAX && oc->y != INT_MAX) { sway_log(SWAY_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y); wlr_output_layout_add(root->output_layout, wlr_output, oc->x, oc->y); } else { From f9945d81fb52c81ab60034dcfc41a2f36f0ed226 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 29 Apr 2025 13:39:00 +0200 Subject: [PATCH 227/361] config/output: Fix missing output config supersedes color_transform and allow_tearing was not handled by supersede_output_config which could lead to configuration being incorrectly applied. --- sway/config/output.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sway/config/output.c b/sway/config/output.c index e061e25bd..df80cab64 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -129,6 +129,13 @@ static void supersede_output_config(struct output_config *dst, struct output_con if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { dst->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; } + if (src->set_color_transform) { + if (dst->color_transform) { + wlr_color_transform_unref(dst->color_transform); + dst->color_transform = NULL; + } + dst->set_color_transform = false; + } if (src->background) { free(dst->background); dst->background = NULL; @@ -144,6 +151,9 @@ static void supersede_output_config(struct output_config *dst, struct output_con if (src->power != -1) { dst->power = -1; } + if (src->allow_tearing != -1) { + dst->allow_tearing = -1; + } } // merge_output_config sets all fields in dst that were set in src From 4ab411cab0f441994fd41ef8ac80ef849bfab271 Mon Sep 17 00:00:00 2001 From: alex-huff Date: Sun, 4 May 2025 17:41:44 -0500 Subject: [PATCH 228/361] transaction: Ensure all tabs are visible in tabbed mode Before this commit when a child of a tabbed or stacking container was taken out of fullscreen and a different sibling was focused in the same transaction, the titlebar of the previously fullscreen container would remain hidden. This commit makes sure that scene tree for decorations is enabled for all containers within a tabbed or stacking container when it is arranged. --- sway/desktop/transaction.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 0b3cbfb4d..781e0008c 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -309,6 +309,7 @@ static void arrange_children(enum sway_container_layout layout, list_t *children arrange_title_bar(child, title_offset, -title_bar_height, next_title_offset - title_offset, title_bar_height); wlr_scene_node_set_enabled(&child->border.tree->node, activated); + wlr_scene_node_set_enabled(&child->scene_tree->node, true); wlr_scene_node_set_position(&child->scene_tree->node, 0, title_bar_height); wlr_scene_node_reparent(&child->scene_tree->node, content); @@ -338,6 +339,7 @@ static void arrange_children(enum sway_container_layout layout, list_t *children arrange_title_bar(child, 0, y - title_height, width, title_bar_height); wlr_scene_node_set_enabled(&child->border.tree->node, activated); + wlr_scene_node_set_enabled(&child->scene_tree->node, true); wlr_scene_node_set_position(&child->scene_tree->node, 0, title_height); wlr_scene_node_reparent(&child->scene_tree->node, content); From 6021f4d83fc1d7e179c334a010af9b37e44a1bb2 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Fri, 25 Apr 2025 11:50:34 -0400 Subject: [PATCH 229/361] input/seatop_down: Update decorations for touchscreen inputs fixes #8675 --- sway/input/seatop_down.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/input/seatop_down.c b/sway/input/seatop_down.c index 340e334bf..f64e5a4f2 100644 --- a/sway/input/seatop_down.c +++ b/sway/input/seatop_down.c @@ -100,6 +100,7 @@ static void handle_touch_down(struct sway_seat *seat, if (focused_node) { seat_set_focus(seat, focused_node); + transaction_commit_dirty(); } } From 8d3a52aa3076a34b008aed8ba142c955329a8874 Mon Sep 17 00:00:00 2001 From: alex-huff Date: Wed, 7 May 2025 21:00:03 -0500 Subject: [PATCH 230/361] move: fix broken titlebar when moving child to new workspace Before this commit, when moving a non-leaf child of a tabbed or stacking container to a new workspace, the child would be detached from the parent container and the grandchildren would be sent to the new workspace but the child itself wouldn't be destroyed causing the titlebar to still be rendered as part of the parent container. Fixes #8648. --- sway/commands/move.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/commands/move.c b/sway/commands/move.c index 8891514ce..90e8585b4 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -222,6 +222,7 @@ static void container_move_to_workspace(struct sway_container *container, container_detach(container); if (workspace_is_empty(workspace) && container->pending.children) { workspace_unwrap_children(workspace, container); + container_reap_empty(container); } else { container->pending.width = container->pending.height = 0; container->width_fraction = container->height_fraction = 0; From 3fbff5b4bb600efa10630013546115a4f6e0d74a Mon Sep 17 00:00:00 2001 From: GreyXor <79602273+GreyXor@users.noreply.github.com> Date: Thu, 15 May 2025 11:50:05 +0200 Subject: [PATCH 231/361] build: bump wlroots version --- meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 646f694c2..07a6bccff 100644 --- a/meson.build +++ b/meson.build @@ -38,14 +38,14 @@ if is_freebsd endif # Execute the wlroots subproject, if any -wlroots_version = ['>=0.19.0', '<0.20.0'] +wlroots_version = ['>=0.20.0', '<0.21.0'] subproject( 'wlroots', default_options: ['examples=false'], required: false, version: wlroots_version, ) -wlroots = dependency('wlroots-0.19', version: wlroots_version, fallback: 'wlroots') +wlroots = dependency('wlroots-0.20', version: wlroots_version, fallback: 'wlroots') wlroots_features = { 'xwayland': false, 'libinput_backend': false, From 652019d6da630e58ed212632f0c3a4231eaab4e4 Mon Sep 17 00:00:00 2001 From: Bill Li Date: Thu, 15 May 2025 22:23:10 +0800 Subject: [PATCH 232/361] input/text_input: chase wlroots update References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5032 Fix #8718 --- sway/input/text_input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/input/text_input.c b/sway/input/text_input.c index c84fac8f6..0eb76eb16 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -632,7 +632,7 @@ void sway_input_method_relay_init(struct sway_seat *seat, wl_list_init(&relay->input_popups); relay->text_input_new.notify = relay_handle_text_input; - wl_signal_add(&server.text_input->events.text_input, + wl_signal_add(&server.text_input->events.new_text_input, &relay->text_input_new); relay->text_input_manager_destroy.notify = relay_handle_text_input_manager_destroy; wl_signal_add(&server.text_input->events.destroy, From a4072486ded62fcde586c10230cc79af833c55b6 Mon Sep 17 00:00:00 2001 From: alex-huff Date: Thu, 15 May 2025 17:51:39 -0500 Subject: [PATCH 233/361] transaction: ensure border scene is enabled for floating containers When a container that was previously an inactive child of a tabbed or stacking layout becomes floating it's border scene-tree remains disabled. This results in only the titlebar being rendered for the container. This commit ensures the border scene-tree is enabled when arranging floating containers. Fixes #8721 --- sway/desktop/transaction.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 781e0008c..74929f8c9 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -534,6 +534,7 @@ static void arrange_workspace_floating(struct sway_workspace *ws) { wlr_scene_node_set_position(&floater->scene_tree->node, floater->current.x, floater->current.y); wlr_scene_node_set_enabled(&floater->scene_tree->node, true); + wlr_scene_node_set_enabled(&floater->border.tree->node, true); arrange_container(floater, floater->current.width, floater->current.height, true, ws->gaps_inner); From 810142dcc4228941b644ad68334d0b6f539b6fab Mon Sep 17 00:00:00 2001 From: odyxz Date: Mon, 6 Jan 2025 13:21:08 +0200 Subject: [PATCH 234/361] raise scratchpad container --- sway/tree/root.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/tree/root.c b/sway/tree/root.c index 19d072b5c..33c29d381 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -199,6 +199,8 @@ void root_scratchpad_show(struct sway_container *con) { if (old_ws) { workspace_consider_destroy(old_ws); } + + container_raise_floating(con); } static void disable_fullscreen(struct sway_container *con, void *data) { From fb6d61b58f4deeea4a5738473424fc80c00f9415 Mon Sep 17 00:00:00 2001 From: alex-huff Date: Tue, 6 May 2025 17:36:16 -0500 Subject: [PATCH 235/361] transaction: fix size of child container decorations in stacking layouts Before this commit stacking containers with more than one child sized the active container's decorations as if there was only one titlebar. Commit a25645a introduced the local variable 'net_height' but incorrectly calculated it for stacking containers. Fixes #8686. --- sway/desktop/transaction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 74929f8c9..f8bc64b57 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -343,7 +343,7 @@ static void arrange_children(enum sway_container_layout layout, list_t *children wlr_scene_node_set_position(&child->scene_tree->node, 0, title_height); wlr_scene_node_reparent(&child->scene_tree->node, content); - int net_height = height - title_bar_height; + int net_height = height - title_height; if (activated && width > 0 && net_height > 0) { arrange_container(child, width, net_height, title_bar_height == 0, 0); } else { From 8fecf3aa8c0cdff23a75a5f20b4a3423a43e9bc5 Mon Sep 17 00:00:00 2001 From: alex-huff Date: Fri, 9 May 2025 14:35:30 -0500 Subject: [PATCH 236/361] transaction: reparent scenes of containers behind fullscreen containers Currently we do a good job of reparenting the scenes of a container when it moves into a disabled workspace. We need to do this since normally the scenes are reparented in the 'arrange_{children,container}' functions but these don't get called for disabled workspaces. However, the 'arrange_{children,container}' functions also don't get called when there is a fullscreen container hiding them. This commit makes sure to call 'disable_workspace' on workspaces with a fullscreen container so that when a container is moved into the workspace its scenes will be properly reparented. Also, when there is a fullscreen global container 'disable_workspace' is called for all workspaces since the scenes of a previously fullscreen global container may still be parented in the 'fullscreen_global' layer. Fixes #8705 #8659 #8432 --- sway/desktop/transaction.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index f8bc64b57..7ec9b68d2 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -587,13 +587,13 @@ static void arrange_output(struct sway_output *output, int width, int height) { wlr_scene_node_set_enabled(&child->layers.tiling->node, !fs); wlr_scene_node_set_enabled(&child->layers.fullscreen->node, fs); - arrange_workspace_floating(child); - wlr_scene_node_set_enabled(&output->layers.shell_background->node, !fs); wlr_scene_node_set_enabled(&output->layers.shell_bottom->node, !fs); wlr_scene_node_set_enabled(&output->layers.fullscreen->node, fs); if (fs) { + disable_workspace(child); + wlr_scene_rect_set_size(output->fullscreen_background, width, height); arrange_fullscreen(child->layers.fullscreen, fs, child, @@ -609,6 +609,8 @@ static void arrange_output(struct sway_output *output, int width, int height) { area->width - gaps->left - gaps->right, area->height - gaps->top - gaps->bottom); } + + arrange_workspace_floating(child); } else { wlr_scene_node_set_enabled(&child->layers.tiling->node, false); wlr_scene_node_set_enabled(&child->layers.fullscreen->node, false); @@ -665,6 +667,13 @@ static void arrange_root(struct sway_root *root) { wlr_scene_output_set_position(output->scene_output, output->lx, output->ly); + // disable all workspaces to get to a known state + for (int j = 0; j < output->current.workspaces->length; j++) { + struct sway_workspace *workspace = output->current.workspaces->items[j]; + disable_workspace(workspace); + } + + // arrange the active workspace if (ws) { arrange_workspace_floating(ws); } From 5cfcd1c7c2ee1e0a199fd5d62b1da962f2102a85 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 19 May 2025 18:25:16 +0200 Subject: [PATCH 237/361] input: fix udev_device leak libinput_device_get_udev_device() returns a ref'ed handle: https://wayland.freedesktop.org/libinput/doc/latest/api/group__device.html#gac13c64ba19fc19094cff0e5354a2a7ce Similar to this wlroots MR: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5074 --- sway/input/libinput.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/sway/input/libinput.c b/sway/input/libinput.c index f76c6505c..b98c4cc86 100644 --- a/sway/input/libinput.c +++ b/sway/input/libinput.c @@ -399,6 +399,19 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) { } } +static bool sway_udev_device_is_builtin(struct udev_device *udev_device) { + const char *id_path = udev_device_get_property_value(udev_device, "ID_PATH"); + if (!id_path) { + return false; + } + + if (has_prefix(id_path, "platform-")) { + return true; + } + + return has_prefix(id_path, "pci-") && strstr(id_path, "-platform-"); +} + bool sway_libinput_device_is_builtin(struct sway_input_device *sway_device) { if (!wlr_input_device_is_libinput(sway_device->wlr_device)) { return false; @@ -412,14 +425,7 @@ bool sway_libinput_device_is_builtin(struct sway_input_device *sway_device) { return false; } - const char *id_path = udev_device_get_property_value(udev_device, "ID_PATH"); - if (!id_path) { - return false; - } - - if (has_prefix(id_path, "platform-")) { - return true; - } - - return has_prefix(id_path, "pci-") && strstr(id_path, "-platform-"); + bool is_builtin = sway_udev_device_is_builtin(udev_device); + udev_device_unref(udev_device); + return is_builtin; } From 1b47277962fa04624a0b6835e81089110d258e7c Mon Sep 17 00:00:00 2001 From: alex-huff Date: Wed, 21 May 2025 18:47:10 -0500 Subject: [PATCH 238/361] layer-shell: reclaim space from unmapped layer surfaces wlroots resets 'initialized' when a layer surface is unmapped and sway doesn't rearrange the layer surfaces in response to a commit of a surface where 'initialized' is false. This results in space not getting reclaimed from a recently unmapped layer surface until some other action causes 'arrange_layers' to get called. This commit makes sure all layer surfaces get rearranged when a layer surface is unmapped. --- sway/desktop/layer_shell.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index b14a9a4b1..08b5b1de2 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -269,12 +269,8 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { wl_container_of(listener, surface, surface_commit); struct wlr_layer_surface_v1 *layer_surface = surface->layer_surface; - if (!layer_surface->initialized) { - return; - } - uint32_t committed = layer_surface->current.committed; - if (committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) { + if (layer_surface->initialized && committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) { enum zwlr_layer_shell_v1_layer layer_type = layer_surface->current.layer; struct wlr_scene_tree *output_layer = sway_layer_get_scene( surface->output, layer_type); From 88c7b4a7ebe281ee771e3a1bfd8968046918a01a Mon Sep 17 00:00:00 2001 From: alex-huff Date: Wed, 21 May 2025 20:31:46 -0500 Subject: [PATCH 239/361] transaction: fix floating fullscreen containers 8fecf3a introduced a regression where fullscreening a child of a floating container would result in a black screen. This is because the order of 'arrange_fullscreen' and 'arrange_worksplace_floating' was swapped causing the fullscreen container's scene to get reparented after it was parented in the fullscreen layer. Fixes #8729 --- sway/desktop/transaction.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 7ec9b68d2..325a30226 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -596,6 +596,7 @@ static void arrange_output(struct sway_output *output, int width, int height) { wlr_scene_rect_set_size(output->fullscreen_background, width, height); + arrange_workspace_floating(child); arrange_fullscreen(child->layers.fullscreen, fs, child, width, height); } else { @@ -608,9 +609,8 @@ static void arrange_output(struct sway_output *output, int width, int height) { arrange_workspace_tiling(child, area->width - gaps->left - gaps->right, area->height - gaps->top - gaps->bottom); + arrange_workspace_floating(child); } - - arrange_workspace_floating(child); } else { wlr_scene_node_set_enabled(&child->layers.tiling->node, false); wlr_scene_node_set_enabled(&child->layers.fullscreen->node, false); From 005924f2609029612d4468c3436a816a48991246 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 6 May 2025 01:35:05 +0200 Subject: [PATCH 240/361] output: Minimize interaction with output after destroy When an output is destroyed, we go through the process of disabling it. This includes evacuating all content away from the output, which can lead to various modifications to the scene. With the scene_output still present, this can lead to things like output_enter events being emitted for the output currently being destroyed. Ensure that the scene output is destroyed first and that the output is immediately considered disabled. References: https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3974 --- sway/desktop/output.c | 16 +++++++++------- sway/tree/output.c | 13 ++++++------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index aec1d0a6b..e6fe2ee4d 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -422,13 +422,6 @@ void force_modeset(void) { } static void begin_destroy(struct sway_output *output) { - if (output->enabled) { - output_disable(output); - } - - output_begin_destroy(output); - - wl_list_remove(&output->link); wl_list_remove(&output->layout_destroy.link); wl_list_remove(&output->destroy.link); @@ -436,8 +429,17 @@ static void begin_destroy(struct sway_output *output) { wl_list_remove(&output->frame.link); wl_list_remove(&output->request_state.link); + // Remove the scene_output first to ensure that the scene does not emit + // events for this output. wlr_scene_output_destroy(output->scene_output); output->scene_output = NULL; + + if (output->enabled) { + output_disable(output); + } + output_begin_destroy(output); + wl_list_remove(&output->link); + output->wlr_output->data = NULL; output->wlr_output = NULL; diff --git a/sway/tree/output.c b/sway/tree/output.c index 65d9e3e71..bc3806298 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -205,11 +205,8 @@ static void output_evacuate(struct sway_output *output) { return; } struct sway_output *fallback_output = NULL; - if (root->outputs->length > 1) { + if (root->outputs->length > 0) { fallback_output = root->outputs->items[0]; - if (fallback_output == output) { - fallback_output = root->outputs->items[1]; - } } while (output->workspaces->length) { @@ -289,11 +286,13 @@ void output_disable(struct sway_output *output) { sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); wl_signal_emit_mutable(&output->events.disable, output); - output_evacuate(output); - + // Remove the output now to avoid interacting with it during e.g., + // transactions, as the output might be physically removed with the scene + // output destroyed. list_del(root->outputs, index); - output->enabled = false; + + output_evacuate(output); } void output_begin_destroy(struct sway_output *output) { From 534491d3aa0c60d5c941f2f46b5d7f8978901f84 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 6 May 2025 11:46:35 +0200 Subject: [PATCH 241/361] tree/workspace: Remove exclude arg from get_highest_available workspace_output_get_highest_available took an output to exclude as argument, meant to avoid accidentally reselecting an output we are evacuating workspaces from. Outputs are now removed from the list before we evacuate, making exclusion unnecessary. Remove the argument. --- include/sway/tree/workspace.h | 2 +- sway/tree/output.c | 4 ++-- sway/tree/workspace.c | 6 +----- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index 58bde20cb..27ed649fd 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -96,7 +96,7 @@ void workspace_output_add_priority(struct sway_workspace *workspace, struct sway_output *output); struct sway_output *workspace_output_get_highest_available( - struct sway_workspace *ws, struct sway_output *exclude); + struct sway_workspace *ws); void workspace_detect_urgent(struct sway_workspace *workspace); diff --git a/sway/tree/output.c b/sway/tree/output.c index bc3806298..b02c1a2ce 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -37,7 +37,7 @@ static void restore_workspaces(struct sway_output *output) { for (int j = 0; j < other->workspaces->length; j++) { struct sway_workspace *ws = other->workspaces->items[j]; struct sway_output *highest = - workspace_output_get_highest_available(ws, NULL); + workspace_output_get_highest_available(ws); if (highest == output) { workspace_detach(ws); output_add_workspace(output, ws); @@ -215,7 +215,7 @@ static void output_evacuate(struct sway_output *output) { workspace_detach(workspace); struct sway_output *new_output = - workspace_output_get_highest_available(workspace, output); + workspace_output_get_highest_available(workspace); if (!new_output) { new_output = fallback_output; } diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index a09dc3a45..f2be4cd13 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -659,13 +659,9 @@ void workspace_output_add_priority(struct sway_workspace *workspace, } struct sway_output *workspace_output_get_highest_available( - struct sway_workspace *ws, struct sway_output *exclude) { + struct sway_workspace *ws) { for (int i = 0; i < ws->output_priority->length; i++) { const char *name = ws->output_priority->items[i]; - if (exclude && output_match_name_or_id(exclude, name)) { - continue; - } - struct sway_output *output = output_by_name_or_id(name); if (output) { return output; From 45267bb576559ac583daa4c73c64baef1d4f5ef3 Mon Sep 17 00:00:00 2001 From: Bonsaiiv Date: Sat, 24 May 2025 16:03:45 +0200 Subject: [PATCH 242/361] Improve example of input section in default config Previous example included a specific device name. This can be confusing for beginners, as the default did not work on most devices. --- config.in | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/config.in b/config.in index a2a01dda9..872ef0574 100644 --- a/config.in +++ b/config.in @@ -46,14 +46,18 @@ output * bg @datadir@/backgrounds/sway/Sway_Wallpaper_Blue_1920x1080.png fill # # Example configuration: # -# input "2:14:SynPS/2_Synaptics_TouchPad" { +# input type:touchpad { # dwt enabled # tap enabled # natural_scroll enabled # middle_emulation enabled # } # -# You can get the names of your inputs by running: swaymsg -t get_inputs +# input type:keyboard { +# xkb_layout "eu" +# } +# +# You can also configure each device individually. # Read `man 5 sway-input` for more information about this section. ### Key bindings From 63689bfb830b68eba8062aedef9928c55713c9bc Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 27 Apr 2025 22:11:41 +0200 Subject: [PATCH 243/361] Log message on for_window command error --- sway/tree/view.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index 16080a2f9..9a5cf06e0 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -517,10 +517,12 @@ void view_execute_criteria(struct sway_view *view) { sway_log(SWAY_DEBUG, "for_window '%s' matches view %p, cmd: '%s'", criteria->raw, view, criteria->cmdlist); list_add(view->executed_criteria, criteria); - list_t *res_list = execute_command( - criteria->cmdlist, NULL, view->container); + list_t *res_list = execute_command(criteria->cmdlist, NULL, view->container); while (res_list->length) { struct cmd_results *res = res_list->items[0]; + if (res->status != CMD_SUCCESS) { + sway_log(SWAY_ERROR, "for_window '%s' failed: %s", criteria->raw, res->error); + } free_cmd_results(res); list_del(res_list, 0); } From 7e7994dbb2a2c04f55b3c74eb61577c51e9a43ae Mon Sep 17 00:00:00 2001 From: Konstantin Pospelov Date: Thu, 27 Mar 2025 20:11:59 +0100 Subject: [PATCH 244/361] swaybar: deduplicate mode and workspace rendering code The render_workspace_button and render_binding_mode_indicator functions are almost the same. This commit extracts the common rendering code into a new render_box function. --- include/swaybar/config.h | 5 ++ swaybar/render.c | 169 ++++++++++++++++----------------------- 2 files changed, 73 insertions(+), 101 deletions(-) diff --git a/include/swaybar/config.h b/include/swaybar/config.h index 361acd991..ad58b3c3c 100644 --- a/include/swaybar/config.h +++ b/include/swaybar/config.h @@ -14,6 +14,11 @@ struct box_colors { uint32_t text; }; +struct box_size { + uint32_t width; + uint32_t height; +}; + struct config_output { struct wl_list link; // swaybar_config::outputs char *name; diff --git a/swaybar/render.c b/swaybar/render.c index 45faefa97..c51321f18 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -13,7 +13,6 @@ #include "swaybar/ipc.h" #include "swaybar/render.h" #include "swaybar/status_line.h" -#include "log.h" #if HAVE_TRAY #include "swaybar/tray/tray.h" #endif @@ -21,7 +20,7 @@ static const int WS_HORIZONTAL_PADDING = 5; static const double WS_VERTICAL_PADDING = 1.5; -static const double BORDER_WIDTH = 1; +static const int BORDER_WIDTH = 1; struct render_context { cairo_t *cairo; @@ -541,6 +540,63 @@ static uint32_t render_status_line(struct render_context *ctx, double *x) { return 0; } +static struct box_size render_box(struct render_context *ctx, double x, + struct box_colors colors, const char *label, bool pango_markup) { + struct swaybar_output *output = ctx->output; + struct swaybar_config *config = output->bar->config; + cairo_t *cairo = ctx->cairo; + + int text_width, text_height; + get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, + 1, pango_markup, "%s", label); + + uint32_t width = text_width + WS_HORIZONTAL_PADDING * 2 + BORDER_WIDTH * 2; + if (width < config->workspace_min_width) { + width = config->workspace_min_width; + } + + uint32_t ideal_height = text_height + WS_VERTICAL_PADDING * 2 + + BORDER_WIDTH * 2; + uint32_t ideal_surface_height = ideal_height; + if (!output->bar->config->height && + output->height < ideal_surface_height) { + return (struct box_size) { + .width = width, + .height = ideal_surface_height, + }; + } + + uint32_t height = output->height; + cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); + cairo_set_source_u32(cairo, colors.background); + ctx->background_color = colors.background; + ctx->has_transparency |= (colors.background & 0xFF) != 0xFF; + cairo_rectangle(cairo, x, 0, width, height); + cairo_fill(cairo); + + cairo_set_source_u32(cairo, colors.border); + cairo_rectangle(cairo, x, 0, width, BORDER_WIDTH); + cairo_fill(cairo); + cairo_rectangle(cairo, x, 0, BORDER_WIDTH, height); + cairo_fill(cairo); + cairo_rectangle(cairo, x + width - BORDER_WIDTH, 0, BORDER_WIDTH, height); + cairo_fill(cairo); + cairo_rectangle(cairo, x, height - BORDER_WIDTH, width, BORDER_WIDTH); + cairo_fill(cairo); + + double text_y = height / 2.0 - text_height / 2.0; + cairo_set_source_u32(cairo, colors.text); + cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); + choose_text_aa_mode(ctx, colors.text); + render_text(cairo, config->font_description, 1, pango_markup, + "%s", label); + + return (struct box_size) { + .width = width, + .height = output->height, + }; +} + static uint32_t render_binding_mode_indicator(struct render_context *ctx, double x) { struct swaybar_output *output = ctx->output; @@ -549,54 +605,9 @@ static uint32_t render_binding_mode_indicator(struct render_context *ctx, return 0; } - cairo_t *cairo = ctx->cairo; - struct swaybar_config *config = output->bar->config; - int text_width, text_height; - get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, - 1, output->bar->mode_pango_markup, - "%s", mode); - - int ws_vertical_padding = WS_VERTICAL_PADDING; - int ws_horizontal_padding = WS_HORIZONTAL_PADDING; - int border_width = BORDER_WIDTH; - - uint32_t ideal_height = text_height + ws_vertical_padding * 2 - + border_width * 2; - uint32_t ideal_surface_height = ideal_height; - if (!output->bar->config->height && - output->height < ideal_surface_height) { - return ideal_surface_height; - } - uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; - if (width < config->workspace_min_width) { - width = config->workspace_min_width; - } - - uint32_t height = output->height; - cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); - cairo_set_source_u32(cairo, config->colors.binding_mode.background); - ctx->background_color = config->colors.binding_mode.background; - ctx->has_transparency |= (config->colors.binding_mode.background & 0xFF) != 0xFF; - cairo_rectangle(cairo, x, 0, width, height); - cairo_fill(cairo); - - cairo_set_source_u32(cairo, config->colors.binding_mode.border); - cairo_rectangle(cairo, x, 0, width, border_width); - cairo_fill(cairo); - cairo_rectangle(cairo, x, 0, border_width, height); - cairo_fill(cairo); - cairo_rectangle(cairo, x + width - border_width, 0, border_width, height); - cairo_fill(cairo); - cairo_rectangle(cairo, x, height - border_width, width, border_width); - cairo_fill(cairo); - - double text_y = height / 2.0 - text_height / 2.0; - cairo_set_source_u32(cairo, config->colors.binding_mode.text); - cairo_move_to(cairo, x + width / 2 - text_width / 2, (int)floor(text_y)); - choose_text_aa_mode(ctx, config->colors.binding_mode.text); - render_text(cairo, config->font_description, 1, output->bar->mode_pango_markup, - "%s", mode); - return output->height; + struct box_size size = render_box(ctx, x, output->bar->config->colors.binding_mode, + mode, output->bar->mode_pango_markup); + return size.height; } static enum hotspot_event_handling workspace_hotspot_callback( @@ -618,6 +629,7 @@ static uint32_t render_workspace_button(struct render_context *ctx, struct swaybar_workspace *ws, double *x) { struct swaybar_output *output = ctx->output; struct swaybar_config *config = output->bar->config; + struct box_colors box_colors; if (ws->urgent) { box_colors = config->colors.urgent_workspace; @@ -629,66 +641,21 @@ static uint32_t render_workspace_button(struct render_context *ctx, box_colors = config->colors.inactive_workspace; } - uint32_t height = output->height; - - cairo_t *cairo = ctx->cairo; - int text_width, text_height; - get_text_size(cairo, config->font_description, &text_width, &text_height, NULL, - 1, config->pango_markup, "%s", ws->label); - - int ws_vertical_padding = WS_VERTICAL_PADDING; - int ws_horizontal_padding = WS_HORIZONTAL_PADDING; - int border_width = BORDER_WIDTH; - - uint32_t ideal_height = ws_vertical_padding * 2 + text_height - + border_width * 2; - uint32_t ideal_surface_height = ideal_height; - if (!output->bar->config->height && - output->height < ideal_surface_height) { - return ideal_surface_height; - } - - uint32_t width = text_width + ws_horizontal_padding * 2 + border_width * 2; - if (width < config->workspace_min_width) { - width = config->workspace_min_width; - } - - cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); - cairo_set_source_u32(cairo, box_colors.background); - ctx->background_color = box_colors.background; - ctx->has_transparency |= (box_colors.background & 0xFF) != 0xFF; - cairo_rectangle(cairo, *x, 0, width, height); - cairo_fill(cairo); - - cairo_set_source_u32(cairo, box_colors.border); - cairo_rectangle(cairo, *x, 0, width, border_width); - cairo_fill(cairo); - cairo_rectangle(cairo, *x, 0, border_width, height); - cairo_fill(cairo); - cairo_rectangle(cairo, *x + width - border_width, 0, border_width, height); - cairo_fill(cairo); - cairo_rectangle(cairo, *x, height - border_width, width, border_width); - cairo_fill(cairo); - - double text_y = height / 2.0 - text_height / 2.0; - cairo_set_source_u32(cairo, box_colors.text); - cairo_move_to(cairo, *x + width / 2 - text_width / 2, (int)floor(text_y)); - choose_text_aa_mode(ctx, box_colors.text); - render_text(cairo, config->font_description, 1, config->pango_markup, - "%s", ws->label); + struct box_size size = render_box(ctx, *x, box_colors, + ws->label, config->pango_markup); struct swaybar_hotspot *hotspot = calloc(1, sizeof(struct swaybar_hotspot)); hotspot->x = *x; hotspot->y = 0; - hotspot->width = width; - hotspot->height = height; + hotspot->width = size.width; + hotspot->height = size.height; hotspot->callback = workspace_hotspot_callback; hotspot->destroy = free; hotspot->data = strdup(ws->name); wl_list_insert(&output->hotspots, &hotspot->link); - *x += width; - return output->height; + *x += size.width; + return size.height; } static uint32_t render_to_cairo(struct render_context *ctx) { From 9fb9e9f7d555ae7504b8ae53250020797d70e887 Mon Sep 17 00:00:00 2001 From: YaoBing Xiao Date: Thu, 5 Jun 2025 13:46:06 +0800 Subject: [PATCH 245/361] server: fix socket path memory leak The socket path allocated with strdup() in server_init() was not being freed in server_fini(). Remove const qualifier and add proper cleanup. --- include/sway/server.h | 2 +- sway/server.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/sway/server.h b/include/sway/server.h index e7d7094f1..1cb72e77e 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -27,7 +27,7 @@ struct sway_session_lock { struct sway_server { struct wl_display *wl_display; struct wl_event_loop *wl_event_loop; - const char *socket; + char *socket; struct wlr_backend *backend; struct wlr_session *session; diff --git a/sway/server.c b/sway/server.c index 979761486..e8a6ce64f 100644 --- a/sway/server.c +++ b/sway/server.c @@ -502,6 +502,7 @@ void server_fini(struct sway_server *server) { wlr_backend_destroy(server->backend); wl_display_destroy(server->wl_display); list_free(server->dirty_nodes); + free(server->socket); } bool server_start(struct sway_server *server) { From 6816b51c86846afc5eaa1dea2541410058347a6e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 5 Jun 2025 18:15:56 +0200 Subject: [PATCH 246/361] Remove trailing spaces --- include/sway/server.h | 2 +- include/sway/tree/container.h | 2 +- sway/commands/ws_auto_back_and_forth.c | 2 +- sway/input/keyboard.c | 4 ++-- sway/input/seatop_default.c | 2 +- sway/sway-output.5.scd | 6 +++--- sway/sway.5.scd | 4 ++-- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index 1cb72e77e..8e859702a 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -117,7 +117,7 @@ struct sway_server { struct wl_listener xdg_activation_v1_new_token; struct wl_listener request_set_cursor_shape; - + struct wlr_tearing_control_manager_v1 *tearing_control_v1; struct wl_listener tearing_control_new_object; struct wl_list tearing_controllers; // sway_tearing_controller::link diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 4fb2d7208..e18fd00ac 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -103,7 +103,7 @@ struct sway_container { char *title; // The view's title (unformatted) char *formatted_title; // The title displayed in the title bar int title_width; - + char *title_format; enum sway_container_layout prev_split_layout; diff --git a/sway/commands/ws_auto_back_and_forth.c b/sway/commands/ws_auto_back_and_forth.c index e4411c8ed..5827c389d 100644 --- a/sway/commands/ws_auto_back_and_forth.c +++ b/sway/commands/ws_auto_back_and_forth.c @@ -8,7 +8,7 @@ struct cmd_results *cmd_ws_auto_back_and_forth(int argc, char **argv) { if ((error = checkarg(argc, "workspace_auto_back_and_forth", EXPECTED_EQUAL_TO, 1))) { return error; } - config->auto_back_and_forth = + config->auto_back_and_forth = parse_boolean(argv[0], config->auto_back_and_forth); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 651c44c5a..d18237375 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -980,10 +980,10 @@ static void sway_keyboard_group_add(struct sway_keyboard *keyboard) { wl_signal_add(&sway_group->wlr_group->keyboard.events.modifiers, &sway_group->keyboard_modifiers); sway_group->keyboard_modifiers.notify = handle_keyboard_group_modifiers; - + wl_signal_add(&sway_group->wlr_group->events.enter, &sway_group->enter); sway_group->enter.notify = handle_keyboard_group_enter; - + wl_signal_add(&sway_group->wlr_group->events.leave, &sway_group->leave); sway_group->leave.notify = handle_keyboard_group_leave; return; diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 6a7dddd9a..a2e99d2bc 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -793,7 +793,7 @@ static void handle_pointer_axis(struct sway_seat *seat, if (!handled) { wlr_seat_pointer_notify_axis(cursor->seat->wlr_seat, event->time_msec, - event->orientation, scroll_factor * event->delta, + event->orientation, scroll_factor * event->delta, roundf(scroll_factor * event->delta_discrete), event->source, event->relative_direction); } diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index dc16c257b..dfe0d678b 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -196,9 +196,9 @@ must be separated by one space. For example: screen tearing is allowed. With immediate page flips, frames from the client are presented as soon - as possible instead of synchronizing with the monitor's vblank interval - (VSync). - + as possible instead of synchronizing with the monitor's vblank interval + (VSync). + It is recommended to set *max_render_time* to *off*. In that case a page flip happens as soon as a client updates. Otherwise, tearing will only happen if rendering takes longer than the configured milliseconds before the next diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 9bc03c9de..dbdf1f1a2 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -228,8 +228,8 @@ set|plus|minus|toggle regardless of the tearing hints. This setting only has an effect if tearing is allowed on the output through - the per-output *allow_tearing* setting. See *sway-output*(5) - for further details. + the per-output *allow_tearing* setting. See *sway-output*(5) for further + details. *move* left|right|up|down [ px] Moves the focused container in the direction specified. The optional _px_ From 3d401d9390694dfe4ed7c408b9805f6d23ed8240 Mon Sep 17 00:00:00 2001 From: Callum Andrew Date: Mon, 9 Jun 2025 16:36:32 +1000 Subject: [PATCH 247/361] config.in: bind XF86Audio* keycodes to playerctl --- config.in | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/config.in b/config.in index 872ef0574..d71bc628b 100644 --- a/config.in +++ b/config.in @@ -205,9 +205,18 @@ bindsym $mod+r mode "resize" bindsym --locked XF86AudioLowerVolume exec pactl set-sink-volume \@DEFAULT_SINK@ -5% bindsym --locked XF86AudioRaiseVolume exec pactl set-sink-volume \@DEFAULT_SINK@ +5% bindsym --locked XF86AudioMicMute exec pactl set-source-mute \@DEFAULT_SOURCE@ toggle + + # Special keys to control media via playerctl + bindsym --locked XF86AudioPlay exec playerctl play-pause + bindsym --locked XF86AudioPause exec playerctl play-pause + bindsym --locked XF86AudioPrev exec playerctl previous + bindsym --locked XF86AudioNext exec playerctl next + bindsym --locked XF86AudioStop exec playerctl stop + # Special keys to adjust brightness via brightnessctl bindsym --locked XF86MonBrightnessDown exec brightnessctl set 5%- bindsym --locked XF86MonBrightnessUp exec brightnessctl set 5%+ + # Special key to take a screenshot with grim bindsym Print exec grim From 0a740a24d98beeb3080552c29a64be6027ccd442 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 9 Jun 2025 16:27:18 +0200 Subject: [PATCH 248/361] build: bump version to 1.12-dev --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 07a6bccff..05e518818 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'sway', 'c', - version: '1.11-rc2', + version: '1.12-dev', license: 'MIT', meson_version: '>=1.3', default_options: [ From 1ab573bf54eec653123f3c6f26ae14c3fcac605a Mon Sep 17 00:00:00 2001 From: Attila Fidan Date: Fri, 13 Jun 2025 18:02:55 +0000 Subject: [PATCH 249/361] text_input: Ignore enable requests from unfocused windows Unfocused windows shouldn't be allowed to activate the IM. This fixes an issue with swaymsg invocations that contain several commands which result in multiple swift focus changes. An application briefly gets text input focus, sends an enable request, then sway processes it and activates the IM only after the commands are all finished and focus is on something else which did not send an enable request. --- sway/input/text_input.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sway/input/text_input.c b/sway/input/text_input.c index 0eb76eb16..2729db625 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -248,6 +248,10 @@ static void relay_send_im_state(struct sway_input_method_relay *relay, static void handle_text_input_enable(struct wl_listener *listener, void *data) { struct sway_text_input *text_input = wl_container_of(listener, text_input, text_input_enable); + if (text_input->input->focused_surface == NULL) { + sway_log(SWAY_DEBUG, "Enabling text input, but no longer focused"); + return; + } if (text_input->relay->input_method == NULL) { sway_log(SWAY_INFO, "Enabling text input when input method is gone"); return; From 6c27c2cdf263090252df4304f00b8425d0edd091 Mon Sep 17 00:00:00 2001 From: Attila Fidan Date: Fri, 13 Jun 2025 18:13:35 +0000 Subject: [PATCH 250/361] text_input: Don't relay IM state from unfocused windows Otherwise, applications can enable their text input and affect IM state while a different application is focused. --- sway/input/text_input.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sway/input/text_input.c b/sway/input/text_input.c index 2729db625..c907585fe 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -264,6 +264,10 @@ static void handle_text_input_commit(struct wl_listener *listener, void *data) { struct sway_text_input *text_input = wl_container_of(listener, text_input, text_input_commit); + if (text_input->input->focused_surface == NULL) { + sway_log(SWAY_DEBUG, "Unfocused text input tried to commit an update"); + return; + } if (!text_input->input->current_enabled) { sway_log(SWAY_INFO, "Inactive text input tried to commit an update"); return; From 17f7c1b7820e4f85913ec70ab3c76f238c15c54c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 12 Jun 2025 00:14:50 +0200 Subject: [PATCH 251/361] build: set wrap_mode=nodownload in default options This can be surprising (e.g. in CI, this can download source code instead of using system libraries) and users can easily turn it back on if desired. --- meson.build | 1 + 1 file changed, 1 insertion(+) diff --git a/meson.build b/meson.build index 05e518818..eab1348ae 100644 --- a/meson.build +++ b/meson.build @@ -8,6 +8,7 @@ project( 'c_std=c11', 'warning_level=2', 'werror=true', + 'wrap_mode=nodownload', ], ) From 4b15b3427fd0835247d0a2e188c00f1d2aa04484 Mon Sep 17 00:00:00 2001 From: hwsmm <9151706+hwsmm@users.noreply.github.com> Date: Mon, 16 Jun 2025 00:41:19 +0900 Subject: [PATCH 252/361] Rename get_current_time_msec to get_current_time_in_msec and move to util.c get_current_time_msec conflicts with a function with the same name in wlroots. --- common/util.c | 6 ++++++ include/util.h | 2 ++ sway/input/cursor.c | 10 ++-------- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/common/util.c b/common/util.c index 7c492bcbf..ae1fe4431 100644 --- a/common/util.c +++ b/common/util.c @@ -141,3 +141,9 @@ bool sway_set_cloexec(int fd, bool cloexec) { } return true; } + +uint32_t get_current_time_in_msec(void) { + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + return now.tv_sec * 1000 + now.tv_nsec / 1000000; +} diff --git a/include/util.h b/include/util.h index f887d4895..92f5916cf 100644 --- a/include/util.h +++ b/include/util.h @@ -61,4 +61,6 @@ const char *sway_wl_output_subpixel_to_string(enum wl_output_subpixel subpixel); bool sway_set_cloexec(int fd, bool cloexec); +uint32_t get_current_time_in_msec(void); + #endif diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 1fd57ec4e..aeab558cb 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -32,12 +32,6 @@ #include "sway/tree/workspace.h" #include "wlr-layer-shell-unstable-v1-protocol.h" -static uint32_t get_current_time_msec(void) { - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - return now.tv_sec * 1000 + now.tv_nsec / 1000000; -} - /** * Returns the node at the cursor's position. If there is a surface at that * location, it is stored in **surface (it may not be a view). @@ -144,7 +138,7 @@ struct sway_node *node_at_coords( } void cursor_rebase(struct sway_cursor *cursor) { - uint32_t time_msec = get_current_time_msec(); + uint32_t time_msec = get_current_time_in_msec(); seatop_rebase(cursor->seat, time_msec); } @@ -359,7 +353,7 @@ void dispatch_cursor_button(struct sway_cursor *cursor, struct wlr_input_device *device, uint32_t time_msec, uint32_t button, enum wl_pointer_button_state state) { if (time_msec == 0) { - time_msec = get_current_time_msec(); + time_msec = get_current_time_in_msec(); } seatop_button(cursor->seat, time_msec, device, button, state); From 25ea1a0af287018da1dbb34fe92e6c8ea3b8a88d Mon Sep 17 00:00:00 2001 From: hwsmm <9151706+hwsmm@users.noreply.github.com> Date: Mon, 16 Jun 2025 00:41:44 +0900 Subject: [PATCH 253/361] seatop_default: Call seatop_rebase with proper timestamp --- sway/input/seatop_default.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index a2e99d2bc..19645b4c9 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -16,6 +16,7 @@ #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "log.h" +#include "util.h" #if WLR_HAS_XWAYLAND #include "sway/xwayland.h" #endif @@ -1148,5 +1149,7 @@ void seatop_begin_default(struct sway_seat *seat) { seat->seatop_impl = &seatop_impl; seat->seatop_data = e; - seatop_rebase(seat, 0); + + uint32_t time_msec = get_current_time_in_msec(); + seatop_rebase(seat, time_msec); } From eb8acfd7b1cd0eeab75fe699e6aa0751150e80a2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 5 Jun 2025 13:15:38 +0200 Subject: [PATCH 254/361] Stop using wlr_scene_buffer_send_frame_done() That function now takes the output as input. We don't always have the output at hand, so use the function operating on a wlr_scene_surface instead. --- sway/desktop/output.c | 19 +++++++++++++------ sway/tree/view.c | 6 +++++- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index e6fe2ee4d..12dc9cc7a 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -97,11 +97,11 @@ struct buffer_timer { }; static int handle_buffer_timer(void *data) { - struct wlr_scene_buffer *buffer = data; + struct wlr_scene_surface *scene_surface = data; struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); - wlr_scene_buffer_send_frame_done(buffer, &now); + wlr_scene_surface_send_frame_done(scene_surface, &now); return 0; } @@ -114,7 +114,9 @@ static void handle_buffer_timer_destroy(struct wl_listener *listener, free(timer); } -static struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_buffer *buffer) { +static struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_surface *scene_surface) { + struct wlr_scene_buffer *buffer = scene_surface->buffer; + struct buffer_timer *timer = scene_descriptor_try_get(&buffer->node, SWAY_SCENE_DESC_BUFFER_TIMER); if (timer) { @@ -127,7 +129,7 @@ static struct buffer_timer *buffer_timer_get_or_create(struct wlr_scene_buffer * } timer->frame_done_timer = wl_event_loop_add_timer(server.wl_event_loop, - handle_buffer_timer, buffer); + handle_buffer_timer, scene_surface); if (!timer->frame_done_timer) { free(timer); return NULL; @@ -151,6 +153,11 @@ static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, return; } + struct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(buffer); + if (scene_surface == NULL) { + return; + } + struct wlr_scene_node *current = &buffer->node; while (true) { struct sway_view *view = scene_descriptor_try_get(current, @@ -173,13 +180,13 @@ static void send_frame_done_iterator(struct wlr_scene_buffer *buffer, struct buffer_timer *timer = NULL; if (output->max_render_time != 0 && view_max_render_time != 0 && delay > 0) { - timer = buffer_timer_get_or_create(buffer); + timer = buffer_timer_get_or_create(scene_surface); } if (timer) { wl_event_source_timer_update(timer->frame_done_timer, delay); } else { - wlr_scene_buffer_send_frame_done(buffer, &data->when); + wlr_scene_surface_send_frame_done(scene_surface, &data->when); } } diff --git a/sway/tree/view.c b/sway/tree/view.c index 9a5cf06e0..742a66c2d 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -1242,7 +1242,11 @@ bool view_can_tear(struct sway_view *view) { static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer, int x, int y, void *data) { struct timespec *when = data; - wl_signal_emit_mutable(&scene_buffer->events.frame_done, when); + struct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(scene_buffer); + if (scene_surface == NULL) { + return; + } + wlr_scene_surface_send_frame_done(scene_surface, when); } void view_send_frame_done(struct sway_view *view) { From 170c9c9525f54e8c1ba03847d5f9b01fc24b8c89 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 25 May 2025 19:38:05 +0200 Subject: [PATCH 255/361] Add support for toplevel capture References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5078 --- include/sway/server.h | 3 +++ include/sway/tree/view.h | 11 ++++++++++- sway/desktop/xdg_shell.c | 16 ++++++++++++---- sway/desktop/xwayland.c | 6 ++++++ sway/server.c | 22 ++++++++++++++++++++++ sway/tree/view.c | 7 +++++++ 6 files changed, 60 insertions(+), 5 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index 8e859702a..72bccd70c 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -112,6 +112,9 @@ struct sway_server { struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager_v1; struct wlr_security_context_manager_v1 *security_context_manager_v1; + struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 *ext_foreign_toplevel_image_capture_source_manager_v1; + struct wl_listener new_foreign_toplevel_capture_request; + struct wlr_xdg_activation_v1 *xdg_activation_v1; struct wl_listener xdg_activation_v1_request_activate; struct wl_listener xdg_activation_v1_new_token; diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 9f084eeb4..6151a0234 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -69,6 +69,9 @@ struct sway_view { struct wlr_scene_tree *content_tree; struct wlr_scene_tree *saved_surface_tree; + struct wlr_scene *image_capture_scene; + struct wlr_ext_image_capture_source_v1 *image_capture_source; + struct sway_container *container; // NULL if unmapped and transactions finished struct wlr_surface *surface; // NULL for unmapped views struct sway_xdg_decoration *xdg_decoration; @@ -124,6 +127,8 @@ struct sway_view { struct sway_xdg_shell_view { struct sway_view view; + struct wlr_scene_tree *image_capture_tree; + struct wl_listener commit; struct wl_listener request_move; struct wl_listener request_resize; @@ -142,6 +147,8 @@ struct sway_xwayland_view { struct wlr_scene_tree *surface_tree; + struct wlr_scene_surface *image_capture_scene_surface; + struct wl_listener commit; struct wl_listener request_move; struct wl_listener request_resize; @@ -192,10 +199,12 @@ struct sway_popup_desc { struct sway_xdg_popup { struct sway_view *view; + struct wlr_xdg_popup *wlr_xdg_popup; struct wlr_scene_tree *scene_tree; struct wlr_scene_tree *xdg_surface_tree; - struct wlr_xdg_popup *wlr_xdg_popup; + + struct wlr_scene_tree *image_capture_tree; struct sway_popup_desc desc; diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 3aed4ec7e..3852806e4 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -20,13 +20,13 @@ static struct sway_xdg_popup *popup_create( struct wlr_xdg_popup *wlr_popup, struct sway_view *view, - struct wlr_scene_tree *parent); + struct wlr_scene_tree *parent, struct wlr_scene_tree *image_capture_parent); static void popup_handle_new_popup(struct wl_listener *listener, void *data) { struct sway_xdg_popup *popup = wl_container_of(listener, popup, new_popup); struct wlr_xdg_popup *wlr_popup = data; - popup_create(wlr_popup, popup->view, popup->xdg_surface_tree); + popup_create(wlr_popup, popup->view, popup->xdg_surface_tree, popup->image_capture_tree); } static void popup_handle_destroy(struct wl_listener *listener, void *data) { @@ -77,7 +77,8 @@ static void popup_handle_reposition(struct wl_listener *listener, void *data) { } static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup, - struct sway_view *view, struct wlr_scene_tree *parent) { + struct sway_view *view, struct wlr_scene_tree *parent, + struct wlr_scene_tree *image_capture_parent) { struct wlr_xdg_surface *xdg_surface = wlr_popup->base; struct sway_xdg_popup *popup = calloc(1, sizeof(struct sway_xdg_popup)); @@ -113,6 +114,11 @@ static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup, return NULL; } + popup->image_capture_tree = wlr_scene_xdg_surface_create(image_capture_parent, xdg_surface); + if (popup->image_capture_tree == NULL) { + return NULL; + } + popup->wlr_xdg_popup = xdg_surface->popup; struct sway_xdg_shell_view *shell_view = wl_container_of(view, shell_view, view); @@ -359,7 +365,7 @@ static void handle_new_popup(struct wl_listener *listener, void *data) { struct wlr_xdg_popup *wlr_popup = data; struct sway_xdg_popup *popup = popup_create(wlr_popup, - &xdg_shell_view->view, root->layers.popup); + &xdg_shell_view->view, root->layers.popup, xdg_shell_view->image_capture_tree); if (!popup) { return; } @@ -570,6 +576,8 @@ void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) { wl_signal_add(&xdg_toplevel->events.destroy, &xdg_shell_view->destroy); wlr_scene_xdg_surface_create(xdg_shell_view->view.content_tree, xdg_toplevel->base); + xdg_shell_view->image_capture_tree = + wlr_scene_xdg_surface_create(&xdg_shell_view->view.image_capture_scene->tree, xdg_toplevel->base); xdg_toplevel->base->data = xdg_shell_view; } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index b83537a0a..76e63ce1f 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -497,6 +497,9 @@ static void handle_unmap(struct wl_listener *listener, void *data) { wl_list_remove(&xwayland_view->commit.link); wl_list_remove(&xwayland_view->surface_tree_destroy.link); + wlr_scene_node_destroy(&xwayland_view->image_capture_scene_surface->buffer->node); + xwayland_view->image_capture_scene_surface = NULL; + if (xwayland_view->surface_tree) { wlr_scene_node_destroy(&xwayland_view->surface_tree->node); xwayland_view->surface_tree = NULL; @@ -537,6 +540,9 @@ static void handle_map(struct wl_listener *listener, void *data) { &xwayland_view->surface_tree_destroy); } + xwayland_view->image_capture_scene_surface = + wlr_scene_surface_create(&xwayland_view->view.image_capture_scene->tree, xsurface->surface); + transaction_commit_dirty(); } diff --git a/sway/server.c b/sway/server.c index e8a6ce64f..6ca5906df 100644 --- a/sway/server.c +++ b/sway/server.c @@ -232,6 +232,21 @@ static void handle_renderer_lost(struct wl_listener *listener, void *data) { server->recreating_renderer = wl_event_loop_add_idle(server->wl_event_loop, do_renderer_recreate, server); } +static void handle_new_foreign_toplevel_capture_request(struct wl_listener *listener, void *data) { + struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request = data; + struct sway_view *view = request->toplevel_handle->data; + + if (view->image_capture_source == NULL) { + view->image_capture_source = wlr_ext_image_capture_source_v1_create_with_scene_node( + &view->image_capture_scene->tree.node, server.wl_event_loop, server.allocator, server.renderer); + if (view->image_capture_source == NULL) { + return; + } + } + + wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept(request, view->image_capture_source); +} + bool server_init(struct sway_server *server) { sway_log(SWAY_DEBUG, "Initializing Wayland server"); server->wl_display = wl_display_create(); @@ -395,6 +410,12 @@ bool server_init(struct sway_server *server) { wlr_content_type_manager_v1_create(server->wl_display, 1); wlr_fractional_scale_manager_v1_create(server->wl_display, 1); + server->ext_foreign_toplevel_image_capture_source_manager_v1 = + wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(server->wl_display, 1); + server->new_foreign_toplevel_capture_request.notify = handle_new_foreign_toplevel_capture_request; + wl_signal_add(&server->ext_foreign_toplevel_image_capture_source_manager_v1->events.new_request, + &server->new_foreign_toplevel_capture_request); + server->tearing_control_v1 = wlr_tearing_control_manager_v1_create(server->wl_display, 1); server->tearing_control_new_object.notify = handle_new_tearing_hint; @@ -488,6 +509,7 @@ void server_fini(struct sway_server *server) { wl_list_remove(&server->xdg_activation_v1_request_activate.link); wl_list_remove(&server->xdg_activation_v1_new_token.link); wl_list_remove(&server->request_set_cursor_shape.link); + wl_list_remove(&server->new_foreign_toplevel_capture_request.link); input_manager_finish(server->input); // TODO: free sway-specific resources diff --git a/sway/tree/view.c b/sway/tree/view.c index 742a66c2d..eca5fcc56 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -49,6 +49,11 @@ bool view_init(struct sway_view *view, enum sway_view_type type, failed = true; } + view->image_capture_scene = wlr_scene_create(); + if (view->image_capture_scene == NULL) { + failed = true; + } + if (failed) { wlr_scene_node_destroy(&view->scene_tree->node); return false; @@ -81,6 +86,7 @@ void view_destroy(struct sway_view *view) { list_free(view->executed_criteria); view_assign_ctx(view, NULL); + wlr_scene_node_destroy(&view->image_capture_scene->tree.node); wlr_scene_node_destroy(&view->scene_tree->node); if (view->impl->destroy) { view->impl->destroy(view); @@ -815,6 +821,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, }; view->ext_foreign_toplevel = wlr_ext_foreign_toplevel_handle_v1_create(server.foreign_toplevel_list, &foreign_toplevel_state); + view->ext_foreign_toplevel->data = view; view->foreign_toplevel = wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); From c2f08075ec00632293bbc63582c7f3ffd75441af Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 23 Jun 2025 09:43:48 +0200 Subject: [PATCH 256/361] tree/view: send event unconditionally in view_send_frame_done() Previously, we were using wl_signal_emit_mutable() directly instead of wlr_scene_buffer_send_frame_done(). This bypassed any visibility checks, which matters before a surface is mapped. Fixes flickering with an invalid size when launching new programs. Fixes: eb8acfd7b1cd ("Stop using wlr_scene_buffer_send_frame_done()") --- sway/tree/view.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index eca5fcc56..7bf185fea 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -1253,7 +1253,7 @@ static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer, if (scene_surface == NULL) { return; } - wlr_scene_surface_send_frame_done(scene_surface, when); + wlr_surface_send_frame_done(scene_surface->surface, when); } void view_send_frame_done(struct sway_view *view) { From 4f59eeef054bc18b25e72b2839707115a3265766 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Wed, 14 May 2025 12:07:06 +0200 Subject: [PATCH 257/361] Remove the temporary SUID warning A temporary SUID detection that would cause sway to exit was introduced when SUID operation was deprecated, intended to avoid cases where a user would not heed the deprecation notice, continued to use SUID and ended up with sway accidentally running as root. Remove the check, as the three years that have passed is sufficient time for users to discover the deprecation and adapt. We did not care if users intentionally want to run sway as root through SUID, we only wanted to avoid surprise root. --- sway/main.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/sway/main.c b/sway/main.c index 69efd6cb4..44d07679e 100644 --- a/sway/main.c +++ b/sway/main.c @@ -108,20 +108,6 @@ static void log_kernel(void) { pclose(f); } -static bool detect_suid(void) { - if (geteuid() != 0 && getegid() != 0) { - return false; - } - - if (getuid() == geteuid() && getgid() == getegid()) { - return false; - } - - sway_log(SWAY_ERROR, "SUID operation is no longer supported, refusing to start. " - "This check will be removed in a future release."); - return true; -} - static void restore_nofile_limit(void) { if (original_nofile_rlimit.rlim_cur == 0) { return; @@ -292,11 +278,6 @@ int main(int argc, char **argv) { } } - // SUID operation is deprecated, so block it for now. - if (detect_suid()) { - exit(EXIT_FAILURE); - } - // 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) { From e28e6484e8eafcac776ac0ec2bee8feddb19361a Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 26 May 2025 14:19:17 +0200 Subject: [PATCH 258/361] sway/tree: Simplify sway_node teardown A sway_node may end up being referenced in either a queued transaction, pending transaction or as a dirty node. To manage this, the transaction system has been responsible for destroying containers, workspaces and outputs at the end of their last referenced transaction. This significantly complicates the teardown flow of surfaces and outputs. Instead, remove the node from transactions and dirty lists so that the callsite can remove and free the node immediately. --- include/sway/desktop/transaction.h | 6 +++ include/sway/output.h | 2 - include/sway/tree/container.h | 2 - include/sway/tree/node.h | 1 - include/sway/tree/view.h | 4 -- include/sway/tree/workspace.h | 4 +- sway/commands/move.c | 14 +++++-- sway/desktop/launcher.c | 2 +- sway/desktop/output.c | 9 +++-- sway/desktop/transaction.c | 59 +++++++++++++++++++---------- sway/desktop/xdg_shell.c | 2 +- sway/desktop/xwayland.c | 2 +- sway/tree/container.c | 61 ++++++++++++------------------ sway/tree/output.c | 41 ++++++++------------ sway/tree/view.c | 27 ++++--------- sway/tree/workspace.c | 38 ++++++++----------- 16 files changed, 125 insertions(+), 149 deletions(-) diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index dd7edb7a5..80af4aaee 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h @@ -23,6 +23,7 @@ struct sway_transaction_instruction; struct sway_view; +struct sway_node; /** * Find all dirty containers, create and commit a transaction containing them, @@ -30,6 +31,11 @@ struct sway_view; */ void transaction_commit_dirty(void); +/** + * Remove a node that will be destroyed from transactions and dirty node lists. + */ +void transaction_remove_node(struct sway_node *node); + /* * Same as transaction_commit_dirty, but signalling that this is a * client-initiated change has already taken effect. diff --git a/include/sway/output.h b/include/sway/output.h index f6354e0ed..43c3dc9ad 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -84,8 +84,6 @@ struct sway_output *output_create(struct wlr_output *wlr_output); void output_destroy(struct sway_output *output); -void output_begin_destroy(struct sway_output *output); - struct sway_output *output_from_wlr_output(struct wlr_output *output); struct sway_output *output_get_in_direction(struct sway_output *reference, diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index e18fd00ac..4b2841042 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -155,8 +155,6 @@ struct sway_container *container_create(struct sway_view *view); void container_destroy(struct sway_container *con); -void container_begin_destroy(struct sway_container *con); - /** * Search a container's descendants a container based on test criteria. Returns * the first container that passes the test. diff --git a/include/sway/tree/node.h b/include/sway/tree/node.h index e2dbcdf05..7b6e936c7 100644 --- a/include/sway/tree/node.h +++ b/include/sway/tree/node.h @@ -39,7 +39,6 @@ struct sway_node { struct sway_transaction_instruction *instruction; size_t ntxnrefs; - bool destroying; // If true, indicates that the container has pending state that differs from // the current. diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 6151a0234..d999b5024 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -101,8 +101,6 @@ struct sway_view { struct wl_listener foreign_close_request; struct wl_listener foreign_destroy; - bool destroying; - list_t *executed_criteria; // struct criteria * union { @@ -296,8 +294,6 @@ bool view_init(struct sway_view *view, enum sway_view_type type, void view_destroy(struct sway_view *view); -void view_begin_destroy(struct sway_view *view); - /** * Map a view, ie. make it visible in the tree. * diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index 27ed649fd..77991b275 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -62,9 +62,7 @@ struct sway_workspace *workspace_create(struct sway_output *output, void workspace_destroy(struct sway_workspace *workspace); -void workspace_begin_destroy(struct sway_workspace *workspace); - -void workspace_consider_destroy(struct sway_workspace *ws); +bool workspace_consider_destroy(struct sway_workspace *ws); char *workspace_next_name(const char *output_name); diff --git a/sway/commands/move.c b/sway/commands/move.c index 90e8585b4..8e52e4bc8 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -611,14 +611,16 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, if (old_parent) { container_reap_empty(old_parent); } else if (old_ws) { - workspace_consider_destroy(old_ws); + if (workspace_consider_destroy(old_ws)) { + old_ws = NULL; + } } // arrange windows if (root->fullscreen_global) { arrange_root(); } else { - if (old_ws && !old_ws->node.destroying) { + if (old_ws) { arrange_workspace(old_ws); } arrange_node(node_get_parent(destination)); @@ -753,7 +755,9 @@ static struct cmd_results *cmd_move_in_direction( if (old_parent) { container_reap_empty(old_parent); } else if (old_ws) { - workspace_consider_destroy(old_ws); + if (workspace_consider_destroy(old_ws)) { + old_ws = NULL; + } } struct sway_workspace *new_ws = container->pending.workspace; @@ -761,7 +765,9 @@ static struct cmd_results *cmd_move_in_direction( if (root->fullscreen_global) { arrange_root(); } else { - arrange_workspace(old_ws); + if (old_ws) { + arrange_workspace(old_ws); + } if (new_ws != old_ws) { arrange_workspace(new_ws); } diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c index 2362e1ba0..5a330554a 100644 --- a/sway/desktop/launcher.c +++ b/sway/desktop/launcher.c @@ -153,7 +153,7 @@ static void ctx_handle_node_destroy(struct wl_listener *listener, void *data) { // same output free(ctx->fallback_name); ctx->fallback_name = strdup(ws->name); - if (!ws->output || ws->output->node.destroying) { + if (!ws->output) { // If the output is being destroyed it would be pointless to track // If the output is being disabled, we'll find out if it's still // disabled when we try to match it. diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 12dc9cc7a..eeb203ffc 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -428,7 +428,7 @@ void force_modeset(void) { apply_stored_output_configs(); } -static void begin_destroy(struct sway_output *output) { +static void output_teardown(struct sway_output *output) { wl_list_remove(&output->layout_destroy.link); wl_list_remove(&output->destroy.link); @@ -444,7 +444,6 @@ static void begin_destroy(struct sway_output *output) { if (output->enabled) { output_disable(output); } - output_begin_destroy(output); wl_list_remove(&output->link); output->wlr_output->data = NULL; @@ -453,17 +452,19 @@ static void begin_destroy(struct sway_output *output) { wl_event_source_remove(output->repaint_timer); output->repaint_timer = NULL; + output_destroy(output); + request_modeset(); } static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, destroy); - begin_destroy(output); + output_teardown(output); } static void handle_layout_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, layout_destroy); - begin_destroy(output); + output_teardown(output); } static void handle_present(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 325a30226..73e860ea1 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -59,22 +59,6 @@ static void transaction_destroy(struct sway_transaction *transaction) { if (node->instruction == instruction) { node->instruction = NULL; } - if (node->destroying && node->ntxnrefs == 0) { - switch (node->type) { - case N_ROOT: - sway_assert(false, "Never reached"); - break; - case N_OUTPUT: - output_destroy(node->sway_output); - break; - case N_WORKSPACE: - workspace_destroy(node->sway_workspace); - break; - case N_CONTAINER: - container_destroy(node->sway_container); - break; - } - } free(instruction); } list_free(transaction->instructions); @@ -239,7 +223,7 @@ static void apply_container_state(struct sway_container *container, if (view) { if (view->saved_surface_tree) { - if (!container->node.destroying || container->node.ntxnrefs == 1) { + if (container->node.ntxnrefs == 1) { view_remove_saved_buffer(view); } } @@ -788,9 +772,6 @@ static bool should_configure(struct sway_node *node, if (!node_is_view(node)) { return false; } - if (node->destroying) { - return false; - } if (!instruction->server_request) { return false; } @@ -825,7 +806,7 @@ static void transaction_commit(struct sway_transaction *transaction) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; struct sway_node *node = instruction->node; - bool hidden = node_is_view(node) && !node->destroying && + bool hidden = node_is_view(node) && !view_is_visible(node->sway_container->view); if (should_configure(node, instruction)) { instruction->serial = view_configure(node->sway_container->view, @@ -967,3 +948,39 @@ void transaction_commit_dirty(void) { void transaction_commit_dirty_client(void) { _transaction_commit_dirty(false); } + +static void _transaction_remove_node(struct sway_transaction *transaction, + struct sway_node *node) { + if (!transaction || !node) { + return; + } + for (int idx = 0; idx < transaction->instructions->length; idx++) { + struct sway_transaction_instruction *instruction = + transaction->instructions->items[idx]; + struct sway_node *n = instruction->node; + if (n != node) { + continue; + } + + n->ntxnrefs--; + n->instruction = NULL; + free(instruction); + list_del(transaction->instructions, idx); + idx--; + } +} + +void transaction_remove_node(struct sway_node *node) { + _transaction_remove_node(server.pending_transaction, node); + _transaction_remove_node(server.queued_transaction, node); + + for (int idx = 0; idx < server.dirty_nodes->length; idx++) { + struct sway_node *n = server.dirty_nodes->items[idx]; + if (n != node) { + continue; + } + n->dirty = false; + list_del(server.dirty_nodes, idx); + idx--; + } +} diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 3852806e4..be19a5d6b 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -535,7 +535,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { if (view->xdg_decoration) { view->xdg_decoration->view = NULL; } - view_begin_destroy(view); + view_destroy(view); } struct sway_view *view_from_wlr_xdg_surface( diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 76e63ce1f..fef0ff781 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -482,7 +482,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xwayland_view->associate.link); wl_list_remove(&xwayland_view->dissociate.link); wl_list_remove(&xwayland_view->override_redirect.link); - view_begin_destroy(&xwayland_view->view); + view_destroy(&xwayland_view->view); } static void handle_unmap(struct wl_listener *listener, void *data) { diff --git a/sway/tree/container.c b/sway/tree/container.c index 0385d7c17..2767bc231 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -55,7 +55,7 @@ static void handle_destroy( struct sway_container *con = wl_container_of( listener, con, output_handler_destroy); - container_begin_destroy(con); + container_destroy(con); } static bool handle_point_accepts_input( @@ -501,36 +501,6 @@ void container_update_title_bar(struct sway_container *con) { } void container_destroy(struct sway_container *con) { - if (!sway_assert(con->node.destroying, - "Tried to free container which wasn't marked as destroying")) { - return; - } - if (!sway_assert(con->node.ntxnrefs == 0, "Tried to free container " - "which is still referenced by transactions")) { - return; - } - free(con->title); - free(con->formatted_title); - free(con->title_format); - list_free(con->pending.children); - list_free(con->current.children); - - list_free_items_and_destroy(con->marks); - - if (con->view && con->view->container == con) { - con->view->container = NULL; - wlr_scene_node_destroy(&con->output_handler->node); - if (con->view->destroying) { - view_destroy(con->view); - } - } - - scene_node_disown_children(con->content_tree); - wlr_scene_node_destroy(&con->scene_tree->node); - free(con); -} - -void container_begin_destroy(struct sway_container *con) { if (con->view) { ipc_event_window(con, "close"); } @@ -547,9 +517,6 @@ void container_begin_destroy(struct sway_container *con) { container_end_mouse_operation(con); - con->node.destroying = true; - node_set_dirty(&con->node); - if (con->scratchpad) { root_scratchpad_remove_container(con); } @@ -567,6 +534,28 @@ void container_begin_destroy(struct sway_container *con) { wl_list_remove(&con->output_leave.link); wl_list_remove(&con->output_handler_destroy.link); } + + transaction_remove_node(&con->node); + if (!sway_assert(con->node.ntxnrefs == 0, "Tried to free container " + "which is still referenced by transactions")) { + return; + } + free(con->title); + free(con->formatted_title); + free(con->title_format); + list_free(con->pending.children); + list_free(con->current.children); + + list_free_items_and_destroy(con->marks); + + if (con->view && con->view->container == con) { + con->view->container = NULL; + wlr_scene_node_destroy(&con->output_handler->node); + } + + scene_node_disown_children(con->content_tree); + wlr_scene_node_destroy(&con->scene_tree->node); + free(con); } void container_reap_empty(struct sway_container *con) { @@ -579,7 +568,7 @@ void container_reap_empty(struct sway_container *con) { return; } struct sway_container *parent = con->pending.parent; - container_begin_destroy(con); + container_destroy(con); con = parent; } if (ws) { @@ -595,7 +584,7 @@ struct sway_container *container_flatten(struct sway_container *container) { struct sway_container *child = container->pending.children->items[0]; struct sway_container *parent = container->pending.parent; container_replace(container, child); - container_begin_destroy(container); + container_destroy(container); container = parent; } return container; diff --git a/sway/tree/output.c b/sway/tree/output.c index b02c1a2ce..b969c8105 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -2,6 +2,7 @@ #include #include #include +#include "sway/desktop/transaction.h" #include "sway/ipc-server.h" #include "sway/layers.h" #include "sway/output.h" @@ -235,7 +236,7 @@ static void output_evacuate(struct sway_output *output) { } if (workspace_num_sticky_containers(workspace) == 0) { - workspace_begin_destroy(workspace); + workspace_destroy(workspace); continue; } } @@ -253,27 +254,6 @@ static void output_evacuate(struct sway_output *output) { } } -void output_destroy(struct sway_output *output) { - if (!sway_assert(output->node.destroying, - "Tried to free output which wasn't marked as destroying")) { - return; - } - if (!sway_assert(output->wlr_output == NULL, - "Tried to free output which still had a wlr_output")) { - return; - } - if (!sway_assert(output->node.ntxnrefs == 0, "Tried to free output " - "which is still referenced by transactions")) { - return; - } - - destroy_scene_layers(output); - list_free(output->workspaces); - list_free(output->current.workspaces); - wlr_color_transform_unref(output->color_transform); - free(output); -} - void output_disable(struct sway_output *output) { if (!sway_assert(output->enabled, "Expected an enabled output")) { return; @@ -295,15 +275,24 @@ void output_disable(struct sway_output *output) { output_evacuate(output); } -void output_begin_destroy(struct sway_output *output) { +void output_destroy(struct sway_output *output) { if (!sway_assert(!output->enabled, "Expected a disabled output")) { return; } - sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); wl_signal_emit_mutable(&output->node.events.destroy, &output->node); - output->node.destroying = true; - node_set_dirty(&output->node); + transaction_remove_node(&output->node); + + if (!sway_assert(output->node.ntxnrefs == 0, "Tried to free output " + "which is still referenced by transactions")) { + return; + } + + destroy_scene_layers(output); + list_free(output->workspaces); + list_free(output->current.workspaces); + wlr_color_transform_unref(output->color_transform); + free(output); } struct sway_output *output_from_wlr_output(struct wlr_output *output) { diff --git a/sway/tree/view.c b/sway/tree/view.c index 7bf185fea..d4f72211b 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -70,11 +70,10 @@ bool view_init(struct sway_view *view, enum sway_view_type type, } void view_destroy(struct sway_view *view) { - if (!sway_assert(view->surface == NULL, "Tried to free mapped view")) { + if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) { return; } - if (!sway_assert(view->destroying, - "Tried to free view which wasn't marked as destroying")) { + if (!sway_assert(view->surface == NULL, "Tried to free mapped view")) { return; } if (!sway_assert(view->container == NULL, @@ -95,17 +94,6 @@ void view_destroy(struct sway_view *view) { } } -void view_begin_destroy(struct sway_view *view) { - if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) { - return; - } - view->destroying = true; - - if (!view->container) { - view_destroy(view); - } -} - const char *view_get_title(struct sway_view *view) { if (view->impl->get_string_prop) { return view->impl->get_string_prop(view, VIEW_PROP_TITLE); @@ -935,17 +923,19 @@ void view_unmap(struct sway_view *view) { struct sway_container *parent = view->container->pending.parent; struct sway_workspace *ws = view->container->pending.workspace; - container_begin_destroy(view->container); + container_destroy(view->container); if (parent) { container_reap_empty(parent); } else if (ws) { - workspace_consider_destroy(ws); + if (workspace_consider_destroy(ws)) { + ws = NULL; + } } if (root->fullscreen_global) { // Container may have been a child of the root fullscreen container arrange_root(); - } else if (ws && !ws->node.destroying) { + } else if (ws) { arrange_workspace(ws); workspace_detect_urgent(ws); } @@ -1099,9 +1089,6 @@ void view_update_title(struct sway_view *view, bool force) { } bool view_is_visible(struct sway_view *view) { - if (view->container->node.destroying) { - return false; - } struct sway_workspace *workspace = view->container->pending.workspace; if (!workspace && view->container->pending.fullscreen_mode != FULLSCREEN_GLOBAL) { bool fs_global_descendant = false; diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index f2be4cd13..44256bcff 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -5,6 +5,7 @@ #include #include #include "stringop.h" +#include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" @@ -134,10 +135,15 @@ struct sway_workspace *workspace_create(struct sway_output *output, } void workspace_destroy(struct sway_workspace *workspace) { - if (!sway_assert(workspace->node.destroying, - "Tried to free workspace which wasn't marked as destroying")) { - return; + sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); + ipc_event_workspace(NULL, workspace, "empty"); // intentional + wl_signal_emit_mutable(&workspace->node.events.destroy, &workspace->node); + + if (workspace->output) { + workspace_detach(workspace); } + transaction_remove_node(&workspace->node); + if (!sway_assert(workspace->node.ntxnrefs == 0, "Tried to free workspace " "which is still referenced by transactions")) { return; @@ -158,36 +164,25 @@ void workspace_destroy(struct sway_workspace *workspace) { free(workspace); } -void workspace_begin_destroy(struct sway_workspace *workspace) { - sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); - ipc_event_workspace(NULL, workspace, "empty"); // intentional - wl_signal_emit_mutable(&workspace->node.events.destroy, &workspace->node); - - if (workspace->output) { - workspace_detach(workspace); - } - workspace->node.destroying = true; - node_set_dirty(&workspace->node); -} - -void workspace_consider_destroy(struct sway_workspace *ws) { +bool workspace_consider_destroy(struct sway_workspace *ws) { if (ws->tiling->length || ws->floating->length) { - return; + return false; } if (ws->output && output_get_active_workspace(ws->output) == ws) { - return; + return false; } struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { struct sway_node *node = seat_get_focus_inactive(seat, &root->node); if (node == &ws->node) { - return; + return false; } } - workspace_begin_destroy(ws); + workspace_destroy(ws); + return true; } static bool workspace_valid_on_output(const char *output_name, @@ -596,9 +591,6 @@ bool workspace_switch(struct sway_workspace *workspace) { } bool workspace_is_visible(struct sway_workspace *ws) { - if (ws->node.destroying) { - return false; - } return output_get_active_workspace(ws->output) == ws; } From 3d6b9a28480a398e3af869d4051181f98a042022 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 31 May 2025 00:02:56 +0200 Subject: [PATCH 259/361] tree/container: Remove child from all lists When a container is detached, we need to remove it from any lists it may be part of. We use container_get_siblings to obtain the relevant list, find our entry and remove it. If the container is in a later list than the one returned by container_get_siblings, or is in multiple lists for some reason, container_detach will fail to remove the container, leaving a dangling pointer when the container is freed. Instead of calling container_get_siblings, check and remove the container from all lists. --- sway/tree/container.c | 59 +++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 2767bc231..af68e22fd 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -500,17 +500,19 @@ void container_update_title_bar(struct sway_container *con) { container_arrange_title_bar(con); } +static void container_remove_from_siblings(struct sway_container *child, bool pending); + void container_destroy(struct sway_container *con) { if (con->view) { ipc_event_window(con, "close"); } // The workspace must have the fullscreen pointer cleared so that the // seat code can find an appropriate new focus. - if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE && con->pending.workspace) { + if (con->pending.workspace && con->pending.workspace->fullscreen == con) { con->pending.workspace->fullscreen = NULL; } - if (con->scratchpad && con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { - container_fullscreen_disable(con); + if (con->current.workspace && con->current.workspace->fullscreen == con) { + con->current.workspace->fullscreen = NULL; } wl_signal_emit_mutable(&con->node.events.destroy, &con->node); @@ -524,11 +526,15 @@ void container_destroy(struct sway_container *con) { if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { container_fullscreen_disable(con); } - - if (con->pending.parent || con->pending.workspace) { - container_detach(con); + if (root->fullscreen_global == con) { + root->fullscreen_global = NULL; } + container_detach(con); + + // Also remove from current children lists as we are freeing the container + container_remove_from_siblings(con, false); + if (con->view && con->view->container == con) { wl_list_remove(&con->output_enter.link); wl_list_remove(&con->output_leave.link); @@ -1490,25 +1496,40 @@ void container_add_child(struct sway_container *parent, node_set_dirty(&parent->node); } -void container_detach(struct sway_container *child) { - if (child->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { - child->pending.workspace->fullscreen = NULL; - } - if (child->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { - root->fullscreen_global = NULL; - } +static void container_remove_from_siblings(struct sway_container *child, bool pending) { + struct sway_container_state *state = pending ? &child->pending : &child->current; - struct sway_container *old_parent = child->pending.parent; - struct sway_workspace *old_workspace = child->pending.workspace; - list_t *siblings = container_get_siblings(child); - if (siblings) { - int index = list_find(siblings, child); + // Remove from all possible children lists + list_t *siblings[] = { + state->parent ? state->parent->pending.children : NULL, + state->workspace ? state->workspace->tiling : NULL, + state->workspace ? state->workspace->floating : NULL, + }; + for (size_t idx = 0; idx < sizeof(siblings) / sizeof(*siblings); idx++) { + if (!siblings[idx]) { + continue; + } + int index = list_find(siblings[idx], child); if (index != -1) { - list_del(siblings, index); + list_del(siblings[idx], index); } } child->pending.parent = NULL; child->pending.workspace = NULL; +} + +void container_detach(struct sway_container *child) { + struct sway_container *old_parent = child->pending.parent; + struct sway_workspace *old_workspace = child->pending.workspace; + + if (root->fullscreen_global == child) { + root->fullscreen_global = NULL; + } + if (old_workspace && old_workspace->fullscreen == child) { + old_workspace->fullscreen = NULL; + } + + container_remove_from_siblings(child, true); container_for_each_child(child, set_workspace, NULL); if (old_parent) { From 0cd45d4ad265c04e2ec9db050d2d176992593693 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 28 Jun 2025 10:51:41 +0200 Subject: [PATCH 260/361] Revert "tree/container: Remove child from all lists" This reverts commit 3d6b9a28480a398e3af869d4051181f98a042022. --- sway/tree/container.c | 59 ++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 40 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index af68e22fd..2767bc231 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -500,19 +500,17 @@ void container_update_title_bar(struct sway_container *con) { container_arrange_title_bar(con); } -static void container_remove_from_siblings(struct sway_container *child, bool pending); - void container_destroy(struct sway_container *con) { if (con->view) { ipc_event_window(con, "close"); } // The workspace must have the fullscreen pointer cleared so that the // seat code can find an appropriate new focus. - if (con->pending.workspace && con->pending.workspace->fullscreen == con) { + if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE && con->pending.workspace) { con->pending.workspace->fullscreen = NULL; } - if (con->current.workspace && con->current.workspace->fullscreen == con) { - con->current.workspace->fullscreen = NULL; + if (con->scratchpad && con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { + container_fullscreen_disable(con); } wl_signal_emit_mutable(&con->node.events.destroy, &con->node); @@ -526,15 +524,11 @@ void container_destroy(struct sway_container *con) { if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { container_fullscreen_disable(con); } - if (root->fullscreen_global == con) { - root->fullscreen_global = NULL; + + if (con->pending.parent || con->pending.workspace) { + container_detach(con); } - container_detach(con); - - // Also remove from current children lists as we are freeing the container - container_remove_from_siblings(con, false); - if (con->view && con->view->container == con) { wl_list_remove(&con->output_enter.link); wl_list_remove(&con->output_leave.link); @@ -1496,40 +1490,25 @@ void container_add_child(struct sway_container *parent, node_set_dirty(&parent->node); } -static void container_remove_from_siblings(struct sway_container *child, bool pending) { - struct sway_container_state *state = pending ? &child->pending : &child->current; +void container_detach(struct sway_container *child) { + if (child->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { + child->pending.workspace->fullscreen = NULL; + } + if (child->pending.fullscreen_mode == FULLSCREEN_GLOBAL) { + root->fullscreen_global = NULL; + } - // Remove from all possible children lists - list_t *siblings[] = { - state->parent ? state->parent->pending.children : NULL, - state->workspace ? state->workspace->tiling : NULL, - state->workspace ? state->workspace->floating : NULL, - }; - for (size_t idx = 0; idx < sizeof(siblings) / sizeof(*siblings); idx++) { - if (!siblings[idx]) { - continue; - } - int index = list_find(siblings[idx], child); + struct sway_container *old_parent = child->pending.parent; + struct sway_workspace *old_workspace = child->pending.workspace; + list_t *siblings = container_get_siblings(child); + if (siblings) { + int index = list_find(siblings, child); if (index != -1) { - list_del(siblings[idx], index); + list_del(siblings, index); } } child->pending.parent = NULL; child->pending.workspace = NULL; -} - -void container_detach(struct sway_container *child) { - struct sway_container *old_parent = child->pending.parent; - struct sway_workspace *old_workspace = child->pending.workspace; - - if (root->fullscreen_global == child) { - root->fullscreen_global = NULL; - } - if (old_workspace && old_workspace->fullscreen == child) { - old_workspace->fullscreen = NULL; - } - - container_remove_from_siblings(child, true); container_for_each_child(child, set_workspace, NULL); if (old_parent) { From 56f2db062daa64a0df77a83823d85fec7c3a9f46 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 28 Jun 2025 10:51:51 +0200 Subject: [PATCH 261/361] Revert "sway/tree: Simplify sway_node teardown" This reverts commit e28e6484e8eafcac776ac0ec2bee8feddb19361a. This change tried to remove nodes from all points of reference to allow immediate destruction. However, it missed things like the children lists cloned by transaction states of parent nodes. Adding all that extra cleanup would not be in the spirit of a PR claiming to simplify teardown. Let's wait for someone to come up with a cleaner approach instead. Fixes: https://github.com/swaywm/sway/pull/8738 --- include/sway/desktop/transaction.h | 6 --- include/sway/output.h | 2 + include/sway/tree/container.h | 2 + include/sway/tree/node.h | 1 + include/sway/tree/view.h | 4 ++ include/sway/tree/workspace.h | 4 +- sway/commands/move.c | 14 ++----- sway/desktop/launcher.c | 2 +- sway/desktop/output.c | 9 ++--- sway/desktop/transaction.c | 59 ++++++++++------------------- sway/desktop/xdg_shell.c | 2 +- sway/desktop/xwayland.c | 2 +- sway/tree/container.c | 61 ++++++++++++++++++------------ sway/tree/output.c | 41 ++++++++++++-------- sway/tree/view.c | 27 +++++++++---- sway/tree/workspace.c | 38 +++++++++++-------- 16 files changed, 149 insertions(+), 125 deletions(-) diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index 80af4aaee..dd7edb7a5 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h @@ -23,7 +23,6 @@ struct sway_transaction_instruction; struct sway_view; -struct sway_node; /** * Find all dirty containers, create and commit a transaction containing them, @@ -31,11 +30,6 @@ struct sway_node; */ void transaction_commit_dirty(void); -/** - * Remove a node that will be destroyed from transactions and dirty node lists. - */ -void transaction_remove_node(struct sway_node *node); - /* * Same as transaction_commit_dirty, but signalling that this is a * client-initiated change has already taken effect. diff --git a/include/sway/output.h b/include/sway/output.h index 43c3dc9ad..f6354e0ed 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -84,6 +84,8 @@ struct sway_output *output_create(struct wlr_output *wlr_output); void output_destroy(struct sway_output *output); +void output_begin_destroy(struct sway_output *output); + struct sway_output *output_from_wlr_output(struct wlr_output *output); struct sway_output *output_get_in_direction(struct sway_output *reference, diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 4b2841042..e18fd00ac 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -155,6 +155,8 @@ struct sway_container *container_create(struct sway_view *view); void container_destroy(struct sway_container *con); +void container_begin_destroy(struct sway_container *con); + /** * Search a container's descendants a container based on test criteria. Returns * the first container that passes the test. diff --git a/include/sway/tree/node.h b/include/sway/tree/node.h index 7b6e936c7..e2dbcdf05 100644 --- a/include/sway/tree/node.h +++ b/include/sway/tree/node.h @@ -39,6 +39,7 @@ struct sway_node { struct sway_transaction_instruction *instruction; size_t ntxnrefs; + bool destroying; // If true, indicates that the container has pending state that differs from // the current. diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index d999b5024..6151a0234 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -101,6 +101,8 @@ struct sway_view { struct wl_listener foreign_close_request; struct wl_listener foreign_destroy; + bool destroying; + list_t *executed_criteria; // struct criteria * union { @@ -294,6 +296,8 @@ bool view_init(struct sway_view *view, enum sway_view_type type, void view_destroy(struct sway_view *view); +void view_begin_destroy(struct sway_view *view); + /** * Map a view, ie. make it visible in the tree. * diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index 77991b275..27ed649fd 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -62,7 +62,9 @@ struct sway_workspace *workspace_create(struct sway_output *output, void workspace_destroy(struct sway_workspace *workspace); -bool workspace_consider_destroy(struct sway_workspace *ws); +void workspace_begin_destroy(struct sway_workspace *workspace); + +void workspace_consider_destroy(struct sway_workspace *ws); char *workspace_next_name(const char *output_name); diff --git a/sway/commands/move.c b/sway/commands/move.c index 8e52e4bc8..90e8585b4 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -611,16 +611,14 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, if (old_parent) { container_reap_empty(old_parent); } else if (old_ws) { - if (workspace_consider_destroy(old_ws)) { - old_ws = NULL; - } + workspace_consider_destroy(old_ws); } // arrange windows if (root->fullscreen_global) { arrange_root(); } else { - if (old_ws) { + if (old_ws && !old_ws->node.destroying) { arrange_workspace(old_ws); } arrange_node(node_get_parent(destination)); @@ -755,9 +753,7 @@ static struct cmd_results *cmd_move_in_direction( if (old_parent) { container_reap_empty(old_parent); } else if (old_ws) { - if (workspace_consider_destroy(old_ws)) { - old_ws = NULL; - } + workspace_consider_destroy(old_ws); } struct sway_workspace *new_ws = container->pending.workspace; @@ -765,9 +761,7 @@ static struct cmd_results *cmd_move_in_direction( if (root->fullscreen_global) { arrange_root(); } else { - if (old_ws) { - arrange_workspace(old_ws); - } + arrange_workspace(old_ws); if (new_ws != old_ws) { arrange_workspace(new_ws); } diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c index 5a330554a..2362e1ba0 100644 --- a/sway/desktop/launcher.c +++ b/sway/desktop/launcher.c @@ -153,7 +153,7 @@ static void ctx_handle_node_destroy(struct wl_listener *listener, void *data) { // same output free(ctx->fallback_name); ctx->fallback_name = strdup(ws->name); - if (!ws->output) { + if (!ws->output || ws->output->node.destroying) { // If the output is being destroyed it would be pointless to track // If the output is being disabled, we'll find out if it's still // disabled when we try to match it. diff --git a/sway/desktop/output.c b/sway/desktop/output.c index eeb203ffc..12dc9cc7a 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -428,7 +428,7 @@ void force_modeset(void) { apply_stored_output_configs(); } -static void output_teardown(struct sway_output *output) { +static void begin_destroy(struct sway_output *output) { wl_list_remove(&output->layout_destroy.link); wl_list_remove(&output->destroy.link); @@ -444,6 +444,7 @@ static void output_teardown(struct sway_output *output) { if (output->enabled) { output_disable(output); } + output_begin_destroy(output); wl_list_remove(&output->link); output->wlr_output->data = NULL; @@ -452,19 +453,17 @@ static void output_teardown(struct sway_output *output) { wl_event_source_remove(output->repaint_timer); output->repaint_timer = NULL; - output_destroy(output); - request_modeset(); } static void handle_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, destroy); - output_teardown(output); + begin_destroy(output); } static void handle_layout_destroy(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, layout_destroy); - output_teardown(output); + begin_destroy(output); } static void handle_present(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 73e860ea1..325a30226 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -59,6 +59,22 @@ static void transaction_destroy(struct sway_transaction *transaction) { if (node->instruction == instruction) { node->instruction = NULL; } + if (node->destroying && node->ntxnrefs == 0) { + switch (node->type) { + case N_ROOT: + sway_assert(false, "Never reached"); + break; + case N_OUTPUT: + output_destroy(node->sway_output); + break; + case N_WORKSPACE: + workspace_destroy(node->sway_workspace); + break; + case N_CONTAINER: + container_destroy(node->sway_container); + break; + } + } free(instruction); } list_free(transaction->instructions); @@ -223,7 +239,7 @@ static void apply_container_state(struct sway_container *container, if (view) { if (view->saved_surface_tree) { - if (container->node.ntxnrefs == 1) { + if (!container->node.destroying || container->node.ntxnrefs == 1) { view_remove_saved_buffer(view); } } @@ -772,6 +788,9 @@ static bool should_configure(struct sway_node *node, if (!node_is_view(node)) { return false; } + if (node->destroying) { + return false; + } if (!instruction->server_request) { return false; } @@ -806,7 +825,7 @@ static void transaction_commit(struct sway_transaction *transaction) { struct sway_transaction_instruction *instruction = transaction->instructions->items[i]; struct sway_node *node = instruction->node; - bool hidden = node_is_view(node) && + bool hidden = node_is_view(node) && !node->destroying && !view_is_visible(node->sway_container->view); if (should_configure(node, instruction)) { instruction->serial = view_configure(node->sway_container->view, @@ -948,39 +967,3 @@ void transaction_commit_dirty(void) { void transaction_commit_dirty_client(void) { _transaction_commit_dirty(false); } - -static void _transaction_remove_node(struct sway_transaction *transaction, - struct sway_node *node) { - if (!transaction || !node) { - return; - } - for (int idx = 0; idx < transaction->instructions->length; idx++) { - struct sway_transaction_instruction *instruction = - transaction->instructions->items[idx]; - struct sway_node *n = instruction->node; - if (n != node) { - continue; - } - - n->ntxnrefs--; - n->instruction = NULL; - free(instruction); - list_del(transaction->instructions, idx); - idx--; - } -} - -void transaction_remove_node(struct sway_node *node) { - _transaction_remove_node(server.pending_transaction, node); - _transaction_remove_node(server.queued_transaction, node); - - for (int idx = 0; idx < server.dirty_nodes->length; idx++) { - struct sway_node *n = server.dirty_nodes->items[idx]; - if (n != node) { - continue; - } - n->dirty = false; - list_del(server.dirty_nodes, idx); - idx--; - } -} diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index be19a5d6b..3852806e4 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -535,7 +535,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { if (view->xdg_decoration) { view->xdg_decoration->view = NULL; } - view_destroy(view); + view_begin_destroy(view); } struct sway_view *view_from_wlr_xdg_surface( diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index fef0ff781..76e63ce1f 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -482,7 +482,7 @@ static void handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&xwayland_view->associate.link); wl_list_remove(&xwayland_view->dissociate.link); wl_list_remove(&xwayland_view->override_redirect.link); - view_destroy(&xwayland_view->view); + view_begin_destroy(&xwayland_view->view); } static void handle_unmap(struct wl_listener *listener, void *data) { diff --git a/sway/tree/container.c b/sway/tree/container.c index 2767bc231..0385d7c17 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -55,7 +55,7 @@ static void handle_destroy( struct sway_container *con = wl_container_of( listener, con, output_handler_destroy); - container_destroy(con); + container_begin_destroy(con); } static bool handle_point_accepts_input( @@ -501,6 +501,36 @@ void container_update_title_bar(struct sway_container *con) { } void container_destroy(struct sway_container *con) { + if (!sway_assert(con->node.destroying, + "Tried to free container which wasn't marked as destroying")) { + return; + } + if (!sway_assert(con->node.ntxnrefs == 0, "Tried to free container " + "which is still referenced by transactions")) { + return; + } + free(con->title); + free(con->formatted_title); + free(con->title_format); + list_free(con->pending.children); + list_free(con->current.children); + + list_free_items_and_destroy(con->marks); + + if (con->view && con->view->container == con) { + con->view->container = NULL; + wlr_scene_node_destroy(&con->output_handler->node); + if (con->view->destroying) { + view_destroy(con->view); + } + } + + scene_node_disown_children(con->content_tree); + wlr_scene_node_destroy(&con->scene_tree->node); + free(con); +} + +void container_begin_destroy(struct sway_container *con) { if (con->view) { ipc_event_window(con, "close"); } @@ -517,6 +547,9 @@ void container_destroy(struct sway_container *con) { container_end_mouse_operation(con); + con->node.destroying = true; + node_set_dirty(&con->node); + if (con->scratchpad) { root_scratchpad_remove_container(con); } @@ -534,28 +567,6 @@ void container_destroy(struct sway_container *con) { wl_list_remove(&con->output_leave.link); wl_list_remove(&con->output_handler_destroy.link); } - - transaction_remove_node(&con->node); - if (!sway_assert(con->node.ntxnrefs == 0, "Tried to free container " - "which is still referenced by transactions")) { - return; - } - free(con->title); - free(con->formatted_title); - free(con->title_format); - list_free(con->pending.children); - list_free(con->current.children); - - list_free_items_and_destroy(con->marks); - - if (con->view && con->view->container == con) { - con->view->container = NULL; - wlr_scene_node_destroy(&con->output_handler->node); - } - - scene_node_disown_children(con->content_tree); - wlr_scene_node_destroy(&con->scene_tree->node); - free(con); } void container_reap_empty(struct sway_container *con) { @@ -568,7 +579,7 @@ void container_reap_empty(struct sway_container *con) { return; } struct sway_container *parent = con->pending.parent; - container_destroy(con); + container_begin_destroy(con); con = parent; } if (ws) { @@ -584,7 +595,7 @@ struct sway_container *container_flatten(struct sway_container *container) { struct sway_container *child = container->pending.children->items[0]; struct sway_container *parent = container->pending.parent; container_replace(container, child); - container_destroy(container); + container_begin_destroy(container); container = parent; } return container; diff --git a/sway/tree/output.c b/sway/tree/output.c index b969c8105..b02c1a2ce 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -2,7 +2,6 @@ #include #include #include -#include "sway/desktop/transaction.h" #include "sway/ipc-server.h" #include "sway/layers.h" #include "sway/output.h" @@ -236,7 +235,7 @@ static void output_evacuate(struct sway_output *output) { } if (workspace_num_sticky_containers(workspace) == 0) { - workspace_destroy(workspace); + workspace_begin_destroy(workspace); continue; } } @@ -254,6 +253,27 @@ static void output_evacuate(struct sway_output *output) { } } +void output_destroy(struct sway_output *output) { + if (!sway_assert(output->node.destroying, + "Tried to free output which wasn't marked as destroying")) { + return; + } + if (!sway_assert(output->wlr_output == NULL, + "Tried to free output which still had a wlr_output")) { + return; + } + if (!sway_assert(output->node.ntxnrefs == 0, "Tried to free output " + "which is still referenced by transactions")) { + return; + } + + destroy_scene_layers(output); + list_free(output->workspaces); + list_free(output->current.workspaces); + wlr_color_transform_unref(output->color_transform); + free(output); +} + void output_disable(struct sway_output *output) { if (!sway_assert(output->enabled, "Expected an enabled output")) { return; @@ -275,24 +295,15 @@ void output_disable(struct sway_output *output) { output_evacuate(output); } -void output_destroy(struct sway_output *output) { +void output_begin_destroy(struct sway_output *output) { if (!sway_assert(!output->enabled, "Expected a disabled output")) { return; } + sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); wl_signal_emit_mutable(&output->node.events.destroy, &output->node); - transaction_remove_node(&output->node); - - if (!sway_assert(output->node.ntxnrefs == 0, "Tried to free output " - "which is still referenced by transactions")) { - return; - } - - destroy_scene_layers(output); - list_free(output->workspaces); - list_free(output->current.workspaces); - wlr_color_transform_unref(output->color_transform); - free(output); + output->node.destroying = true; + node_set_dirty(&output->node); } struct sway_output *output_from_wlr_output(struct wlr_output *output) { diff --git a/sway/tree/view.c b/sway/tree/view.c index d4f72211b..7bf185fea 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -70,10 +70,11 @@ bool view_init(struct sway_view *view, enum sway_view_type type, } void view_destroy(struct sway_view *view) { - if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) { + if (!sway_assert(view->surface == NULL, "Tried to free mapped view")) { return; } - if (!sway_assert(view->surface == NULL, "Tried to free mapped view")) { + if (!sway_assert(view->destroying, + "Tried to free view which wasn't marked as destroying")) { return; } if (!sway_assert(view->container == NULL, @@ -94,6 +95,17 @@ void view_destroy(struct sway_view *view) { } } +void view_begin_destroy(struct sway_view *view) { + if (!sway_assert(view->surface == NULL, "Tried to destroy a mapped view")) { + return; + } + view->destroying = true; + + if (!view->container) { + view_destroy(view); + } +} + const char *view_get_title(struct sway_view *view) { if (view->impl->get_string_prop) { return view->impl->get_string_prop(view, VIEW_PROP_TITLE); @@ -923,19 +935,17 @@ void view_unmap(struct sway_view *view) { struct sway_container *parent = view->container->pending.parent; struct sway_workspace *ws = view->container->pending.workspace; - container_destroy(view->container); + container_begin_destroy(view->container); if (parent) { container_reap_empty(parent); } else if (ws) { - if (workspace_consider_destroy(ws)) { - ws = NULL; - } + workspace_consider_destroy(ws); } if (root->fullscreen_global) { // Container may have been a child of the root fullscreen container arrange_root(); - } else if (ws) { + } else if (ws && !ws->node.destroying) { arrange_workspace(ws); workspace_detect_urgent(ws); } @@ -1089,6 +1099,9 @@ void view_update_title(struct sway_view *view, bool force) { } bool view_is_visible(struct sway_view *view) { + if (view->container->node.destroying) { + return false; + } struct sway_workspace *workspace = view->container->pending.workspace; if (!workspace && view->container->pending.fullscreen_mode != FULLSCREEN_GLOBAL) { bool fs_global_descendant = false; diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 44256bcff..f2be4cd13 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -5,7 +5,6 @@ #include #include #include "stringop.h" -#include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" @@ -135,15 +134,10 @@ struct sway_workspace *workspace_create(struct sway_output *output, } void workspace_destroy(struct sway_workspace *workspace) { - sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); - ipc_event_workspace(NULL, workspace, "empty"); // intentional - wl_signal_emit_mutable(&workspace->node.events.destroy, &workspace->node); - - if (workspace->output) { - workspace_detach(workspace); + if (!sway_assert(workspace->node.destroying, + "Tried to free workspace which wasn't marked as destroying")) { + return; } - transaction_remove_node(&workspace->node); - if (!sway_assert(workspace->node.ntxnrefs == 0, "Tried to free workspace " "which is still referenced by transactions")) { return; @@ -164,25 +158,36 @@ void workspace_destroy(struct sway_workspace *workspace) { free(workspace); } -bool workspace_consider_destroy(struct sway_workspace *ws) { +void workspace_begin_destroy(struct sway_workspace *workspace) { + sway_log(SWAY_DEBUG, "Destroying workspace '%s'", workspace->name); + ipc_event_workspace(NULL, workspace, "empty"); // intentional + wl_signal_emit_mutable(&workspace->node.events.destroy, &workspace->node); + + if (workspace->output) { + workspace_detach(workspace); + } + workspace->node.destroying = true; + node_set_dirty(&workspace->node); +} + +void workspace_consider_destroy(struct sway_workspace *ws) { if (ws->tiling->length || ws->floating->length) { - return false; + return; } if (ws->output && output_get_active_workspace(ws->output) == ws) { - return false; + return; } struct sway_seat *seat; wl_list_for_each(seat, &server.input->seats, link) { struct sway_node *node = seat_get_focus_inactive(seat, &root->node); if (node == &ws->node) { - return false; + return; } } - workspace_destroy(ws); - return true; + workspace_begin_destroy(ws); } static bool workspace_valid_on_output(const char *output_name, @@ -591,6 +596,9 @@ bool workspace_switch(struct sway_workspace *workspace) { } bool workspace_is_visible(struct sway_workspace *ws) { + if (ws->node.destroying) { + return false; + } return output_get_active_workspace(ws->output) == ws; } From a1ac2a2e93ffb3341253af30603cf16483d766bb Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 6 May 2025 09:20:46 +0200 Subject: [PATCH 262/361] Drop sway_output.events.disable In general wl_signal isn't well-suited for Sway: Sway doesn't need any modularity, and signals make it trickier to track down exactly what happens down the stack. Replace Sway's output disable signal with a simple list tracking for the only user. --- include/sway/layers.h | 5 ++++- include/sway/output.h | 5 +---- sway/desktop/layer_shell.c | 22 ++++++++++------------ sway/tree/output.c | 5 ++--- 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/include/sway/layers.h b/include/sway/layers.h index fd6384e0e..27b5dde1b 100644 --- a/include/sway/layers.h +++ b/include/sway/layers.h @@ -9,7 +9,6 @@ struct sway_layer_surface { struct wl_listener map; struct wl_listener unmap; struct wl_listener surface_commit; - struct wl_listener output_destroy; struct wl_listener node_destroy; struct wl_listener new_popup; @@ -19,6 +18,8 @@ struct sway_layer_surface { struct sway_popup_desc desc; struct sway_output *output; + struct wl_list link; // sway_output.layer_surfaces + struct wlr_scene_layer_surface_v1 *scene; struct wlr_scene_tree *tree; struct wlr_layer_surface_v1 *layer_surface; @@ -41,4 +42,6 @@ struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( void arrange_layers(struct sway_output *output); +void destroy_layers(struct sway_output *output); + #endif diff --git a/include/sway/output.h b/include/sway/output.h index f6354e0ed..756a0ec22 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -52,6 +52,7 @@ struct sway_output { bool enabled; list_t *workspaces; + struct wl_list layer_surfaces; // sway_layer_surface.link struct sway_output_state current; @@ -61,10 +62,6 @@ struct sway_output { struct wl_listener frame; struct wl_listener request_state; - struct { - struct wl_signal disable; - } events; - struct wlr_color_transform *color_transform; struct timespec last_presentation; diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 08b5b1de2..8c54d71aa 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -216,14 +216,6 @@ static struct sway_layer_surface *find_mapped_layer_by_client( return NULL; } -static void handle_output_destroy(struct wl_listener *listener, void *data) { - struct sway_layer_surface *layer = - wl_container_of(listener, layer, output_destroy); - - layer->output = NULL; - wlr_layer_surface_v1_destroy(layer->layer_surface); -} - static void handle_node_destroy(struct wl_listener *listener, void *data) { struct sway_layer_surface *layer = wl_container_of(listener, layer, node_destroy); @@ -257,10 +249,10 @@ static void handle_node_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&layer->surface_commit.link); wl_list_remove(&layer->node_destroy.link); wl_list_remove(&layer->new_popup.link); - wl_list_remove(&layer->output_destroy.link); layer->layer_surface->data = NULL; + wl_list_remove(&layer->link); free(layer); } @@ -475,6 +467,7 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { } surface->output = output; + wl_list_insert(&output->layer_surfaces, &surface->link); // now that the surface's output is known, we can advertise its scale wlr_fractional_scale_v1_notify_scale(surface->layer_surface->surface, @@ -492,9 +485,14 @@ void handle_layer_shell_surface(struct wl_listener *listener, void *data) { surface->new_popup.notify = handle_new_popup; wl_signal_add(&layer_surface->events.new_popup, &surface->new_popup); - surface->output_destroy.notify = handle_output_destroy; - wl_signal_add(&output->events.disable, &surface->output_destroy); - surface->node_destroy.notify = handle_node_destroy; wl_signal_add(&scene_surface->tree->node.events.destroy, &surface->node_destroy); } + +void destroy_layers(struct sway_output *output) { + struct sway_layer_surface *layer, *layer_tmp; + wl_list_for_each_safe(layer, layer_tmp, &output->layer_surfaces, link) { + layer->output = NULL; + wlr_layer_surface_v1_destroy(layer->layer_surface); + } +} diff --git a/sway/tree/output.c b/sway/tree/output.c index b02c1a2ce..42a403da8 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -136,12 +136,11 @@ struct sway_output *output_create(struct wlr_output *wlr_output) { output->detected_subpixel = wlr_output->subpixel; output->scale_filter = SCALE_FILTER_NEAREST; - wl_signal_init(&output->events.disable); - wl_list_insert(&root->all_outputs, &output->link); output->workspaces = create_list(); output->current.workspaces = create_list(); + wl_list_init(&output->layer_surfaces); return output; } @@ -284,7 +283,6 @@ void output_disable(struct sway_output *output) { } sway_log(SWAY_DEBUG, "Disabling output '%s'", output->wlr_output->name); - wl_signal_emit_mutable(&output->events.disable, output); // Remove the output now to avoid interacting with it during e.g., // transactions, as the output might be physically removed with the scene @@ -292,6 +290,7 @@ void output_disable(struct sway_output *output) { list_del(root->outputs, index); output->enabled = false; + destroy_layers(output); output_evacuate(output); } From c7d7d56f61f820989aa4ca6ee6267fe0bd31f5f6 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 5 Jul 2025 13:25:51 +0200 Subject: [PATCH 263/361] ipc-json, swaymsg: indicate when adaptive sync is unsupported --- sway/ipc-json.c | 6 ++++++ swaymsg/main.c | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index d3adedd4e..6a0d462f6 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -318,6 +318,12 @@ static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_obj json_object_array_add(modes_array, mode_object); } json_object_object_add(object, "modes", modes_array); + + json_object *features_object = json_object_new_object(); + json_object_object_add(features_object, "adaptive_sync", + json_object_new_boolean(wlr_output->adaptive_sync_supported || + wlr_output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED)); + json_object_object_add(object, "features", features_object); } static void ipc_json_describe_output(struct sway_output *output, diff --git a/swaymsg/main.c b/swaymsg/main.c index 6a9eb1987..9152b7942 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -210,6 +210,9 @@ static void pretty_print_output(json_object *o) { json_object_object_get_ex(current_mode, "width", &width); json_object_object_get_ex(current_mode, "height", &height); json_object_object_get_ex(current_mode, "refresh", &refresh); + json_object *features, *features_adaptive_sync; + json_object_object_get_ex(o, "features", &features); + json_object_object_get_ex(features, "adaptive_sync", &features_adaptive_sync); if (json_object_get_boolean(non_desktop)) { printf( @@ -252,7 +255,9 @@ static void pretty_print_output(json_object *o) { printf(max_render_time_int == 0 ? "off\n" : "%d ms\n", max_render_time_int); printf(" Adaptive sync: %s\n", - json_object_get_string(adaptive_sync_status)); + json_object_get_boolean(features_adaptive_sync) ? + json_object_get_string(adaptive_sync_status) : + "unsupported"); printf(" Allow tearing: %s\n", json_object_get_boolean(allow_tearing) ? "yes" : "no"); From bac8c0f4d0a569ac9f63a2055bb8c886778dcf06 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 27 Jan 2025 21:00:54 +0100 Subject: [PATCH 264/361] Add support for HDR10 output --- include/sway/commands.h | 1 + include/sway/config.h | 3 +- include/sway/output.h | 2 + sway/commands/output.c | 1 + sway/commands/output/hdr.c | 37 +++++++++++++++ sway/config/output.c | 96 ++++++++++++++++++++++++++++++-------- sway/ipc-json.c | 2 +- sway/meson.build | 1 + sway/sway-ipc.7.scd | 3 ++ sway/sway-output.5.scd | 11 +++++ swaymsg/main.c | 5 +- 11 files changed, 139 insertions(+), 23 deletions(-) create mode 100644 sway/commands/output/hdr.c diff --git a/include/sway/commands.h b/include/sway/commands.h index 5210d3ba7..389c382eb 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -290,6 +290,7 @@ sway_cmd output_cmd_color_profile; sway_cmd output_cmd_disable; sway_cmd output_cmd_dpms; sway_cmd output_cmd_enable; +sway_cmd output_cmd_hdr; sway_cmd output_cmd_max_render_time; sway_cmd output_cmd_mode; sway_cmd output_cmd_modeline; diff --git a/include/sway/config.h b/include/sway/config.h index bb770c6f7..3c380933e 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -261,7 +261,7 @@ enum scale_filter_mode { }; enum render_bit_depth { - RENDER_BIT_DEPTH_DEFAULT, // the default is currently 8 + RENDER_BIT_DEPTH_DEFAULT, // the default is currently 8 for SDR, 10 for HDR RENDER_BIT_DEPTH_6, RENDER_BIT_DEPTH_8, RENDER_BIT_DEPTH_10, @@ -291,6 +291,7 @@ struct output_config { bool set_color_transform; struct wlr_color_transform *color_transform; int allow_tearing; + int hdr; char *background; char *background_option; diff --git a/include/sway/output.h b/include/sway/output.h index 756a0ec22..06f8bac23 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -68,7 +68,9 @@ struct sway_output { uint32_t refresh_nsec; int max_render_time; // In milliseconds struct wl_event_source *repaint_timer; + bool allow_tearing; + bool hdr; }; struct sway_output_non_desktop { diff --git a/sway/commands/output.c b/sway/commands/output.c index 9d58413f2..afff23f6a 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -15,6 +15,7 @@ static const struct cmd_handler output_handlers[] = { { "disable", output_cmd_disable }, { "dpms", output_cmd_dpms }, { "enable", output_cmd_enable }, + { "hdr", output_cmd_hdr }, { "max_render_time", output_cmd_max_render_time }, { "mode", output_cmd_mode }, { "modeline", output_cmd_modeline }, diff --git a/sway/commands/output/hdr.c b/sway/commands/output/hdr.c new file mode 100644 index 000000000..26d202216 --- /dev/null +++ b/sway/commands/output/hdr.c @@ -0,0 +1,37 @@ +#include +#include "sway/commands.h" +#include "sway/config.h" +#include "sway/output.h" +#include "util.h" + +struct cmd_results *output_cmd_hdr(int argc, char **argv) { + if (!config->handler_context.output_config) { + return cmd_results_new(CMD_FAILURE, "Missing output config"); + } + if (argc == 0) { + return cmd_results_new(CMD_INVALID, "Missing hdr argument"); + } + + bool current = false; + if (strcasecmp(argv[0], "toggle") == 0) { + const char *oc_name = config->handler_context.output_config->name; + if (strcmp(oc_name, "*") == 0) { + return cmd_results_new(CMD_INVALID, + "Cannot apply toggle to all outputs"); + } + + struct sway_output *output = all_output_by_name_or_id(oc_name); + if (!output) { + return cmd_results_new(CMD_FAILURE, + "Cannot apply toggle to unknown output %s", oc_name); + } + + current = output->hdr; + } + + config->handler_context.output_config->hdr = parse_boolean(argv[0], current); + + config->handler_context.leftovers.argc = argc - 1; + config->handler_context.leftovers.argv = argv + 1; + return NULL; +} diff --git a/sway/config/output.c b/sway/config/output.c index df80cab64..ab2cd2006 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -79,6 +79,7 @@ struct output_config *new_output_config(const char *name) { oc->color_transform = NULL; oc->power = -1; oc->allow_tearing = -1; + oc->hdr = -1; return oc; } @@ -154,6 +155,9 @@ static void supersede_output_config(struct output_config *dst, struct output_con if (src->allow_tearing != -1) { dst->allow_tearing = -1; } + if (src->hdr != -1) { + dst->hdr = -1; + } } // merge_output_config sets all fields in dst that were set in src @@ -229,6 +233,9 @@ static void merge_output_config(struct output_config *dst, struct output_config if (src->allow_tearing != -1) { dst->allow_tearing = src->allow_tearing; } + if (src->hdr != -1) { + dst->hdr = src->hdr; + } } void store_output_config(struct output_config *oc) { @@ -271,11 +278,11 @@ void 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 subpixel %s transform %d) (bg %s %s) (power %d) " - "(max render time: %d) (allow tearing: %d)", + "(max render time: %d) (allow tearing: %d) (hdr: %d)", oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), oc->transform, oc->background, oc->background_option, oc->power, - oc->max_render_time, oc->allow_tearing); + oc->max_render_time, oc->allow_tearing, oc->hdr); // If the configuration was not merged into an existing configuration, add // it to the list. Otherwise we're done with it and can free it. @@ -341,6 +348,41 @@ static void set_modeline(struct wlr_output *output, #endif } +static void set_hdr(struct wlr_output *output, struct wlr_output_state *pending, bool enabled) { + enum wlr_color_named_primaries primaries = WLR_COLOR_NAMED_PRIMARIES_BT2020; + enum wlr_color_transfer_function tf = WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ; + if (enabled && !(output->supported_primaries & primaries)) { + sway_log(SWAY_ERROR, "Cannot enable HDR on output %s: BT2020 primaries not supported by output", + output->name); + enabled = false; + } + if (enabled && !(output->supported_transfer_functions & WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ)) { + sway_log(SWAY_ERROR, "Cannot enable HDR on output %s: PQ transfer function not supported by output", + output->name); + enabled = false; + } + if (enabled && !server.renderer->features.output_color_transform) { + sway_log(SWAY_ERROR, "Cannot enable HDR on output %s: renderer doesn't support output color transforms", + output->name); + enabled = false; + } + + if (!enabled) { + if (output->supported_primaries != 0 || output->supported_transfer_functions != 0) { + sway_log(SWAY_DEBUG, "Disabling HDR on output %s", output->name); + wlr_output_state_set_image_description(pending, NULL); + } + return; + } + + sway_log(SWAY_DEBUG, "Enabling HDR on output %s", output->name); + const struct wlr_output_image_description image_desc = { + .primaries = primaries, + .transfer_function = tf, + }; + wlr_output_state_set_image_description(pending, &image_desc); +} + /* Some manufacturers hardcode the aspect-ratio of the output in the physical * size field. */ static bool phys_size_is_aspect_ratio(struct wlr_output *output) { @@ -415,6 +457,16 @@ static enum render_bit_depth bit_depth_from_format(uint32_t render_format) { return RENDER_BIT_DEPTH_DEFAULT; } +static enum render_bit_depth get_config_render_bit_depth(const struct output_config *oc) { + if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { + return oc->render_bit_depth; + } + if (oc && oc->hdr == 1) { + return RENDER_BIT_DEPTH_10; + } + return RENDER_BIT_DEPTH_8; +} + static bool render_format_is_bgr(uint32_t fmt) { return fmt == DRM_FORMAT_XBGR2101010 || fmt == DRM_FORMAT_XBGR8888; } @@ -485,24 +537,29 @@ static void queue_output_config(struct output_config *oc, } } - if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { - if (oc->render_bit_depth == RENDER_BIT_DEPTH_10 && - bit_depth_from_format(output->wlr_output->render_format) == oc->render_bit_depth) { - // 10-bit was set successfully before, try to save some tests by reusing the format - wlr_output_state_set_render_format(pending, output->wlr_output->render_format); - } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_10) { - wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010); - } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_6){ - wlr_output_state_set_render_format(pending, DRM_FORMAT_RGB565); - } else { - wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888); - } + enum render_bit_depth render_bit_depth = get_config_render_bit_depth(oc); + if (render_bit_depth == RENDER_BIT_DEPTH_10 && + bit_depth_from_format(output->wlr_output->render_format) == render_bit_depth) { + // 10-bit was set successfully before, try to save some tests by reusing the format + wlr_output_state_set_render_format(pending, output->wlr_output->render_format); + } else if (render_bit_depth == RENDER_BIT_DEPTH_10) { + wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010); + } else if (render_bit_depth == RENDER_BIT_DEPTH_6) { + wlr_output_state_set_render_format(pending, DRM_FORMAT_RGB565); } else { wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888); } + + bool hdr = oc && oc->hdr == 1; + if (hdr && oc->color_transform != NULL) { + sway_log(SWAY_ERROR, "Cannot HDR on output %s: output has an ICC profile set", wlr_output->name); + hdr = false; + } + set_hdr(wlr_output, pending, hdr); } -static bool finalize_output_config(struct output_config *oc, struct sway_output *output) { +static bool finalize_output_config(struct output_config *oc, struct sway_output *output, + const struct wlr_output_state *applied) { if (output == root->fallback_output) { return false; } @@ -561,6 +618,7 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output output->max_render_time = oc && oc->max_render_time > 0 ? oc->max_render_time : 0; output->allow_tearing = oc && oc->allow_tearing > 0; + output->hdr = applied->image_description != NULL; return true; } @@ -785,10 +843,7 @@ static bool search_render_format(struct search_context *ctx, size_t output_idx) const struct wlr_drm_format_set *primary_formats = wlr_output_get_primary_formats(wlr_output, server.allocator->buffer_caps); - enum render_bit_depth needed_bits = RENDER_BIT_DEPTH_8; - if (cfg->config && cfg->config->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { - needed_bits = cfg->config->render_bit_depth; - } + enum render_bit_depth needed_bits = get_config_render_bit_depth(cfg->config); for (size_t idx = 0; fmts[idx] != DRM_FORMAT_INVALID; idx++) { enum render_bit_depth format_bits = bit_depth_from_format(fmts[idx]); if (needed_bits < format_bits) { @@ -943,9 +998,10 @@ static bool apply_resolved_output_configs(struct matched_output_config *configs, for (size_t idx = 0; idx < configs_len; idx++) { struct matched_output_config *cfg = &configs[idx]; + struct wlr_backend_output_state *backend_state = &states[idx]; sway_log(SWAY_DEBUG, "Finalizing config for %s", cfg->output->wlr_output->name); - finalize_output_config(cfg->config, cfg->output); + finalize_output_config(cfg->config, cfg->output, &backend_state->base); } // Output layout being applied in finalize_output_config can shift outputs diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 6a0d462f6..30ed24304 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -406,8 +406,8 @@ static void ipc_json_describe_enabled_output(struct sway_output *output, } json_object_object_add(object, "max_render_time", json_object_new_int(output->max_render_time)); - json_object_object_add(object, "allow_tearing", json_object_new_boolean(output->allow_tearing)); + json_object_object_add(object, "hdr", json_object_new_boolean(output->hdr)); } json_object *ipc_json_describe_disabled_output(struct sway_output *output) { diff --git a/sway/meson.build b/sway/meson.build index 8042c89be..cb03a4d28 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -195,6 +195,7 @@ sway_sources = files( 'commands/output/disable.c', 'commands/output/dpms.c', 'commands/output/enable.c', + 'commands/output/hdr.c', 'commands/output/max_render_time.c', 'commands/output/mode.c', 'commands/output/position.c', diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index 833db0ef8..87f0828b7 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -244,6 +244,9 @@ following properties: |- rect : object : The bounds for the output consisting of _x_, _y_, _width_, and _height_ +|- hdr +: boolean +: Whether HDR is enabled *Example Reply:* diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index dfe0d678b..cc48589c3 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -210,6 +210,17 @@ must be separated by one space. For example: This setting only has effect when a window is fullscreen on the output. +*output* hdr on|off|toggle + Enables or disables HDR (High Dynamic Range). HDR enables a larger color + gamut and brightness range. HDR uses the BT2020 primaries and the PQ + transfer function. + + When HDR is enabled, _render_bit_depth_ is implicitly set to 10 unless + explicitly configured. Using a lower render bit depth may result in color + banding artifacts. + + HDR needs to be supported by the output and renderer to be enabled. + # SEE ALSO *sway*(5) *sway-input*(5) diff --git a/swaymsg/main.c b/swaymsg/main.c index 9152b7942..1c6e807cb 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -189,7 +189,8 @@ static void pretty_print_output(json_object *o) { json_object_object_get_ex(o, "current_workspace", &ws); json_object_object_get_ex(o, "non_desktop", &non_desktop); json_object *make, *model, *serial, *scale, *scale_filter, *subpixel, - *transform, *max_render_time, *adaptive_sync_status, *allow_tearing; + *transform, *max_render_time, *adaptive_sync_status, *allow_tearing, + *hdr; json_object_object_get_ex(o, "make", &make); json_object_object_get_ex(o, "model", &model); json_object_object_get_ex(o, "serial", &serial); @@ -200,6 +201,7 @@ static void pretty_print_output(json_object *o) { json_object_object_get_ex(o, "max_render_time", &max_render_time); json_object_object_get_ex(o, "adaptive_sync_status", &adaptive_sync_status); json_object_object_get_ex(o, "allow_tearing", &allow_tearing); + json_object_object_get_ex(o, "hdr", &hdr); json_object *x, *y; json_object_object_get_ex(rect, "x", &x); json_object_object_get_ex(rect, "y", &y); @@ -261,6 +263,7 @@ static void pretty_print_output(json_object *o) { printf(" Allow tearing: %s\n", json_object_get_boolean(allow_tearing) ? "yes" : "no"); + printf(" HDR: %s\n", json_object_get_boolean(hdr) ? "on" : "off"); } else { printf( "Output %s '%s %s %s' (disabled)\n", From 6fed1f9d8933588484e2303e53ac4270a417d78a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 26 Jan 2025 14:36:54 +0100 Subject: [PATCH 265/361] Add support for color-management-v1 References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4962 --- meson.build | 2 +- protocols/meson.build | 1 + sway/server.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index eab1348ae..de5a620c4 100644 --- a/meson.build +++ b/meson.build @@ -65,7 +65,7 @@ pcre2 = dependency('libpcre2-8') wayland_server = dependency('wayland-server', version: '>=1.21.0') wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') -wayland_protos = dependency('wayland-protocols', version: '>=1.24', default_options: ['tests=false']) +wayland_protos = dependency('wayland-protocols', version: '>=1.41', default_options: ['tests=false']) xkbcommon = dependency('xkbcommon', version: '>=1.5.0') cairo = dependency('cairo') pango = dependency('pango') diff --git a/protocols/meson.build b/protocols/meson.build index 4d24e7071..3414119b8 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -9,6 +9,7 @@ wayland_scanner = find_program( protocols = [ wl_protocol_dir / 'stable/tablet/tablet-v2.xml', wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', + wl_protocol_dir / 'staging/color-management/color-management-v1.xml', wl_protocol_dir / 'staging/content-type/content-type-v1.xml', wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', wl_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml', diff --git a/sway/server.c b/sway/server.c index 6ca5906df..fb6204326 100644 --- a/sway/server.c +++ b/sway/server.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -443,6 +444,33 @@ bool server_init(struct sway_server *server) { server->request_set_cursor_shape.notify = handle_request_set_cursor_shape; wl_signal_add(&cursor_shape_manager->events.request_set_shape, &server->request_set_cursor_shape); + if (server->renderer->features.input_color_transform) { + const enum wp_color_manager_v1_render_intent render_intents[] = { + WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL, + }; + const enum wp_color_manager_v1_transfer_function transfer_functions[] = { + WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB, + WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ, + WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR, + }; + const enum wp_color_manager_v1_primaries primaries[] = { + WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, + WP_COLOR_MANAGER_V1_PRIMARIES_BT2020, + }; + wlr_color_manager_v1_create(server->wl_display, 1, &(struct wlr_color_manager_v1_options){ + .features = { + .parametric = true, + .set_mastering_display_primaries = true, + }, + .render_intents = render_intents, + .render_intents_len = sizeof(render_intents) / sizeof(render_intents[0]), + .transfer_functions = transfer_functions, + .transfer_functions_len = sizeof(transfer_functions) / sizeof(transfer_functions[0]), + .primaries = primaries, + .primaries_len = sizeof(primaries) / sizeof(primaries[0]), + }); + } + wl_list_init(&server->pending_launcher_ctxs); // Avoid using "wayland-0" as display socket From 94c819cc1f9328223509883e4b62939bdf85b760 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 8 Jul 2025 18:52:29 +0200 Subject: [PATCH 266/361] Add features.hdr to output IPC response --- include/sway/output.h | 2 ++ sway/config/output.c | 38 +++++++++++++++++++++----------------- sway/ipc-json.c | 2 ++ swaymsg/main.c | 10 ++++++++-- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 06f8bac23..787527ee7 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -131,6 +131,8 @@ struct sway_container *output_find_container(struct sway_output *output, void output_get_box(struct sway_output *output, struct wlr_box *box); +bool output_supports_hdr(struct wlr_output *output, const char **unsupported_reason_ptr); + enum sway_container_layout output_get_default_layout( struct sway_output *output); diff --git a/sway/config/output.c b/sway/config/output.c index ab2cd2006..2e3c05db3 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -348,22 +348,26 @@ static void set_modeline(struct wlr_output *output, #endif } +bool output_supports_hdr(struct wlr_output *output, const char **unsupported_reason_ptr) { + const char *unsupported_reason = NULL; + if (!(output->supported_primaries & WLR_COLOR_NAMED_PRIMARIES_BT2020)) { + unsupported_reason = "BT2020 primaries not supported by output"; + } else if (!(output->supported_transfer_functions & WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ)) { + unsupported_reason = "PQ transfer function not supported by output"; + } else if (!server.renderer->features.output_color_transform) { + unsupported_reason = "renderer doesn't support output color transforms"; + } + if (unsupported_reason_ptr != NULL) { + *unsupported_reason_ptr = unsupported_reason; + } + return unsupported_reason == NULL; +} + static void set_hdr(struct wlr_output *output, struct wlr_output_state *pending, bool enabled) { - enum wlr_color_named_primaries primaries = WLR_COLOR_NAMED_PRIMARIES_BT2020; - enum wlr_color_transfer_function tf = WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ; - if (enabled && !(output->supported_primaries & primaries)) { - sway_log(SWAY_ERROR, "Cannot enable HDR on output %s: BT2020 primaries not supported by output", - output->name); - enabled = false; - } - if (enabled && !(output->supported_transfer_functions & WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ)) { - sway_log(SWAY_ERROR, "Cannot enable HDR on output %s: PQ transfer function not supported by output", - output->name); - enabled = false; - } - if (enabled && !server.renderer->features.output_color_transform) { - sway_log(SWAY_ERROR, "Cannot enable HDR on output %s: renderer doesn't support output color transforms", - output->name); + const char *unsupported_reason = NULL; + if (!output_supports_hdr(output, &unsupported_reason)) { + sway_log(SWAY_ERROR, "Cannot enable HDR on output %s: %s", + output->name, unsupported_reason); enabled = false; } @@ -377,8 +381,8 @@ static void set_hdr(struct wlr_output *output, struct wlr_output_state *pending, sway_log(SWAY_DEBUG, "Enabling HDR on output %s", output->name); const struct wlr_output_image_description image_desc = { - .primaries = primaries, - .transfer_function = tf, + .primaries = WLR_COLOR_NAMED_PRIMARIES_BT2020, + .transfer_function = WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ, }; wlr_output_state_set_image_description(pending, &image_desc); } diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 30ed24304..3933f3ef7 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -323,6 +323,8 @@ static void ipc_json_describe_wlr_output(struct wlr_output *wlr_output, json_obj json_object_object_add(features_object, "adaptive_sync", json_object_new_boolean(wlr_output->adaptive_sync_supported || wlr_output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED)); + json_object_object_add(features_object, "hdr", + json_object_new_boolean(output_supports_hdr(wlr_output, NULL))); json_object_object_add(object, "features", features_object); } diff --git a/swaymsg/main.c b/swaymsg/main.c index 1c6e807cb..d58f29e2c 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -212,9 +212,10 @@ static void pretty_print_output(json_object *o) { json_object_object_get_ex(current_mode, "width", &width); json_object_object_get_ex(current_mode, "height", &height); json_object_object_get_ex(current_mode, "refresh", &refresh); - json_object *features, *features_adaptive_sync; + json_object *features, *features_adaptive_sync, *features_hdr; json_object_object_get_ex(o, "features", &features); json_object_object_get_ex(features, "adaptive_sync", &features_adaptive_sync); + json_object_object_get_ex(features, "hdr", &features_hdr); if (json_object_get_boolean(non_desktop)) { printf( @@ -263,7 +264,12 @@ static void pretty_print_output(json_object *o) { printf(" Allow tearing: %s\n", json_object_get_boolean(allow_tearing) ? "yes" : "no"); - printf(" HDR: %s\n", json_object_get_boolean(hdr) ? "on" : "off"); + + const char *hdr_str = "unsupported"; + if (json_object_get_boolean(features_hdr)) { + hdr_str = json_object_get_boolean(hdr) ? "on" : "off"; + } + printf(" HDR: %s\n", hdr_str); } else { printf( "Output %s '%s %s %s' (disabled)\n", From 8d7c7562762a81dc87d78634618fe518347ba991 Mon Sep 17 00:00:00 2001 From: Nuran Askarov <55441226+nuranaskarov@users.noreply.github.com> Date: Sat, 14 Jun 2025 14:59:25 +0000 Subject: [PATCH 267/361] Add README.az.md --- README.az.md | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 README.az.md diff --git a/README.az.md b/README.az.md new file mode 100644 index 000000000..3c5e0e413 --- /dev/null +++ b/README.az.md @@ -0,0 +1,66 @@ +# sway + +sway [i3]-ə uyğun [Wayland] kompozitorudur. [Tez-tez verilən sualları] oxuyun. +[IRC kanalına] qoşulun ("irc.libera.chat"-da #sway). + +## Buraxılış İmzaları + +Buraxılışlar [E88F5E48] ilə imzalanıb və [GitHub-da][GitHub releases] dərc edilib. + +## Quraşdırma + +### Paketlərdən + +Sway bir çox distributivdə mövcuddur. Öz distributiviniz üçün +"sway" paketini quraşdırmağa çalışın. + +### Mənbə kodundan kompilyasiya + +Test və ya inkişaf üçün sway və wlroots-un HEAD-ini qurmaq istəyirsinizsə, +[bu viki səhifəsini][Development setup] nəzərdən keçirin. + +Asılılıqları quraşdırın: + +* meson \* +* [wlroots] +* wayland +* wayland-protocols \* +* pcre2 +* json-c +* pango +* cairo +* gdk-pixbuf2 (ixtiyari: sistem trayı üçün əlavə şəkil formatları) +* [swaybg] (ixtiyari: divar kağızı) +* [scdoc] (ixtiyari: man səhifələri) \* +* git (ixtiyari: versiya məlumatı) \* + +_\* Kompilyasiya asılılıqları_ + +Bu əmrləri icra edin: + + meson build/ + ninja -C build/ + sudo ninja -C build/ install + +## Konfiqurasiya + +Əgər artıq i3-dən istifadə edirsinizsə, i3 konfiqurasiyanızı `~/.config/sway/config` +ünvanına köçürün və o, dərhal işləyəcək. Əks halda, nümunə konfiqurasiya faylını +`~/.config/sway/config` ünvanına köçürün. O, adətən `/etc/sway/config` ünvanında yerləşir. +Konfiqurasiya haqqında məlumat üçün `man 5 sway` əmrini icra edin. + +## İşə Salma + +TTY-dan `sway`-ı işə salın. Bəzi ekran menecerləri işləyə bilər, lakin sway tərəfindən +dəstəklənmir (gdm-in kifayət qədər yaxşı işlədiyi məlumdur). + +[i3]: https://i3wm.org/ +[Wayland]: http://wayland.freedesktop.org/ +[Tez-tez verilən sualları]: https://github.com/swaywm/sway/wiki +[IRC kanalına]: https://web.libera.chat/gamja/?channels=#sway +[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 +[GitHub releases]: https://github.com/swaywm/sway/releases +[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup +[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots +[swaybg]: https://github.com/swaywm/swaybg/ +[scdoc]: https://git.sr.ht/~sircmpwn/scdoc From f57c82a6f7cbd0c6499def867fc98459fb72a1d5 Mon Sep 17 00:00:00 2001 From: Nuran Askarov <55441226+nuranaskarov@users.noreply.github.com> Date: Sat, 14 Jun 2025 15:00:20 +0000 Subject: [PATCH 268/361] Add link to README.az.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 15c7c0999..8e4234f0d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # sway -**[English][en]** - [عربي][ar] - [Česky][cs] - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - [ქართული][ge] - [Ελληνικά][gr] - [हिन्दी][hi] - [Magyar][hu] - [فارسی][ir] - [Italiano][it] - [日本語][ja] - [한국어][ko] - [Nederlands][nl] - [Norsk][no] - [Polski][pl] - [Português][pt] - [Română][ro] - [Русский][ru] - [Svenska][sv] - [Türkçe][tr] - [Українська][uk] - [中文-简体][zh-CN] - [中文-繁體][zh-TW] +**[English][en]** - [عربي][ar] - [Azərbaycanca][az] - [Česky][cs] - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - [ქართული][ge] - [Ελληνικά][gr] - [हिन्दी][hi] - [Magyar][hu] - [فارسی][ir] - [Italiano][it] - [日本語][ja] - [한국어][ko] - [Nederlands][nl] - [Norsk][no] - [Polski][pl] - [Português][pt] - [Română][ro] - [Русский][ru] - [Svenska][sv] - [Türkçe][tr] - [Українська][uk] - [中文-简体][zh-CN] - [中文-繁體][zh-TW] sway is an [i3]-compatible [Wayland] compositor. Read the [FAQ]. Join the [IRC channel] \(#sway on irc.libera.chat). @@ -58,6 +58,7 @@ sway (gdm is known to work fairly well). [en]: https://github.com/swaywm/sway#readme [ar]: README.ar.md +[az]: README.az.md [cs]: README.cs.md [de]: README.de.md [dk]: README.dk.md From 3826535ab0eba08160809445e3f12e0281b824d2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 10 Jul 2025 16:44:48 +0200 Subject: [PATCH 269/361] Wire up xdg-toplevel-tag-v1 --- include/sway/server.h | 2 ++ include/sway/tree/view.h | 6 ++++++ sway/desktop/xdg_shell.c | 16 +++++++++++++++- sway/server.c | 9 +++++++++ sway/tree/view.c | 7 +++++++ 5 files changed, 39 insertions(+), 1 deletion(-) diff --git a/include/sway/server.h b/include/sway/server.h index 72bccd70c..f50d48f05 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -119,6 +119,8 @@ struct sway_server { struct wl_listener xdg_activation_v1_request_activate; struct wl_listener xdg_activation_v1_new_token; + struct wl_listener xdg_toplevel_tag_manager_v1_set_tag; + struct wl_listener request_set_cursor_shape; struct wlr_tearing_control_manager_v1 *tearing_control_v1; diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 6151a0234..ae81c5bbf 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -25,6 +25,7 @@ enum sway_view_type { enum sway_view_prop { VIEW_PROP_TITLE, VIEW_PROP_APP_ID, + VIEW_PROP_TAG, VIEW_PROP_CLASS, VIEW_PROP_INSTANCE, VIEW_PROP_WINDOW_TYPE, @@ -128,6 +129,7 @@ struct sway_xdg_shell_view { struct sway_view view; struct wlr_scene_tree *image_capture_tree; + char *tag; struct wl_listener commit; struct wl_listener request_move; @@ -236,6 +238,8 @@ const char *view_get_sandbox_app_id(struct sway_view *view); const char *view_get_sandbox_instance_id(struct sway_view *view); +const char *view_get_tag(struct sway_view *view); + const char *view_get_shell(struct sway_view *view); void view_get_constraints(struct sway_view *view, double *min_width, @@ -360,4 +364,6 @@ void view_send_frame_done(struct sway_view *view); bool view_can_tear(struct sway_view *view); +void xdg_toplevel_tag_manager_v1_handle_set_tag(struct wl_listener *listener, void *data); + #endif diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 3852806e4..db09e91d7 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "log.h" #include "sway/decoration.h" @@ -157,7 +158,8 @@ static void get_constraints(struct sway_view *view, double *min_width, static const char *get_string_prop(struct sway_view *view, enum sway_view_prop prop) { - if (xdg_shell_view_from_view(view) == NULL) { + struct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); + if (xdg_shell_view == NULL) { return NULL; } switch (prop) { @@ -165,6 +167,8 @@ static const char *get_string_prop(struct sway_view *view, return view->wlr_xdg_toplevel->title; case VIEW_PROP_APP_ID: return view->wlr_xdg_toplevel->app_id; + case VIEW_PROP_TAG: + return xdg_shell_view->tag; default: return NULL; } @@ -265,6 +269,7 @@ static void destroy(struct sway_view *view) { if (xdg_shell_view == NULL) { return; } + free(xdg_shell_view->tag); free(xdg_shell_view); } @@ -581,3 +586,12 @@ void handle_xdg_shell_toplevel(struct wl_listener *listener, void *data) { xdg_toplevel->base->data = xdg_shell_view; } + +void xdg_toplevel_tag_manager_v1_handle_set_tag(struct wl_listener *listener, void *data) { + const struct wlr_xdg_toplevel_tag_manager_v1_set_tag_event *event = data; + struct sway_view *view = view_from_wlr_xdg_surface(event->toplevel->base); + struct sway_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view); + free(xdg_shell_view->tag); + xdg_shell_view->tag = strdup(event->tag); + view_execute_criteria(view); +} diff --git a/sway/server.c b/sway/server.c index fb6204326..8615066ee 100644 --- a/sway/server.c +++ b/sway/server.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include "config.h" #include "list.h" @@ -439,6 +440,13 @@ bool server_init(struct sway_server *server) { wl_signal_add(&server->xdg_activation_v1->events.new_token, &server->xdg_activation_v1_new_token); + struct wlr_xdg_toplevel_tag_manager_v1 *xdg_toplevel_tag_manager_v1 = + wlr_xdg_toplevel_tag_manager_v1_create(server->wl_display, 1); + server->xdg_toplevel_tag_manager_v1_set_tag.notify = + xdg_toplevel_tag_manager_v1_handle_set_tag; + wl_signal_add(&xdg_toplevel_tag_manager_v1->events.set_tag, + &server->xdg_toplevel_tag_manager_v1_set_tag); + struct wlr_cursor_shape_manager_v1 *cursor_shape_manager = wlr_cursor_shape_manager_v1_create(server->wl_display, 1); server->request_set_cursor_shape.notify = handle_request_set_cursor_shape; @@ -536,6 +544,7 @@ void server_fini(struct sway_server *server) { wl_list_remove(&server->tearing_control_new_object.link); wl_list_remove(&server->xdg_activation_v1_request_activate.link); wl_list_remove(&server->xdg_activation_v1_new_token.link); + wl_list_remove(&server->xdg_toplevel_tag_manager_v1_set_tag.link); wl_list_remove(&server->request_set_cursor_shape.link); wl_list_remove(&server->new_foreign_toplevel_capture_request.link); input_manager_finish(server->input); diff --git a/sway/tree/view.c b/sway/tree/view.c index 7bf185fea..61aa53776 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -190,6 +190,13 @@ const char *view_get_sandbox_instance_id(struct sway_view *view) { return security_context ? security_context->instance_id : NULL; } +const char *view_get_tag(struct sway_view *view) { + if (view->impl->get_string_prop) { + return view->impl->get_string_prop(view, VIEW_PROP_TAG); + } + return NULL; +} + const char *view_get_shell(struct sway_view *view) { switch(view->type) { case SWAY_VIEW_XDG_SHELL: From 08142c3f3ab41320cffe4d6cc03c09a9c6c53ce8 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 10 Jul 2025 16:45:16 +0200 Subject: [PATCH 270/361] Add xdg_toplevel tag to IPC --- sway/ipc-json.c | 3 +++ sway/sway-ipc.7.scd | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 3933f3ef7..3b69ad384 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -627,6 +627,9 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(object, "sandbox_instance_id", sandbox_instance_id ? json_object_new_string(sandbox_instance_id) : NULL); + const char *tag = view_get_tag(c->view); + json_object_object_add(object, "tag", tag ? json_object_new_string(tag) : NULL); + json_object *idle_inhibitors = json_object_new_object(); struct sway_idle_inhibitor_v1 *user_inhibitor = diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index 87f0828b7..4b0d5c962 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -419,6 +419,10 @@ node and will have the following properties: : string : (Only windows) The instance ID provided by the associated sandbox engine (or _null_) +|- tag +: string +: (Only windows) For an xdg-shell window, tag of the toplevel, if set. + Otherwise, _null_ |- window : integer : (Only xwayland windows) The X11 window ID for the xwayland window From cb33701f5ee742b659afd660fa98c294f6faf5a8 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 10 Jul 2025 16:45:29 +0200 Subject: [PATCH 271/361] Add xdg_toplevel tag to criteria --- include/sway/criteria.h | 1 + sway/criteria.c | 30 +++++++++++++++++++++++++++++- sway/sway.5.scd | 3 +++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/include/sway/criteria.h b/include/sway/criteria.h index 8ba8c9989..fad278e02 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -56,6 +56,7 @@ struct criteria { struct pattern *sandbox_engine; struct pattern *sandbox_app_id; struct pattern *sandbox_instance_id; + struct pattern *tag; }; bool criteria_is_empty(struct criteria *criteria); diff --git a/sway/criteria.c b/sway/criteria.c index 29f73f697..e200d4c8f 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -37,7 +37,8 @@ bool criteria_is_empty(struct criteria *criteria) { && !criteria->pid && !criteria->sandbox_engine && !criteria->sandbox_app_id - && !criteria->sandbox_instance_id; + && !criteria->sandbox_instance_id + && !criteria->tag; } // The error pointer is used for parsing functions, and saves having to pass it @@ -104,6 +105,7 @@ void criteria_destroy(struct criteria *criteria) { pattern_destroy(criteria->sandbox_engine); pattern_destroy(criteria->sandbox_app_id); pattern_destroy(criteria->sandbox_instance_id); + pattern_destroy(criteria->tag); free(criteria->target); free(criteria->cmdlist); free(criteria->raw); @@ -314,6 +316,26 @@ static bool criteria_matches_view(struct criteria *criteria, } } + if (criteria->tag) { + const char *tag = view_get_tag(view); + if (!tag) { + return false; + } + + switch (criteria->tag->match_type) { + case PATTERN_FOCUSED: + if (focused && lenient_strcmp(tag, view_get_tag(focused))) { + return false; + } + break; + case PATTERN_PCRE2: + if (regex_cmp(tag, criteria->tag->regex) < 0) { + return false; + } + break; + } + } + if (!criteria_matches_container(criteria, view->container)) { return false; } @@ -544,6 +566,7 @@ enum criteria_token { T_SANDBOX_ENGINE, T_SANDBOX_APP_ID, T_SANDBOX_INSTANCE_ID, + T_TAG, T_INVALID, }; @@ -589,6 +612,8 @@ static enum criteria_token token_from_name(char *name) { return T_SANDBOX_APP_ID; } else if (strcmp(name, "sandbox_instance_id") == 0) { return T_SANDBOX_INSTANCE_ID; + } else if (strcmp(name, "tag") == 0) { + return T_TAG; } return T_INVALID; } @@ -700,6 +725,9 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { case T_SANDBOX_INSTANCE_ID: pattern_create(&criteria->sandbox_instance_id, value); break; + case T_TAG: + pattern_create(&criteria->tag, value); + break; case T_INVALID: break; } diff --git a/sway/sway.5.scd b/sway/sway.5.scd index dbdf1f1a2..689783a27 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -1055,6 +1055,9 @@ The following attributes may be matched with: Can be a regular expression. If value is \_\_focused\_\_, then the shell must be the same as that of the currently focused window. +*tag* + Compare value against the tag. _tag_ is specific to Wayland applications. + *tiling* Matches tiling windows. From e50b16a69969d136595efe2b595cf55c9cef39fb Mon Sep 17 00:00:00 2001 From: Fenveireth Date: Sun, 20 Jul 2025 12:47:39 +0000 Subject: [PATCH 272/361] tree/view: save new wlr_scene_buffer fields Visble flicker can occur during transactions, if these are not copied to the 'saved' scene --- sway/tree/view.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sway/tree/view.c b/sway/tree/view.c index 61aa53776..3d6211249 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -1206,6 +1206,10 @@ static void view_save_buffer_iterator(struct wlr_scene_buffer *buffer, wlr_scene_buffer_set_dest_size(sbuf, buffer->dst_width, buffer->dst_height); wlr_scene_buffer_set_opaque_region(sbuf, &buffer->opaque_region); + wlr_scene_buffer_set_opacity(sbuf, buffer->opacity); + wlr_scene_buffer_set_filter_mode(sbuf, buffer->filter_mode); + wlr_scene_buffer_set_transfer_function(sbuf, buffer->transfer_function); + wlr_scene_buffer_set_primaries(sbuf, buffer->primaries); wlr_scene_buffer_set_source_box(sbuf, &buffer->src_box); wlr_scene_node_set_position(&sbuf->node, sx, sy); wlr_scene_buffer_set_transform(sbuf, buffer->transform); From 14fbe9242fd43c5f58b2b55ec11bd1ead9dab3eb Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 9 Jul 2025 10:51:17 +0200 Subject: [PATCH 273/361] Revert drag_lock default to disabled I misunderstood the recommendation in the libinput release notes. Instead of making enabled_sticky the default, leave the default set to disabled. Fixes: bbadf9b8b10d ("Add support for LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY") Closes: https://github.com/swaywm/sway/issues/8758 --- sway/input/libinput.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/sway/input/libinput.c b/sway/input/libinput.c index b98c4cc86..fd7533d99 100644 --- a/sway/input/libinput.c +++ b/sway/input/libinput.c @@ -272,10 +272,6 @@ bool sway_input_configure_libinput_device(struct sway_input_device *input_device } if (ic->drag_lock != INT_MIN) { changed |= set_tap_drag_lock(device, ic->drag_lock); - } else { -#if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY - changed |= set_tap_drag_lock(device, LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY); -#endif } if (ic->pointer_accel != FLT_MIN) { changed |= set_accel_speed(device, ic->pointer_accel); @@ -358,12 +354,8 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) { libinput_device_config_tap_get_default_button_map(device)); changed |= set_tap_drag(device, libinput_device_config_tap_get_default_drag_enabled(device)); -#if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY - changed |= set_tap_drag_lock(device, LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY); -#else changed |= set_tap_drag_lock(device, libinput_device_config_tap_get_default_drag_lock_enabled(device)); -#endif changed |= set_accel_speed(device, libinput_device_config_accel_get_default_speed(device)); changed |= set_rotation_angle(device, From 357d341f8fd68cd6902ea029a46baf5ce3411336 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 31 Jul 2025 15:44:49 +0200 Subject: [PATCH 274/361] tree/node: Do not mark destroying nodes as dirty Node destruction currently runs through the transaction system such that a particular node is only destroyed after its use in an ongoing transaction. If a node is dirtied after the node is marked as destroying but before it is destroyed, the pointer added to dirty_nodes would become a dangling pointer once the node was destroyed. Do not dirty destroying nodes, and ensure that destroying is only set after the last dirty. --- sway/tree/container.c | 2 +- sway/tree/node.c | 2 +- sway/tree/output.c | 2 +- sway/tree/workspace.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 0385d7c17..b63ca0a28 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -547,8 +547,8 @@ void container_begin_destroy(struct sway_container *con) { container_end_mouse_operation(con); - con->node.destroying = true; node_set_dirty(&con->node); + con->node.destroying = true; if (con->scratchpad) { root_scratchpad_remove_container(con); diff --git a/sway/tree/node.c b/sway/tree/node.c index 7aaf97627..48ae325e4 100644 --- a/sway/tree/node.c +++ b/sway/tree/node.c @@ -29,7 +29,7 @@ const char *node_type_to_str(enum sway_node_type type) { } void node_set_dirty(struct sway_node *node) { - if (node->dirty) { + if (node->dirty || node->destroying) { return; } node->dirty = true; diff --git a/sway/tree/output.c b/sway/tree/output.c index 42a403da8..90ec9331d 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -301,8 +301,8 @@ void output_begin_destroy(struct sway_output *output) { sway_log(SWAY_DEBUG, "Destroying output '%s'", output->wlr_output->name); wl_signal_emit_mutable(&output->node.events.destroy, &output->node); - output->node.destroying = true; node_set_dirty(&output->node); + output->node.destroying = true; } struct sway_output *output_from_wlr_output(struct wlr_output *output) { diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index f2be4cd13..733a002b4 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -166,8 +166,8 @@ void workspace_begin_destroy(struct sway_workspace *workspace) { if (workspace->output) { workspace_detach(workspace); } - workspace->node.destroying = true; node_set_dirty(&workspace->node); + workspace->node.destroying = true; } void workspace_consider_destroy(struct sway_workspace *ws) { From 87fbcf0574260c4f307627e4e1cf3f71eeb48a31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Buczy=C5=84ski?= <56112903+tomekb234@users.noreply.github.com> Date: Sat, 21 Jun 2025 00:07:02 +0200 Subject: [PATCH 275/361] Add cursor->hidden check to handle_rebase Fixes #6245 --- sway/input/seatop_default.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 19645b4c9..df8232afc 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -1111,7 +1111,7 @@ static void handle_rebase(struct sway_seat *seat, uint32_t time_msec) { cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); if (surface) { - if (seat_is_input_allowed(seat, surface)) { + if (seat_is_input_allowed(seat, surface) && !cursor->hidden) { wlr_seat_pointer_notify_enter(seat->wlr_seat, surface, sx, sy); wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy); } From f50e307227c8938a57c098edd77098786ea6613a Mon Sep 17 00:00:00 2001 From: bonsaiiV <68749581+bonsaiiV@users.noreply.github.com> Date: Sun, 3 Aug 2025 13:25:40 +0000 Subject: [PATCH 276/361] sway/commands/layout: flatten parent once Applying layout changes to the parent of the parent, in case the parent only has a single child, stops the creation of a chain of single child containers. Closes: https://github.com/swaywm/sway/issues/7945 --- sway/commands/layout.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sway/commands/layout.c b/sway/commands/layout.c index 12ce48398..a32c908b8 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -134,6 +134,15 @@ struct cmd_results *cmd_layout(int argc, char **argv) { // Operate on parent container, like i3. if (container) { container = container->pending.parent; + // If parent has only a singe child operate on its parent and + // flatten once, like i3 + if (container && container->pending.children->length == 1) { + struct sway_container *child = container->pending.children->items[0]; + struct sway_container *parent = container->pending.parent; + container_replace(container, child); + container_begin_destroy(container); + container = parent; + } } // We could be working with a container OR a workspace. These are different From 340505bb6f6ad2418448445fa7e5d58f771c3b4d Mon Sep 17 00:00:00 2001 From: Tim Hallmann Date: Thu, 3 Feb 2022 19:35:55 +0100 Subject: [PATCH 277/361] commands/resize: make resize consider all siblings Fixes a compatibility issue with i3 where resizing works as described hereafter: > Direction can either be one of up, down, left or right. Or you can be > less specific and use width or height, in which case i3 will take/give > space from all the other containers. Sway previously considered only the direct neighbours, not all siblings. Fixes #5936 --- sway/commands/resize.c | 91 ++++++++++++++++++------------------------ sway/sway.5.scd | 12 ++++-- 2 files changed, 47 insertions(+), 56 deletions(-) diff --git a/sway/commands/resize.c b/sway/commands/resize.c index 49731a641..3530ee5a8 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -80,61 +80,46 @@ void container_resize_tiled(struct sway_container *con, } // For HORIZONTAL or VERTICAL, we are growing in two directions so select - // both adjacent siblings. For RIGHT or DOWN, just select the next sibling. - // For LEFT or UP, convert it to a RIGHT or DOWN resize and reassign con to - // the previous sibling. - struct sway_container *prev = NULL; - struct sway_container *next = NULL; + // all adjacent siblings. For RIGHT or DOWN or LEFT or UP select just the + // previous or next sibling. + list_t *resize = create_list(); list_t *siblings = container_get_siblings(con); int index = container_sibling_index(con); if (axis == AXIS_HORIZONTAL || axis == AXIS_VERTICAL) { - if (index == 0) { - next = siblings->items[1]; - } else if (index == siblings->length - 1) { - // Convert edge to top/left - next = con; - con = siblings->items[index - 1]; - amount = -amount; - } else { - prev = siblings->items[index - 1]; - next = siblings->items[index + 1]; - } + list_cat(resize, siblings); } else if (axis == WLR_EDGE_TOP || axis == WLR_EDGE_LEFT) { if (!sway_assert(index > 0, "Didn't expect first child")) { - return; + goto cleanup; } - next = con; - con = siblings->items[index - 1]; - amount = -amount; + list_add(resize, siblings->items[index - 1]); + list_add(resize, con); } else { if (!sway_assert(index < siblings->length - 1, "Didn't expect last child")) { - return; + goto cleanup; } - next = siblings->items[index + 1]; + list_add(resize, con); + list_add(resize, siblings->items[index + 1]); } // Apply new dimensions - int sibling_amount = prev ? ceil((double)amount / 2.0) : amount; + int sibling_amount = ceil((double)amount / (double)(resize->length - 1)); if (is_horizontal(axis)) { - if (con->pending.width + amount < MIN_SANE_W) { - return; - } - if (next->pending.width - sibling_amount < MIN_SANE_W) { - return; - } - if (prev && prev->pending.width - sibling_amount < MIN_SANE_W) { - return; + for (int i = 0; i < resize->length; i++) { + struct sway_container *sibling = resize->items[i]; + double change = sibling == con ? amount : -sibling_amount; + if (sibling->pending.width + change < MIN_SANE_W) { + goto cleanup; + } } if (con->child_total_width <= 0) { - return; + goto cleanup; } // We're going to resize so snap all the width fractions to full pixels // to avoid rounding issues - list_t *siblings = container_get_siblings(con); for (int i = 0; i < siblings->length; ++i) { struct sway_container *con = siblings->items[i]; con->width_fraction = con->pending.width / con->child_total_width; @@ -142,30 +127,27 @@ void container_resize_tiled(struct sway_container *con, double amount_fraction = (double)amount / con->child_total_width; double sibling_amount_fraction = - prev ? amount_fraction / 2.0 : amount_fraction; + amount_fraction / (double)(resize->length - 1); - con->width_fraction += amount_fraction; - next->width_fraction -= sibling_amount_fraction; - if (prev) { - prev->width_fraction -= sibling_amount_fraction; + for (int i = 0; i < resize->length; i++) { + struct sway_container *sibling = resize->items[i]; + sibling->width_fraction += + sibling == con ? amount_fraction : -sibling_amount_fraction; } } else { - if (con->pending.height + amount < MIN_SANE_H) { - return; - } - if (next->pending.height - sibling_amount < MIN_SANE_H) { - return; - } - if (prev && prev->pending.height - sibling_amount < MIN_SANE_H) { - return; + for (int i = 0; i < resize->length; i++) { + struct sway_container *sibling = resize->items[i]; + double change = sibling == con ? amount : -sibling_amount; + if (sibling->pending.height + change < MIN_SANE_H) { + goto cleanup; + } } if (con->child_total_height <= 0) { - return; + goto cleanup; } // We're going to resize so snap all the height fractions to full pixels // to avoid rounding issues - list_t *siblings = container_get_siblings(con); for (int i = 0; i < siblings->length; ++i) { struct sway_container *con = siblings->items[i]; con->height_fraction = con->pending.height / con->child_total_height; @@ -173,12 +155,12 @@ void container_resize_tiled(struct sway_container *con, double amount_fraction = (double)amount / con->child_total_height; double sibling_amount_fraction = - prev ? amount_fraction / 2.0 : amount_fraction; + amount_fraction / (double)(resize->length - 1); - con->height_fraction += amount_fraction; - next->height_fraction -= sibling_amount_fraction; - if (prev) { - prev->height_fraction -= sibling_amount_fraction; + for (int i = 0; i < resize->length; i++) { + struct sway_container *sibling = resize->items[i]; + sibling->height_fraction += + sibling == con ? amount_fraction : -sibling_amount_fraction; } } @@ -187,6 +169,9 @@ void container_resize_tiled(struct sway_container *con, } else { arrange_workspace(con->pending.workspace); } + +cleanup: + list_free(resize); } /** diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 689783a27..53393bcc9 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -302,28 +302,34 @@ set|plus|minus|toggle *rename* workspace [] to Rename either or the focused workspace to the -*resize* shrink|grow width|height [ [px|ppt]] +*resize* shrink|grow up|right|down|left|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 in px and tiled containers by ppt. _amount_ will default to 10 if omitted. + For tiling containers, space is taken/given from the container in the + specified direction. If _width_ or _height_ is specified, space will be + taken/given from all other containers. *resize* set height [px|ppt] Sets the height of the container to _height_, specified in pixels or percentage points. If the units are omitted, floating containers are resized in px and tiled containers by ppt. If _height_ is 0, the container - will not be resized. + will not be resized. For tiling containers, space is taken/given from all + other containers. *resize* set [width] [px|ppt] Sets the width of the container to _width_, specified in pixels or percentage points. If the units are omitted, floating containers are resized in px and tiled containers by ppt. If _width_ is 0, the container - will not be resized. + will not be resized. For tiling containers, space is taken/given from all + other containers. *resize* set [width] [px|ppt] [height] [px|ppt] Sets the width and height of the container to _width_ and _height_, specified in pixels or percentage points. If the units are omitted, floating containers are resized in px and tiled containers by ppt. If _width_ or _height_ is 0, the container will not be resized on that axis. + For tiling containers, space is taken/given from all other containers. *scratchpad* show Shows a window from the scratchpad. Repeatedly using this command will From 0770a8d6435700d87e5753e53fdd2792c1437ea5 Mon Sep 17 00:00:00 2001 From: Joaquim Monteiro Date: Fri, 25 Jul 2025 04:30:42 +0100 Subject: [PATCH 278/361] Add wl_fixes interface --- sway/server.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/server.c b/sway/server.c index 8615066ee..8ad62c719 100644 --- a/sway/server.c +++ b/sway/server.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -257,6 +258,7 @@ bool server_init(struct sway_server *server) { wl_display_set_global_filter(server->wl_display, filter_global, NULL); wl_display_set_default_max_buffer_size(server->wl_display, 1024 * 1024); + wlr_fixes_create(server->wl_display, 1); root = root_create(server->wl_display); server->backend = wlr_backend_autocreate(server->wl_event_loop, &server->session); From b3dcde8d69c3f1304b076968a7a64f54d0c958be Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 13 Aug 2025 22:53:13 +0200 Subject: [PATCH 279/361] Stop generating wayland-protocols server headers We still need to generate wlr-protocols server headers, as well as client headers and code. References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5075 --- protocols/meson.build | 8 -------- sway/tree/container.c | 1 - 2 files changed, 9 deletions(-) diff --git a/protocols/meson.build b/protocols/meson.build index 3414119b8..e7ada59b8 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -9,15 +9,7 @@ wayland_scanner = find_program( protocols = [ wl_protocol_dir / 'stable/tablet/tablet-v2.xml', wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', - wl_protocol_dir / 'staging/color-management/color-management-v1.xml', - wl_protocol_dir / 'staging/content-type/content-type-v1.xml', wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', - wl_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml', - wl_protocol_dir / 'staging/ext-image-capture-source/ext-image-capture-source-v1.xml', - wl_protocol_dir / 'staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml', - wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml', - wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', - wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml', wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml', 'wlr-layer-shell-unstable-v1.xml', 'idle.xml', diff --git a/sway/tree/container.c b/sway/tree/container.c index b63ca0a28..c9ec852fc 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -7,7 +7,6 @@ #include #include #include -#include "linux-dmabuf-unstable-v1-protocol.h" #include "sway/config.h" #include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" From 73c244fb4807a29c6599d42c15e8a8759225b2d6 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Fri, 22 Aug 2025 15:37:44 +0200 Subject: [PATCH 280/361] config/output: Only error when enabling HDR We currently emit errors about HDR support even if HDR is not being requested, which mean errors on every regular modeset when monitors not supporting HDR are connected. Only emit errors when attempting to enable HDR on such device. --- 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 2e3c05db3..ed8147b8b 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -365,7 +365,7 @@ bool output_supports_hdr(struct wlr_output *output, const char **unsupported_rea static void set_hdr(struct wlr_output *output, struct wlr_output_state *pending, bool enabled) { const char *unsupported_reason = NULL; - if (!output_supports_hdr(output, &unsupported_reason)) { + if (enabled && !output_supports_hdr(output, &unsupported_reason)) { sway_log(SWAY_ERROR, "Cannot enable HDR on output %s: %s", output->name, unsupported_reason); enabled = false; From aaab7f961e0b9b0e52d43ca4debfeb8fcb4f7592 Mon Sep 17 00:00:00 2001 From: Bill Li Date: Mon, 8 Sep 2025 23:48:45 +0800 Subject: [PATCH 281/361] input/text_input: chase wlroots update References:https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5107 --- sway/input/text_input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/input/text_input.c b/sway/input/text_input.c index c907585fe..8dc64f019 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -648,7 +648,7 @@ void sway_input_method_relay_init(struct sway_seat *seat, relay->input_method_new.notify = relay_handle_input_method; wl_signal_add( - &server.input_method->events.input_method, + &server.input_method->events.new_input_method, &relay->input_method_new); relay->input_method_manager_destroy.notify = relay_handle_input_method_manager_destroy; wl_signal_add(&server.input_method->events.destroy, From c5456be7506adece2cdf922ed6d919db597944ab Mon Sep 17 00:00:00 2001 From: Nikola Kocic Date: Tue, 9 Sep 2025 13:43:20 +0200 Subject: [PATCH 282/361] xdg-shell: fix reported WM capabilities Previously it was reporting window_menu and maximize instead of fullscreen because wlr_xdg_toplevel_set_wm_capabilities expects a bitmask (WLR_XDG_TOPLEVEL_WM_CAPABILITIES_*), and XDG_TOPLEVEL_WM_CAPABILITIES_* are supposed to be used as values in wl_array, so the values are different: XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN = 3 WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN = 4 --- sway/desktop/xdg_shell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index db09e91d7..696a45ada 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -301,7 +301,7 @@ static void handle_commit(struct wl_listener *listener, void *data) { // XXX: https://github.com/swaywm/sway/issues/2176 wlr_xdg_surface_schedule_configure(xdg_surface); wlr_xdg_toplevel_set_wm_capabilities(view->wlr_xdg_toplevel, - XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); + WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); // TODO: wlr_xdg_toplevel_set_bounds() return; } From ca45c22376e8fadf6a8cfdbd29592f0026b726ad Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 9 Sep 2025 10:06:27 +0200 Subject: [PATCH 283/361] readme: mark display managers as supported It's been a long while since we've heard about issues related to display managers. Some used to be unreliable in the past, but by now most issues have been ironed out. Let's start supporting them officially. I've considered translating other languages but I'm concerned about messing up the sentence. I've updated languages I'm comfortable with. --- README.fr.md | 4 +--- README.ko.md | 2 +- README.md | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/README.fr.md b/README.fr.md index 7752fc70f..668bd4768 100644 --- a/README.fr.md +++ b/README.fr.md @@ -61,9 +61,7 @@ documentation pour la configuration de sway. ## Exécution -Exécutez `sway` à partir d'un TTY. Certains gestionnaires d'affichage peuvent -fonctionner, mais ne sont pas supportés par Sway (gdm est réputé pour assez -bien fonctionner). +Exécutez `sway` à partir d'un TTY ou d'un gestionnaires d'affichage. [Wayland]: http://wayland.freedesktop.org/ [i3]: https://i3wm.org/ diff --git a/README.ko.md b/README.ko.md index e086c174c..0d9c8c37e 100644 --- a/README.ko.md +++ b/README.ko.md @@ -52,4 +52,4 @@ i3를 이미 사용 중이라면, i3 config을 `~/.config/sway/config`로 복사 ## 실행 -TTY에서 `sway`를 실행하세요. 일부 display manager는 작동하지만, sway로 부터 지원되지 않습니다(gdm은 상당히 잘 작동한다고 알려져 있습니다). +TTY나 display manager에서 `sway`를 실행하세요. diff --git a/README.md b/README.md index 8e4234f0d..458017c1e 100644 --- a/README.md +++ b/README.md @@ -53,8 +53,7 @@ Run `man 5 sway` for information on the configuration. ## Running -Run `sway` from a TTY. Some display managers may work but are not supported by -sway (gdm is known to work fairly well). +Run `sway` from a TTY or from a display manager. [en]: https://github.com/swaywm/sway#readme [ar]: README.ar.md From 70c51c44f65826d1c8cd8b68467c5100dfb4f18d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20D=C3=B8rum?= Date: Thu, 11 Sep 2025 17:05:36 +0200 Subject: [PATCH 284/361] update danish readme to reflect that display managers are supported --- README.dk.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.dk.md b/README.dk.md index 5ce94cdef..4ae029492 100644 --- a/README.dk.md +++ b/README.dk.md @@ -54,8 +54,7 @@ Hvis du allerede bruger i3 kan du bare kopiere din i3 konfiguration til ## Eksekvering -Kør `sway` fra en TTY. Nogle display managers kan fungere, men Sway yder ikke -support til dem (gdm er kendt for at fungere temmelig godt). +Kør `sway` fra en TTY eller fra en display manager. [i3]: https://i3wm.org/ [Wayland]: http://wayland.freedesktop.org/ From e6fc3ffa3f2b8bd4beddbbebdeab6f39b80553ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Gasi=C5=84ski?= Date: Thu, 11 Sep 2025 22:59:01 +0200 Subject: [PATCH 285/361] readme: Update Polish translation regarding the support of display managers --- README.pl.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.pl.md b/README.pl.md index 65b3c3a1b..f7802d786 100644 --- a/README.pl.md +++ b/README.pl.md @@ -53,5 +53,4 @@ 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). +Wykonaj polecenie `sway` z poziomu TTY lub menedżera wyświetlania. From 68ac52ffc2702d4ba6c4901e439e1457226077ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oto=20=C5=A0=C5=A5=C3=A1va?= Date: Thu, 11 Sep 2025 17:00:04 +0200 Subject: [PATCH 286/361] readme: Update and reword Czech translation --- README.cs.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.cs.md b/README.cs.md index 14320a390..6cdc04a69 100644 --- a/README.cs.md +++ b/README.cs.md @@ -1,23 +1,24 @@ # sway -sway je s [i3] kompatibilní [Wayland] kompozitor. Přečtěte si [FAQ]. Připojte se na -[IRC kanál][IRC channel] \(#sway na irc.libera.chat). +sway je [waylandový][Wayland] kompozitor kompatibilní s [i3]. Přečtěte si +[FAQ (anglicky)][FAQ]. Připojte se na [IRC kanál (anglicky)][IRC channel] +\(#sway na irc.libera.chat). ## Podpisy vydání -Vydání jsou podepsána [E88F5E48] a publikována [na GitHubu][GitHub releases]. +Vydané verze jsou podepsány klíčem [E88F5E48] a publikovány +[na GitHubu][GitHub releases]. ## Instalace -### Z balíčků +### Z balíků -Sway je dostupný ve spoustě distribucí. Zkuste nainstalovat balíček "sway" ve vaší -distribuci. +Sway je dostupný v mnoha distribucích. Zkuste v té vaší nainstalovat balík "sway". ### Kompilace ze zdrojových kódů -Podívejte se na [tuto stránku wiki][Development setup], pokud chcete sestavit HEAD -sway a wlroots pro testování nebo vývoj. +Pokud chcete sestavit HEAD repozitáře sway a wlroots pro testování nebo vývoj, +použijte návod na [této stránce na wiki (anglicky)][Development setup]. Nainstalujte závislosti: @@ -29,9 +30,9 @@ Nainstalujte závislosti: * json-c * pango * cairo -* gdk-pixbuf2 (volitelné: oznamovací oblast) -* [swaybg] (volitelné: tapeta) -* [scdoc] (volitelné: manuálové stránky) \* +* gdk-pixbuf2 (volitelné: dodatečné formáty ikon pro oznamovací oblast) +* [swaybg] (volitelné: tapeta plochy) +* [scdoc] (volitelné: man stránky) \* * git (volitelné: informace o verzi) \* _\* Závislost pouze pro kompilaci_ @@ -51,8 +52,7 @@ Pro více informací o konfiguraci spusťte `man 5 sway`. ## Spuštění -Spusťte `sway` z TTY. Některé správce zobrazení mohou fungovat, ale nejsou -podporovány sway (je známo, že gdm funguje docela dobře). +Spusťte `sway` z TTY nebo ze správce displeje. [en]: https://github.com/swaywm/sway#readme [ar]: README.ar.md From 862e9b8c20307e6a6bb0f812bf54c70651dbb189 Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Fri, 12 Sep 2025 22:05:22 +0900 Subject: [PATCH 287/361] readme: sync Russian translation --- README.ru.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.ru.md b/README.ru.md index edc0eda78..ff4e6560b 100644 --- a/README.ru.md +++ b/README.ru.md @@ -54,8 +54,7 @@ _\*Зависимости для сборки_ ## Запуск -Выполните команду `sway` прямо из TTY. Некоторые дисплейные менеджеры могут работать, но они не поддерживаются со стороны -sway (gdm работает довольно неплохо). +Выполните команду `sway` прямо из TTY или дисплейного менеджера. [i3]: https://i3wm.org/ [Wayland]: http://wayland.freedesktop.org/ From 35b69158d7b76dc3dcaffd813d719dcc877a52ca Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Fri, 12 Sep 2025 22:08:15 +0900 Subject: [PATCH 288/361] readme: sync Japanese translation --- README.ja.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.ja.md b/README.ja.md index 4e9a9971d..3941fbc7d 100644 --- a/README.ja.md +++ b/README.ja.md @@ -52,5 +52,4 @@ _\*コンパイル時の依存_ ## 実行 -`sway`をTTYから実行してください。いくつかのディスプレイマネージャは動くかもしれませんが、Swayからサポートされていません(gdmは非常に良く動作することが知られています)。 - +`sway`をTTYまたはディスプレイマネージャから実行してください。 From a41b25020d46f26f0c8b6de66f29f06766fbc8dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20D=C3=B8rum?= Date: Fri, 12 Sep 2025 15:43:30 +0200 Subject: [PATCH 289/361] Update Norwegian README to make it more in line with the English one, as well as improving grammar --- README.no.md | 51 ++++++++++++++++++++++++--------------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/README.no.md b/README.no.md index 35d6e320f..c37d3f9b4 100644 --- a/README.no.md +++ b/README.no.md @@ -1,29 +1,25 @@ # Sway -Sway er en [i3]-kompatibel [Wayland] compositor. Les [Ofte stilte spørsmål]. -Delta på [IRC kanalen][IRC kanal] \(#sway på irc.libera.chat). +Sway er en [i3]-kompatibel [Wayland]-compositor. Les [Ofte stilte spørsmål]. +Delta på [IRC-kanalen][IRC-kanal] \(#sway på irc.libera.chat). -## Utgivelses Signaturer +## Signaturer -Utgivelser er signert med [E88F5E48] og publisert [på GitHub][GitHub -releases]. +Utgivelser er signert med [E88F5E48] og publisert [på GitHub][GitHub releases]. ## Installasjon -### Fra system pakker +### Fra systempakker -Sway er tilgjengelig i mange distribusjoner. Prøv å installere "sway" pakken +Sway er tilgjengelig i mange distribusjoner. Prøv å installere pakken "sway" fra din distro sine repoer. -Er du interessert i å pakke Sway for din distribusjon kan du ta turen innom -IRC-kanalen eller send en e-post til sir@cmpwn.com for råd. - ### Kompilering fra kildekode -Se [denne wiki-siden][Oppsetting for utvikling] hvis du vil bygge fra HEAD grenen av sway og -wlroots for testing eller utvikling. +Se [denne wiki-siden][Oppsetting for utvikling] hvis du vil bygge fra HEAD-grenen av +sway og wlroots for testing eller utvikling. -Installasjonsavhengigheter: +Installer avhengigheter: * meson \* * [wlroots] @@ -33,36 +29,37 @@ Installasjonsavhengigheter: * json-c * pango * cairo -* gdk-pixbuf2 (valgfritt: system tray) +* gdk-pixbuf2 (valgfritt: støtte for ekstra bildeformater i system tray) +* [swaybg] (valgfritt: bakgrunnsbilde) * [scdoc] (valgfritt: man pages) \* -* git \* +* git (valgfritt: versjonsinformasjon) \* -_\*Kompileringsavhengigheter_ +_\* Kompileringsavhengigheter_ Kjør følgende kommandoer: - meson build - ninja -C build - sudo ninja -C build install + meson build/ + ninja -C build/ + sudo ninja -C build/ install ## Konfigurasjon -Hvis du allerede bruker i3 kan du bare kopiere din i3 konfigurasjon til -`~/.config/sway/config`. Ellers skal du kopiere eksempel konfigurasjonsfilen til -`~/.config/sway/config`. Eksempel filen er normalt plasert i `/etc/sway/config`. Kjør -`man 5 sway` for å få oplysninger om konfigurasjonen. +Hvis du allerede bruker i3 kan du bare kopiere din i3-konfigurasjon til +`~/.config/sway/config`. Ellers skal du kopiere eksempelkonfigurasjonsfilen til +`~/.config/sway/config`. Eksempelfilen er normalt plasert i `/etc/sway/config`. +Kjør `man 5 sway` for å få opplysninger om konfigurasjonen. -## Utførelse +## Kjøring -Kjør `sway` fra en TTY. Noen display managers kan fungere, men Sway har ikke -støtte for dem (gdm er kjent for å fungere ganske bra). +Kjør `sway` fra en TTY eller fra en display manager. [i3]: https://i3wm.org/ [Wayland]: http://wayland.freedesktop.org/ [Ofte stilte spørsmål]: https://github.com/swaywm/sway/wiki -[IRC kanal]: https://web.libera.chat/gamja/?channels=#sway +[IRC-kanal]: https://web.libera.chat/gamja/?channels=#sway [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 [GitHub releases]: https://github.com/swaywm/sway/releases [Oppsetting for utvikling]: https://github.com/swaywm/sway/wiki/Development-Setup [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots +[swaybg]: https://github.com/swaywm/swaybg/ [scdoc]: https://git.sr.ht/~sircmpwn/scdoc From 50a8750e01f197826631db3c231b55ed48a24cc4 Mon Sep 17 00:00:00 2001 From: aceydot Date: Fri, 12 Sep 2025 19:55:36 +0200 Subject: [PATCH 290/361] Add Serbian README --- README.md | 3 ++- README.sr.md | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 README.sr.md diff --git a/README.md b/README.md index 458017c1e..f8f4b8f8b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # sway -**[English][en]** - [عربي][ar] - [Azərbaycanca][az] - [Česky][cs] - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - [ქართული][ge] - [Ελληνικά][gr] - [हिन्दी][hi] - [Magyar][hu] - [فارسی][ir] - [Italiano][it] - [日本語][ja] - [한국어][ko] - [Nederlands][nl] - [Norsk][no] - [Polski][pl] - [Português][pt] - [Română][ro] - [Русский][ru] - [Svenska][sv] - [Türkçe][tr] - [Українська][uk] - [中文-简体][zh-CN] - [中文-繁體][zh-TW] +**[English][en]** - [عربي][ar] - [Azərbaycanca][az] - [Česky][cs] - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - [ქართული][ge] - [Ελληνικά][gr] - [हिन्दी][hi] - [Magyar][hu] - [فارسی][ir] - [Italiano][it] - [日本語][ja] - [한국어][ko] - [Nederlands][nl] - [Norsk][no] - [Polski][pl] - [Português][pt] - [Română][ro] - [Русский][ru] - [Српски][sr] - [Svenska][sv] - [Türkçe][tr] - [Українська][uk] - [中文-简体][zh-CN] - [中文-繁體][zh-TW] sway is an [i3]-compatible [Wayland] compositor. Read the [FAQ]. Join the [IRC channel] \(#sway on irc.libera.chat). @@ -77,6 +77,7 @@ Run `sway` from a TTY or from a display manager. [pt]: README.pt.md [ro]: README.ro.md [ru]: README.ru.md +[sr]: README.sr.md [sv]: README.sv.md [tr]: README.tr.md [uk]: README.uk.md diff --git a/README.sr.md b/README.sr.md new file mode 100644 index 000000000..011b99831 --- /dev/null +++ b/README.sr.md @@ -0,0 +1,65 @@ +# sway + +sway је [i3]-компатибилан [Wayland] композитор. Прочитајте [FAQ]. Придружите се +[IRC каналу] \(#sway на irc.libera.chat). + +## Потписи Издања + +Издања су потписана са [E88F5E48] и објављена [на GitHub-у][GitHub releases]. + +## Инсталација + +### Из пакета + +Sway је доступан у многим дистрибуцијама. Покушајте да инсталирате "sway" пакет за +вашу. + +### Компајлирање из Извора + +Погледајте [ову вики страницу][Development setup], ако желите да компајлирате HEAD верзију +sway-а и wlroots-а за тестирање или развој. + +Инсталирајте зависности: + +* meson \* +* [wlroots] +* wayland +* wayland-protocols \* +* pcre2 +* json-c +* pango +* cairo +* gdk-pixbuf2 (опционо: додатни формати слика за системску траку) +* [swaybg] (опционо: позадина) +* [scdoc] (опционо: man странице) \* +* git (опционо: информације о верзији) \* + +_\* Потребно само за компајлирање_ + +Покрените следеће команде: + + meson build/ + ninja -C build/ + sudo ninja -C build/ install + +## Конфигурација + +Ако већ користите i3, копирајте вашу i3 конфигурацију у `~/.config/sway/config` и +радиће одмах. У супротном, копирајте пример конфигурационе датотеке у +`~/.config/sway/config`. Обично се налази у `/etc/sway/config`. +Покрените `man 5 sway` за информације о конфигурацији. + +## Покретање + +Покрените `sway` из TTY-a или из менаџера приказа. + +[i3]: https://i3wm.org/ +[Wayland]: http://wayland.freedesktop.org/ +[FAQ]: https://github.com/swaywm/sway/wiki +[IRC каналу]: https://web.libera.chat/gamja/?channels=#sway +[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 +[GitHub releases]: https://github.com/swaywm/sway/releases +[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup +[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots +[swaybg]: https://github.com/swaywm/swaybg/ +[scdoc]: https://git.sr.ht/~sircmpwn/scdoc From 7c1e192ea33c3b7b60b9fe2bef72a1e27cfb702d Mon Sep 17 00:00:00 2001 From: nortio Date: Sun, 14 Sep 2025 19:38:17 +0200 Subject: [PATCH 291/361] Update README.it.md for Italian Updated italian readme to follow the recent changes to the English one. I also removed "gestore di accesso" which in theory translates to display manager, but in practice is never used in this context. Other wikis such as [Debian](https://wiki.debian.org/it/DisplayManager) and [Ubuntu](https://wiki.ubuntu-it.org/AmbienteGrafico/DisplayManager) just use the term "Display Manager" as is. Another small thing that I corrected is "canale di IRC" which sounds a bit weird. --- README.it.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.it.md b/README.it.md index 82bb57837..272f61a02 100644 --- a/README.it.md +++ b/README.it.md @@ -1,7 +1,7 @@ # sway sway è un compositore di [Wayland] compatibile con [i3]. Leggi le [FAQ]. -Unisciti al [canale di IRC] \(#sway su irc.libera.chat). +Unisciti al [canale IRC] \(#sway su irc.libera.chat). ## Firma delle versioni @@ -52,13 +52,12 @@ configurazione. ## Esecuzione -Lancia `sway` da un TTY. Alcuni gestori d'accesso potrebbero funzionare ma non -sono supportati da sway (gdm funziona abbastanza bene). +Lancia `sway` da un TTY o da un display manager. [i3]: https://i3wm.org/ [Wayland]: http://wayland.freedesktop.org/ [FAQ]: https://github.com/swaywm/sway/wiki -[canale di IRC]: https://web.libera.chat/gamja/?channels=#sway +[canale IRC]: https://web.libera.chat/gamja/?channels=#sway [E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 [GitHub releases]: https://github.com/swaywm/sway/releases [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup From a7d9535eb3b2056cea6a88e174fb1e75c33469f2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 11 Sep 2025 11:21:01 +0200 Subject: [PATCH 292/361] input/text_input: stop using listener data These are now NULL. Fixes: aaab7f961e0b ("input/text_input: chase wlroots update") Closes: https://github.com/swaywm/sway/issues/8864 References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5107 --- sway/input/text_input.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/sway/input/text_input.c b/sway/input/text_input.c index 8dc64f019..e496bff1b 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -42,23 +42,21 @@ static void handle_im_commit(struct wl_listener *listener, void *data) { if (!text_input) { return; } - struct wlr_input_method_v2 *context = data; - assert(context == relay->input_method); - if (context->current.preedit.text) { + if (relay->input_method->current.preedit.text) { wlr_text_input_v3_send_preedit_string(text_input->input, - context->current.preedit.text, - context->current.preedit.cursor_begin, - context->current.preedit.cursor_end); + relay->input_method->current.preedit.text, + relay->input_method->current.preedit.cursor_begin, + relay->input_method->current.preedit.cursor_end); } - if (context->current.commit_text) { + if (relay->input_method->current.commit_text) { wlr_text_input_v3_send_commit_string(text_input->input, - context->current.commit_text); + relay->input_method->current.commit_text); } - if (context->current.delete.before_length - || context->current.delete.after_length) { + if (relay->input_method->current.delete.before_length + || relay->input_method->current.delete.after_length) { wlr_text_input_v3_send_delete_surrounding_text(text_input->input, - context->current.delete.before_length, - context->current.delete.after_length); + relay->input_method->current.delete.before_length, + relay->input_method->current.delete.after_length); } wlr_text_input_v3_send_done(text_input->input); } @@ -66,7 +64,7 @@ static void handle_im_commit(struct wl_listener *listener, void *data) { static void handle_im_keyboard_grab_destroy(struct wl_listener *listener, void *data) { struct sway_input_method_relay *relay = wl_container_of(listener, relay, input_method_keyboard_grab_destroy); - struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = data; + struct wlr_input_method_keyboard_grab_v2 *keyboard_grab = relay->input_method->keyboard_grab; struct wlr_seat *wlr_seat = keyboard_grab->input_method->seat; wl_list_remove(&relay->input_method_keyboard_grab_destroy.link); @@ -110,8 +108,6 @@ static void text_input_set_pending_focused_surface( static void handle_im_destroy(struct wl_listener *listener, void *data) { struct sway_input_method_relay *relay = wl_container_of(listener, relay, input_method_destroy); - struct wlr_input_method_v2 *context = data; - assert(context == relay->input_method); wl_list_remove(&relay->input_method_commit.link); wl_list_remove(&relay->input_method_grab_keyboard.link); wl_list_remove(&relay->input_method_destroy.link); @@ -322,8 +318,6 @@ static void handle_pending_focused_surface_destroy(struct wl_listener *listener, void *data) { struct sway_text_input *text_input = wl_container_of(listener, text_input, pending_focused_surface_destroy); - struct wlr_surface *surface = data; - assert(text_input->pending_focused_surface == surface); text_input->pending_focused_surface = NULL; wl_list_remove(&text_input->pending_focused_surface_destroy.link); wl_list_init(&text_input->pending_focused_surface_destroy.link); From bc96d0acdf8544d81df669566b50032333ce16d9 Mon Sep 17 00:00:00 2001 From: Felix Pehla <29adc1fd92@gmail.com> Date: Mon, 15 Sep 2025 12:47:58 +0200 Subject: [PATCH 293/361] README: update German translation Update the information about display manager support (see #8861), as well as markdown formatting of links and change some wording in the German README. --- README.de.md | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/README.de.md b/README.de.md index 68b411d95..d20cb4c99 100644 --- a/README.de.md +++ b/README.de.md @@ -1,21 +1,21 @@ # Sway -Sway ist ein [i3](https://i3wm.org/)-kompatibler [Wayland](http://wayland.freedesktop.org/)-Compositor. Lies die [FAQ](https://github.com/swaywm/sway/wiki). Tritt dem [IRC Channel](https://web.libera.chat/gamja/?channels=#sway) bei (#sway on irc.libera.chat; Englisch). +Sway ist ein [i3]-kompatibler [Wayland]-Compositor. Lies die [FAQ]. Tritt dem [IRC Channel] bei (#sway on irc.libera.chat; Englisch). ## Signaturen -Jedes Release wird mit dem PGP-Schlüssel [E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48) signiert und [auf GitHub](https://github.com/swaywm/sway/releases) veröffentlicht. +Jeder Release wird mit dem PGP-Schlüssel [E88F5E48] signiert und [auf GitHub][GitHub releases] veröffentlicht. ## Installation ### Über die Paketverwaltung -Sway kann in vielen Distributionen direkt durch die Paketverwaltung installiert werden. Versuche einfach das Packet "sway" zu installieren. +Sway kann in vielen Distributionen direkt durch die Paketverwaltung installiert werden. Versuche einfach das Paket "sway" zu installieren. ### Quellcode selbst kompilieren sway benötigt die folgenden Pakete: -* meson\* -* [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) +* meson \* +* [wlroots] * wayland * wayland-protocols\* * pcre2 @@ -23,21 +23,34 @@ sway benötigt die folgenden Pakete: * pango * cairo * gdk-pixbuf2 (Optional, wird für das Benachrichtigungsfeld (System Tray) benötigt) -* [scdoc](https://git.sr.ht/~sircmpwn/scdoc) (Optional, wird für die Dokumentation (Man Pages) benötigt)\* +* [swaybg] (Optional, wird für das Setzen von Desktophintergrundbildern benötigt) +* [scdoc] (Optional, wird für die Dokumentation (Man Pages) benötigt)\* * git (Optional: Versionsinfo)\* -_\*Werden nur während des Kompilierens benötigt_ +_\*Werden nur für das Kompilieren benötigt_ Führe die folgenden Befehle aus: - meson build - ninja -C build - sudo ninja -C build install + meson build/ + ninja -C build/ + sudo ninja -C build/ install + +Schaue in das [Wiki][Development setup] (Englisch) für Informationen, falls du zum Testen oder Entwickeln den neuesten Stand (HEAD) von sway und wlroots kompilieren willst. ## Konfiguration Falls du von i3 migrierst, kannst du deine Konfigurationsdatei nach `~/.config/sway/config` kopieren und die Einstellungen sollten ohne Weiteres funktionieren. Ansonsten kannst du die Beispielkonfiguration, die normalerweise in `/etc/sway/config` liegt, nach `~/.config/sway/config` kopieren. Die Dokumentation zur Konfigurationsdatei findest du in `man 5 sway`. ## Sway starten -Sway kann einfach mit dem Befehl `sway` vom TTY gestartet werden. -Display-Manager werden nicht offiziell unterstützt. Es gibt aber durchaus einige, die mit Sway funktionieren (z.B. gdm). +Sway kann einfach mit dem Befehl `sway` vom TTY oder mithilfe eines Displaymanagers gestartet werden. + +[i3]: https://i3wm.org/ +[Wayland]: http://wayland.freedesktop.org/ +[FAQ]: https://github.com/swaywm/sway/wiki +[IRC channel]: https://web.libera.chat/gamja/?channels=#sway +[E88F5E48]: https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48 +[GitHub releases]: https://github.com/swaywm/sway/releases +[Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup +[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots +[swaybg]: https://github.com/swaywm/swaybg +[scdoc]: https://git.sr.ht/~sircmpwn/scdoc From d9e615c507c1d669171e8222ef8b32bf8de07ed5 Mon Sep 17 00:00:00 2001 From: llyyr Date: Sun, 21 Sep 2025 06:44:24 +0530 Subject: [PATCH 294/361] sway/server: set color_manager for root scene Chase https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5122/ --- sway/server.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sway/server.c b/sway/server.c index 8ad62c719..4e606a391 100644 --- a/sway/server.c +++ b/sway/server.c @@ -467,7 +467,8 @@ bool server_init(struct sway_server *server) { WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, WP_COLOR_MANAGER_V1_PRIMARIES_BT2020, }; - wlr_color_manager_v1_create(server->wl_display, 1, &(struct wlr_color_manager_v1_options){ + struct wlr_color_manager_v1 *cm = wlr_color_manager_v1_create( + server->wl_display, 1, &(struct wlr_color_manager_v1_options){ .features = { .parametric = true, .set_mastering_display_primaries = true, @@ -479,6 +480,7 @@ bool server_init(struct sway_server *server) { .primaries = primaries, .primaries_len = sizeof(primaries) / sizeof(primaries[0]), }); + wlr_scene_set_color_manager_v1(root->root_scene, cm); } wl_list_init(&server->pending_launcher_ctxs); From b4a9a1716f70f080edf146f82698aa2c1bb99bcc Mon Sep 17 00:00:00 2001 From: nyxed Date: Thu, 2 Oct 2025 17:33:59 +0200 Subject: [PATCH 295/361] build: switch to explicit 'meson setup' syntax --- .builds/alpine.yml | 6 +++--- .builds/archlinux.yml | 4 ++-- .builds/freebsd.yml | 2 +- README.ar.md | 2 +- README.az.md | 2 +- README.cs.md | 2 +- README.de.md | 2 +- README.dk.md | 2 +- README.es.md | 2 +- README.fr.md | 2 +- README.ge.md | 2 +- README.gr.md | 2 +- README.hi.md | 2 +- README.hu.md | 2 +- README.ir.md | 2 +- README.it.md | 2 +- README.ja.md | 2 +- README.ko.md | 2 +- README.md | 2 +- README.nl.md | 2 +- README.no.md | 2 +- README.pl.md | 2 +- README.pt.md | 2 +- README.ro.md | 2 +- README.ru.md | 2 +- README.sr.md | 2 +- README.sv.md | 2 +- README.tr.md | 2 +- README.uk.md | 2 +- README.zh-CN.md | 2 +- README.zh-TW.md | 2 +- 31 files changed, 34 insertions(+), 34 deletions(-) diff --git a/.builds/alpine.yml b/.builds/alpine.yml index 7a1fa58e0..fa693906d 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml @@ -29,12 +29,12 @@ sources: tasks: - wlroots: | cd wlroots - meson --prefix=/usr build -Dexamples=false + meson setup --prefix=/usr build -Dexamples=false ninja -C build sudo ninja -C build install - setup: | cd sway - meson build --fatal-meson-warnings -Dauto_features=enabled -Dtray=disabled + meson setup build --fatal-meson-warnings -Dauto_features=enabled -Dtray=disabled - build: | cd sway ninja -C build @@ -52,5 +52,5 @@ tasks: mkdir subprojects ln -s ../../wlroots subprojects/wlroots rm -rf build - meson build --fatal-meson-warnings --default-library=static --force-fallback-for=wlroots + meson setup build --fatal-meson-warnings --default-library=static --force-fallback-for=wlroots ninja -C build diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index e249571ee..2c8ffcf45 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -26,12 +26,12 @@ sources: tasks: - wlroots: | cd wlroots - meson --prefix=/usr build -Dexamples=false + meson setup --prefix=/usr build -Dexamples=false ninja -C build sudo ninja -C build install - setup: | cd sway - meson build --fatal-meson-warnings -Dauto_features=enabled -Dsd-bus-provider=libsystemd + meson setup build --fatal-meson-warnings -Dauto_features=enabled -Dsd-bus-provider=libsystemd - build: | cd sway ninja -C build diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 977fe467e..a3df06e6d 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -39,7 +39,7 @@ tasks: cd subprojects ln -s ../../wlroots wlroots cd .. - meson build --fatal-meson-warnings -Dtray=enabled -Dsd-bus-provider=basu + meson setup build --fatal-meson-warnings -Dtray=enabled -Dsd-bus-provider=basu - build: | cd sway ninja -C build diff --git a/README.ar.md b/README.ar.md index 4f9bf828a..2f64f2b53 100644 --- a/README.ar.md +++ b/README.ar.md @@ -37,7 +37,7 @@ _\* Compile-time dep_ نفذ هذه الأوامر: - meson build/ + meson setup build/ ninja -C build/ sudo ninja -C build/ install diff --git a/README.az.md b/README.az.md index 3c5e0e413..50c8b0dec 100644 --- a/README.az.md +++ b/README.az.md @@ -38,7 +38,7 @@ _\* Kompilyasiya asılılıqları_ Bu əmrləri icra edin: - meson build/ + meson setup build/ ninja -C build/ sudo ninja -C build/ install diff --git a/README.cs.md b/README.cs.md index 6cdc04a69..0b4085e7f 100644 --- a/README.cs.md +++ b/README.cs.md @@ -39,7 +39,7 @@ _\* Závislost pouze pro kompilaci_ Spusťte tyto příkazy: - meson build/ + meson setup build/ ninja -C build/ sudo ninja -C build/ install diff --git a/README.de.md b/README.de.md index d20cb4c99..0c0b18637 100644 --- a/README.de.md +++ b/README.de.md @@ -31,7 +31,7 @@ _\*Werden nur für das Kompilieren benötigt_ Führe die folgenden Befehle aus: - meson build/ + meson setup build/ ninja -C build/ sudo ninja -C build/ install diff --git a/README.dk.md b/README.dk.md index 4ae029492..8e4cc3724 100644 --- a/README.dk.md +++ b/README.dk.md @@ -41,7 +41,7 @@ _\*Kompileringsafhængighed_ Kør følgende kommandoer: - meson build + meson setup build ninja -C build sudo ninja -C build install diff --git a/README.es.md b/README.es.md index 1f1657dfa..3ad8e29e3 100644 --- a/README.es.md +++ b/README.es.md @@ -40,7 +40,7 @@ _\*Compile-time dep_ Desde su consola, ejecute las órdenes: - meson build + meson setup build ninja -C build sudo ninja -C build install diff --git a/README.fr.md b/README.fr.md index 668bd4768..c4ef699a4 100644 --- a/README.fr.md +++ b/README.fr.md @@ -47,7 +47,7 @@ _\* Requis uniquement pour la compilation_ Exécutez ces commandes : - meson build + meson setup build ninja -C build sudo ninja -C build install diff --git a/README.ge.md b/README.ge.md index bb8b9a34c..de75be837 100644 --- a/README.ge.md +++ b/README.ge.md @@ -35,7 +35,7 @@ _\* Compile-time dep_ გაუშვით ეს ბრძანებები: - meson build/ + meson setup build/ ninja -C build/ sudo ninja -C build/ install diff --git a/README.gr.md b/README.gr.md index d697f78e3..629465846 100644 --- a/README.gr.md +++ b/README.gr.md @@ -40,7 +40,7 @@ _\*Compile-time dep_ Τρέξτε αυτά τα commands: - meson build/ + meson setup build/ ninja -C build/ sudo ninja -C build/ install diff --git a/README.hi.md b/README.hi.md index eae5e90a5..eb792b790 100644 --- a/README.hi.md +++ b/README.hi.md @@ -44,7 +44,7 @@ _\* Compilation के समय आवश्यक_ ये commands चलाएं: - meson build/ + meson setup build/ ninja -C build/ sudo ninja -C build/ install diff --git a/README.hu.md b/README.hu.md index 2fcc36dd5..b66a24b23 100644 --- a/README.hu.md +++ b/README.hu.md @@ -40,7 +40,7 @@ _\*Fordításidejű függőség_ Futtasd ezeket a parancsokat: - meson build + meson setup build ninja -C build sudo ninja -C build install diff --git a/README.ir.md b/README.ir.md index a485a4059..f7d00e803 100644 --- a/README.ir.md +++ b/README.ir.md @@ -41,7 +41,7 @@ _\*نیازمندی‌های زمان کامپایل برنامه_ این فرمان‌ها را اجرا کنید: - meson build + meson setup build ninja -C build sudo ninja -C build install diff --git a/README.it.md b/README.it.md index 272f61a02..30cdc21e0 100644 --- a/README.it.md +++ b/README.it.md @@ -38,7 +38,7 @@ _\* Dipendenza necessaria per la compilazione_ Esegui questi comandi: - meson build/ + meson setup build/ ninja -C build/ sudo ninja -C build/ install diff --git a/README.ja.md b/README.ja.md index 3941fbc7d..d6a8202e3 100644 --- a/README.ja.md +++ b/README.ja.md @@ -42,7 +42,7 @@ _\*コンパイル時の依存_ 次のコマンドを実行してください: - meson build + meson setup build ninja -C build sudo ninja -C build install diff --git a/README.ko.md b/README.ko.md index 0d9c8c37e..c9f31beff 100644 --- a/README.ko.md +++ b/README.ko.md @@ -39,7 +39,7 @@ _\*컴파일 떄 필요_ 다음 명령을 실행하세요: - meson build + meson setup build ninja -C build sudo ninja -C build install diff --git a/README.md b/README.md index f8f4b8f8b..882db57ee 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ _\* Compile-time dep_ Run these commands: - meson build/ + meson setup build/ ninja -C build/ sudo ninja -C build/ install diff --git a/README.nl.md b/README.nl.md index bf1ea9750..f2f7bb497 100644 --- a/README.nl.md +++ b/README.nl.md @@ -40,7 +40,7 @@ _\* Compileerafhankelijkheden_ Voer deze opdrachten uit: - meson build + meson setup build ninja -C build sudo ninja -C build install diff --git a/README.no.md b/README.no.md index c37d3f9b4..77676062d 100644 --- a/README.no.md +++ b/README.no.md @@ -38,7 +38,7 @@ _\* Kompileringsavhengigheter_ Kjør følgende kommandoer: - meson build/ + meson setup build/ ninja -C build/ sudo ninja -C build/ install diff --git a/README.pl.md b/README.pl.md index f7802d786..0c12a2b86 100644 --- a/README.pl.md +++ b/README.pl.md @@ -40,7 +40,7 @@ _\*zależności kompilacji_ Wykonaj następujące polecenia: - meson build + meson setup build ninja -C build sudo ninja -C build install diff --git a/README.pt.md b/README.pt.md index c1611a31c..4660e0bed 100644 --- a/README.pt.md +++ b/README.pt.md @@ -42,7 +42,7 @@ _\*Dependência de tempo de compilação_ Execute esses comandos: - meson build + meson setup build ninja -C build sudo ninja -C build install diff --git a/README.ro.md b/README.ro.md index a3559a8bb..1f63b5955 100644 --- a/README.ro.md +++ b/README.ro.md @@ -38,7 +38,7 @@ Dependențe pentru instalare: Rulați aceste comenzi: ``` - meson build + meson setup build ninja -C build sudo ninja -C build install ``` diff --git a/README.ru.md b/README.ru.md index ff4e6560b..5eee59efa 100644 --- a/README.ru.md +++ b/README.ru.md @@ -41,7 +41,7 @@ _\*Зависимости для сборки_ Выполните эти команды: - meson build + meson setup build ninja -C build sudo ninja -C build install diff --git a/README.sr.md b/README.sr.md index 011b99831..bd573566a 100644 --- a/README.sr.md +++ b/README.sr.md @@ -38,7 +38,7 @@ _\* Потребно само за компајлирање_ Покрените следеће команде: - meson build/ + meson setup build/ ninja -C build/ sudo ninja -C build/ install diff --git a/README.sv.md b/README.sv.md index 49caf9285..bff9d4a85 100644 --- a/README.sv.md +++ b/README.sv.md @@ -35,7 +35,7 @@ _\* Krav för kompilering_ Kör dessa kommandon: - meson build/ + meson setup build/ ninja -C build/ sudo ninja -C build/ install diff --git a/README.tr.md b/README.tr.md index 40de14748..3769ee755 100644 --- a/README.tr.md +++ b/README.tr.md @@ -38,7 +38,7 @@ _\*Derleme-anı bağımlılıkları_ Şu komutları çalıştırın: - meson build + meson setup build ninja -C build sudo ninja -C build install diff --git a/README.uk.md b/README.uk.md index 33359cffc..01c43afee 100644 --- a/README.uk.md +++ b/README.uk.md @@ -51,7 +51,7 @@ _\*Лише для компіляції_ Виконайте ці команди: - meson build + meson setup build ninja -C build sudo ninja -C build install diff --git a/README.zh-CN.md b/README.zh-CN.md index a6f4518ae..b733c71ab 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -35,7 +35,7 @@ _\*编译时依赖_ 运行如下命令: - meson build/ + meson setup build/ ninja -C build/ sudo ninja -C build/ install diff --git a/README.zh-TW.md b/README.zh-TW.md index 2de2f63fc..7cf848ea3 100644 --- a/README.zh-TW.md +++ b/README.zh-TW.md @@ -40,7 +40,7 @@ _\*編譯時相依_ 執行這些指令: - meson build + meson setup build ninja -C build sudo ninja -C build install From ecfea6b8aebcc6a7f663e2f23144e146dbe726d3 Mon Sep 17 00:00:00 2001 From: Louis POIROT--HATTERMANN Date: Thu, 2 Oct 2025 15:54:24 +0200 Subject: [PATCH 296/361] commands/scratchpad: don't hide scratchpad if no pending workspace Fixes: https://github.com/swaywm/sway/issues/8909 --- sway/tree/root.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/sway/tree/root.c b/sway/tree/root.c index 33c29d381..cf7170a48 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -214,9 +214,7 @@ void root_scratchpad_hide(struct sway_container *con) { struct sway_node *focus = seat_get_focus_inactive(seat, &root->node); struct sway_workspace *ws = con->pending.workspace; - if (con->pending.fullscreen_mode == FULLSCREEN_GLOBAL && !con->pending.workspace) { - // If the container was made fullscreen global while in the scratchpad, - // it should be shown until fullscreen has been disabled + if (!con->pending.workspace) { return; } From 90d3270970cc963454455b572883a051d3f376a1 Mon Sep 17 00:00:00 2001 From: llyyr Date: Mon, 29 Sep 2025 16:27:30 +0530 Subject: [PATCH 297/361] sway/input/cursor: send frame event when simulating pointer from tablet --- sway/input/cursor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index aeab558cb..df62ef41b 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -578,6 +578,7 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor, } else { wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tablet_v2_tool); pointer_motion(cursor, time_msec, input_device->wlr_device, dx, dy, dx, dy); + wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); } } From b7eb6177e1d634620d73d56224cf154e3b854e0f Mon Sep 17 00:00:00 2001 From: bonsaiiV <68749581+bonsaiiV@users.noreply.github.com> Date: Sat, 18 Oct 2025 16:55:46 +0000 Subject: [PATCH 298/361] sway/commands/layout: fix flatten parent once Fixes: f50e307227c8 ("sway/commands/layout: flatten parent once") --- sway/commands/layout.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sway/commands/layout.c b/sway/commands/layout.c index a32c908b8..22dfdf3d7 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -137,11 +137,15 @@ struct cmd_results *cmd_layout(int argc, char **argv) { // If parent has only a singe child operate on its parent and // flatten once, like i3 if (container && container->pending.children->length == 1) { - struct sway_container *child = container->pending.children->items[0]; + // Also check grandparent to avoid restricting layouts struct sway_container *parent = container->pending.parent; - container_replace(container, child); - container_begin_destroy(container); - container = parent; + if (parent && parent->pending.children->length == 1) { + struct sway_container *child = container->pending.children->items[0]; + struct sway_container *parent = container->pending.parent; + container_replace(container, child); + container_begin_destroy(container); + container = parent; + } } } From 055be4ec35eec4eaaf066a18ccbf5132ebed0694 Mon Sep 17 00:00:00 2001 From: llyyr Date: Wed, 29 Oct 2025 03:53:24 +0530 Subject: [PATCH 299/361] sway/server: advertise gamma2.2 and bt1886 transfer functions --- sway/server.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/server.c b/sway/server.c index 4e606a391..063b96331 100644 --- a/sway/server.c +++ b/sway/server.c @@ -462,6 +462,8 @@ bool server_init(struct sway_server *server) { WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB, WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ, WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR, + WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22, + WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886, }; const enum wp_color_manager_v1_primaries primaries[] = { WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, From d735bb99a9e97619e633257ea001b42e2bfe95a5 Mon Sep 17 00:00:00 2001 From: llyyr Date: Thu, 13 Nov 2025 21:21:09 +0530 Subject: [PATCH 300/361] sway/server: advertise wlr_color_representation_manager_v1 --- sway/server.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sway/server.c b/sway/server.c index 063b96331..7f399a2bb 100644 --- a/sway/server.c +++ b/sway/server.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -485,6 +486,9 @@ bool server_init(struct sway_server *server) { wlr_scene_set_color_manager_v1(root->root_scene, cm); } + wlr_color_representation_manager_v1_create_with_renderer( + server->wl_display, 1, server->renderer); + wl_list_init(&server->pending_launcher_ctxs); // Avoid using "wayland-0" as display socket From b54d1205d866a0bbc3b00c511dace77aa7113160 Mon Sep 17 00:00:00 2001 From: g-rden <94605617+g-rden@users.noreply.github.com> Date: Thu, 20 Nov 2025 09:21:25 +0000 Subject: [PATCH 301/361] swaynag/main: free config_path Otherwise config_path is never free'd --- swaynag/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/swaynag/main.c b/swaynag/main.c index b68157ff2..53c2203ff 100644 --- a/swaynag/main.c +++ b/swaynag/main.c @@ -45,6 +45,7 @@ int main(int argc, char **argv) { if (config_path) { sway_log(SWAY_DEBUG, "Loading config file: %s", config_path); status = swaynag_load_config(config_path, &swaynag, types); + free(config_path); if (status != 0) { goto cleanup; } From 842ea067737934823ccdef9adb8fbec6481bc472 Mon Sep 17 00:00:00 2001 From: Damino Date: Thu, 20 Nov 2025 21:03:11 -0500 Subject: [PATCH 302/361] tree/view: check for null workspace output Fixes #8950 --- sway/tree/view.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index 3d6211249..be813be98 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -307,7 +307,7 @@ void view_autoconfigure(struct sway_view *view) { } struct sway_output *output = ws ? ws->output : NULL; - if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { + if (output && con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { con->pending.content_x = output->lx; con->pending.content_y = output->ly; con->pending.content_width = output->width; From 897e7688bb415c40f8bc104bd526ddba537255f1 Mon Sep 17 00:00:00 2001 From: johnpyp Date: Mon, 17 Nov 2025 21:29:49 -0500 Subject: [PATCH 303/361] commit dirty transactions upon window attribute changes --- sway/desktop/xdg_shell.c | 3 +++ sway/desktop/xwayland.c | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 696a45ada..48638817c 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -354,6 +354,7 @@ static void handle_set_title(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_view->view; view_update_title(view, false); view_execute_criteria(view); + transaction_commit_dirty(); } static void handle_set_app_id(struct wl_listener *listener, void *data) { @@ -362,6 +363,7 @@ static void handle_set_app_id(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_view->view; view_update_app_id(view); view_execute_criteria(view); + transaction_commit_dirty(); } static void handle_new_popup(struct wl_listener *listener, void *data) { @@ -594,4 +596,5 @@ void xdg_toplevel_tag_manager_v1_handle_set_tag(struct wl_listener *listener, vo free(xdg_shell_view->tag); xdg_shell_view->tag = strdup(event->tag); view_execute_criteria(view); + transaction_commit_dirty(); } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 76e63ce1f..65016b3b1 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -689,6 +689,7 @@ static void handle_set_title(struct wl_listener *listener, void *data) { } view_update_title(view, false); view_execute_criteria(view); + transaction_commit_dirty(); } static void handle_set_class(struct wl_listener *listener, void *data) { @@ -700,6 +701,7 @@ static void handle_set_class(struct wl_listener *listener, void *data) { return; } view_execute_criteria(view); + transaction_commit_dirty(); } static void handle_set_role(struct wl_listener *listener, void *data) { @@ -711,6 +713,7 @@ static void handle_set_role(struct wl_listener *listener, void *data) { return; } view_execute_criteria(view); + transaction_commit_dirty(); } static void handle_set_startup_id(struct wl_listener *listener, void *data) { @@ -747,6 +750,7 @@ static void handle_set_window_type(struct wl_listener *listener, void *data) { return; } view_execute_criteria(view); + transaction_commit_dirty(); } static void handle_set_hints(struct wl_listener *listener, void *data) { From f4aba22582184c9a4a20fd7a9ffd70c63b4b393d Mon Sep 17 00:00:00 2001 From: rewine Date: Thu, 27 Nov 2025 22:03:48 +0800 Subject: [PATCH 304/361] Drop org_kde_kwin_idle files Has been replaced by ext_idle_notifier_v1 Signed-off-by: rewine --- protocols/idle.xml | 49 ------------------------------------------- protocols/meson.build | 1 - 2 files changed, 50 deletions(-) delete mode 100644 protocols/idle.xml diff --git a/protocols/idle.xml b/protocols/idle.xml deleted file mode 100644 index 92d9989c7..000000000 --- a/protocols/idle.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - . - ]]> - - - This interface allows to monitor user idle time on a given seat. The interface - allows to register timers which trigger after no user activity was registered - on the seat for a given interval. It notifies when user activity resumes. - - This is useful for applications wanting to perform actions when the user is not - interacting with the system, e.g. chat applications setting the user as away, power - management features to dim screen, etc.. - - - - - - - - - - - - - - - - - - - - - - diff --git a/protocols/meson.build b/protocols/meson.build index e7ada59b8..a1cb35344 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -12,7 +12,6 @@ protocols = [ wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml', 'wlr-layer-shell-unstable-v1.xml', - 'idle.xml', 'wlr-output-power-management-unstable-v1.xml', ] From fa81ce8ee64d690b881d97b734583e26cb2acb72 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 4 Oct 2025 11:54:31 +0200 Subject: [PATCH 305/361] Use helpers to get supported TFs/primaries References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5086 --- sway/server.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/sway/server.c b/sway/server.c index 7f399a2bb..3446cc97f 100644 --- a/sway/server.c +++ b/sway/server.c @@ -459,17 +459,12 @@ bool server_init(struct sway_server *server) { const enum wp_color_manager_v1_render_intent render_intents[] = { WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL, }; - const enum wp_color_manager_v1_transfer_function transfer_functions[] = { - WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB, - WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ, - WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR, - WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22, - WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886, - }; - const enum wp_color_manager_v1_primaries primaries[] = { - WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, - WP_COLOR_MANAGER_V1_PRIMARIES_BT2020, - }; + size_t transfer_functions_len = 0; + enum wp_color_manager_v1_transfer_function *transfer_functions = + wlr_color_manager_v1_transfer_function_list_from_renderer(server->renderer, &transfer_functions_len); + size_t primaries_len = 0; + enum wp_color_manager_v1_primaries *primaries = + wlr_color_manager_v1_primaries_list_from_renderer(server->renderer, &primaries_len); struct wlr_color_manager_v1 *cm = wlr_color_manager_v1_create( server->wl_display, 1, &(struct wlr_color_manager_v1_options){ .features = { @@ -479,10 +474,12 @@ bool server_init(struct sway_server *server) { .render_intents = render_intents, .render_intents_len = sizeof(render_intents) / sizeof(render_intents[0]), .transfer_functions = transfer_functions, - .transfer_functions_len = sizeof(transfer_functions) / sizeof(transfer_functions[0]), + .transfer_functions_len = transfer_functions_len, .primaries = primaries, - .primaries_len = sizeof(primaries) / sizeof(primaries[0]), + .primaries_len = primaries_len, }); + free(transfer_functions); + free(primaries); wlr_scene_set_color_manager_v1(root->root_scene, cm); } From 238f0d4a8b399f0df6791c47eb54c8636722d5a9 Mon Sep 17 00:00:00 2001 From: llyyr Date: Thu, 18 Dec 2025 23:02:32 +0530 Subject: [PATCH 306/361] tiling_resize: fix use-after-free on view unmap during resize Closing a tiled window (mod+shift+q) while resizing (mod+click) causes an use-after-free in handle_unref. Both conditions can be true in this case, which will result in dereferencing `e` on the second check after it has already been freed by the first `seatop_begin_default`. Fix by combining separate checks for the main container and its horizontal/vertical siblings into a single condition. The second check was added in 9e272a7986aa586a73951069aa76068e408a2c3f and I've checked that this fix does not regress that issue. --- sway/input/seatop_resize_tiling.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c index 15fd333b3..be7b3c128 100644 --- a/sway/input/seatop_resize_tiling.c +++ b/sway/input/seatop_resize_tiling.c @@ -105,10 +105,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { 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_begin_default(seat); - } - if (e->h_sib == con || e->v_sib == con) { + if (e->con == con || e->h_sib == con || e->v_sib == con) { seatop_begin_default(seat); } } From dbe86400357fb1d41693874f7d9f70f285fd1737 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 21 Dec 2025 13:52:52 +0100 Subject: [PATCH 307/361] desktop/xdg_shell: skip configure in request_maximize handler if unmapped wlr_xdg_surface_schedule_configure() requires the xdg_surface to be initialized. Mirror the early return from request_fullscreen. --- sway/desktop/xdg_shell.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 48638817c..7217e1369 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -386,6 +386,9 @@ static void handle_request_maximize(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_maximize); struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; + if (!toplevel->base->surface->mapped) { + return; + } wlr_xdg_surface_schedule_configure(toplevel->base); } From f654fe8edeefa17f2e63b9e21fd62aa7bb1ef122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Poisot?= Date: Thu, 30 Oct 2025 11:42:20 +0000 Subject: [PATCH 308/361] sway/config: apply output CT before initial commit Currently, config apply sets the output's image description before the initial commit, but sets the output's color transform after the commit. In the case of a config reload removing a color profile and enabling HDR, both the color transform and image description will be set, which trips an assert in wlroots --- sway/config/output.c | 51 +++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index ed8147b8b..b884a785d 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -562,8 +562,28 @@ static void queue_output_config(struct output_config *oc, set_hdr(wlr_output, pending, hdr); } +struct config_output_state { + struct wlr_color_transform *color_transform; +}; + +static void config_output_state_finish(struct config_output_state *state) { + wlr_color_transform_unref(state->color_transform); +} + +static struct wlr_color_transform *get_color_profile(struct wlr_output *output, + struct output_config *oc) { + if (oc && oc->set_color_transform) { + if (oc->color_transform) { + wlr_color_transform_ref(oc->color_transform); + } + return oc->color_transform; + } else { + return NULL; + } +} + static bool finalize_output_config(struct output_config *oc, struct sway_output *output, - const struct wlr_output_state *applied) { + const struct wlr_output_state *applied, const struct config_output_state *config_applied) { if (output == root->fallback_output) { return false; } @@ -609,16 +629,11 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output output_enable(output); } - if (oc && oc->set_color_transform) { - if (oc->color_transform) { - wlr_color_transform_ref(oc->color_transform); - } - wlr_color_transform_unref(output->color_transform); - output->color_transform = oc->color_transform; - } else { - wlr_color_transform_unref(output->color_transform); - output->color_transform = NULL; + wlr_color_transform_unref(output->color_transform); + if (config_applied->color_transform != NULL) { + wlr_color_transform_ref(config_applied->color_transform); } + output->color_transform = config_applied->color_transform; output->max_render_time = oc && oc->max_render_time > 0 ? oc->max_render_time : 0; output->allow_tearing = oc && oc->allow_tearing > 0; @@ -935,17 +950,25 @@ static bool apply_resolved_output_configs(struct matched_output_config *configs, if (!states) { return false; } + struct config_output_state *config_states = calloc(configs_len, sizeof(*config_states)); + if (!config_states) { + free(states); + return false; + } sway_log(SWAY_DEBUG, "Committing %zd outputs", configs_len); for (size_t idx = 0; idx < configs_len; idx++) { struct matched_output_config *cfg = &configs[idx]; struct wlr_backend_output_state *backend_state = &states[idx]; + struct config_output_state *config_state = &config_states[idx]; backend_state->output = cfg->output->wlr_output; wlr_output_state_init(&backend_state->base); queue_output_config(cfg->config, cfg->output, &backend_state->base); dump_output_state(cfg->output->wlr_output, &backend_state->base); + + config_state->color_transform = get_color_profile(cfg->output->wlr_output, cfg->config); } struct wlr_output_swapchain_manager swapchain_mgr; @@ -975,11 +998,12 @@ static bool apply_resolved_output_configs(struct matched_output_config *configs, for (size_t idx = 0; idx < configs_len; idx++) { struct matched_output_config *cfg = &configs[idx]; struct wlr_backend_output_state *backend_state = &states[idx]; + struct config_output_state *config_state = &config_states[idx]; struct wlr_scene_output_state_options opts = { .swapchain = wlr_output_swapchain_manager_get_swapchain( &swapchain_mgr, backend_state->output), - .color_transform = cfg->output->color_transform, + .color_transform = config_state->color_transform, }; struct wlr_scene_output *scene_output = cfg->output->scene_output; struct wlr_output_state *state = &backend_state->base; @@ -1003,9 +1027,10 @@ static bool apply_resolved_output_configs(struct matched_output_config *configs, for (size_t idx = 0; idx < configs_len; idx++) { struct matched_output_config *cfg = &configs[idx]; struct wlr_backend_output_state *backend_state = &states[idx]; + struct config_output_state *config_state = &config_states[idx]; sway_log(SWAY_DEBUG, "Finalizing config for %s", cfg->output->wlr_output->name); - finalize_output_config(cfg->config, cfg->output, &backend_state->base); + finalize_output_config(cfg->config, cfg->output, &backend_state->base, config_state); } // Output layout being applied in finalize_output_config can shift outputs @@ -1026,8 +1051,10 @@ out: for (size_t idx = 0; idx < configs_len; idx++) { struct wlr_backend_output_state *backend_state = &states[idx]; wlr_output_state_finish(&backend_state->base); + config_output_state_finish(&config_states[idx]); } free(states); + free(config_states); // Reconfigure all devices, since input config may have been applied before // this output came online, and some config items (like map_to_output) are From 776d445ec5f8a8d5791511c3909d2b02de7c3c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Poisot?= Date: Sat, 1 Nov 2025 16:15:26 +0000 Subject: [PATCH 309/361] sway/commands/output: color_profile "srgb" applies the piece-wise TF Document that "gamma22" replaced the previous default. --- sway/commands/output/color_profile.c | 13 +++++++++++-- sway/sway-output.5.scd | 4 ++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/sway/commands/output/color_profile.c b/sway/commands/output/color_profile.c index 98481329b..b145d449b 100644 --- a/sway/commands/output/color_profile.c +++ b/sway/commands/output/color_profile.c @@ -59,11 +59,19 @@ struct cmd_results *output_cmd_color_profile(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Missing color_profile first argument."); } - if (strcmp(*argv, "srgb") == 0) { + if (strcmp(*argv, "gamma22") == 0) { wlr_color_transform_unref(config->handler_context.output_config->color_transform); config->handler_context.output_config->color_transform = NULL; config->handler_context.output_config->set_color_transform = true; + config->handler_context.leftovers.argc = argc - 1; + config->handler_context.leftovers.argv = argv + 1; + } else if (strcmp(*argv, "srgb") == 0) { + wlr_color_transform_unref(config->handler_context.output_config->color_transform); + config->handler_context.output_config->color_transform = + wlr_color_transform_init_linear_to_inverse_eotf(WLR_COLOR_TRANSFER_FUNCTION_SRGB); + config->handler_context.output_config->set_color_transform = true; + config->handler_context.leftovers.argc = argc - 1; config->handler_context.leftovers.argv = argv + 1; } else if (strcmp(*argv, "icc") == 0) { @@ -106,7 +114,8 @@ struct cmd_results *output_cmd_color_profile(int argc, char **argv) { config->handler_context.leftovers.argv = argv + 2; } else { return cmd_results_new(CMD_INVALID, - "Invalid color profile specification: first argument should be icc|srgb"); + "Invalid color profile specification: " + "first argument should be gamma22|icc|srgb"); } return NULL; diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index cc48589c3..afe213643 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -178,8 +178,8 @@ must be separated by one space. For example: updated to work with different bit depths. This command is experimental, and may be removed or changed in the future. -*output* color_profile srgb|[icc ] - Sets the color profile for an output. The default is _srgb_. should be a +*output* color_profile gamma22|srgb|[icc ] + Sets the color profile for an output. The default is _gamma22_. should be a path to a display ICC profile. Not all renderers support this feature; currently it only works with the From 26eb393d6dd7631dc5b7edd0df95342d606e907c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Poisot?= Date: Fri, 5 Dec 2025 21:51:54 +0000 Subject: [PATCH 310/361] sway/commands/output: add color_profile "--device-primaries" When a display is connected, create a color transform from its self-reported color characteristics --- include/sway/config.h | 8 +++- sway/commands/output/color_profile.c | 18 +++++++-- sway/config/output.c | 59 +++++++++++++++++++++++++--- sway/sway-output.5.scd | 16 ++++++-- 4 files changed, 88 insertions(+), 13 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 3c380933e..16b822fea 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -267,6 +267,12 @@ enum render_bit_depth { RENDER_BIT_DEPTH_10, }; +enum color_profile { + COLOR_PROFILE_DEFAULT, // default is Transform with NULL color_transform + COLOR_PROFILE_TRANSFORM, // use color_transform from output_config + COLOR_PROFILE_TRANSFORM_WITH_DEVICE_PRIMARIES, // create transform from wlr_output +}; + /** * Size and position configuration for a particular output. * @@ -288,7 +294,7 @@ struct output_config { int max_render_time; // In milliseconds int adaptive_sync; enum render_bit_depth render_bit_depth; - bool set_color_transform; + enum color_profile color_profile; struct wlr_color_transform *color_transform; int allow_tearing; int hdr; diff --git a/sway/commands/output/color_profile.c b/sway/commands/output/color_profile.c index b145d449b..0456b19b7 100644 --- a/sway/commands/output/color_profile.c +++ b/sway/commands/output/color_profile.c @@ -55,6 +55,14 @@ struct cmd_results *output_cmd_color_profile(int argc, char **argv) { if (!config->handler_context.output_config) { return cmd_results_new(CMD_FAILURE, "Missing output config"); } + + enum color_profile new_mode = COLOR_PROFILE_TRANSFORM; + if (argc >= 2 && strcmp(*argv, "--device-primaries") == 0) { + new_mode = COLOR_PROFILE_TRANSFORM_WITH_DEVICE_PRIMARIES; + argc--; + argv++; + } + if (!argc) { return cmd_results_new(CMD_INVALID, "Missing color_profile first argument."); } @@ -62,7 +70,7 @@ struct cmd_results *output_cmd_color_profile(int argc, char **argv) { if (strcmp(*argv, "gamma22") == 0) { wlr_color_transform_unref(config->handler_context.output_config->color_transform); config->handler_context.output_config->color_transform = NULL; - config->handler_context.output_config->set_color_transform = true; + config->handler_context.output_config->color_profile = new_mode; config->handler_context.leftovers.argc = argc - 1; config->handler_context.leftovers.argv = argv + 1; @@ -70,7 +78,7 @@ struct cmd_results *output_cmd_color_profile(int argc, char **argv) { wlr_color_transform_unref(config->handler_context.output_config->color_transform); config->handler_context.output_config->color_transform = wlr_color_transform_init_linear_to_inverse_eotf(WLR_COLOR_TRANSFER_FUNCTION_SRGB); - config->handler_context.output_config->set_color_transform = true; + config->handler_context.output_config->color_profile = new_mode; config->handler_context.leftovers.argc = argc - 1; config->handler_context.leftovers.argv = argv + 1; @@ -79,6 +87,10 @@ struct cmd_results *output_cmd_color_profile(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Invalid color profile specification: icc type requires a file"); } + if (new_mode != COLOR_PROFILE_TRANSFORM) { + return cmd_results_new(CMD_INVALID, + "Invalid color profile specification: --device-primaries cannot be used with icc"); + } char *icc_path = strdup(argv[1]); if (!expand_path(&icc_path)) { @@ -108,7 +120,7 @@ struct cmd_results *output_cmd_color_profile(int argc, char **argv) { wlr_color_transform_unref(config->handler_context.output_config->color_transform); config->handler_context.output_config->color_transform = tmp; - config->handler_context.output_config->set_color_transform = true; + config->handler_context.output_config->color_profile = COLOR_PROFILE_TRANSFORM; config->handler_context.leftovers.argc = argc - 2; config->handler_context.leftovers.argv = argv + 2; diff --git a/sway/config/output.c b/sway/config/output.c index b884a785d..3d25b46c7 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -75,7 +75,7 @@ struct output_config *new_output_config(const char *name) { oc->max_render_time = -1; oc->adaptive_sync = -1; oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; - oc->set_color_transform = false; + oc->color_profile = COLOR_PROFILE_DEFAULT; oc->color_transform = NULL; oc->power = -1; oc->allow_tearing = -1; @@ -130,12 +130,12 @@ static void supersede_output_config(struct output_config *dst, struct output_con if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { dst->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; } - if (src->set_color_transform) { + if (src->color_profile != COLOR_PROFILE_DEFAULT) { if (dst->color_transform) { wlr_color_transform_unref(dst->color_transform); dst->color_transform = NULL; } - dst->set_color_transform = false; + dst->color_profile = COLOR_PROFILE_DEFAULT; } if (src->background) { free(dst->background); @@ -207,12 +207,12 @@ static void merge_output_config(struct output_config *dst, struct output_config if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { dst->render_bit_depth = src->render_bit_depth; } - if (src->set_color_transform) { + if (src->color_profile != COLOR_PROFILE_DEFAULT) { if (src->color_transform) { wlr_color_transform_ref(src->color_transform); } wlr_color_transform_unref(dst->color_transform); - dst->set_color_transform = true; + dst->color_profile = src->color_profile; dst->color_transform = src->color_transform; } if (src->background) { @@ -570,13 +570,60 @@ static void config_output_state_finish(struct config_output_state *state) { wlr_color_transform_unref(state->color_transform); } +static struct wlr_color_transform *color_profile_from_device(struct wlr_output *wlr_output, + struct wlr_color_transform *transfer_function) { + struct wlr_color_primaries srgb_primaries; + wlr_color_primaries_from_named(&srgb_primaries, WLR_COLOR_NAMED_PRIMARIES_SRGB); + + const struct wlr_color_primaries *primaries = wlr_output->default_primaries; + if (primaries == NULL) { + sway_log(SWAY_INFO, "output has no reported color information"); + if (transfer_function) { + wlr_color_transform_ref(transfer_function); + } + return transfer_function; + } else if (memcmp(primaries, &srgb_primaries, sizeof(*primaries)) == 0) { + sway_log(SWAY_INFO, "output reports sRGB colors, no correction needed"); + if (transfer_function) { + wlr_color_transform_ref(transfer_function); + } + return transfer_function; + } else { + sway_log(SWAY_INFO, "Creating color profile from reported color primaries: " + "R(%f, %f) G(%f, %f) B(%f, %f) W(%f, %f)", + primaries->red.x, primaries->red.y, primaries->green.x, primaries->green.y, + primaries->blue.x, primaries->blue.y, primaries->white.x, primaries->white.y); + float matrix[9]; + wlr_color_primaries_transform_absolute_colorimetric(&srgb_primaries, primaries, matrix); + struct wlr_color_transform *matrix_transform = wlr_color_transform_init_matrix(matrix); + if (matrix_transform == NULL) { + return NULL; + } + struct wlr_color_transform *resolved_tf = transfer_function ? + wlr_color_transform_ref(transfer_function) : + wlr_color_transform_init_linear_to_inverse_eotf(WLR_COLOR_TRANSFER_FUNCTION_GAMMA22); + if (resolved_tf == NULL) { + wlr_color_transform_unref(matrix_transform); + return NULL; + } + struct wlr_color_transform *transforms[] = { matrix_transform, resolved_tf }; + size_t transforms_len = sizeof(transforms) / sizeof(transforms[0]); + struct wlr_color_transform *result = wlr_color_transform_init_pipeline(transforms, transforms_len); + wlr_color_transform_unref(matrix_transform); + wlr_color_transform_unref(resolved_tf); + return result; + } +} + static struct wlr_color_transform *get_color_profile(struct wlr_output *output, struct output_config *oc) { - if (oc && oc->set_color_transform) { + if (oc && oc->color_profile == COLOR_PROFILE_TRANSFORM) { if (oc->color_transform) { wlr_color_transform_ref(oc->color_transform); } return oc->color_transform; + } else if (oc && oc->color_profile == COLOR_PROFILE_TRANSFORM_WITH_DEVICE_PRIMARIES) { + return color_profile_from_device(output, oc->color_transform); } else { return NULL; } diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index afe213643..faf59a1f2 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -178,9 +178,19 @@ must be separated by one space. For example: updated to work with different bit depths. This command is experimental, and may be removed or changed in the future. -*output* color_profile gamma22|srgb|[icc ] - Sets the color profile for an output. The default is _gamma22_. should be a - path to a display ICC profile. +*output* color_profile [--device-primaries] gamma22|srgb + Sets the color profile for an output. The default is _gamma22_. + + _--device-primaries_ will use the output's self-reported color primaries + when available (e.g. from display EDID). + + Not all renderers support this feature; currently it only works with the + the Vulkan renderer. It is not compatible with HDR support features. + +*output* color_profile icc + Sets the color profile for an output. + + should be a path to a display ICC profile. Not all renderers support this feature; currently it only works with the the Vulkan renderer. Even where supported, the application of the color From 723b1ea3b7617b6d61143d5cb723c59c916db515 Mon Sep 17 00:00:00 2001 From: llyyr Date: Mon, 5 Jan 2026 20:56:45 +0530 Subject: [PATCH 311/361] sway/input/seat: chase wlroots breaking change Ref: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5230 --- sway/input/seat.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/input/seat.c b/sway/input/seat.c index 1b63f625b..ab31b6746 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1015,9 +1015,9 @@ void seat_configure_xcursor(struct sway_seat *seat) { server.xwayland.xcursor_manager, "default", 1); if (xcursor != NULL) { struct wlr_xcursor_image *image = xcursor->images[0]; + struct wlr_buffer *buffer = wlr_xcursor_image_get_buffer(image); wlr_xwayland_set_cursor( - server.xwayland.wlr_xwayland, image->buffer, - image->width * 4, image->width, image->height, + server.xwayland.wlr_xwayland, buffer, image->hotspot_x, image->hotspot_y); } } From c09d0f1d4a10e958465b38d9b5cf379cdbdfd304 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 6 Jan 2026 00:19:57 +0100 Subject: [PATCH 312/361] Use goto-based error handling in view_init() This is less surprising than checking for the failed flag in the happy path. --- sway/tree/view.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index be813be98..7fd066978 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -43,20 +43,17 @@ bool view_init(struct sway_view *view, enum sway_view_type type, bool failed = false; view->scene_tree = alloc_scene_tree(root->staging, &failed); view->content_tree = alloc_scene_tree(view->scene_tree, &failed); + if (failed) { + goto err; + } - if (!failed && !scene_descriptor_assign(&view->scene_tree->node, - SWAY_SCENE_DESC_VIEW, view)) { - failed = true; + if (!scene_descriptor_assign(&view->scene_tree->node, SWAY_SCENE_DESC_VIEW, view)) { + goto err; } view->image_capture_scene = wlr_scene_create(); if (view->image_capture_scene == NULL) { - failed = true; - } - - if (failed) { - wlr_scene_node_destroy(&view->scene_tree->node); - return false; + goto err; } view->type = type; @@ -67,6 +64,10 @@ bool view_init(struct sway_view *view, enum sway_view_type type, view->tearing_mode = TEARING_WINDOW_HINT; wl_signal_init(&view->events.unmap); return true; + +err: + wlr_scene_node_destroy(&view->scene_tree->node); + return false; } void view_destroy(struct sway_view *view) { From 8224d5fcf89e2cab19c742538f191c67952c9d8b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 4 Jan 2026 17:52:58 +0100 Subject: [PATCH 313/361] Disable Xwayland restacking for toplevel capture scenes The main scene should drive Xwayland restacking. References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5108 --- sway/tree/view.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/tree/view.c b/sway/tree/view.c index 7fd066978..eab2a5e2b 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -55,6 +55,7 @@ bool view_init(struct sway_view *view, enum sway_view_type type, if (view->image_capture_scene == NULL) { goto err; } + view->image_capture_scene->restack_xwayland_surfaces = false; view->type = type; view->impl = impl; From 3c0588a44fbb190af2b6d137910c11cf2793c33e Mon Sep 17 00:00:00 2001 From: Rex Fernando Date: Tue, 6 Jan 2026 16:54:35 +0100 Subject: [PATCH 314/361] sway/input/cursor: fix crash on first stylus tap after reboot via null check handle_tool_tip() previously used event->tool->data without checking for NULL. When launching sway for the first time after a reboot, and then tapping with a USI 2.0 stylus, the tool tip event is fired before the tool proximity event. As event->tool is initialized during the tool proximity handler handle_tool_proximity(), this was causing a crash. The fix adds a NULL check before accessing the fields of event->tool->data. In case of a NULL, a log message is emitted indicating that the tool tip event fired before proximity. This logic is identical to the logic in handle_tool_axis(). Fixes issue #8907 --- sway/input/cursor.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index df62ef41b..fc0f11fd8 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -644,6 +644,11 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { cursor_handle_activity_from_device(cursor, &event->tablet->base); struct sway_tablet_tool *sway_tool = event->tool->data; + if (!sway_tool) { + sway_log(SWAY_DEBUG, "tool tip before proximity"); + return; + } + struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; struct sway_seat *seat = cursor->seat; From 40aabb80c645519107dc325abc53e4176e896fb9 Mon Sep 17 00:00:00 2001 From: Ole Date: Wed, 14 Jan 2026 00:12:18 +0100 Subject: [PATCH 315/361] sway-ipc(7): Clarify that the reply shown is an example Fix documentation of sway-ipc. The example reply for GET_BINDIG_STATE is an example and not the exact reply. This bug seems to come from copy/paste from the ipc command SYNC directly above. --- sway/sway-ipc.7.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index 4b0d5c962..2658228a1 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -1116,7 +1116,7 @@ Returns the currently active binding mode. A single object that contains the property _name_, which is set to the currently active binding mode as a string. -*Exact Reply:* +*Example Reply:* ``` { "name": "default" From e3c24125658c895bf23ab5ebddde55560d3f3ac5 Mon Sep 17 00:00:00 2001 From: llyyr Date: Thu, 29 Jan 2026 04:44:02 +0530 Subject: [PATCH 316/361] sway: advertise color_manager_v1 version 2 Ref: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5223 --- sway/server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/server.c b/sway/server.c index 3446cc97f..6003f4559 100644 --- a/sway/server.c +++ b/sway/server.c @@ -466,7 +466,7 @@ bool server_init(struct sway_server *server) { enum wp_color_manager_v1_primaries *primaries = wlr_color_manager_v1_primaries_list_from_renderer(server->renderer, &primaries_len); struct wlr_color_manager_v1 *cm = wlr_color_manager_v1_create( - server->wl_display, 1, &(struct wlr_color_manager_v1_options){ + server->wl_display, 2, &(struct wlr_color_manager_v1_options){ .features = { .parametric = true, .set_mastering_display_primaries = true, From b081eba05d463cace54eebc1e2f73b23f470949a Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Sat, 31 Jan 2026 17:17:07 -0500 Subject: [PATCH 317/361] bar_cmd_colors: fix crash on incorrect usage should display error message instead to be consistent with other subcommands. --- sway/commands/bar/colors.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sway/commands/bar/colors.c b/sway/commands/bar/colors.c index 275fa3c64..da606c1b8 100644 --- a/sway/commands/bar/colors.c +++ b/sway/commands/bar/colors.c @@ -72,6 +72,10 @@ static struct cmd_results *parse_three_colors(char ***colors, } struct cmd_results *bar_cmd_colors(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "colors", EXPECTED_AT_LEAST, 1))) { + return error; + } return config_subcommand(argv, argc, bar_colors_handlers, sizeof(bar_colors_handlers)); } From 992d201512a26b49f070f22b32e988d2bd51105e Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Tue, 3 Feb 2026 10:26:50 +0000 Subject: [PATCH 318/361] sway: change unsupported GPU message to swaynag This commit shows a swaynag message when an unsupported GPU is detected which must be acknowledged by users. It also adds an environment variable (`SWAY_UNSUPPORTED_GPU`) which may be used instead of the `--unsupported-gpu` argument. The `static` storage class for flag variables in main has also been removed, as this should have no effect on the program. Resolves: #8999 --- include/sway/server.h | 2 +- sway/main.c | 24 +++++++++++++++++++++++- sway/server.c | 17 +++-------------- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index f50d48f05..978f05d0a 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -160,7 +160,7 @@ struct sway_debug { extern struct sway_debug debug; -extern bool allow_unsupported_gpu; +extern bool unsupported_gpu_detected; void sway_terminate(int exit_code); diff --git a/sway/main.c b/sway/main.c index 44d07679e..e5dc497b3 100644 --- a/sway/main.c +++ b/sway/main.c @@ -224,7 +224,7 @@ static const char usage[] = "\n"; int main(int argc, char **argv) { - static bool verbose = false, debug = false, validate = false; + bool verbose = false, debug = false, validate = false, allow_unsupported_gpu = false; char *config_path = NULL; @@ -286,6 +286,12 @@ int main(int argc, char **argv) { exit(EXIT_FAILURE); } + char *unsupported_gpu_env = getenv("SWAY_UNSUPPORTED_GPU"); + // we let the flag override the environment variable + if (!allow_unsupported_gpu && unsupported_gpu_env) { + allow_unsupported_gpu = parse_boolean(unsupported_gpu_env, false); + } + // As the 'callback' function for wlr_log is equivalent to that for // sway, we do not need to override it. if (debug) { @@ -373,6 +379,18 @@ int main(int argc, char **argv) { swaynag_show(&config->swaynag_config_errors); } + struct swaynag_instance nag_gpu = (struct swaynag_instance){ + .args = "--type error " + "--message 'Proprietary GPU drivers are not supported by sway. Do not report issues.' ", + .detailed = false, + }; + + if (unsupported_gpu_detected && !allow_unsupported_gpu) { + if (!swaynag_spawn(config->swaynag_command, &nag_gpu)) { + sway_log(SWAY_ERROR, "Unable to start swaynag"); + } + } + server_run(&server); shutdown: @@ -385,6 +403,10 @@ shutdown: free(config_path); free_config(config); + if (nag_gpu.client != NULL) { + wl_client_destroy(nag_gpu.client); + } + pango_cairo_font_map_set_default(NULL); return exit_value; diff --git a/sway/server.c b/sway/server.c index 6003f4559..aef0d0b0c 100644 --- a/sway/server.c +++ b/sway/server.c @@ -78,7 +78,7 @@ #define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1 #define SWAY_PRESENTATION_VERSION 2 -bool allow_unsupported_gpu = false; +bool unsupported_gpu_detected = false; #if WLR_HAS_DRM_BACKEND static void handle_drm_lease_request(struct wl_listener *listener, void *data) { @@ -161,27 +161,16 @@ static void detect_proprietary(struct wlr_backend *backend, void *data) { return; } - bool is_unsupported = false; if (strcmp(version->name, "nvidia-drm") == 0) { - is_unsupported = true; + unsupported_gpu_detected = true; sway_log(SWAY_ERROR, "!!! Proprietary Nvidia drivers are in use !!!"); - if (!allow_unsupported_gpu) { - sway_log(SWAY_ERROR, "Use Nouveau instead"); - } } if (strcmp(version->name, "evdi") == 0) { - is_unsupported = true; + unsupported_gpu_detected = true; sway_log(SWAY_ERROR, "!!! Proprietary DisplayLink drivers are in use !!!"); } - if (!allow_unsupported_gpu && is_unsupported) { - sway_log(SWAY_ERROR, - "Proprietary drivers are NOT supported. To launch sway anyway, " - "launch with --unsupported-gpu and DO NOT report issues."); - exit(EXIT_FAILURE); - } - drmFreeVersion(version); } From 845cdb190f0c577789425cd9ea4772bd2fcfc836 Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Tue, 3 Feb 2026 14:51:08 +0000 Subject: [PATCH 319/361] sway: add details to unsupported gpu nag --- sway/main.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/sway/main.c b/sway/main.c index e5dc497b3..a94389266 100644 --- a/sway/main.c +++ b/sway/main.c @@ -381,14 +381,16 @@ int main(int argc, char **argv) { struct swaynag_instance nag_gpu = (struct swaynag_instance){ .args = "--type error " - "--message 'Proprietary GPU drivers are not supported by sway. Do not report issues.' ", - .detailed = false, + "--message 'Proprietary GPU drivers are not supported by sway. Do not report issues.' " + "--detailed-message", + .detailed = true, }; if (unsupported_gpu_detected && !allow_unsupported_gpu) { - if (!swaynag_spawn(config->swaynag_command, &nag_gpu)) { - sway_log(SWAY_ERROR, "Unable to start swaynag"); - } + swaynag_log(config->swaynag_command, &nag_gpu, + "To remove this message, launch sway with --unsupported-gpu " + "or set the environment variable SWAY_UNSUPPORTED_GPU=true."); + swaynag_show(&nag_gpu); } server_run(&server); From 438010938c38020c071841b43509185d67c462c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Mar=C3=A7ais?= Date: Mon, 12 Jan 2026 15:50:39 +0100 Subject: [PATCH 320/361] Fix logical error in criteria matching When evaluating a __focused__ pattern, sway's logic is to consider the pattern match to have failed for a given view and attribute if a view is focused and the value of attribute in question of the focused view is not that of the view in question. Expected behaviour is that the pattern matches if there is a focused view and that the values of the attribute for the focused view and the other view are equal. (See #6753, which is the case where the `workspace` attribute is matched against.). I.e., if we write out the function `criteria_matches_view` as a logical formula, expected behaviour is criteria_matches_view(criteria, view) <=> forall (attribute, pattern) in criteria, (pattern is __focused__ and focused view exists and view.attribute == focused.attribute) or (pattern is not __focused__ and view.attribute =~ pattern) but it is actually (pay attention to the fourth line) criteria_matches_view(criteria, view) <=> forall (attribute, pattern) in criteria, (pattern is __focused__ and (no focused view or view.attribute == focused.attribute)) or (pattern is not __focused__ and view.attribute =~ pattern). Fix program logic to reflect (the disjunctive form of) the first formula to be compatible with i3. (In passing, this fixes #6753.) --- sway/criteria.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/sway/criteria.c b/sway/criteria.c index e200d4c8f..230f47a18 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -204,7 +204,7 @@ static bool criteria_matches_view(struct criteria *criteria, switch (criteria->title->match_type) { case PATTERN_FOCUSED: - if (focused && lenient_strcmp(title, view_get_title(focused))) { + if (!focused || lenient_strcmp(title, view_get_title(focused))) { return false; } break; @@ -224,7 +224,7 @@ static bool criteria_matches_view(struct criteria *criteria, switch (criteria->shell->match_type) { case PATTERN_FOCUSED: - if (focused && strcmp(shell, view_get_shell(focused))) { + if (!focused || strcmp(shell, view_get_shell(focused))) { return false; } break; @@ -244,7 +244,7 @@ static bool criteria_matches_view(struct criteria *criteria, switch (criteria->app_id->match_type) { case PATTERN_FOCUSED: - if (focused && lenient_strcmp(app_id, view_get_app_id(focused))) { + if (!focused || lenient_strcmp(app_id, view_get_app_id(focused))) { return false; } break; @@ -264,7 +264,7 @@ static bool criteria_matches_view(struct criteria *criteria, switch (criteria->sandbox_engine->match_type) { case PATTERN_FOCUSED: - if (focused && lenient_strcmp(sandbox_engine, view_get_sandbox_engine(focused))) { + if (!focused || lenient_strcmp(sandbox_engine, view_get_sandbox_engine(focused))) { return false; } break; @@ -284,7 +284,7 @@ static bool criteria_matches_view(struct criteria *criteria, switch (criteria->sandbox_app_id->match_type) { case PATTERN_FOCUSED: - if (focused && lenient_strcmp(sandbox_app_id, view_get_sandbox_app_id(focused))) { + if (!focused || lenient_strcmp(sandbox_app_id, view_get_sandbox_app_id(focused))) { return false; } break; @@ -304,7 +304,7 @@ static bool criteria_matches_view(struct criteria *criteria, switch (criteria->sandbox_instance_id->match_type) { case PATTERN_FOCUSED: - if (focused && lenient_strcmp(sandbox_instance_id, view_get_sandbox_instance_id(focused))) { + if (!focused || lenient_strcmp(sandbox_instance_id, view_get_sandbox_instance_id(focused))) { return false; } break; @@ -324,7 +324,7 @@ static bool criteria_matches_view(struct criteria *criteria, switch (criteria->tag->match_type) { case PATTERN_FOCUSED: - if (focused && lenient_strcmp(tag, view_get_tag(focused))) { + if (!focused || lenient_strcmp(tag, view_get_tag(focused))) { return false; } break; @@ -356,7 +356,7 @@ static bool criteria_matches_view(struct criteria *criteria, switch (criteria->class->match_type) { case PATTERN_FOCUSED: - if (focused && lenient_strcmp(class, view_get_class(focused))) { + if (!focused || lenient_strcmp(class, view_get_class(focused))) { return false; } break; @@ -376,7 +376,7 @@ static bool criteria_matches_view(struct criteria *criteria, switch (criteria->instance->match_type) { case PATTERN_FOCUSED: - if (focused && lenient_strcmp(instance, view_get_instance(focused))) { + if (!focused || lenient_strcmp(instance, view_get_instance(focused))) { return false; } break; @@ -396,7 +396,7 @@ static bool criteria_matches_view(struct criteria *criteria, switch (criteria->window_role->match_type) { case PATTERN_FOCUSED: - if (focused && lenient_strcmp(window_role, view_get_window_role(focused))) { + if (!focused || lenient_strcmp(window_role, view_get_window_role(focused))) { return false; } break; @@ -454,7 +454,7 @@ static bool criteria_matches_view(struct criteria *criteria, switch (criteria->workspace->match_type) { case PATTERN_FOCUSED: - if (focused && + if (!focused || strcmp(ws->name, focused->container->pending.workspace->name)) { return false; } From fa497964fd55632beacf5f425e964ae4893e25b9 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 12 Feb 2026 21:51:54 +0100 Subject: [PATCH 321/361] Add support for keypad slide switches See the libinput docs: https://wayland.freedesktop.org/libinput/doc/latest/api/group__device.html#gga507e97278f2eb7d2271023ef2a3d31a4aed050386a5b52b2b51d05d95bb594c30 --- meson.build | 2 +- sway/commands/bind.c | 4 ++++ sway/sway.5.scd | 11 ++++++----- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/meson.build b/meson.build index de5a620c4..974a4071b 100644 --- a/meson.build +++ b/meson.build @@ -110,7 +110,7 @@ conf_data.set10('HAVE_LIBSYSTEMD', sdbus.found() and sdbus.name() == 'libsystemd conf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind') conf_data.set10('HAVE_BASU', sdbus.found() and sdbus.name() == 'basu') conf_data.set10('HAVE_TRAY', have_tray) -foreach sym : ['LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', 'LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY'] +foreach sym : ['LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', 'LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY', 'LIBINPUT_SWITCH_KEYPAD_SLIDE'] conf_data.set10('HAVE_' + sym, cc.has_header_symbol('libinput.h', sym, dependencies: libinput)) endforeach diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 15373d5a8..627d994a0 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -543,6 +543,10 @@ struct cmd_results *cmd_bind_or_unbind_switch(int argc, char **argv, binding->type = WLR_SWITCH_TYPE_TABLET_MODE; } else if (strcmp(split->items[0], "lid") == 0) { binding->type = WLR_SWITCH_TYPE_LID; +#if HAVE_LIBINPUT_SWITCH_KEYPAD_SLIDE + } else if (strcmp(split->items[0], "keypad_slide") == 0) { + binding->type = WLR_SWITCH_TYPE_KEYPAD_SLIDE; +#endif } else { free_switch_binding(binding); return cmd_results_new(CMD_FAILURE, diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 53393bcc9..952d243d2 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -506,11 +506,12 @@ runtime. *bindswitch* [--locked] [--no-warn] [--reload] : 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. + Supported switches are _lid_ (laptop lid), _tablet_ (tablet mode) and + _keypad_slide_ (whether the device keypad is exposed or not) switches. Valid + values for _state_ are _on_, _off_ and _toggle_. These switches are on when + the device lid is shut, when tablet mode is active and when the keypad is + exposed 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. If there is a matching binding with From 99e17d5efb28cf1017743a709aa28f2e0177f43f Mon Sep 17 00:00:00 2001 From: Milad Alizadeh Date: Wed, 18 Feb 2026 16:32:11 +0000 Subject: [PATCH 322/361] tree/workspace: fix output priority in workspace assignment workspace_valid_on_output() and workspace_next_name() check whether an output appears anywhere in a workspace's output list, ignoring priority order. This allows a lower-priority output to claim a workspace even when a higher-priority output is available. Fix by stopping early when iterating the output list: if a different available output is found before the current one, the workspace belongs to that higher-priority output. --- sway/tree/workspace.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 733a002b4..9a8e53d25 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -205,6 +205,9 @@ static bool workspace_valid_on_output(const char *output_name, if (output_match_name_or_id(output, wsc->outputs->items[i])) { return true; } + if (output_by_name_or_id(wsc->outputs->items[i])) { + return false; // a higher-priority output is available + } } return false; @@ -326,6 +329,9 @@ char *workspace_next_name(const char *output_name) { target = strdup(wsc->workspace); break; } + if (output_by_name_or_id(wsc->outputs->items[j])) { + break; // a higher-priority output is available + } } if (found) { break; From f66b69762d6844cee473c591dbf3789800ecb785 Mon Sep 17 00:00:00 2001 From: Milad Alizadeh Date: Fri, 20 Feb 2026 21:49:14 +0000 Subject: [PATCH 323/361] simplify output priority check --- sway/tree/workspace.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 9a8e53d25..49a830e7f 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -202,11 +202,10 @@ static bool workspace_valid_on_output(const char *output_name, } for (int i = 0; i < wsc->outputs->length; i++) { - if (output_match_name_or_id(output, wsc->outputs->items[i])) { - return true; - } - if (output_by_name_or_id(wsc->outputs->items[i])) { - return false; // a higher-priority output is available + struct sway_output *ws_output = + output_by_name_or_id(wsc->outputs->items[i]); + if (ws_output) { + return ws_output == output; } } @@ -323,15 +322,16 @@ char *workspace_next_name(const char *output_name) { } bool found = false; for (int j = 0; j < wsc->outputs->length; ++j) { - if (output_match_name_or_id(output, wsc->outputs->items[j])) { - found = true; - free(target); - target = strdup(wsc->workspace); + struct sway_output *ws_output = + output_by_name_or_id(wsc->outputs->items[j]); + if (ws_output) { + if (ws_output == output) { + found = true; + free(target); + target = strdup(wsc->workspace); + } break; } - if (output_by_name_or_id(wsc->outputs->items[j])) { - break; // a higher-priority output is available - } } if (found) { break; From ee61bf1d36fd743fab06ff0fce8204f7ff5523e3 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 24 Feb 2026 11:40:00 +0100 Subject: [PATCH 324/361] linux-dmabuf-v1: Bump to version 5 References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4459 --- sway/server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/server.c b/sway/server.c index aef0d0b0c..f58ea9471 100644 --- a/sway/server.c +++ b/sway/server.c @@ -272,7 +272,7 @@ bool server_init(struct sway_server *server) { if (wlr_renderer_get_texture_formats(server->renderer, WLR_BUFFER_CAP_DMABUF) != NULL) { server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( - server->wl_display, 4, server->renderer); + server->wl_display, 5, server->renderer); } if (wlr_renderer_get_drm_fd(server->renderer) >= 0 && server->renderer->features.timeline && From 468d4bc537e003b55ce7942db11396868b6e488a Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 24 Feb 2026 11:34:01 +0100 Subject: [PATCH 325/361] cursor-shape-v1: Bump to version 2 This adds two new shapes: dnd_ask and all_resize. References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4866 --- sway/server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/server.c b/sway/server.c index f58ea9471..cb8f4cf04 100644 --- a/sway/server.c +++ b/sway/server.c @@ -440,7 +440,7 @@ bool server_init(struct sway_server *server) { &server->xdg_toplevel_tag_manager_v1_set_tag); struct wlr_cursor_shape_manager_v1 *cursor_shape_manager = - wlr_cursor_shape_manager_v1_create(server->wl_display, 1); + wlr_cursor_shape_manager_v1_create(server->wl_display, 2); server->request_set_cursor_shape.notify = handle_request_set_cursor_shape; wl_signal_add(&cursor_shape_manager->events.request_set_shape, &server->request_set_cursor_shape); From 8b1f48d25b7d27ed9c3fe3e08a2b948e01d8deec Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 24 Feb 2026 11:55:04 +0100 Subject: [PATCH 326/361] layer-shell-v1: Bump to version 5 This adds support for set_exclusive_edge. References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5275 --- sway/server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/server.c b/sway/server.c index cb8f4cf04..5d446579f 100644 --- a/sway/server.c +++ b/sway/server.c @@ -74,7 +74,7 @@ #endif #define SWAY_XDG_SHELL_VERSION 5 -#define SWAY_LAYER_SHELL_VERSION 4 +#define SWAY_LAYER_SHELL_VERSION 5 #define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1 #define SWAY_PRESENTATION_VERSION 2 From d44248ce64c3fe3a477ab096a0e43c7c92241004 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 24 Feb 2026 14:58:43 +0100 Subject: [PATCH 327/361] protocols: Bump vendored wlr-protocols --- protocols/wlr-layer-shell-unstable-v1.xml | 21 +++++++++++++++++-- ...lr-output-power-management-unstable-v1.xml | 4 ++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/protocols/wlr-layer-shell-unstable-v1.xml b/protocols/wlr-layer-shell-unstable-v1.xml index d62fd51e9..e9f27e4fd 100644 --- a/protocols/wlr-layer-shell-unstable-v1.xml +++ b/protocols/wlr-layer-shell-unstable-v1.xml @@ -25,7 +25,7 @@ THIS SOFTWARE. - + Clients can use this interface to assign the surface_layer role to wl_surfaces. Such surfaces are assigned to a "layer" of the output and @@ -100,7 +100,7 @@ - + An interface that may be implemented by a wl_surface, for surfaces that are designed to be rendered as a layer of a stacked desktop-like @@ -367,6 +367,7 @@ + @@ -386,5 +387,21 @@ + + + + + + Requests an edge for the exclusive zone to apply. The exclusive + edge will be automatically deduced from anchor points when possible, + but when the surface is anchored to a corner, it will be necessary + to set it explicitly to disambiguate, as it is not possible to deduce + which one of the two corner edges should be used. + + The edge must be one the surface is anchored to, otherwise the + invalid_exclusive_edge protocol error will be raised. + + + diff --git a/protocols/wlr-output-power-management-unstable-v1.xml b/protocols/wlr-output-power-management-unstable-v1.xml index a97783991..20dbb7760 100644 --- a/protocols/wlr-output-power-management-unstable-v1.xml +++ b/protocols/wlr-output-power-management-unstable-v1.xml @@ -50,7 +50,7 @@ - Create a output power management mode control that can be used to + Create an output power management mode control that can be used to adjust the power management mode for a given output. @@ -79,7 +79,7 @@ - + From d6425c527a0b0af26e763f2a1201df7c7e24cbc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Poisot?= Date: Thu, 26 Feb 2026 20:40:04 +0000 Subject: [PATCH 328/361] sway/config/output: fix hdr+color_profile conflict detection Fixes: 26eb393d6dd7631dc5b7edd0df95342d606e907c `color_profile --device-primaries gamma22` was not inhibiting HDR like all other non-default color_profile values --- sway/config/output.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index 3d25b46c7..6d6afdc25 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -555,8 +555,10 @@ static void queue_output_config(struct output_config *oc, } bool hdr = oc && oc->hdr == 1; - if (hdr && oc->color_transform != NULL) { - sway_log(SWAY_ERROR, "Cannot HDR on output %s: output has an ICC profile set", wlr_output->name); + bool color_profile = oc && (oc->color_transform != NULL + || oc->color_profile == COLOR_PROFILE_TRANSFORM_WITH_DEVICE_PRIMARIES); + if (hdr && color_profile) { + sway_log(SWAY_ERROR, "Cannot use HDR on output %s: output has a color profile set", wlr_output->name); hdr = false; } set_hdr(wlr_output, pending, hdr); From 0356a020c1c14b4f9e9c3583d6a87dd7764714a2 Mon Sep 17 00:00:00 2001 From: "Lars-Ragnar A. Haugen" Date: Thu, 26 Feb 2026 16:34:37 +0100 Subject: [PATCH 329/361] layer-shell: handle popup reposition for unconstraining Layer shell popups were missing a handler for the xdg_popup reposition event. When a client (e.g. GTK4) creates a popup and then sends a reposition request, wlroots resets the scheduled geometry back to the positioner's original value. Without a reposition handler, the unconstrained geometry computed on the initial commit was lost, causing popups such as tooltips to render outside the screen viewport. This was most visible with GTK4 layer shell apps (e.g. taskbars) where tooltips would appear below the bottom edge of the screen instead of being flipped/slid into the visible area. Also switch the destroy listener from wlr_popup->base->events.destroy to wlr_popup->events.destroy so that cleanup runs before wlroots asserts that all popup signal listeners have been removed. Fixes #8518 --- include/sway/layers.h | 1 + sway/desktop/layer_shell.c | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/sway/layers.h b/include/sway/layers.h index 27b5dde1b..e257da0bd 100644 --- a/include/sway/layers.h +++ b/include/sway/layers.h @@ -33,6 +33,7 @@ struct sway_layer_popup { struct wl_listener destroy; struct wl_listener new_popup; struct wl_listener commit; + struct wl_listener reposition; }; struct sway_output; diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 8c54d71aa..c8f485971 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -321,6 +321,7 @@ static void popup_handle_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&popup->destroy.link); wl_list_remove(&popup->new_popup.link); wl_list_remove(&popup->commit.link); + wl_list_remove(&popup->reposition.link); free(popup); } @@ -356,6 +357,11 @@ static void popup_handle_commit(struct wl_listener *listener, void *data) { } } +static void popup_handle_reposition(struct wl_listener *listener, void *data) { + struct sway_layer_popup *popup = wl_container_of(listener, popup, reposition); + popup_unconstrain(popup); +} + static void popup_handle_new_popup(struct wl_listener *listener, void *data); static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup, @@ -376,11 +382,13 @@ static struct sway_layer_popup *create_popup(struct wlr_xdg_popup *wlr_popup, } popup->destroy.notify = popup_handle_destroy; - wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); + wl_signal_add(&wlr_popup->events.destroy, &popup->destroy); popup->new_popup.notify = popup_handle_new_popup; wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); popup->commit.notify = popup_handle_commit; wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); + popup->reposition.notify = popup_handle_reposition; + wl_signal_add(&wlr_popup->events.reposition, &popup->reposition); return popup; } From c57daaf0d1640b45579d75ce9775b8c0d03299b7 Mon Sep 17 00:00:00 2001 From: Willow Barraco Date: Wed, 25 Feb 2026 07:42:24 +0100 Subject: [PATCH 330/361] Skip checking if criteria is matching the view when not mapped Multiple checks in this method use view->container, but it can be NULL at some points. Bail early to avoid crash. --- sway/criteria.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sway/criteria.c b/sway/criteria.c index 230f47a18..6be6e7042 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -196,6 +196,10 @@ static bool criteria_matches_view(struct criteria *criteria, struct sway_container *focus = seat_get_focused_container(seat); struct sway_view *focused = focus ? focus->view : NULL; + if (!view->container) { + return false; + } + if (criteria->title) { const char *title = view_get_title(view); if (!title) { From 6d25b100a23a17e9663cab5c286934089f2c4460 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Fri, 13 Mar 2026 14:51:47 -0400 Subject: [PATCH 331/361] Chase wlroots!3879 https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3879 --- include/sway/tree/container.h | 3 +-- sway/tree/container.c | 51 ++++++++++++++++++----------------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index e18fd00ac..12a53c843 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -93,8 +93,7 @@ struct sway_container { struct wlr_scene_tree *content_tree; struct wlr_scene_buffer *output_handler; - struct wl_listener output_enter; - struct wl_listener output_leave; + struct wl_listener outputs_update; struct wl_listener output_handler_destroy; struct sway_container_state current; diff --git a/sway/tree/container.c b/sway/tree/container.c index c9ec852fc..fd9abadc6 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -25,27 +25,34 @@ #include "log.h" #include "stringop.h" -static void handle_output_enter( +static void handle_outputs_update( struct wl_listener *listener, void *data) { struct sway_container *con = wl_container_of( - listener, con, output_enter); - struct wlr_scene_output *output = data; + listener, con, outputs_update); + struct wlr_scene_outputs_update_event *event = data; - if (con->view->foreign_toplevel) { - wlr_foreign_toplevel_handle_v1_output_enter( - con->view->foreign_toplevel, output->output); - } -} + struct wlr_foreign_toplevel_handle_v1 *toplevel = con->view->foreign_toplevel; + if (toplevel) { + struct wlr_foreign_toplevel_handle_v1_output *toplevel_output, *tmp; + wl_list_for_each_safe(toplevel_output, tmp, &toplevel->outputs, link) { + bool active = false; + for (size_t i = 0; i < event->size; i++) { + struct wlr_scene_output *scene_output = event->active[i]; + if (scene_output->output == toplevel_output->output) { + active = true; + break; + } + } -static void handle_output_leave( - struct wl_listener *listener, void *data) { - struct sway_container *con = wl_container_of( - listener, con, output_leave); - struct wlr_scene_output *output = data; + if (!active) { + wlr_foreign_toplevel_handle_v1_output_leave(toplevel, toplevel_output->output); + } + } - if (con->view->foreign_toplevel) { - wlr_foreign_toplevel_handle_v1_output_leave( - con->view->foreign_toplevel, output->output); + for (size_t i = 0; i < event->size; i++) { + struct wlr_scene_output *scene_output = event->active[i]; + wlr_foreign_toplevel_handle_v1_output_enter(toplevel, scene_output->output); + } } } @@ -136,12 +143,9 @@ struct sway_container *container_create(struct sway_view *view) { } if (!failed) { - c->output_enter.notify = handle_output_enter; - wl_signal_add(&c->output_handler->events.output_enter, - &c->output_enter); - c->output_leave.notify = handle_output_leave; - wl_signal_add(&c->output_handler->events.output_leave, - &c->output_leave); + c->outputs_update.notify = handle_outputs_update; + wl_signal_add(&c->output_handler->events.outputs_update, + &c->outputs_update); c->output_handler_destroy.notify = handle_destroy; wl_signal_add(&c->output_handler->node.events.destroy, &c->output_handler_destroy); @@ -562,8 +566,7 @@ void container_begin_destroy(struct sway_container *con) { } if (con->view && con->view->container == con) { - wl_list_remove(&con->output_enter.link); - wl_list_remove(&con->output_leave.link); + wl_list_remove(&con->outputs_update.link); wl_list_remove(&con->output_handler_destroy.link); } } From 2c2a2ec38055092f368b44d4affefdfa8df17ff9 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 14 Mar 2026 13:08:24 +0100 Subject: [PATCH 332/361] common/pango: ensure we return zero on get_text_size() error Avoid leaving out pointers undefined. --- common/pango.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/pango.c b/common/pango.c index e52b52b93..9efa93f7e 100644 --- a/common/pango.c +++ b/common/pango.c @@ -84,6 +84,8 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc, void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height, int *baseline, double scale, bool markup, const char *fmt, ...) { + *width = *height = *baseline = 0; + va_list args; va_start(args, fmt); char *buf = vformat_str(fmt, args); From 9d0dbe66c307bbb227ce746d76b66b647350906b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 14 Mar 2026 13:09:52 +0100 Subject: [PATCH 333/361] common/pango: log on get_text_size()/render_text() format failure --- common/pango.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/pango.c b/common/pango.c index 9efa93f7e..5c7ff3076 100644 --- a/common/pango.c +++ b/common/pango.c @@ -91,6 +91,7 @@ void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, char *buf = vformat_str(fmt, args); va_end(args); if (buf == NULL) { + sway_log(SWAY_ERROR, "Failed to format string"); return; } @@ -127,6 +128,7 @@ void render_text(cairo_t *cairo, const PangoFontDescription *desc, char *buf = vformat_str(fmt, args); va_end(args); if (buf == NULL) { + sway_log(SWAY_ERROR, "Failed to format string"); return; } From 40e1dcd29f1950236e184bd653ee5ede55d8202b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 14 Mar 2026 13:21:55 +0100 Subject: [PATCH 334/361] common/pango: add error handling to get_text_size()/render_text() Closes: https://github.com/swaywm/sway/issues/9054 --- common/pango.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/common/pango.c b/common/pango.c index 5c7ff3076..2445b7227 100644 --- a/common/pango.c +++ b/common/pango.c @@ -97,12 +97,20 @@ void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, PangoLayout *layout = get_pango_layout(cairo, desc, buf, scale, markup); pango_cairo_update_layout(cairo, layout); + cairo_status_t status = cairo_status(cairo); + if (status != CAIRO_STATUS_SUCCESS) { + sway_log(SWAY_ERROR, "pango_cairo_update_layout() failed: %s", + cairo_status_to_string(status)); + goto out; + } + pango_layout_get_pixel_size(layout, width, height); if (baseline) { *baseline = pango_layout_get_baseline(layout) / PANGO_SCALE; } - g_object_unref(layout); +out: + g_object_unref(layout); free(buf); } @@ -137,9 +145,18 @@ void render_text(cairo_t *cairo, const PangoFontDescription *desc, cairo_get_font_options(cairo, fo); pango_cairo_context_set_font_options(pango_layout_get_context(layout), fo); cairo_font_options_destroy(fo); - pango_cairo_update_layout(cairo, layout); - pango_cairo_show_layout(cairo, layout); - g_object_unref(layout); + pango_cairo_update_layout(cairo, layout); + cairo_status_t status = cairo_status(cairo); + if (status != CAIRO_STATUS_SUCCESS) { + sway_log(SWAY_ERROR, "pango_cairo_update_layout() failed: %s", + cairo_status_to_string(status)); + goto out; + } + + pango_cairo_show_layout(cairo, layout); + +out: + g_object_unref(layout); free(buf); } From 85a4b19ac44f766561d4b47f587d97575c780ad0 Mon Sep 17 00:00:00 2001 From: llyyr Date: Fri, 20 Mar 2026 01:49:08 +0530 Subject: [PATCH 335/361] build: bump wlroots version Upstream bumped to 0.21.0-dev https://gitlab.freedesktop.org/wlroots/wlroots/-/commit/627da39e76d1f5a3cd18730bd305a0351bb1a121 --- meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 974a4071b..77d7ff613 100644 --- a/meson.build +++ b/meson.build @@ -39,14 +39,14 @@ if is_freebsd endif # Execute the wlroots subproject, if any -wlroots_version = ['>=0.20.0', '<0.21.0'] +wlroots_version = ['>=0.21.0', '<0.22.0'] subproject( 'wlroots', default_options: ['examples=false'], required: false, version: wlroots_version, ) -wlroots = dependency('wlroots-0.20', version: wlroots_version, fallback: 'wlroots') +wlroots = dependency('wlroots-0.21', version: wlroots_version, fallback: 'wlroots') wlroots_features = { 'xwayland': false, 'libinput_backend': false, From 82227d6103940bade22d7fa1e63256228df37b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Poisot?= Date: Fri, 20 Mar 2026 08:47:57 +0000 Subject: [PATCH 336/361] common/pango: get_text_size out pointers may be NULL Fixes: 2c2a2ec38055092f368b44d4affefdfa8df17ff9 Closes: https://github.com/swaywm/sway/issues/9082 --- common/pango.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/common/pango.c b/common/pango.c index 2445b7227..662b3699e 100644 --- a/common/pango.c +++ b/common/pango.c @@ -84,7 +84,15 @@ PangoLayout *get_pango_layout(cairo_t *cairo, const PangoFontDescription *desc, void get_text_size(cairo_t *cairo, const PangoFontDescription *desc, int *width, int *height, int *baseline, double scale, bool markup, const char *fmt, ...) { - *width = *height = *baseline = 0; + if (width) { + *width = 0; + } + if (height) { + *height = 0; + } + if (baseline) { + *baseline = 0; + } va_list args; va_start(args, fmt); From 8378c560c15906d365a49095deba96d2b560da47 Mon Sep 17 00:00:00 2001 From: Stephane Fontaine Date: Sat, 17 Jan 2026 16:12:24 +0400 Subject: [PATCH 337/361] call disable container in arrange_root --- sway/desktop/transaction.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 325a30226..d912b39bb 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -648,15 +648,7 @@ static void arrange_root(struct sway_root *root) { for (int i = 0; i < root->scratchpad->length; i++) { struct sway_container *con = root->scratchpad->items[i]; - // When a container is moved to a scratchpad, it's possible that it - // was moved into a floating container as part of the same transaction. - // In this case, we need to make sure we reparent all the container's - // children so that disabling the container will disable all descendants. - if (!con->view) for (int ii = 0; ii < con->current.children->length; ii++) { - struct sway_container *child = con->current.children->items[ii]; - wlr_scene_node_reparent(&child->scene_tree->node, con->content_tree); - } - + disable_container(con); wlr_scene_node_set_enabled(&con->scene_tree->node, false); } From e4870d84a204213b7dbf4d445ae07e2b9c84c7b1 Mon Sep 17 00:00:00 2001 From: llyyr Date: Sat, 21 Mar 2026 12:01:18 +0530 Subject: [PATCH 338/361] sway_text_node: fix cairo_create without a backing surface This fixes sway not being able to draw text on text nodes. cairo_create(NULL) returns a nil object in an error state rather than NULL, causing the null check to never trigger and passing a broken cairo context to get_text_size, which was fine until 40e1dcd29f19 adding error handling to it and causing pango_cairo_update_layout to fail with a NULL pointer. --- sway/sway_text_node.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c index 89ece91e2..46ddac93c 100644 --- a/sway/sway_text_node.c +++ b/sway/sway_text_node.c @@ -198,7 +198,10 @@ static void handle_destroy(struct wl_listener *listener, void *data) { static void text_calc_size(struct text_buffer *buffer) { struct sway_text_node *props = &buffer->props; - cairo_t *c = cairo_create(NULL); + cairo_surface_t *recorder = cairo_recording_surface_create( + CAIRO_CONTENT_COLOR_ALPHA, NULL); + cairo_t *c = cairo_create(recorder); + cairo_surface_destroy(recorder); if (!c) { sway_log(SWAY_ERROR, "cairo_t allocation failed"); return; From dea166a27c22fdac11a92d2194c5c2c2be8cedd8 Mon Sep 17 00:00:00 2001 From: llyyr Date: Sat, 21 Mar 2026 12:09:29 +0530 Subject: [PATCH 339/361] common/pango: use pangocairo directly instead of cairo_create(NULL) We never need a cairo context for anything here. Use pango_cairo_font_map_get_default() and pango_font_map_create_context() directly instead of bootstrapping via a nil cairo context. Same as last commit, but just a cosmetic fix in this case since we don't actually use the cairo context for anything --- common/pango.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/common/pango.c b/common/pango.c index 662b3699e..781d7b312 100644 --- a/common/pango.c +++ b/common/pango.c @@ -123,8 +123,8 @@ out: } void get_text_metrics(const PangoFontDescription *description, int *height, int *baseline) { - cairo_t *cairo = cairo_create(NULL); - PangoContext *pango = pango_cairo_create_context(cairo); + PangoFontMap *fontmap = pango_cairo_font_map_get_default(); + PangoContext *pango = pango_font_map_create_context(fontmap); pango_context_set_round_glyph_positions(pango, false); // When passing NULL as a language, pango uses the current locale. PangoFontMetrics *metrics = pango_context_get_metrics(pango, description, NULL); @@ -134,7 +134,6 @@ void get_text_metrics(const PangoFontDescription *description, int *height, int pango_font_metrics_unref(metrics); g_object_unref(pango); - cairo_destroy(cairo); } void render_text(cairo_t *cairo, const PangoFontDescription *desc, From 131045ce554f040224d25738b46524ab2fc23a3a Mon Sep 17 00:00:00 2001 From: llyyr Date: Sat, 21 Mar 2026 12:14:48 +0530 Subject: [PATCH 340/361] sway_text_node: properly check cairo_t status in text_calc_size cairo_create never returns NULL, so the previous null check never triggered. Use cairo_status instead. --- sway/sway_text_node.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c index 46ddac93c..c4fd3a260 100644 --- a/sway/sway_text_node.c +++ b/sway/sway_text_node.c @@ -202,18 +202,20 @@ static void text_calc_size(struct text_buffer *buffer) { CAIRO_CONTENT_COLOR_ALPHA, NULL); cairo_t *c = cairo_create(recorder); cairo_surface_destroy(recorder); - if (!c) { - sway_log(SWAY_ERROR, "cairo_t allocation failed"); - return; + if (cairo_status(c) != CAIRO_STATUS_SUCCESS) { + sway_log(SWAY_ERROR, "cairo_t allocation failed: %s", + cairo_status_to_string(cairo_status(c))); + goto out; } cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST); get_text_size(c, config->font_description, &props->width, NULL, &props->baseline, 1, props->pango_markup, "%s", buffer->text); - cairo_destroy(c); wlr_scene_buffer_set_dest_size(buffer->buffer_node, get_text_width(props), props->height); +out: + cairo_destroy(c); } struct sway_text_node *sway_text_node_create(struct wlr_scene_tree *parent, From 7ba11d6dee4b4bc4e916157e27159e671b90a446 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 12 Mar 2026 06:02:42 +0100 Subject: [PATCH 341/361] Make workspace_move_to_output reusable Move workspace_move_to_output out of the command handler, so it can be re-used for ext_workspace_handle_v1::assign. --- include/sway/tree/workspace.h | 3 +++ sway/commands/move.c | 36 ++--------------------------------- sway/tree/workspace.c | 32 +++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 34 deletions(-) diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index 27ed649fd..e8b9903e2 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -157,4 +157,7 @@ size_t workspace_num_sticky_containers(struct sway_workspace *ws); */ void workspace_squash(struct sway_workspace *workspace); +void workspace_move_to_output(struct sway_workspace *workspace, + struct sway_output *output); + #endif diff --git a/sway/commands/move.c b/sway/commands/move.c index 90e8585b4..43fce0d6d 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -627,40 +627,6 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, return cmd_results_new(CMD_SUCCESS, NULL); } -static void workspace_move_to_output(struct sway_workspace *workspace, - struct sway_output *output) { - if (workspace->output == output) { - return; - } - struct sway_output *old_output = workspace->output; - 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); - - // If moving the last workspace from the old output, create a new workspace - // on the old output - struct sway_seat *seat = config->handler_context.seat; - if (old_output->workspaces->length == 0) { - char *ws_name = workspace_next_name(old_output->wlr_output->name); - struct sway_workspace *ws = workspace_create(old_output, ws_name); - free(ws_name); - seat_set_raw_focus(seat, &ws->node); - } - - workspace_consider_destroy(new_output_old_ws); - - output_sort_workspaces(output); - struct sway_node *focus = seat_get_focus_inactive(seat, &workspace->node); - seat_set_focus(seat, focus); - workspace_output_raise_priority(workspace, old_output, output); - ipc_event_workspace(NULL, workspace, "move"); -} - static struct cmd_results *cmd_move_workspace(int argc, char **argv) { struct cmd_results *error = NULL; if ((error = checkarg(argc, "move workspace", EXPECTED_AT_LEAST, 1))) { @@ -696,6 +662,8 @@ static struct cmd_results *cmd_move_workspace(int argc, char **argv) { arrange_output(new_output); struct sway_seat *seat = config->handler_context.seat; + struct sway_node *focus = seat_get_focus_inactive(seat, &workspace->node); + seat_set_focus(seat, focus); 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 49a830e7f..e479cbad4 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -985,3 +985,35 @@ void workspace_squash(struct sway_workspace *workspace) { i += container_squash(child); } } + +void workspace_move_to_output(struct sway_workspace *workspace, + struct sway_output *output) { + if (workspace->output == output) { + return; + } + struct sway_output *old_output = workspace->output; + 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); + + // If moving the last workspace from the old output, create a new workspace + // on the old output + if (old_output->workspaces->length == 0) { + char *ws_name = workspace_next_name(old_output->wlr_output->name); + struct sway_workspace *ws = workspace_create(old_output, ws_name); + free(ws_name); + struct sway_seat *seat = input_manager_current_seat(); + seat_set_raw_focus(seat, &ws->node); + } + + workspace_consider_destroy(new_output_old_ws); + + output_sort_workspaces(output); + workspace_output_raise_priority(workspace, old_output, output); + ipc_event_workspace(NULL, workspace, "move"); +} From f50f78c0d92e441111c8183636f4c3c589aff8b1 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 12 Mar 2026 01:09:47 +0100 Subject: [PATCH 342/361] ext-workspace-v1: initial implementation Maintain a 1:1 relationship between workspace groups and outputs, so that moving a workspace across groups effectively moves it across outputs. ext_workspace_handle_v1::id is never emitted; sway has no concept of ids or of stable vs temporary workspaces. Everything is ephemeral to the current session. ext_workspace_handle_v1::coordinates is never emitted; sway does not organise workspaces into any sort of grid. ext_workspace_handle_v1::assign is mostly untested, because no client current implements this. Perhaps it's best to not-advertise the feature for now? Deactivating a workspace is a no-op. This functionality doesn't really align with sway, although it could potentially be implemented to "switch to previous workspace on this output" as a follow-up. Removing a workspace is a no-op. Implements: https://github.com/swaywm/sway/issues/8812 --- include/sway/output.h | 1 + include/sway/server.h | 3 + include/sway/tree/workspace.h | 6 ++ sway/commands/rename.c | 3 + sway/input/seat.c | 10 +++ sway/server.c | 3 + sway/tree/output.c | 8 ++ sway/tree/workspace.c | 143 +++++++++++++++++++++++++++++++++- 8 files changed, 176 insertions(+), 1 deletion(-) diff --git a/include/sway/output.h b/include/sway/output.h index 787527ee7..ae2e50d36 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -63,6 +63,7 @@ struct sway_output { struct wl_listener request_state; struct wlr_color_transform *color_transform; + struct wlr_ext_workspace_group_handle_v1 *ext_workspace_group; struct timespec last_presentation; uint32_t refresh_nsec; diff --git a/include/sway/server.h b/include/sway/server.h index 978f05d0a..318cd39c1 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -127,6 +127,9 @@ struct sway_server { struct wl_listener tearing_control_new_object; struct wl_list tearing_controllers; // sway_tearing_controller::link + struct wlr_ext_workspace_manager_v1 *workspace_manager_v1; + struct wl_listener workspace_manager_v1_commit; + struct wl_list pending_launcher_ctxs; // launcher_ctx::link // The timeout for transactions, after which a transaction is applied diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index e8b9903e2..7ce48f173 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -3,6 +3,7 @@ #include #include +#include #include "sway/config.h" #include "sway/tree/container.h" #include "sway/tree/node.h" @@ -51,6 +52,7 @@ struct sway_workspace { bool urgent; struct sway_workspace_state current; + struct wlr_ext_workspace_handle_v1 *ext_workspace; // Always set. }; struct workspace_config *workspace_find_config(const char *ws_name); @@ -160,4 +162,8 @@ void workspace_squash(struct sway_workspace *workspace); void workspace_move_to_output(struct sway_workspace *workspace, struct sway_output *output); +void sway_ext_workspace_init(void); +void sway_ext_workspace_output_enable(struct sway_output *output); +void sway_ext_workspace_output_disable(struct sway_output *output); + #endif diff --git a/sway/commands/rename.c b/sway/commands/rename.c index 0d36cc21e..63fac05cc 100644 --- a/sway/commands/rename.c +++ b/sway/commands/rename.c @@ -1,6 +1,7 @@ #include #include #include +#include #include "log.h" #include "stringop.h" #include "sway/commands.h" @@ -95,6 +96,8 @@ struct cmd_results *cmd_rename(int argc, char **argv) { free(workspace->name); workspace->name = new_name; + wlr_ext_workspace_handle_v1_set_name(workspace->ext_workspace, workspace->name); + output_sort_workspaces(workspace->output); ipc_event_workspace(NULL, workspace, "rename"); diff --git a/sway/input/seat.c b/sway/input/seat.c index ab31b6746..ebdbd91ed 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -1204,6 +1205,15 @@ static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *n ipc_event_window(container, "focus"); } + if (last_workspace && last_workspace != new_workspace) { + wlr_ext_workspace_handle_v1_set_active(last_workspace->ext_workspace, + workspace_is_visible(last_workspace)); + } + if (new_workspace) { + wlr_ext_workspace_handle_v1_set_active(new_workspace->ext_workspace, + workspace_is_visible(new_workspace)); + } + // Move sticky containers to new workspace if (new_workspace && new_output_last_ws && new_workspace != new_output_last_ws) { diff --git a/sway/server.c b/sway/server.c index 5d446579f..a49017424 100644 --- a/sway/server.c +++ b/sway/server.c @@ -63,6 +63,7 @@ #include "sway/server.h" #include "sway/input/cursor.h" #include "sway/tree/root.h" +#include "sway/tree/workspace.h" #if WLR_HAS_XWAYLAND #include @@ -377,6 +378,7 @@ bool server_init(struct sway_server *server) { wlr_foreign_toplevel_manager_v1_create(server->wl_display); sway_session_lock_init(); + sway_ext_workspace_init(); #if WLR_HAS_DRM_BACKEND server->drm_lease_manager= @@ -543,6 +545,7 @@ void server_fini(struct sway_server *server) { wl_list_remove(&server->xdg_toplevel_tag_manager_v1_set_tag.link); wl_list_remove(&server->request_set_cursor_shape.link); wl_list_remove(&server->new_foreign_toplevel_capture_request.link); + wl_list_remove(&server->workspace_manager_v1_commit.link); input_manager_finish(server->input); // TODO: free sway-specific resources diff --git a/sway/tree/output.c b/sway/tree/output.c index 90ec9331d..c401d0f1d 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -2,6 +2,8 @@ #include #include #include +#include +#include "sway/tree/workspace.h" #include "sway/ipc-server.h" #include "sway/layers.h" #include "sway/output.h" @@ -153,6 +155,7 @@ void output_enable(struct sway_output *output) { output->enabled = true; list_add(root->outputs, output); + sway_ext_workspace_output_enable(output); restore_workspaces(output); struct sway_workspace *ws = NULL; @@ -292,6 +295,7 @@ void output_disable(struct sway_output *output) { destroy_layers(output); output_evacuate(output); + sway_ext_workspace_output_disable(output); } void output_begin_destroy(struct sway_output *output) { @@ -333,6 +337,10 @@ void output_add_workspace(struct sway_output *output, } list_add(output->workspaces, workspace); workspace->output = output; + if (workspace->output && workspace->output->ext_workspace_group) { + wlr_ext_workspace_handle_v1_set_group(workspace->ext_workspace, + workspace->output->ext_workspace_group); + } node_set_dirty(&output->node); node_set_dirty(&workspace->node); } diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index e479cbad4..d366d19aa 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -3,8 +3,12 @@ #include #include #include +#include #include +#include +#include "log.h" #include "stringop.h" +#include "sway/desktop/transaction.h" #include "sway/input/input-manager.h" #include "sway/input/cursor.h" #include "sway/input/seat.h" @@ -17,9 +21,124 @@ #include "sway/tree/view.h" #include "sway/tree/workspace.h" #include "list.h" -#include "log.h" #include "util.h" +static const uint32_t WORKSPACE_CAPABILITIES = + EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ACTIVATE | + EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ASSIGN; + +static const uint32_t GROUP_CAPABILITIES = + EXT_WORKSPACE_GROUP_HANDLE_V1_GROUP_CAPABILITIES_CREATE_WORKSPACE; + +// Helper to find the output associated with a workspace group. +static struct sway_output *group_to_output( + struct wlr_ext_workspace_group_handle_v1 *group) { + for (int i = 0; i < root->outputs->length; i++) { + struct sway_output *output = root->outputs->items[i]; + if (output->ext_workspace_group == group) { + return output; + } + } + abort(); // unreachable +} + +// Callback for ext-workspace-v1 commit events. +static void handle_commit(struct wl_listener *listener, void *data) { + struct sway_server *server = + wl_container_of(listener, server, workspace_manager_v1_commit); + struct wlr_ext_workspace_v1_commit_event *event = data; + + struct wlr_ext_workspace_v1_request *req, *tmp; + wl_list_for_each_safe(req, tmp, event->requests, link) { + switch (req->type) { + case WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE: + if (req->activate.workspace) { + workspace_switch(req->activate.workspace->data); + } + break; + case WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE:; + struct sway_output *output = group_to_output(req->create_workspace.group); + sway_assert(output, "NULL output given to create_workspace"); + + char *name; + if (req->create_workspace.name) { + if (workspace_by_name(req->create_workspace.name)) { + sway_log(SWAY_ERROR, "Refusing to create workspace with duplicate name."); + break; // Already exists. + } + name = strdup(req->create_workspace.name); + } else { + name = workspace_next_name(output->wlr_output->name); + } + + struct sway_workspace *new_ws = workspace_create(output, name); + if (new_ws) { + workspace_switch(new_ws); + } + free(name); + break; + case WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN:; + if (!req->assign.workspace || !req->assign.group) break; + + struct sway_workspace *ws = req->assign.workspace->data; + struct sway_output *new_output = group_to_output(req->assign.group); + struct sway_output *old_output = ws->output; + workspace_move_to_output(ws, new_output); + arrange_output(old_output); + arrange_output(new_output); + break; + case WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE: + case WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE: + break; // No-op. + } + } + + transaction_commit_dirty(); +} + +// Initialize ext-workspace. Must be called once at startup. +void sway_ext_workspace_init(void) { + server.workspace_manager_v1 = + wlr_ext_workspace_manager_v1_create(server.wl_display, 1); + if (!server.workspace_manager_v1) { + sway_log(SWAY_ERROR, "Failed to create ext_workspace_manager_v1"); + return; + } + + server.workspace_manager_v1_commit.notify = handle_commit; + wl_signal_add(&server.workspace_manager_v1->events.commit, + &server.workspace_manager_v1_commit); +} + +// Must be called whenever an output is enabled. +void sway_ext_workspace_output_enable(struct sway_output *output) { + if (!output->wlr_output) { + return; + } + + output->ext_workspace_group = + wlr_ext_workspace_group_handle_v1_create( + server.workspace_manager_v1, GROUP_CAPABILITIES); + if (!output->ext_workspace_group) { + sway_log(SWAY_ERROR, "Failed to create workspace group for output '%s'", + output->wlr_output->name); + return; + } + + wlr_ext_workspace_group_handle_v1_output_enter( + output->ext_workspace_group, output->wlr_output); +} + +// Must be called whenever an output is disabled. +void sway_ext_workspace_output_disable(struct sway_output *output) { + if (!output->ext_workspace_group) { + return; + } + + wlr_ext_workspace_group_handle_v1_destroy(output->ext_workspace_group); + output->ext_workspace_group = NULL; +} + struct workspace_config *workspace_find_config(const char *ws_name) { for (int i = 0; i < config->workspace_configs->length; ++i) { struct workspace_config *wsc = config->workspace_configs->items[i]; @@ -70,6 +189,16 @@ struct sway_workspace *workspace_create(struct sway_output *output, sway_log(SWAY_ERROR, "Unable to allocate sway_workspace"); return NULL; } + + ws->ext_workspace = wlr_ext_workspace_handle_v1_create( + server.workspace_manager_v1, NULL, WORKSPACE_CAPABILITIES); + if (!ws->ext_workspace) { + sway_log(SWAY_ERROR, "Failed to create ext_workspace for '%s'", name); + free(ws); + return NULL; + } + ws->ext_workspace->data = ws; + node_init(&ws->node, N_WORKSPACE, ws); bool failed = false; @@ -79,6 +208,7 @@ struct sway_workspace *workspace_create(struct sway_output *output, if (failed) { wlr_scene_node_destroy(&ws->layers.tiling->node); wlr_scene_node_destroy(&ws->layers.fullscreen->node); + wlr_ext_workspace_handle_v1_destroy(ws->ext_workspace); free(ws); return NULL; } @@ -127,6 +257,13 @@ struct sway_workspace *workspace_create(struct sway_output *output, output_add_workspace(output, ws); output_sort_workspaces(output); + wlr_ext_workspace_handle_v1_set_name(ws->ext_workspace, ws->name); + if (ws->output && ws->output->ext_workspace_group) { + wlr_ext_workspace_handle_v1_set_group(ws->ext_workspace, + ws->output->ext_workspace_group); + } + wlr_ext_workspace_handle_v1_set_active(ws->ext_workspace, + workspace_is_visible(ws)); ipc_event_workspace(NULL, ws, "init"); wl_signal_emit_mutable(&root->events.new_node, &ws->node); @@ -163,6 +300,9 @@ void workspace_begin_destroy(struct sway_workspace *workspace) { ipc_event_workspace(NULL, workspace, "empty"); // intentional wl_signal_emit_mutable(&workspace->node.events.destroy, &workspace->node); + wlr_ext_workspace_handle_v1_destroy(workspace->ext_workspace); + workspace->ext_workspace = NULL; + if (workspace->output) { workspace_detach(workspace); } @@ -687,6 +827,7 @@ void workspace_detect_urgent(struct sway_workspace *workspace) { if (workspace->urgent != new_urgent) { workspace->urgent = new_urgent; + wlr_ext_workspace_handle_v1_set_urgent(workspace->ext_workspace, workspace->urgent); ipc_event_workspace(NULL, workspace, "urgent"); } } From ec7e0186e04528c483242202e4431dec79a8498b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 26 Mar 2026 16:30:37 +0100 Subject: [PATCH 343/361] build: bump version to 1.13-dev --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 77d7ff613..17d65c334 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'sway', 'c', - version: '1.12-dev', + version: '1.13-dev', license: 'MIT', meson_version: '>=1.3', default_options: [ From 909a2ddb5fff528e735341529a028d2ef21836db Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Mon, 23 Mar 2026 22:18:24 +0100 Subject: [PATCH 344/361] Centre fullscreen surfaces smaller than output Sway renders fullscreen surfaces smaller than the output left-aligned. From xdg-shell: > If the surface doesn't cover the whole output, the compositor will > position the surface in the center of the output and compensate with > with border fill covering the rest of the output. The content of the > border fill is undefined, but should be assumed to be in some way that > attempts to blend into the surrounding area (e.g. solid black). Render surfaces smaller than the output centred. Can be tested easily with: weston-simple-egl -f -r Fixes: https://github.com/swaywm/sway/issues/8845 --- sway/tree/view.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index eab2a5e2b..6dde3f63c 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -988,7 +988,7 @@ void view_center_and_clip_surface(struct sway_view *view) { bool clip_to_geometry = true; - if (container_is_floating(con)) { + if (container_is_floating(con) || con->pending.fullscreen_mode != FULLSCREEN_NONE) { // We always center the current coordinates rather than the next, as the // geometry immediately affects the currently active rendering. int x = (int) fmax(0, (con->current.content_width - view->geometry.width) / 2); From 1606311553cb58a280e320907098bd0f443fef6e Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 9 Apr 2026 02:09:59 +0200 Subject: [PATCH 345/361] Don't ignore initialisation errors server_init ignores all errors. In practice, theses result in a segfault, potentially much later and losing any unsaved work. Properly handle initialisation errors and bail immediately. --- include/sway/server.h | 2 +- include/sway/tree/workspace.h | 2 +- sway/lock.c | 7 +- sway/server.c | 229 +++++++++++++++++++++++++++++++--- sway/tree/workspace.c | 5 +- 5 files changed, 222 insertions(+), 23 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index 318cd39c1..8c8114882 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -176,7 +176,7 @@ void handle_new_output(struct wl_listener *listener, void *data); void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); void handle_layer_shell_surface(struct wl_listener *listener, void *data); -void sway_session_lock_init(void); +bool sway_session_lock_init(void); void sway_session_lock_add_output(struct sway_session_lock *lock, struct sway_output *output); bool sway_session_lock_has_surface(struct sway_session_lock *lock, diff --git a/include/sway/tree/workspace.h b/include/sway/tree/workspace.h index 7ce48f173..50bf02cd1 100644 --- a/include/sway/tree/workspace.h +++ b/include/sway/tree/workspace.h @@ -162,7 +162,7 @@ void workspace_squash(struct sway_workspace *workspace); void workspace_move_to_output(struct sway_workspace *workspace, struct sway_output *output); -void sway_ext_workspace_init(void); +bool sway_ext_workspace_init(void); void sway_ext_workspace_output_enable(struct sway_output *output); void sway_ext_workspace_output_disable(struct sway_output *output); diff --git a/sway/lock.c b/sway/lock.c index c8975c747..b1df65718 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -344,8 +344,12 @@ bool sway_session_lock_has_surface(struct sway_session_lock *lock, return false; } -void sway_session_lock_init(void) { +bool sway_session_lock_init(void) { server.session_lock.manager = wlr_session_lock_manager_v1_create(server.wl_display); + if (!server.session_lock.manager) { + sway_log(SWAY_ERROR, "Failed to create session lock manager"); + return false; + } server.session_lock.new_lock.notify = handle_session_lock; server.session_lock.manager_destroy.notify = handle_session_lock_destroy; @@ -353,4 +357,5 @@ void sway_session_lock_init(void) { &server.session_lock.new_lock); wl_signal_add(&server.session_lock.manager->events.destroy, &server.session_lock.manager_destroy); + return true; } diff --git a/sway/server.c b/sway/server.c index a49017424..8bdafb674 100644 --- a/sway/server.c +++ b/sway/server.c @@ -244,13 +244,25 @@ static void handle_new_foreign_toplevel_capture_request(struct wl_listener *list bool server_init(struct sway_server *server) { sway_log(SWAY_DEBUG, "Initializing Wayland server"); server->wl_display = wl_display_create(); + if (!server->wl_display) { + sway_log(SWAY_ERROR, "Failed to create wl_display"); + return false; + } server->wl_event_loop = wl_display_get_event_loop(server->wl_display); wl_display_set_global_filter(server->wl_display, filter_global, NULL); wl_display_set_default_max_buffer_size(server->wl_display, 1024 * 1024); - wlr_fixes_create(server->wl_display, 1); + if (!wlr_fixes_create(server->wl_display, 1)) { + sway_log(SWAY_ERROR, "Failed to create wp_fixes global"); + return false; + } root = root_create(server->wl_display); + if (!root) { + sway_log(SWAY_ERROR, "Failed to create root"); + wl_display_destroy(server->wl_display); + return false; + } server->backend = wlr_backend_autocreate(server->wl_event_loop, &server->session); if (!server->backend) { @@ -271,15 +283,23 @@ bool server_init(struct sway_server *server) { wlr_renderer_init_wl_shm(server->renderer, server->wl_display); - if (wlr_renderer_get_texture_formats(server->renderer, WLR_BUFFER_CAP_DMABUF) != NULL) { + if (wlr_renderer_get_drm_fd(server->renderer) >= 0 && + wlr_renderer_get_texture_formats(server->renderer, WLR_BUFFER_CAP_DMABUF) != NULL) { server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( server->wl_display, 5, server->renderer); + if (!server->linux_dmabuf_v1) { + sway_log(SWAY_ERROR, "Failed to create linux-dmabuf v1"); + return false; + } } if (wlr_renderer_get_drm_fd(server->renderer) >= 0 && server->renderer->features.timeline && server->backend->features.timeline) { - wlr_linux_drm_syncobj_manager_v1_create(server->wl_display, 1, - wlr_renderer_get_drm_fd(server->renderer)); + if (!wlr_linux_drm_syncobj_manager_v1_create(server->wl_display, 1, + wlr_renderer_get_drm_fd(server->renderer))) { + sway_log(SWAY_ERROR, "Failed to create linux-drm-syncobj v1"); + return false; + } } server->allocator = wlr_allocator_autocreate(server->backend, @@ -291,14 +311,29 @@ bool server_init(struct sway_server *server) { server->compositor = wlr_compositor_create(server->wl_display, 6, server->renderer); + if (!server->compositor) { + sway_log(SWAY_ERROR, "Failed to create compositor"); + return false; + } - wlr_subcompositor_create(server->wl_display); + if (!wlr_subcompositor_create(server->wl_display)) { + sway_log(SWAY_ERROR, "Failed to create subcompositor"); + return false; + } server->data_device_manager = wlr_data_device_manager_create(server->wl_display); + if (!server->data_device_manager) { + sway_log(SWAY_ERROR, "Failed to create data device manager"); + return false; + } server->gamma_control_manager_v1 = wlr_gamma_control_manager_v1_create(server->wl_display); + if (!server->gamma_control_manager_v1) { + sway_log(SWAY_ERROR, "Failed to create gamma control manager"); + return false; + } wlr_scene_set_gamma_control_manager_v1(root->root_scene, server->gamma_control_manager_v1); @@ -307,26 +342,53 @@ bool server_init(struct sway_server *server) { server->xdg_output_manager_v1 = wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); + if (!server->xdg_output_manager_v1) { + sway_log(SWAY_ERROR, "Failed to create XDG output manager"); + return false; + } server->idle_notifier_v1 = wlr_idle_notifier_v1_create(server->wl_display); - sway_idle_inhibit_manager_v1_init(); + if (!server->idle_notifier_v1) { + sway_log(SWAY_ERROR, "Failed to create idle notifier"); + return false; + } + if (!sway_idle_inhibit_manager_v1_init()) { + sway_log(SWAY_ERROR, "Failed to init idle inhibit manager"); + return false; + } server->layer_shell = wlr_layer_shell_v1_create(server->wl_display, SWAY_LAYER_SHELL_VERSION); + if (!server->layer_shell) { + sway_log(SWAY_ERROR, "Failed to create layer shell"); + return false; + } wl_signal_add(&server->layer_shell->events.new_surface, &server->layer_shell_surface); server->layer_shell_surface.notify = handle_layer_shell_surface; server->xdg_shell = wlr_xdg_shell_create(server->wl_display, SWAY_XDG_SHELL_VERSION); + if (!server->xdg_shell) { + sway_log(SWAY_ERROR, "Failed to create XDG shell"); + return false; + } wl_signal_add(&server->xdg_shell->events.new_toplevel, &server->xdg_shell_toplevel); server->xdg_shell_toplevel.notify = handle_xdg_shell_toplevel; server->tablet_v2 = wlr_tablet_v2_create(server->wl_display); + if (!server->tablet_v2) { + sway_log(SWAY_ERROR, "Failed to create tablet manager"); + return false; + } server->server_decoration_manager = wlr_server_decoration_manager_create(server->wl_display); + if (!server->server_decoration_manager) { + sway_log(SWAY_ERROR, "Failed to create server decoration manager"); + return false; + } wlr_server_decoration_manager_set_default_mode( server->server_decoration_manager, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); @@ -337,6 +399,10 @@ bool server_init(struct sway_server *server) { server->xdg_decoration_manager = wlr_xdg_decoration_manager_v1_create(server->wl_display); + if (!server->xdg_decoration_manager) { + sway_log(SWAY_ERROR, "Failed to create XDG decoration manager"); + return false; + } wl_signal_add( &server->xdg_decoration_manager->events.new_toplevel_decoration, &server->xdg_decoration); @@ -345,18 +411,36 @@ bool server_init(struct sway_server *server) { server->relative_pointer_manager = wlr_relative_pointer_manager_v1_create(server->wl_display); + if (!server->relative_pointer_manager) { + sway_log(SWAY_ERROR, "Failed to create relative pointer manager"); + return false; + } server->pointer_constraints = wlr_pointer_constraints_v1_create(server->wl_display); + if (!server->pointer_constraints) { + sway_log(SWAY_ERROR, "Failed to create pointer constraints"); + return false; + } server->pointer_constraint.notify = handle_pointer_constraint; wl_signal_add(&server->pointer_constraints->events.new_constraint, &server->pointer_constraint); - wlr_presentation_create(server->wl_display, server->backend, SWAY_PRESENTATION_VERSION); - wlr_alpha_modifier_v1_create(server->wl_display); + if (!wlr_presentation_create(server->wl_display, server->backend, SWAY_PRESENTATION_VERSION)) { + sway_log(SWAY_ERROR, "Failed to create presentation"); + return false; + } + if (!wlr_alpha_modifier_v1_create(server->wl_display)) { + sway_log(SWAY_ERROR, "Failed to create alpha modifier"); + return false; + } server->output_manager_v1 = wlr_output_manager_v1_create(server->wl_display); + if (!server->output_manager_v1) { + sway_log(SWAY_ERROR, "Failed to create output manager"); + return false; + } server->output_manager_apply.notify = handle_output_manager_apply; wl_signal_add(&server->output_manager_v1->events.apply, &server->output_manager_apply); @@ -366,19 +450,43 @@ bool server_init(struct sway_server *server) { server->output_power_manager_v1 = wlr_output_power_manager_v1_create(server->wl_display); + if (!server->output_power_manager_v1) { + sway_log(SWAY_ERROR, "Failed to create output power manager"); + return false; + } server->output_power_manager_set_mode.notify = handle_output_power_manager_set_mode; wl_signal_add(&server->output_power_manager_v1->events.set_mode, &server->output_power_manager_set_mode); server->input_method = wlr_input_method_manager_v2_create(server->wl_display); + if (!server->input_method) { + sway_log(SWAY_ERROR, "Failed to create input method manager"); + return false; + } server->text_input = wlr_text_input_manager_v3_create(server->wl_display); + if (!server->text_input) { + sway_log(SWAY_ERROR, "Failed to create text input manager"); + return false; + } server->foreign_toplevel_list = wlr_ext_foreign_toplevel_list_v1_create(server->wl_display, SWAY_FOREIGN_TOPLEVEL_LIST_VERSION); + if (!server->foreign_toplevel_list) { + sway_log(SWAY_ERROR, "Failed to create foreign toplevel list"); + return false; + } server->foreign_toplevel_manager = wlr_foreign_toplevel_manager_v1_create(server->wl_display); + if (!server->foreign_toplevel_manager) { + sway_log(SWAY_ERROR, "Failed to create foreign toplevel manager"); + return false; + } - sway_session_lock_init(); - sway_ext_workspace_init(); + if (!sway_session_lock_init()) { + return false; + } + if (!sway_ext_workspace_init()) { + return false; + } #if WLR_HAS_DRM_BACKEND server->drm_lease_manager= @@ -394,26 +502,74 @@ bool server_init(struct sway_server *server) { #endif server->export_dmabuf_manager_v1 = wlr_export_dmabuf_manager_v1_create(server->wl_display); + if (!server->export_dmabuf_manager_v1) { + sway_log(SWAY_ERROR, "Failed to create export dmabuf manager"); + return false; + } server->screencopy_manager_v1 = wlr_screencopy_manager_v1_create(server->wl_display); + if (!server->screencopy_manager_v1) { + sway_log(SWAY_ERROR, "Failed to create screencopy manager"); + return false; + } server->ext_image_copy_capture_manager_v1 = wlr_ext_image_copy_capture_manager_v1_create(server->wl_display, 1); - wlr_ext_output_image_capture_source_manager_v1_create(server->wl_display, 1); + if (!server->ext_image_copy_capture_manager_v1) { + sway_log(SWAY_ERROR, "Failed to create ext image copy capture manager"); + return false; + } + if (!wlr_ext_output_image_capture_source_manager_v1_create(server->wl_display, 1)) { + sway_log(SWAY_ERROR, "Failed to create ext output image capture source manager"); + return false; + } server->wlr_data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display); + if (!server->wlr_data_control_manager_v1) { + sway_log(SWAY_ERROR, "Failed to create data control manager"); + return false; + } server->ext_data_control_manager_v1 = wlr_ext_data_control_manager_v1_create(server->wl_display, 1); + if (!server->ext_data_control_manager_v1) { + sway_log(SWAY_ERROR, "Failed to create ext data control manager"); + return false; + } server->security_context_manager_v1 = wlr_security_context_manager_v1_create(server->wl_display); - wlr_viewporter_create(server->wl_display); - wlr_single_pixel_buffer_manager_v1_create(server->wl_display); + if (!server->security_context_manager_v1) { + sway_log(SWAY_ERROR, "Failed to create security context manager"); + return false; + } + if (!wlr_viewporter_create(server->wl_display)) { + sway_log(SWAY_ERROR, "Failed to create viewporter"); + return false; + } + if (!wlr_single_pixel_buffer_manager_v1_create(server->wl_display)) { + sway_log(SWAY_ERROR, "Failed to create single pixel buffer manager"); + return false; + } server->content_type_manager_v1 = wlr_content_type_manager_v1_create(server->wl_display, 1); - wlr_fractional_scale_manager_v1_create(server->wl_display, 1); + if (!server->content_type_manager_v1) { + sway_log(SWAY_ERROR, "Failed to create content type manager"); + return false; + } + if (!wlr_fractional_scale_manager_v1_create(server->wl_display, 1)) { + sway_log(SWAY_ERROR, "Failed to create fractional scale manager"); + return false; + } server->ext_foreign_toplevel_image_capture_source_manager_v1 = wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(server->wl_display, 1); + if (!server->ext_foreign_toplevel_image_capture_source_manager_v1) { + sway_log(SWAY_ERROR, "Failed to create ext foreign toplevel image capture source manager"); + return false; + } server->new_foreign_toplevel_capture_request.notify = handle_new_foreign_toplevel_capture_request; wl_signal_add(&server->ext_foreign_toplevel_image_capture_source_manager_v1->events.new_request, &server->new_foreign_toplevel_capture_request); server->tearing_control_v1 = wlr_tearing_control_manager_v1_create(server->wl_display, 1); + if (!server->tearing_control_v1) { + sway_log(SWAY_ERROR, "Failed to create tearing control manager"); + return false; + } server->tearing_control_new_object.notify = handle_new_tearing_hint; wl_signal_add(&server->tearing_control_v1->events.new_object, &server->tearing_control_new_object); @@ -421,10 +577,24 @@ bool server_init(struct sway_server *server) { struct wlr_xdg_foreign_registry *foreign_registry = wlr_xdg_foreign_registry_create(server->wl_display); - wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry); - wlr_xdg_foreign_v2_create(server->wl_display, foreign_registry); + if (!foreign_registry) { + sway_log(SWAY_ERROR, "Failed to create XDG foreign registry"); + return false; + } + if (!wlr_xdg_foreign_v1_create(server->wl_display, foreign_registry)) { + sway_log(SWAY_ERROR, "Failed to create XDG foreign v1"); + return false; + } + if (!wlr_xdg_foreign_v2_create(server->wl_display, foreign_registry)) { + sway_log(SWAY_ERROR, "Failed to create XDG foreign v2"); + return false; + } server->xdg_activation_v1 = wlr_xdg_activation_v1_create(server->wl_display); + if (!server->xdg_activation_v1) { + sway_log(SWAY_ERROR, "Failed to create XDG activation"); + return false; + } server->xdg_activation_v1_request_activate.notify = xdg_activation_v1_handle_request_activate; wl_signal_add(&server->xdg_activation_v1->events.request_activate, @@ -436,6 +606,10 @@ bool server_init(struct sway_server *server) { struct wlr_xdg_toplevel_tag_manager_v1 *xdg_toplevel_tag_manager_v1 = wlr_xdg_toplevel_tag_manager_v1_create(server->wl_display, 1); + if (!xdg_toplevel_tag_manager_v1) { + sway_log(SWAY_ERROR, "Failed to create XDG toplevel tag manager"); + return false; + } server->xdg_toplevel_tag_manager_v1_set_tag.notify = xdg_toplevel_tag_manager_v1_handle_set_tag; wl_signal_add(&xdg_toplevel_tag_manager_v1->events.set_tag, @@ -443,6 +617,10 @@ bool server_init(struct sway_server *server) { struct wlr_cursor_shape_manager_v1 *cursor_shape_manager = wlr_cursor_shape_manager_v1_create(server->wl_display, 2); + if (!cursor_shape_manager) { + sway_log(SWAY_ERROR, "Failed to create cursor shape manager"); + return false; + } server->request_set_cursor_shape.notify = handle_request_set_cursor_shape; wl_signal_add(&cursor_shape_manager->events.request_set_shape, &server->request_set_cursor_shape); @@ -471,11 +649,18 @@ bool server_init(struct sway_server *server) { }); free(transfer_functions); free(primaries); + if (!cm) { + sway_log(SWAY_ERROR, "Failed to create color manager"); + return false; + } wlr_scene_set_color_manager_v1(root->root_scene, cm); } - wlr_color_representation_manager_v1_create_with_renderer( - server->wl_display, 1, server->renderer); + if (!wlr_color_representation_manager_v1_create_with_renderer( + server->wl_display, 1, server->renderer)) { + sway_log(SWAY_ERROR, "Failed to create color representation manager"); + return false; + } wl_list_init(&server->pending_launcher_ctxs); @@ -515,8 +700,16 @@ bool server_init(struct sway_server *server) { } server->dirty_nodes = create_list(); + if (!server->dirty_nodes) { + sway_log(SWAY_ERROR, "Failed to create dirty nodes list"); + return false; + } server->input = input_manager_create(server); + if (!server->input) { + sway_log(SWAY_ERROR, "Failed to create input manager"); + return false; + } input_manager_get_default_seat(); // create seat0 return true; diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index d366d19aa..23311a456 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -97,17 +97,18 @@ static void handle_commit(struct wl_listener *listener, void *data) { } // Initialize ext-workspace. Must be called once at startup. -void sway_ext_workspace_init(void) { +bool sway_ext_workspace_init(void) { server.workspace_manager_v1 = wlr_ext_workspace_manager_v1_create(server.wl_display, 1); if (!server.workspace_manager_v1) { sway_log(SWAY_ERROR, "Failed to create ext_workspace_manager_v1"); - return; + return false; } server.workspace_manager_v1_commit.notify = handle_commit; wl_signal_add(&server.workspace_manager_v1->events.commit, &server.workspace_manager_v1_commit); + return true; } // Must be called whenever an output is enabled. From 81246fc6dcdb1087fc02ea307b1c4b64e24395c3 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Fri, 20 Mar 2026 23:29:30 -0400 Subject: [PATCH 346/361] container: Move foreign toplevel enter/leave events to view It made sense to put it on the container level because the protocol cares about the toplevel and that includes its decorations. But, this breaks down when we consider if the container's view is fullscreen and the container decorations are disabled. Moving it to the view manages this expected lifetime better. Since the buffer is now part of the view, the buffer will get negative coordinates to act as if it's part of the container when we want to. A known issue is that we will send spurious leave/enter events while we reconfigure the scene for entering/exiting fullscreen. The fix for this loops back to atomic updates to scene and that is outside of the scope of this commit. Fixes: #9000 --- include/sway/tree/container.h | 4 --- include/sway/tree/view.h | 3 ++ sway/desktop/transaction.c | 16 ++++++--- sway/tree/container.c | 66 ----------------------------------- sway/tree/view.c | 53 ++++++++++++++++++++++++++-- 5 files changed, 66 insertions(+), 76 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 12a53c843..d8780544d 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -91,10 +91,6 @@ struct sway_container { } border; struct wlr_scene_tree *content_tree; - struct wlr_scene_buffer *output_handler; - - struct wl_listener outputs_update; - struct wl_listener output_handler_destroy; struct sway_container_state current; struct sway_container_state pending; diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index ae81c5bbf..26148a92a 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -69,6 +69,9 @@ struct sway_view { struct wlr_scene_tree *scene_tree; struct wlr_scene_tree *content_tree; struct wlr_scene_tree *saved_surface_tree; + struct wlr_scene_buffer *output_handler; + + struct wl_listener outputs_update; struct wlr_scene *image_capture_scene; struct wlr_ext_image_capture_source_v1 *image_capture_source; diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index d912b39bb..d4e853dee 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -395,10 +395,6 @@ static void arrange_container(struct sway_container *con, // make sure it's enabled for viewing wlr_scene_node_set_enabled(&con->scene_tree->node, true); - if (con->output_handler) { - wlr_scene_buffer_set_dest_size(con->output_handler, width, height); - } - if (con->view) { int border_top = container_titlebar_height(); int border_width = con->current.border_thickness; @@ -456,6 +452,13 @@ static void arrange_container(struct sway_container *con, wlr_scene_node_reparent(&con->view->scene_tree->node, con->content_tree); wlr_scene_node_set_position(&con->view->scene_tree->node, border_left, border_top); + + // the output handler for the view wants to detect events for the entire + // container so give it negative coordinates to move it back over the + // decorations + wlr_scene_node_set_position(&con->view->output_handler->node, + -border_left, -border_top); + wlr_scene_buffer_set_dest_size(con->view->output_handler, width, height); } else { // make sure to disable the title bar if the parent is not managing it if (title_bar) { @@ -495,6 +498,11 @@ static void arrange_fullscreen(struct wlr_scene_tree *tree, // if we only care about the view, disable any decorations wlr_scene_node_set_enabled(&fs->scene_tree->node, false); + + // reconfigure the output handler (for foreign toplevel) to cover the + // view without container decorations + wlr_scene_node_set_position(&fs->view->output_handler->node, 0, 0); + wlr_scene_buffer_set_dest_size(fs->view->output_handler, width, height); } else { fs_node = &fs->scene_tree->node; arrange_container(fs, width, height, true, container_get_gaps(fs)); diff --git a/sway/tree/container.c b/sway/tree/container.c index fd9abadc6..6880841bd 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -25,50 +25,6 @@ #include "log.h" #include "stringop.h" -static void handle_outputs_update( - struct wl_listener *listener, void *data) { - struct sway_container *con = wl_container_of( - listener, con, outputs_update); - struct wlr_scene_outputs_update_event *event = data; - - struct wlr_foreign_toplevel_handle_v1 *toplevel = con->view->foreign_toplevel; - if (toplevel) { - struct wlr_foreign_toplevel_handle_v1_output *toplevel_output, *tmp; - wl_list_for_each_safe(toplevel_output, tmp, &toplevel->outputs, link) { - bool active = false; - for (size_t i = 0; i < event->size; i++) { - struct wlr_scene_output *scene_output = event->active[i]; - if (scene_output->output == toplevel_output->output) { - active = true; - break; - } - } - - if (!active) { - wlr_foreign_toplevel_handle_v1_output_leave(toplevel, toplevel_output->output); - } - } - - for (size_t i = 0; i < event->size; i++) { - struct wlr_scene_output *scene_output = event->active[i]; - wlr_foreign_toplevel_handle_v1_output_enter(toplevel, scene_output->output); - } - } -} - -static void handle_destroy( - struct wl_listener *listener, void *data) { - struct sway_container *con = wl_container_of( - listener, con, output_handler_destroy); - - container_begin_destroy(con); -} - -static bool handle_point_accepts_input( - struct wlr_scene_buffer *buffer, double *x, double *y) { - return false; -} - static struct wlr_scene_rect *alloc_rect_node(struct wlr_scene_tree *parent, bool *failed) { if (*failed) { @@ -135,22 +91,6 @@ struct sway_container *container_create(struct sway_view *view) { c->border.bottom = alloc_rect_node(c->border.tree, &failed); c->border.left = alloc_rect_node(c->border.tree, &failed); c->border.right = alloc_rect_node(c->border.tree, &failed); - - c->output_handler = wlr_scene_buffer_create(c->border.tree, NULL); - if (!c->output_handler) { - sway_log(SWAY_ERROR, "Failed to allocate a scene node"); - failed = true; - } - - if (!failed) { - c->outputs_update.notify = handle_outputs_update; - wl_signal_add(&c->output_handler->events.outputs_update, - &c->outputs_update); - c->output_handler_destroy.notify = handle_destroy; - wl_signal_add(&c->output_handler->node.events.destroy, - &c->output_handler_destroy); - c->output_handler->point_accepts_input = handle_point_accepts_input; - } } if (!failed && !scene_descriptor_assign(&c->scene_tree->node, @@ -522,7 +462,6 @@ void container_destroy(struct sway_container *con) { if (con->view && con->view->container == con) { con->view->container = NULL; - wlr_scene_node_destroy(&con->output_handler->node); if (con->view->destroying) { view_destroy(con->view); } @@ -564,11 +503,6 @@ void container_begin_destroy(struct sway_container *con) { if (con->pending.parent || con->pending.workspace) { container_detach(con); } - - if (con->view && con->view->container == con) { - wl_list_remove(&con->outputs_update.link); - wl_list_remove(&con->output_handler_destroy.link); - } } void container_reap_empty(struct sway_container *con) { diff --git a/sway/tree/view.c b/sway/tree/view.c index 6dde3f63c..83b4972b1 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -38,6 +38,41 @@ #include "sway/xdg_decoration.h" #include "stringop.h" +static void handle_outputs_update( + struct wl_listener *listener, void *data) { + struct sway_view *view = wl_container_of(listener, view, outputs_update); + struct wlr_scene_outputs_update_event *event = data; + + struct wlr_foreign_toplevel_handle_v1 *toplevel = view->foreign_toplevel; + if (toplevel) { + struct wlr_foreign_toplevel_handle_v1_output *toplevel_output, *tmp; + wl_list_for_each_safe(toplevel_output, tmp, &toplevel->outputs, link) { + bool active = false; + for (size_t i = 0; i < event->size; i++) { + struct wlr_scene_output *scene_output = event->active[i]; + if (scene_output->output == toplevel_output->output) { + active = true; + break; + } + } + + if (!active) { + wlr_foreign_toplevel_handle_v1_output_leave(toplevel, toplevel_output->output); + } + } + + for (size_t i = 0; i < event->size; i++) { + struct wlr_scene_output *scene_output = event->active[i]; + wlr_foreign_toplevel_handle_v1_output_enter(toplevel, scene_output->output); + } + } +} + +static bool handle_point_accepts_input( + struct wlr_scene_buffer *buffer, double *x, double *y) { + return false; +} + bool view_init(struct sway_view *view, enum sway_view_type type, const struct sway_view_impl *impl) { bool failed = false; @@ -51,12 +86,23 @@ bool view_init(struct sway_view *view, enum sway_view_type type, goto err; } + view->output_handler = wlr_scene_buffer_create(view->scene_tree, NULL); + if (!view->output_handler) { + sway_log(SWAY_ERROR, "Failed to allocate a scene node"); + goto err; + } + view->image_capture_scene = wlr_scene_create(); if (view->image_capture_scene == NULL) { goto err; } view->image_capture_scene->restack_xwayland_surfaces = false; + view->outputs_update.notify = handle_outputs_update; + wl_signal_add(&view->output_handler->events.outputs_update, + &view->outputs_update); + view->output_handler->point_accepts_input = handle_point_accepts_input; + view->type = type; view->impl = impl; view->executed_criteria = create_list(); @@ -102,6 +148,7 @@ void view_begin_destroy(struct sway_view *view) { return; } view->destroying = true; + wl_list_remove(&view->outputs_update.link); if (!view->container) { view_destroy(view); @@ -1229,8 +1276,10 @@ void view_save_buffer(struct sway_view *view) { return; } - // Enable and disable the saved surface tree like so to atomitaclly update - // the tree. This will prevent over damaging or other weirdness. + // Make sure the output handler is placed above the saved surface so we don't send + // spurious events to the foreign toplevel handler. Also, make the saved surface tree + // is disabled until it is ready to replace the real surface. + wlr_scene_node_place_below(&view->saved_surface_tree->node, &view->output_handler->node); wlr_scene_node_set_enabled(&view->saved_surface_tree->node, false); wlr_scene_node_for_each_buffer(&view->content_tree->node, From e51f9d7183c89706558015ce61e0460631ba3577 Mon Sep 17 00:00:00 2001 From: someever Date: Sun, 12 Apr 2026 20:10:59 +0300 Subject: [PATCH 347/361] Fix typos in sway-ipc.7.scd Some sections of the man page were difficult to understand. This fixes several typos and grammatical errors. --- sway/sway-ipc.7.scd | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index 2658228a1..70c831375 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -802,7 +802,7 @@ node and will have the following properties: Retrieve the currently set marks *REPLY*++ -An array of marks current set. Since each mark can only be set for one +An array of marks currently 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:* @@ -833,13 +833,13 @@ An array of bar IDs, which are strings *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 +with the bar ID specified 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): +the payload. The following properties exist, with more information about their +values in *sway-bar*(5): [- *PROPERTY* :- *DATA TYPE* @@ -1040,7 +1040,7 @@ An object containing the following properties: ## 8. GET_BINDING_MODES *MESSAGE*++ -Retrieve the list of binding modes that currently configured +Retrieve the list of binding modes that are currently configured *REPLY*++ An array of strings, with each string being the name of a binding mode. This @@ -1154,13 +1154,13 @@ following properties: _tablet\_tool_, _tablet\_pad_, or _switch_ |- xkb_active_layout_name : string -: (Only keyboards) The name of the active keyboard layout in use +: (Only keyboards) The name of the active keyboard layout |- xkb_layout_names : array : (Only keyboards) A list a layout names configured for the keyboard |- xkb_active_layout_index : integer -: (Only keyboards) The index of the active keyboard layout in use +: (Only keyboards) The index of the active keyboard layout |- scroll_factor : floating : (Only pointers) Multiplier applied on scroll event values. @@ -1467,7 +1467,7 @@ one seat. Each object has the following properties: # EVENTS -Events are a way for client to get notified of changes to sway. A client can +Events are a way for clients 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: @@ -1478,7 +1478,7 @@ available: |- 0x80000000 : workspace :[ Sent whenever an event involving a workspace occurs such as initialization - of a new workspace or a different workspace gains focus + of a new workspace or another workspace gaining focus |- 0x80000001 : output : Sent when outputs are updated @@ -1487,7 +1487,7 @@ available: : Sent whenever the binding mode changes |- 0x80000003 : window -: Sent whenever an event involving a window occurs such as being reparented, +: Sent whenever an event involving a window occurs such as it being reparented, focused, or closed |- 0x80000004 : barconfig_update @@ -1503,7 +1503,7 @@ available: : Sent when an ipc client sends a _SEND\_TICK_ message |- 0x80000014 : bar_state_update -: Send when the visibility of a bar should change due to a modifier +: Sent when the visibility of a bar should change due to a modifier |- 0x80000015 : input : Sent when something related to input devices changes @@ -1522,10 +1522,10 @@ single object with the following properties: :[ The type of change that occurred. See below for more information |- current : object -: An object representing the workspace effected or _null_ for _reload_ changes +: An object representing the affected workspace or _null_ for _reload_ changes |- old : object -: For a _focus_ change, this is will be an object representing the workspace +: For a _focus_ change, this will be an object representing the workspace being switched from. Otherwise, it is _null_ @@ -1543,7 +1543,7 @@ The following change types are currently available: |- rename : The workspace was renamed |- urgent -: A window on the workspace has had their urgency hint set or all urgency hints +: A window on the workspace has had its urgency hint set or all urgency hints for windows on the workspace have been cleared |- reload : The configuration file has been reloaded @@ -1653,7 +1653,7 @@ object with the following properties: :[ The type of change that occurred. See below for more information |- container : object -: An object representing the window effected +: An object representing the affected window The following change types are currently available: @@ -1816,7 +1816,7 @@ event is a single object with the following properties: : 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 +: The payload provided in a _SEND\_TICK_ message, if any. Otherwise, an empty string @@ -1838,7 +1838,7 @@ event is a single object with the following properties: :- *DESCRIPTION* |- id : string -:[ The bar ID effected +:[ The bar ID affected |- visible_by_modifier : boolean : Whether the bar should be made visible due to a modifier being pressed @@ -1854,7 +1854,7 @@ event is a single object with the following properties: ## 0x80000015. INPUT -Sent when something related to the input devices changes. The event is a single +Sent when something related to input devices changes. The event is a single object with the following properties: [- *PROPERTY* @@ -1865,7 +1865,7 @@ object with the following properties: :[ What has changed |- input : object -: An object representing the input that is identical the ones GET_INPUTS gives +: An object representing the input that is identical to the ones GET_INPUTS gives The following change types are currently available: [- *TYPE* From 136765a530f9cd2242c152292aaaeec46443c968 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Wed, 15 Apr 2026 16:14:40 -0400 Subject: [PATCH 348/361] input/seat: end keyboard grab when clearing focus When focus leaves a surface, ensure any active keyboard grab is terminated. This prevents stale xdg-popup grabs from persisting after the popup is destroyed, which could otherwise cause keyboard input to remain stuck on the old window. fixes #8919 --- sway/input/seat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/input/seat.c b/sway/input/seat.c index ebdbd91ed..42e37bf03 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -14,7 +14,6 @@ #include #include #include -#include "config.h" #include "list.h" #include "log.h" #include "sway/config.h" @@ -1080,6 +1079,7 @@ static void send_unfocus(struct sway_container *con, void *data) { static void seat_send_unfocus(struct sway_node *node, struct sway_seat *seat) { sway_cursor_constrain(seat->cursor, NULL); wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); + wlr_seat_keyboard_end_grab(seat->wlr_seat); if (node->type == N_WORKSPACE) { workspace_for_each_container(node->sway_workspace, send_unfocus, seat); } else { From 9a5f09c867894dacf25f54929cfd808b301712b1 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Fri, 17 Apr 2026 18:10:35 -0400 Subject: [PATCH 349/361] input/seat: fix drag-and-drop regression and improve popup dismissal --- sway/input/seat.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/sway/input/seat.c b/sway/input/seat.c index 42e37bf03..83772f52c 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1071,6 +1071,7 @@ bool seat_is_input_allowed(struct sway_seat *seat, static void send_unfocus(struct sway_container *con, void *data) { if (con->view) { + view_close_popups(con->view); view_set_activated(con->view, false); } } @@ -1079,7 +1080,6 @@ static void send_unfocus(struct sway_container *con, void *data) { static void seat_send_unfocus(struct sway_node *node, struct sway_seat *seat) { sway_cursor_constrain(seat->cursor, NULL); wlr_seat_keyboard_notify_clear_focus(seat->wlr_seat); - wlr_seat_keyboard_end_grab(seat->wlr_seat); if (node->type == N_WORKSPACE) { workspace_for_each_container(node->sway_workspace, send_unfocus, seat); } else { @@ -1136,10 +1136,6 @@ static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *n struct sway_workspace *last_workspace = seat_get_focused_workspace(seat); if (node == NULL) { - // Close any popups on the old focus - if (node_is_view(last_focus)) { - view_close_popups(last_focus->sway_container->view); - } seat_send_unfocus(last_focus, seat); sway_input_method_relay_set_focus(&seat->im_relay, NULL); seat->has_focus = false; @@ -1228,11 +1224,6 @@ static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *n } } - // Close any popups on the old focus - if (last_focus && node_is_view(last_focus)) { - view_close_popups(last_focus->sway_container->view); - } - // If urgent, either unset the urgency or start a timer to unset it if (container && container->view && view_is_urgent(container->view) && !container->view->urgent_timer) { From 91c662fa59f49e5cb547ca83e5741a8bece3aaf9 Mon Sep 17 00:00:00 2001 From: llyyr Date: Thu, 23 Apr 2026 00:40:25 +0530 Subject: [PATCH 350/361] sway/server: chase wlroots!5327 Ref: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5327 --- sway/server.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/server.c b/sway/server.c index 8bdafb674..c481c7e99 100644 --- a/sway/server.c +++ b/sway/server.c @@ -227,7 +227,7 @@ static void handle_renderer_lost(struct wl_listener *listener, void *data) { } static void handle_new_foreign_toplevel_capture_request(struct wl_listener *listener, void *data) { - struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request = data; + struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_event *request = data; struct sway_view *view = request->toplevel_handle->data; if (view->image_capture_source == NULL) { @@ -561,7 +561,7 @@ bool server_init(struct sway_server *server) { return false; } server->new_foreign_toplevel_capture_request.notify = handle_new_foreign_toplevel_capture_request; - wl_signal_add(&server->ext_foreign_toplevel_image_capture_source_manager_v1->events.new_request, + wl_signal_add(&server->ext_foreign_toplevel_image_capture_source_manager_v1->events.capture_request, &server->new_foreign_toplevel_capture_request); server->tearing_control_v1 = From 80a940a99254fe5cc2ffce20c0e52aacb60ec7f6 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 23 Apr 2026 14:09:02 +0200 Subject: [PATCH 351/361] Treat ext-workspace-v1 as privileged And do not expose it to clients in security contexts. Fixes: https://github.com/swaywm/sway/issues/9120 --- sway/server.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/server.c b/sway/server.c index c481c7e99..92d4fe5ed 100644 --- a/sway/server.c +++ b/sway/server.c @@ -126,7 +126,8 @@ static bool is_privileged(const struct wl_global *global) { global == server.input->virtual_keyboard->global || global == server.input->virtual_pointer->global || global == server.input->transient_seat_manager->global || - global == server.xdg_output_manager_v1->global; + global == server.xdg_output_manager_v1->global || + global == server.workspace_manager_v1->global; } static bool filter_global(const struct wl_client *client, From 1cbb8a440f157047292709c171e59f0feeb26475 Mon Sep 17 00:00:00 2001 From: Hugo Osvaldo Barrera Date: Thu, 23 Apr 2026 15:30:18 +0200 Subject: [PATCH 352/361] Ignore failures creating linux-drm-syncobj Failures creating this global are non-fatal. Fixes: 1606311553cb58a280e320907098bd0f443fef6e Fixes: https://github.com/swaywm/sway/issues/9110 --- sway/server.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/sway/server.c b/sway/server.c index 92d4fe5ed..8c55c0395 100644 --- a/sway/server.c +++ b/sway/server.c @@ -296,11 +296,8 @@ bool server_init(struct sway_server *server) { if (wlr_renderer_get_drm_fd(server->renderer) >= 0 && server->renderer->features.timeline && server->backend->features.timeline) { - if (!wlr_linux_drm_syncobj_manager_v1_create(server->wl_display, 1, - wlr_renderer_get_drm_fd(server->renderer))) { - sway_log(SWAY_ERROR, "Failed to create linux-drm-syncobj v1"); - return false; - } + wlr_linux_drm_syncobj_manager_v1_create(server->wl_display, 1, + wlr_renderer_get_drm_fd(server->renderer)); } server->allocator = wlr_allocator_autocreate(server->backend, From 1084d2e87abc8f608270042df7c88f76302d8d9e Mon Sep 17 00:00:00 2001 From: llyyr Date: Wed, 1 Apr 2026 01:24:28 +0530 Subject: [PATCH 353/361] sway/desktop/transaction: skip freeing dirty nodes This fixes a race that causes UAF when turning on multiple outputs after they've been off for a while. When output_begin_destroy is called while a transaction that references the output is in-flight, node_set_dirty adds the node to server.dirty_nodes list and ntxnrefs is still held by that transaction. Once the transaction completes and ntxnrefs drops to 0, transaction_destroy frees the output, leaving a dangling pointer in server.dirty_nodes. The next transaction_commit_dirty call then walks the dirty_nodes list and crashes The fix is to skip the free in transaction_destroy if node->dirty is set, this means transaction_commit_dirty hasn't processed this node yet and will bump ntxnrefs shortly. The free will happen once that transaction completes and ntxnrefs reaches 0 again and transaction_commit_dirty will allocate a fresh instruction and increment ntxnrefs again when it processes the node. --- sway/desktop/transaction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index d4e853dee..082fe20ce 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -59,7 +59,7 @@ static void transaction_destroy(struct sway_transaction *transaction) { if (node->instruction == instruction) { node->instruction = NULL; } - if (node->destroying && node->ntxnrefs == 0) { + if (node->destroying && node->ntxnrefs == 0 && !node->dirty) { switch (node->type) { case N_ROOT: sway_assert(false, "Never reached"); From c857ca3a978896f4f9bdd481ec5cf395662a2dc5 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Sun, 1 Feb 2026 21:53:06 -0500 Subject: [PATCH 354/361] tree/workspace: fix crash on dragging container to workspace edge When a container is moved, `finalize_move` previously assumed that calling `workspace_split` would always result in a workspace with exactly 1 child (the wrapper container). Consequently, it safely assumed that inserting a container with `after = 1` was always valid. However, if the moved container was the only child of its workspace, calling `container_detach` drops the workspace's tiling length to 0. Calling `workspace_split` on an empty workspace simply changes its layout enum and returns, leaving the length at 0. Passing `after` (which evaluates to 1 when moving right/down) into `workspace_insert_tiling` then causes an out-of-bounds insertion and a subsequent segmentation fault during `container_build_representation`. This commit fixes the issue by dynamically calculating the insertion index based on the actual length of the workspace's tiling list at the moment of insertion, rather than overloading the `after` boolean flag as a hardcoded index. --- sway/input/seatop_move_tiling.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index c525b77a9..f19c3f18b 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -373,7 +373,7 @@ static void finalize_move(struct sway_seat *seat) { enum sway_container_layout new_layout = edge == WLR_EDGE_TOP || edge == WLR_EDGE_BOTTOM ? L_VERT : L_HORIZ; workspace_split(new_ws, new_layout); - workspace_insert_tiling(new_ws, con, after); + workspace_insert_tiling(new_ws, con, after ? new_ws->tiling->length : 0); } if (old_parent) { From 0bf8731114f8b74d97066cd1d480ed1aad735163 Mon Sep 17 00:00:00 2001 From: Jan Palus Date: Sun, 10 May 2026 01:07:45 +0200 Subject: [PATCH 355/361] man: update drag_lock default ref. #8800 Fixes #9119 --- 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 18744a22a..389b20570 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -154,7 +154,7 @@ The following commands may only be used in the configuration file. *input* drag_lock enabled|disabled|enabled_sticky Enables or disables drag lock for specified input device. The default is - _enabled_sticky_. + _disabled_. *input* dwt enabled|disabled Enables or disables disable-while-typing for the specified input device. From 9c663b1fa1c2ef5c6df0427ba905f347f6594ab6 Mon Sep 17 00:00:00 2001 From: MATHIP6 <116129737+MATHIP6@users.noreply.github.com> Date: Wed, 13 May 2026 20:01:33 -0400 Subject: [PATCH 356/361] Fix typo in README.fr.md Corrected a typographical error in the documentation regarding the usage of 'man 5 sway'. Updated 'pour' to 'sur' for better clarity. --- README.fr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.fr.md b/README.fr.md index c4ef699a4..a751a52cb 100644 --- a/README.fr.md +++ b/README.fr.md @@ -57,7 +57,7 @@ Si vous utilisez déjà i3, copiez votre configuration i3 vers `~/.config/sway/config` et sway fonctionnera directement. Sinon, copiez l'exemple de fichier de configuration vers `~/.config/sway/config`. Il se trouve généralement dans `/etc/sway/config`. Exécutez `man 5 sway` pour lire la -documentation pour la configuration de sway. +documentation sur la configuration de sway. ## Exécution From e52c14d535ff2956fb66374f36b7dda740eccbac Mon Sep 17 00:00:00 2001 From: Orestis Floros Date: Sat, 23 May 2026 10:16:11 +0200 Subject: [PATCH 357/361] i3-compat: swaybar: default to first bar-id When launching swaybar from the command line without a --bar-id flag, default to the first configured bar. This is a mirror of i3 PR https://github.com/i3/i3/pull/4231. This makes it easier to call swaybar from the command line since the majority of swaybar users have only one bar configured. --- swaybar/ipc.c | 22 ++++++++++++++++++++++ swaybar/main.c | 6 ------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 68d8dd32d..c3929a35f 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -417,6 +417,28 @@ void ipc_execute_binding(struct swaybar *bar, struct swaybar_binding *bind) { } bool ipc_initialize(struct swaybar *bar) { + if (!bar->id) { + uint32_t len = 0; + char *res = ipc_single_command(bar->ipc_socketfd, + IPC_GET_BAR_CONFIG, "", &len); + json_object *bars = json_tokener_parse(res); + if (!json_object_is_type(bars, json_type_array) + || json_object_array_length(bars) == 0) { + sway_log(SWAY_ERROR, "No bar configuration found, " + "please configure a bar block in your sway config file."); + json_object_put(bars); + free(res); + return false; + } + json_object *first = json_object_array_get_idx(bars, 0); + bar->id = strdup(json_object_get_string(first)); + json_object_put(bars); + free(res); + sway_log(SWAY_INFO, "Using first bar config: %s. " + "Use --bar_id to manually select a different bar configuration.", + bar->id); + } + uint32_t len = strlen(bar->id); char *res = ipc_single_command(bar->ipc_socketfd, IPC_GET_BAR_CONFIG, bar->id, &len); diff --git a/swaybar/main.c b/swaybar/main.c index e1b0cecac..c2020ff09 100644 --- a/swaybar/main.c +++ b/swaybar/main.c @@ -72,12 +72,6 @@ int main(int argc, char **argv) { sway_log_init(SWAY_INFO, NULL); } - if (!swaybar.id) { - sway_log(SWAY_ERROR, "No bar_id passed. " - "Provide --bar_id or let sway start swaybar"); - return 1; - } - if (!socket_path) { socket_path = get_socketpath(); if (!socket_path) { From abb959602f063c4c4799456ff38f6b5aeb9b18f0 Mon Sep 17 00:00:00 2001 From: Scott Leggett Date: Fri, 22 May 2026 22:08:41 +0800 Subject: [PATCH 358/361] tree/workspace: fix unwrapping children to avoid redundant split Before this change, with `workspace_layout tabbed`, moving T[app app] to an empty workspace resulted in T[T[app] T[app]], as each child was individually wrapped in a new split container. It looks like commit 92891fb1 introduced workspace_unwrap_children to fix an issue where moving a tabbed/stacked container to a new workspace with workspace_layout tabbed would incorrectly nest the container, creating T[T[app app]]. However, workspace_unwrap_children used workspace_add_tiling to add the detached children to the workspace. Since commit ece6a1d4, workspace_add_tiling checks for a configured default_layout and if it finds one, calls container_split for every added window. This commit changes the unwrapping logic to use workspace_insert_tiling_direct instead of workspace_add_tiling, avoiding the default_layout splitting behaviour. --- sway/tree/workspace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index 23311a456..9d78d7080 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -920,7 +920,7 @@ void workspace_unwrap_children(struct sway_workspace *ws, while (wrap->pending.children->length) { struct sway_container *child = wrap->pending.children->items[0]; container_detach(child); - workspace_add_tiling(ws, child); + workspace_insert_tiling_direct(ws, child, ws->tiling->length); } } From 3774506bd092563538af2b6d2e332c84a8ac153c Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Mon, 27 Apr 2026 00:16:26 -0400 Subject: [PATCH 359/361] seatop_move_tiling: clamp edge thresholds to parent box When a fullscreen view is present on a workspace that has panels (e.g. a bar with exclusive zone), the view's content geometry extends beyond the workspace's usable area. The edge-detection thresholds computed from the view's content_x/content_width could then lie outside the parent container's box, causing the indicator width or height to become negative. This triggered an assertion in wlr_scene_rect_set_size. Fix this by clamping the threshold coordinates to the parent container's boundaries before using them to size the drop indicator. fixes #9102 --- sway/input/seatop_move_tiling.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index f19c3f18b..3a764d9ac 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -234,18 +234,22 @@ static void handle_motion_postthreshold(struct sway_seat *seat) { if (layout == L_HORIZ || layout == L_TABBED) { if (cursor->cursor->y < thresh_top) { edge = WLR_EDGE_TOP; + if (thresh_top < box.y) thresh_top = box.y; box.height = thresh_top - box.y; } else if (cursor->cursor->y > thresh_bottom) { edge = WLR_EDGE_BOTTOM; + if (thresh_bottom > box.y + box.height) thresh_bottom = box.y + box.height; box.height = box.y + box.height - thresh_bottom; box.y = thresh_bottom; } } else if (layout == L_VERT || layout == L_STACKED) { if (cursor->cursor->x < thresh_left) { edge = WLR_EDGE_LEFT; + if (thresh_left < box.x) thresh_left = box.x; box.width = thresh_left - box.x; } else if (cursor->cursor->x > thresh_right) { edge = WLR_EDGE_RIGHT; + if (thresh_right > box.x + box.width) thresh_right = box.x + box.width; box.width = box.x + box.width - thresh_right; box.x = thresh_right; } From 3cb86e4a2aa8e1938d97e54f15843c4e3dfbae27 Mon Sep 17 00:00:00 2001 From: Konstantin Pospelov Date: Sun, 3 May 2026 21:33:05 +0200 Subject: [PATCH 360/361] input/seat: check only the last output workspace on focus `seat_set_workspace_focus()` contains two references to the "last workspace": - `last_workspace` is the last focused workspace on all outputs. - `new_output_last_ws` is the last focused workspace on the new workspace output. They are the same when using a single output, but with multiple outputs they can diverge: `last_workspace` will be the focused workspace on the previous output, `new_output_last_ws` will be the previously focused workspace on the current output. This confusion seems to have led to #9139 when `last_workspace` was used instead of `new_output_last_ws`. However, we can safely drop `last_workspace` and use `new_output_last_ws` for everything that `seat_set_workspace_focus()` does: - Calling `node_set_dirty()` on the new output. - Calling `wlr_ext_workspace_handle_v1_set_active()` on the last workspace (this fixes #9139). - Skip `workspace_consider_destroy()` on `last_workspace`, since the other output still has it focused. This commit also renames `new_output_last_ws` to `last_workspace` to keep the name simple. --- sway/input/seat.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/sway/input/seat.c b/sway/input/seat.c index 83772f52c..0434d637c 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1133,8 +1133,6 @@ static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *n return; } - struct sway_workspace *last_workspace = seat_get_focused_workspace(seat); - if (node == NULL) { seat_send_unfocus(last_focus, seat); sway_input_method_relay_set_focus(&seat->im_relay, NULL); @@ -1157,17 +1155,15 @@ static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *n return; } + // Find the output's last workspace, which might have to be removed if empty struct sway_output *new_output = new_workspace ? new_workspace->output : NULL; - + struct sway_workspace *last_workspace = + new_output ? output_get_active_workspace(new_output) : NULL; if (last_workspace != new_workspace && new_output) { node_set_dirty(&new_output->node); } - // find new output's old workspace, which might have to be removed if empty - struct sway_workspace *new_output_last_ws = - new_output ? output_get_active_workspace(new_output) : NULL; - // Unfocus the previous focus if (last_focus) { seat_send_unfocus(last_focus, seat); @@ -1211,11 +1207,11 @@ static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *n } // Move sticky containers to new workspace - 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) { + if (new_workspace && last_workspace + && new_workspace != last_workspace) { + for (int i = 0; i < last_workspace->floating->length; ++i) { struct sway_container *floater = - new_output_last_ws->floating->items[i]; + last_workspace->floating->items[i]; if (container_is_sticky(floater)) { container_detach(floater); workspace_add_floating(new_workspace, floater); @@ -1244,13 +1240,9 @@ static void seat_set_workspace_focus(struct sway_seat *seat, struct sway_node *n } } - if (new_output_last_ws) { - workspace_consider_destroy(new_output_last_ws); - } - if (last_workspace && last_workspace != new_output_last_ws) { + if (last_workspace) { workspace_consider_destroy(last_workspace); } - seat->has_focus = true; if (config->smart_gaps && new_workspace) { From f1b40bc288f3be3bcc6a3c71f28ca9bb2529e70b Mon Sep 17 00:00:00 2001 From: Scott Leggett Date: Fri, 22 May 2026 23:03:31 +0800 Subject: [PATCH 361/361] seatop_move_tiling: fix stack overflow when dropping container into itself This change avoids a cyclic reference in the window tree when dragging a container and dropping it into one of its descendants. The drop target resolution logic previously only checked if the target node was the container itself, but missed checking if the target was a descendant of the dragged container. Fixes: #7949 --- sway/input/seatop_move_tiling.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sway/input/seatop_move_tiling.c b/sway/input/seatop_move_tiling.c index 3a764d9ac..025197852 100644 --- a/sway/input/seatop_move_tiling.c +++ b/sway/input/seatop_move_tiling.c @@ -256,8 +256,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); + if (e->target_node && (e->target_node == &e->con->node || + node_has_ancestor(e->target_node, &e->con->node))) { + e->target_node = node_get_parent(&e->con->node); } e->target_edge = edge; update_indicator(e, &box);