From bc258a3be2f946c1c93bcbe40735b2db068e0ea8 Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Fri, 5 Apr 2024 16:40:28 +0200 Subject: [PATCH 01/50] 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 02/50] 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 03/50] 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 04/50] 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 05/50] 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 06/50] 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 07/50] 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 08/50] 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 09/50] 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 10/50] 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 11/50] 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 12/50] 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 13/50] 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 14/50] 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 15/50] 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 16/50] 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 17/50] 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 18/50] 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 19/50] 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 20/50] 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 21/50] 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 22/50] 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 23/50] =?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 24/50] 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 25/50] 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 26/50] 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 27/50] 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 28/50] 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 29/50] 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 30/50] 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 31/50] 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 32/50] 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 33/50] 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 34/50] 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 35/50] 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 36/50] 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 37/50] 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 38/50] 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 39/50] 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 40/50] 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 41/50] 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 42/50] 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 43/50] 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 44/50] 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 45/50] 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 46/50] 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 47/50] 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 48/50] 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 49/50] 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 50/50] 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,