Skip to content

Commit 1267e47

Browse files
kennylevinsenemersion
authored andcommitted
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 bc258a3 commit 1267e47

File tree

2 files changed

+143
-162
lines changed

2 files changed

+143
-162
lines changed

include/sway/config.h

+8-1
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,14 @@ bool apply_output_configs(struct matched_output_config *configs,
693693

694694
void apply_all_output_configs(void);
695695

696-
struct output_config *store_output_config(struct output_config *oc);
696+
/**
697+
* store_output_config stores a new output config. An output may be matched by
698+
* three different config types, in order of precedence: Identifier, name and
699+
* wildcard. When storing a config type of lower precedence, assume that the
700+
* user wants the config to take immediate effect by superseding (clearing) the
701+
* same values from higher presedence configuration.
702+
*/
703+
void store_output_config(struct output_config *oc);
697704

698705
struct output_config *find_output_config(struct sway_output *output);
699706

sway/config/output.c

+135-161
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,96 +207,46 @@ 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-
}
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;
210+
void store_output_config(struct output_config *oc) {
211+
bool merged = false;
212+
bool wildcard = strcmp(oc->name, "*") == 0;
213+
struct sway_output *output = wildcard ? NULL : output_by_name_or_id(oc->name);
214+
if (!output && !wildcard) {
215+
// There is no config by this name, just add it in
216+
goto done;
159217
}
160218

161-
const char *name = output->wlr_output->name;
162219
char id[128];
163220
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;
168-
}
169-
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);
221+
for (int i = 0; i < config->output_configs->length; i++) {
222+
struct output_config *old = config->output_configs->items[i];
223+
224+
// If the old config matches the new config's name, regardless of
225+
// whether it was name or identifier, merge on top of the existing
226+
// config. If the new config is a wildcard, this also merges on top of
227+
// old wildcard configs.
228+
if (strcmp(old->name, oc->name) == 0) {
229+
merge_output_config(old, oc);
230+
merged = true;
231+
continue;
197232
}
198-
}
199-
free(id_on_name);
200-
}
201233

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);
208-
}
234+
// If the new config is a wildcard config we supersede all non-wildcard
235+
// configs. Old wildcard configs have already been handled above.
236+
if (wildcard) {
237+
supersede_output_config(old, oc);
238+
continue;
239+
}
209240

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;
241+
// If the new config matches an output's name, and the old config
242+
// matches on that output's identifier, supersede it.
243+
if (strcmp(old->name, id) == 0 &&
244+
strcmp(oc->name, output->wlr_output->name) == 0) {
245+
supersede_output_config(old, oc);
227246
}
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);
233247
}
234248

249+
done:
235250
sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz "
236251
"position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) "
237252
"(max render time: %d)",
@@ -240,7 +255,13 @@ struct output_config *store_output_config(struct output_config *oc) {
240255
oc->transform, oc->background, oc->background_option, oc->power,
241256
oc->max_render_time);
242257

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+
}
244265
}
245266

246267
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,
587608
oc->max_render_time = 0;
588609
}
589610

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

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");
617+
struct output_config *result = new_output_config(name);
615618
if (config->reloading) {
616619
default_output_config(result, sway_output->wlr_output);
617620
}
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-
}
621+
622+
char id[128];
623+
output_get_identifier(id, sizeof(id), sway_output);
624+
625+
int i;
626+
bool match = false;
627+
if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) {
628+
match = true;
629+
oc = config->output_configs->items[i];
630+
merge_output_config(result, oc);
631+
}
632+
if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) {
633+
match = true;
634+
oc = config->output_configs->items[i];
635+
merge_output_config(result, oc);
636+
}
637+
if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) {
638+
match = true;
639+
oc = config->output_configs->items[i];
640+
merge_output_config(result, oc);
641+
}
642+
643+
if (!match && !config->reloading) {
644+
// No name, identifier, or wildcard config. Since we are not
645+
// reloading with defaults, the output config will be empty, so
646+
// just return NULL
647+
free_output_config(result);
648+
return NULL;
668649
}
669650

670-
free(id_on_name);
671651
return result;
672652
}
673653

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-
680654
bool apply_output_configs(struct matched_output_config *configs,
681655
size_t configs_len, bool test_only) {
682656
struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states));

0 commit comments

Comments
 (0)