aboutsummaryrefslogtreecommitdiffstats
path: root/sway
diff options
context:
space:
mode:
authorLibravatar Kenny Levinsen <kl@kl.wtf>2024-03-16 17:55:20 +0100
committerLibravatar Simon Ser <contact@emersion.fr>2024-04-12 17:32:26 +0200
commit1267e47de913d2cda2644ad89bba4e9c55842cd3 (patch)
tree3595ce67416ff57f0cb50965664a3af5714a50be /sway
parentinput: add Super as alternative for Mod4 (diff)
downloadsway-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.c296
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
83static 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
82static void merge_output_config(struct output_config *dst, struct output_config *src) { 147static 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
145static void merge_wildcard_on_all(struct output_config *wildcard) { 210void 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
155static 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
202struct 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
249done:
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
246static void set_mode(struct wlr_output *output, struct wlr_output_state *pending, 267static 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
590static 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.
613struct 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
674struct 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
680bool apply_output_configs(struct matched_output_config *configs, 654bool 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));