diff options
author | Kenny Levinsen <kl@kl.wtf> | 2024-03-16 17:55:20 +0100 |
---|---|---|
committer | Simon Ser <contact@emersion.fr> | 2024-04-12 17:32:26 +0200 |
commit | 1267e47de913d2cda2644ad89bba4e9c55842cd3 (patch) | |
tree | 3595ce67416ff57f0cb50965664a3af5714a50be /sway | |
parent | input: add Super as alternative for Mod4 (diff) | |
download | sway-1267e47de913d2cda2644ad89bba4e9c55842cd3.tar.gz sway-1267e47de913d2cda2644ad89bba4e9c55842cd3.tar.zst sway-1267e47de913d2cda2644ad89bba4e9c55842cd3.zip |
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
Diffstat (limited to 'sway')
-rw-r--r-- | sway/config/output.c | 296 |
1 files changed, 135 insertions, 161 deletions
diff --git a/sway/config/output.c b/sway/config/output.c index 3f1c3126..e5ff240a 100644 --- a/sway/config/output.c +++ b/sway/config/output.c | |||
@@ -79,6 +79,71 @@ struct output_config *new_output_config(const char *name) { | |||
79 | return oc; | 79 | return oc; |
80 | } | 80 | } |
81 | 81 | ||
82 | // supersede_output_config clears all fields in dst that were set in src | ||
83 | static void supersede_output_config(struct output_config *dst, struct output_config *src) { | ||
84 | if (src->enabled != -1) { | ||
85 | dst->enabled = -1; | ||
86 | } | ||
87 | if (src->width != -1) { | ||
88 | dst->width = -1; | ||
89 | } | ||
90 | if (src->height != -1) { | ||
91 | dst->height = -1; | ||
92 | } | ||
93 | if (src->x != -1) { | ||
94 | dst->x = -1; | ||
95 | } | ||
96 | if (src->y != -1) { | ||
97 | dst->y = -1; | ||
98 | } | ||
99 | if (src->scale != -1) { | ||
100 | dst->scale = -1; | ||
101 | } | ||
102 | if (src->scale_filter != SCALE_FILTER_DEFAULT) { | ||
103 | dst->scale_filter = SCALE_FILTER_DEFAULT; | ||
104 | } | ||
105 | if (src->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) { | ||
106 | dst->subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; | ||
107 | } | ||
108 | if (src->refresh_rate != -1) { | ||
109 | dst->refresh_rate = -1; | ||
110 | } | ||
111 | if (src->custom_mode != -1) { | ||
112 | dst->custom_mode = -1; | ||
113 | } | ||
114 | if (src->drm_mode.type != (uint32_t) -1) { | ||
115 | dst->drm_mode.type = -1; | ||
116 | } | ||
117 | if (src->transform != -1) { | ||
118 | dst->transform = -1; | ||
119 | } | ||
120 | if (src->max_render_time != -1) { | ||
121 | dst->max_render_time = -1; | ||
122 | } | ||
123 | if (src->adaptive_sync != -1) { | ||
124 | dst->adaptive_sync = -1; | ||
125 | } | ||
126 | if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { | ||
127 | dst->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; | ||
128 | } | ||
129 | if (src->background) { | ||
130 | free(dst->background); | ||
131 | dst->background = NULL; | ||
132 | } | ||
133 | if (src->background_option) { | ||
134 | free(dst->background_option); | ||
135 | dst->background_option = NULL; | ||
136 | } | ||
137 | if (src->background_fallback) { | ||
138 | free(dst->background_fallback); | ||
139 | dst->background_fallback = NULL; | ||
140 | } | ||
141 | if (src->power != -1) { | ||
142 | dst->power = -1; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | // merge_output_config sets all fields in dst that were set in src | ||
82 | static void merge_output_config(struct output_config *dst, struct output_config *src) { | 147 | static void merge_output_config(struct output_config *dst, struct output_config *src) { |
83 | if (src->enabled != -1) { | 148 | if (src->enabled != -1) { |
84 | dst->enabled = src->enabled; | 149 | dst->enabled = src->enabled; |
@@ -142,96 +207,46 @@ static void merge_output_config(struct output_config *dst, struct output_config | |||
142 | } | 207 | } |
143 | } | 208 | } |
144 | 209 | ||
145 | static void merge_wildcard_on_all(struct output_config *wildcard) { | 210 | void store_output_config(struct output_config *oc) { |
146 | for (int i = 0; i < config->output_configs->length; i++) { | 211 | bool merged = false; |
147 | struct output_config *oc = config->output_configs->items[i]; | 212 | bool wildcard = strcmp(oc->name, "*") == 0; |
148 | if (strcmp(wildcard->name, oc->name) != 0) { | 213 | struct sway_output *output = wildcard ? NULL : output_by_name_or_id(oc->name); |
149 | sway_log(SWAY_DEBUG, "Merging output * config on %s", oc->name); | 214 | if (!output && !wildcard) { |
150 | merge_output_config(oc, wildcard); | 215 | // There is no config by this name, just add it in |
151 | } | 216 | goto done; |
152 | } | ||
153 | } | ||
154 | |||
155 | static void merge_id_on_name(struct output_config *oc) { | ||
156 | struct sway_output *output = all_output_by_name_or_id(oc->name); | ||
157 | if (output == NULL) { | ||
158 | return; | ||
159 | } | 217 | } |
160 | 218 | ||
161 | const char *name = output->wlr_output->name; | ||
162 | char id[128]; | 219 | char id[128]; |
163 | output_get_identifier(id, sizeof(id), output); | 220 | output_get_identifier(id, sizeof(id), output); |
164 | 221 | for (int i = 0; i < config->output_configs->length; i++) { | |
165 | char *id_on_name = format_str("%s on %s", id, name); | 222 | struct output_config *old = config->output_configs->items[i]; |
166 | if (!id_on_name) { | 223 | |
167 | return; | 224 | // If the old config matches the new config's name, regardless of |
168 | } | 225 | // whether it was name or identifier, merge on top of the existing |
169 | 226 | // config. If the new config is a wildcard, this also merges on top of | |
170 | int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); | 227 | // old wildcard configs. |
171 | if (i >= 0) { | 228 | if (strcmp(old->name, oc->name) == 0) { |
172 | sway_log(SWAY_DEBUG, "Merging on top of existing id on name config"); | 229 | merge_output_config(old, oc); |
173 | merge_output_config(config->output_configs->items[i], oc); | 230 | merged = true; |
174 | } else { | 231 | continue; |
175 | // If both a name and identifier config, exist generate an id on name | ||
176 | int ni = list_seq_find(config->output_configs, output_name_cmp, name); | ||
177 | int ii = list_seq_find(config->output_configs, output_name_cmp, id); | ||
178 | if ((ni >= 0 && ii >= 0) || (ni >= 0 && strcmp(oc->name, id) == 0) | ||
179 | || (ii >= 0 && strcmp(oc->name, name) == 0)) { | ||
180 | struct output_config *ion_oc = new_output_config(id_on_name); | ||
181 | if (ni >= 0) { | ||
182 | merge_output_config(ion_oc, config->output_configs->items[ni]); | ||
183 | } | ||
184 | if (ii >= 0) { | ||
185 | merge_output_config(ion_oc, config->output_configs->items[ii]); | ||
186 | } | ||
187 | merge_output_config(ion_oc, oc); | ||
188 | list_add(config->output_configs, ion_oc); | ||
189 | sway_log(SWAY_DEBUG, "Generated id on name output config \"%s\"" | ||
190 | " (enabled: %d) (%dx%d@%fHz position %d,%d scale %f " | ||
191 | "transform %d) (bg %s %s) (power %d) (max render time: %d)", | ||
192 | ion_oc->name, ion_oc->enabled, ion_oc->width, ion_oc->height, | ||
193 | ion_oc->refresh_rate, ion_oc->x, ion_oc->y, ion_oc->scale, | ||
194 | ion_oc->transform, ion_oc->background, | ||
195 | ion_oc->background_option, ion_oc->power, | ||
196 | ion_oc->max_render_time); | ||
197 | } | 232 | } |
198 | } | ||
199 | free(id_on_name); | ||
200 | } | ||
201 | 233 | ||
202 | struct output_config *store_output_config(struct output_config *oc) { | 234 | // If the new config is a wildcard config we supersede all non-wildcard |
203 | bool wildcard = strcmp(oc->name, "*") == 0; | 235 | // configs. Old wildcard configs have already been handled above. |
204 | if (wildcard) { | 236 | if (wildcard) { |
205 | merge_wildcard_on_all(oc); | 237 | supersede_output_config(old, oc); |
206 | } else { | 238 | continue; |
207 | merge_id_on_name(oc); | 239 | } |
208 | } | ||
209 | 240 | ||
210 | int i = list_seq_find(config->output_configs, output_name_cmp, oc->name); | 241 | // If the new config matches an output's name, and the old config |
211 | if (i >= 0) { | 242 | // matches on that output's identifier, supersede it. |
212 | sway_log(SWAY_DEBUG, "Merging on top of existing output config"); | 243 | if (strcmp(old->name, id) == 0 && |
213 | struct output_config *current = config->output_configs->items[i]; | 244 | strcmp(oc->name, output->wlr_output->name) == 0) { |
214 | merge_output_config(current, oc); | 245 | supersede_output_config(old, oc); |
215 | free_output_config(oc); | ||
216 | oc = current; | ||
217 | } else if (!wildcard) { | ||
218 | sway_log(SWAY_DEBUG, "Adding non-wildcard output config"); | ||
219 | i = list_seq_find(config->output_configs, output_name_cmp, "*"); | ||
220 | if (i >= 0) { | ||
221 | sway_log(SWAY_DEBUG, "Merging on top of output * config"); | ||
222 | struct output_config *current = new_output_config(oc->name); | ||
223 | merge_output_config(current, config->output_configs->items[i]); | ||
224 | merge_output_config(current, oc); | ||
225 | free_output_config(oc); | ||
226 | oc = current; | ||
227 | } | 246 | } |
228 | list_add(config->output_configs, oc); | ||
229 | } else { | ||
230 | // New wildcard config. Just add it | ||
231 | sway_log(SWAY_DEBUG, "Adding output * config"); | ||
232 | list_add(config->output_configs, oc); | ||
233 | } | 247 | } |
234 | 248 | ||
249 | done: | ||
235 | sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " | 250 | sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " |
236 | "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) " | 251 | "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) " |
237 | "(max render time: %d)", | 252 | "(max render time: %d)", |
@@ -240,7 +255,13 @@ struct output_config *store_output_config(struct output_config *oc) { | |||
240 | oc->transform, oc->background, oc->background_option, oc->power, | 255 | oc->transform, oc->background, oc->background_option, oc->power, |
241 | oc->max_render_time); | 256 | oc->max_render_time); |
242 | 257 | ||
243 | return oc; | 258 | // If the configuration was not merged into an existing configuration, add |
259 | // it to the list. Otherwise we're done with it and can free it. | ||
260 | if (!merged) { | ||
261 | list_add(config->output_configs, oc); | ||
262 | } else { | ||
263 | free_output_config(oc); | ||
264 | } | ||
244 | } | 265 | } |
245 | 266 | ||
246 | static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, | 267 | static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, |
@@ -587,96 +608,49 @@ static void default_output_config(struct output_config *oc, | |||
587 | oc->max_render_time = 0; | 608 | oc->max_render_time = 0; |
588 | } | 609 | } |
589 | 610 | ||
590 | static struct output_config *get_output_config(char *identifier, | 611 | // find_output_config returns a merged output_config containing all stored |
591 | struct sway_output *sway_output) { | 612 | // configuration that applies to the specified output. |
613 | struct output_config *find_output_config(struct sway_output *sway_output) { | ||
592 | const char *name = sway_output->wlr_output->name; | 614 | const char *name = sway_output->wlr_output->name; |
615 | struct output_config *oc = NULL; | ||
593 | 616 | ||
594 | struct output_config *oc_id_on_name = NULL; | 617 | struct output_config *result = new_output_config(name); |
595 | struct output_config *oc_name = NULL; | ||
596 | struct output_config *oc_id = NULL; | ||
597 | |||
598 | char *id_on_name = format_str("%s on %s", identifier, name); | ||
599 | int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name); | ||
600 | if (i >= 0) { | ||
601 | oc_id_on_name = config->output_configs->items[i]; | ||
602 | } else { | ||
603 | i = list_seq_find(config->output_configs, output_name_cmp, name); | ||
604 | if (i >= 0) { | ||
605 | oc_name = config->output_configs->items[i]; | ||
606 | } | ||
607 | |||
608 | i = list_seq_find(config->output_configs, output_name_cmp, identifier); | ||
609 | if (i >= 0) { | ||
610 | oc_id = config->output_configs->items[i]; | ||
611 | } | ||
612 | } | ||
613 | |||
614 | struct output_config *result = new_output_config("temp"); | ||
615 | if (config->reloading) { | 618 | if (config->reloading) { |
616 | default_output_config(result, sway_output->wlr_output); | 619 | default_output_config(result, sway_output->wlr_output); |
617 | } | 620 | } |
618 | if (oc_id_on_name) { | 621 | |
619 | // Already have an identifier on name config, use that | 622 | char id[128]; |
620 | free(result->name); | 623 | output_get_identifier(id, sizeof(id), sway_output); |
621 | result->name = strdup(id_on_name); | 624 | |
622 | merge_output_config(result, oc_id_on_name); | 625 | int i; |
623 | } else if (oc_name && oc_id) { | 626 | bool match = false; |
624 | // Generate a config named `<identifier> on <name>` which contains a | 627 | if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) { |
625 | // merged copy of the identifier on name. This will make sure that both | 628 | match = true; |
626 | // identifier and name configs are respected, with identifier getting | 629 | oc = config->output_configs->items[i]; |
627 | // priority | 630 | merge_output_config(result, oc); |
628 | struct output_config *temp = new_output_config(id_on_name); | 631 | } |
629 | merge_output_config(temp, oc_name); | 632 | if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) { |
630 | merge_output_config(temp, oc_id); | 633 | match = true; |
631 | list_add(config->output_configs, temp); | 634 | oc = config->output_configs->items[i]; |
632 | 635 | merge_output_config(result, oc); | |
633 | free(result->name); | 636 | } |
634 | result->name = strdup(id_on_name); | 637 | if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) { |
635 | merge_output_config(result, temp); | 638 | match = true; |
636 | 639 | oc = config->output_configs->items[i]; | |
637 | sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)" | 640 | merge_output_config(result, oc); |
638 | " (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)" | 641 | } |
639 | " (power %d) (max render time: %d)", result->name, result->enabled, | 642 | |
640 | result->width, result->height, result->refresh_rate, | 643 | if (!match && !config->reloading) { |
641 | result->x, result->y, result->scale, result->transform, | 644 | // No name, identifier, or wildcard config. Since we are not |
642 | result->background, result->background_option, result->power, | 645 | // reloading with defaults, the output config will be empty, so |
643 | result->max_render_time); | 646 | // just return NULL |
644 | } else if (oc_name) { | 647 | free_output_config(result); |
645 | // No identifier config, just return a copy of the name config | 648 | return NULL; |
646 | free(result->name); | ||
647 | result->name = strdup(name); | ||
648 | merge_output_config(result, oc_name); | ||
649 | } else if (oc_id) { | ||
650 | // No name config, just return a copy of the identifier config | ||
651 | free(result->name); | ||
652 | result->name = strdup(identifier); | ||
653 | merge_output_config(result, oc_id); | ||
654 | } else { | ||
655 | i = list_seq_find(config->output_configs, output_name_cmp, "*"); | ||
656 | if (i >= 0) { | ||
657 | // No name or identifier config, but there is a wildcard config | ||
658 | free(result->name); | ||
659 | result->name = strdup("*"); | ||
660 | merge_output_config(result, config->output_configs->items[i]); | ||
661 | } else if (!config->reloading) { | ||
662 | // No name, identifier, or wildcard config. Since we are not | ||
663 | // reloading with defaults, the output config will be empty, so | ||
664 | // just return NULL | ||
665 | free_output_config(result); | ||
666 | result = NULL; | ||
667 | } | ||
668 | } | 649 | } |
669 | 650 | ||
670 | free(id_on_name); | ||
671 | return result; | 651 | return result; |
672 | } | 652 | } |
673 | 653 | ||
674 | struct output_config *find_output_config(struct sway_output *output) { | ||
675 | char id[128]; | ||
676 | output_get_identifier(id, sizeof(id), output); | ||
677 | return get_output_config(id, output); | ||
678 | } | ||
679 | |||
680 | bool apply_output_configs(struct matched_output_config *configs, | 654 | bool apply_output_configs(struct matched_output_config *configs, |
681 | size_t configs_len, bool test_only) { | 655 | size_t configs_len, bool test_only) { |
682 | struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states)); | 656 | struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states)); |