Skip to content

Commit b39d0d4

Browse files
committedMar 16, 2024
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: swaywm#8048
1 parent ed4adc8 commit b39d0d4

File tree

2 files changed

+132
-161
lines changed

2 files changed

+132
-161
lines changed
 

‎include/sway/config.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -692,7 +692,7 @@ bool apply_output_configs(struct matched_output_config *configs, size_t configs_
692692

693693
void apply_all_output_configs(void);
694694

695-
struct output_config *store_output_config(struct output_config *oc);
695+
void store_output_config(struct output_config *oc);
696696

697697
struct output_config *find_output_config(struct sway_output *output);
698698

‎sway/config/output.c

+131-160
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,71 @@ struct output_config *new_output_config(const char *name) {
7979
return oc;
8080
}
8181

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
82147
static void merge_output_config(struct output_config *dst, struct output_config *src) {
83148
if (src->enabled != -1) {
84149
dst->enabled = src->enabled;
@@ -142,94 +207,49 @@ static void merge_output_config(struct output_config *dst, struct output_config
142207
}
143208
}
144209

145-
static void merge_wildcard_on_all(struct output_config *wildcard) {
146-
for (int i = 0; i < config->output_configs->length; i++) {
147-
struct output_config *oc = config->output_configs->items[i];
148-
if (strcmp(wildcard->name, oc->name) != 0) {
149-
sway_log(SWAY_DEBUG, "Merging output * config on %s", oc->name);
150-
merge_output_config(oc, wildcard);
151-
}
152-
}
153-
}
210+
// store_output_config stores a new output config. An output may be matched by
211+
// three different config types, in order of precedence: Identifier, name and
212+
// wildcard. When storing a config type of lower precedence, assume that the
213+
// user wants the config to take immediate effect by clearing the same values
214+
// from higher presedence configuration.
215+
void store_output_config(struct output_config *oc) {
154216

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-
}
160-
161-
const char *name = output->wlr_output->name;
217+
// Find the output that this configuration will apply to.
162218
char id[128];
163-
output_get_identifier(id, sizeof(id), output);
164-
165-
char *id_on_name = format_str("%s on %s", id, name);
166-
if (!id_on_name) {
167-
return;
219+
bool merged = false, is_name = false;
220+
bool wildcard = strcmp(oc->name, "*") == 0;
221+
struct sway_output *output = wildcard ? NULL : output_by_name_or_id(oc->name);
222+
if (output) {
223+
output_get_identifier(id, sizeof(id), output);
224+
is_name = strcmp(oc->name, output->wlr_output->name) == 0;
225+
} else if (!wildcard) {
226+
// There is no config by this name, just add it in
227+
goto done;
168228
}
169229

170-
int i = list_seq_find(config->output_configs, output_name_cmp, id_on_name);
171-
if (i >= 0) {
172-
sway_log(SWAY_DEBUG, "Merging on top of existing id on name config");
173-
merge_output_config(config->output_configs->items[i], oc);
174-
} else {
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);
230+
// If the configuration matches the output by name, merge with existing
231+
// name configuration and clear identifier configuration. If the
232+
// configuration matches the output by identifier, merge with existing
233+
// identifier configuration.
234+
for (int i = 0; i < config->output_configs->length; i++) {
235+
struct output_config *old = config->output_configs->items[i];
236+
if (strcmp(old->name, oc->name) == 0) {
237+
merge_output_config(old, oc);
238+
merged = true;
239+
continue;
197240
}
198-
}
199-
free(id_on_name);
200-
}
241+
bool matches_identifier = output && strcmp(old->name, id) == 0;
201242

202-
struct output_config *store_output_config(struct output_config *oc) {
203-
bool wildcard = strcmp(oc->name, "*") == 0;
204-
if (wildcard) {
205-
merge_wildcard_on_all(oc);
206-
} else {
207-
merge_id_on_name(oc);
243+
if (wildcard || (is_name && matches_identifier)) {
244+
supersede_output_config(old, oc);
245+
}
208246
}
209247

210-
int i = list_seq_find(config->output_configs, output_name_cmp, oc->name);
211-
if (i >= 0) {
212-
sway_log(SWAY_DEBUG, "Merging on top of existing output config");
213-
struct output_config *current = config->output_configs->items[i];
214-
merge_output_config(current, 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-
}
248+
done:
249+
if (!merged) {
228250
list_add(config->output_configs, oc);
229251
} else {
230-
// New wildcard config. Just add it
231-
sway_log(SWAY_DEBUG, "Adding output * config");
232-
list_add(config->output_configs, oc);
252+
free_output_config(oc);
233253
}
234254

235255
sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
@@ -239,8 +259,6 @@ struct output_config *store_output_config(struct output_config *oc) {
239259
oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel),
240260
oc->transform, oc->background, oc->background_option, oc->power,
241261
oc->max_render_time);
242-
243-
return oc;
244262
}
245263

246264
static void set_mode(struct wlr_output *output, struct wlr_output_state *pending,
@@ -587,96 +605,49 @@ static void default_output_config(struct output_config *oc,
587605
oc->max_render_time = 0;
588606
}
589607

590-
static struct output_config *get_output_config(char *identifier,
591-
struct sway_output *sway_output) {
608+
// find_output_config returns a merged output_config containing all stored
609+
// configuration that applies to the specified output.
610+
struct output_config *find_output_config(struct sway_output *sway_output) {
592611
const char *name = sway_output->wlr_output->name;
612+
struct output_config *oc = NULL;
593613

594-
struct output_config *oc_id_on_name = NULL;
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");
614+
struct output_config *result = new_output_config(name);
615615
if (config->reloading) {
616616
default_output_config(result, sway_output->wlr_output);
617617
}
618-
if (oc_id_on_name) {
619-
// Already have an identifier on name config, use that
620-
free(result->name);
621-
result->name = strdup(id_on_name);
622-
merge_output_config(result, oc_id_on_name);
623-
} else if (oc_name && oc_id) {
624-
// Generate a config named `<identifier> on <name>` which contains a
625-
// merged copy of the identifier on name. This will make sure that both
626-
// identifier and name configs are respected, with identifier getting
627-
// priority
628-
struct output_config *temp = new_output_config(id_on_name);
629-
merge_output_config(temp, oc_name);
630-
merge_output_config(temp, oc_id);
631-
list_add(config->output_configs, temp);
632-
633-
free(result->name);
634-
result->name = strdup(id_on_name);
635-
merge_output_config(result, temp);
636-
637-
sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)"
638-
" (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)"
639-
" (power %d) (max render time: %d)", result->name, result->enabled,
640-
result->width, result->height, result->refresh_rate,
641-
result->x, result->y, result->scale, result->transform,
642-
result->background, result->background_option, result->power,
643-
result->max_render_time);
644-
} else if (oc_name) {
645-
// No identifier config, just return a copy of the name config
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-
}
618+
619+
char id[128];
620+
output_get_identifier(id, sizeof(id), sway_output);
621+
622+
int i;
623+
bool match = false;
624+
if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) {
625+
match = true;
626+
oc = config->output_configs->items[i];
627+
merge_output_config(result, oc);
628+
}
629+
if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) {
630+
match = true;
631+
oc = config->output_configs->items[i];
632+
merge_output_config(result, oc);
633+
}
634+
if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) {
635+
match = true;
636+
oc = config->output_configs->items[i];
637+
merge_output_config(result, oc);
638+
}
639+
640+
if (!match && !config->reloading) {
641+
// No name, identifier, or wildcard config. Since we are not
642+
// reloading with defaults, the output config will be empty, so
643+
// just return NULL
644+
free_output_config(result);
645+
return NULL;
668646
}
669647

670-
free(id_on_name);
671648
return result;
672649
}
673650

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-
680651
bool apply_output_configs(struct matched_output_config *configs, size_t configs_len) {
681652
struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states));
682653
if (!states) {

0 commit comments

Comments
 (0)