Rework the output matching code to be simpler

Instead of doing merges of output configs in several places just keep a
list of all config changes and apply them in order when needed.

Fixes #5632
This commit is contained in:
Pedro Côrte-Real 2020-08-18 17:51:13 +01:00
parent 7ca9ef12f8
commit ac4f6ed583
3 changed files with 30 additions and 221 deletions

View file

@ -644,7 +644,7 @@ struct output_config *store_output_config(struct output_config *oc);
struct output_config *find_output_config(struct sway_output *output); struct output_config *find_output_config(struct sway_output *output);
void apply_output_config_to_outputs(struct output_config *oc); void apply_output_config_to_outputs();
void reset_outputs(void); void reset_outputs(void);

View file

@ -99,13 +99,13 @@ struct cmd_results *cmd_output(int argc, char **argv) {
bool background = output->background; bool background = output->background;
output = store_output_config(output); store_output_config(output);
// If reloading, the output configs will be applied after reading the // If reloading, the output configs will be applied after reading the
// entire config and before the deferred commands so that an auto generated // entire config and before the deferred commands so that an auto generated
// workspace name is not given to re-enabled outputs. // workspace name is not given to re-enabled outputs.
if (!config->reloading && !config->validating) { if (!config->reloading && !config->validating) {
apply_output_config_to_outputs(output); apply_output_config_to_outputs();
if (background) { if (background) {
spawn_swaybg(); spawn_swaybg();
} }

View file

@ -5,6 +5,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
#include <fnmatch.h>
#include <wlr/types/wlr_cursor.h> #include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_output_layout.h> #include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output.h> #include <wlr/types/wlr_output.h>
@ -15,13 +16,6 @@
#include "log.h" #include "log.h"
#include "util.h" #include "util.h"
int output_name_cmp(const void *item, const void *data) {
const struct output_config *output = item;
const char *name = data;
return strcmp(output->name, name);
}
void output_get_identifier(char *identifier, size_t len, void output_get_identifier(char *identifier, size_t len,
struct sway_output *output) { struct sway_output *output) {
struct wlr_output *wlr_output = output->wlr_output; struct wlr_output *wlr_output = output->wlr_output;
@ -125,104 +119,8 @@ void merge_output_config(struct output_config *dst, struct output_config *src) {
} }
} }
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) {
char *id_on_name = NULL;
char id[128];
char *name = NULL;
struct sway_output *output;
wl_list_for_each(output, &root->all_outputs, link) {
name = output->wlr_output->name;
output_get_identifier(id, sizeof(id), output);
if (strcmp(name, oc->name) == 0 || strcmp(id, oc->name) == 0) {
size_t length = snprintf(NULL, 0, "%s on %s", id, name) + 1;
id_on_name = malloc(length);
if (!id_on_name) {
sway_log(SWAY_ERROR, "Failed to allocate id on name string");
return;
}
snprintf(id_on_name, length, "%s on %s", id, name);
break;
}
}
if (!id_on_name) {
return;
}
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) (dpms %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->dpms_state,
ion_oc->max_render_time);
}
}
free(id_on_name);
}
struct output_config *store_output_config(struct output_config *oc) { 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); 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);
}
sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " 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) (dpms %d) " "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (dpms %d) "
@ -513,125 +411,43 @@ static void default_output_config(struct output_config *oc,
oc->max_render_time = 0; oc->max_render_time = 0;
} }
static struct output_config *get_output_config(char *identifier, struct output_config *find_output_config(struct sway_output *sway_output) {
struct sway_output *sway_output) { // Start with a default config for this output
const char *name = sway_output->wlr_output->name; struct output_config *result = new_output_config("merge");
struct output_config *oc_id_on_name = NULL;
struct output_config *oc_name = NULL;
struct output_config *oc_id = NULL;
size_t length = snprintf(NULL, 0, "%s on %s", identifier, name) + 1;
char *id_on_name = malloc(length);
snprintf(id_on_name, length, "%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");
if (config->reloading) {
default_output_config(result, sway_output->wlr_output); 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 `<identifier> on <name>` 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); // Apply all matches in order
result->name = strdup(id_on_name); char id[128];
merge_output_config(result, temp); output_get_identifier(id, sizeof(id), sway_output);
char *name = sway_output->wlr_output->name;
sway_log(SWAY_DEBUG, "Generated output config \"%s\" (enabled: %d)" for (int i = 0; i < config->output_configs->length; ++i) {
" (%dx%d@%fHz position %d,%d scale %f transform %d) (bg %s %s)" struct output_config *oc = config->output_configs->items[i];
" (dpms %d) (max render time: %d)", result->name, result->enabled, if (!strcmp(oc->name, "*") || !strcmp(oc->name, name) || !strcmp(oc->name, id)) {
result->width, result->height, result->refresh_rate, merge_output_config(result, oc);
result->x, result->y, result->scale, result->transform,
result->background, result->background_option, result->dpms_state,
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;
} }
} }
free(id_on_name); struct output_config *oc = result;
sway_log(SWAY_DEBUG, "Found output config %s (enabled: %d) (%dx%d@%fHz "
"position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (dpms %d) "
"(max render time: %d)",
oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate,
oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel),
oc->transform, oc->background, oc->background_option, oc->dpms_state,
oc->max_render_time);
return result; return result;
} }
struct output_config *find_output_config(struct sway_output *output) { void apply_output_config_to_outputs(void) {
char id[128];
output_get_identifier(id, sizeof(id), output);
return get_output_config(id, output);
}
void apply_output_config_to_outputs(struct output_config *oc) {
// Try to find the output container and apply configuration now. If // Try to find the output container and apply configuration now. If
// this is during startup then there will be no container and config // this is during startup then there will be no container and config
// will be applied during normal "new output" event from wlroots. // will be applied during normal "new output" event from wlroots.
bool wildcard = strcmp(oc->name, "*") == 0;
char id[128];
struct sway_output *sway_output, *tmp; struct sway_output *sway_output, *tmp;
wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) { wl_list_for_each_safe(sway_output, tmp, &root->all_outputs, link) {
char *name = sway_output->wlr_output->name; struct output_config *oc = find_output_config(sway_output);
output_get_identifier(id, sizeof(id), sway_output); apply_output_config(oc, sway_output);
if (wildcard || !strcmp(name, oc->name) || !strcmp(id, oc->name)) { free_output_config(oc);
struct output_config *current = get_output_config(id, sway_output);
if (!current) {
// No stored output config matched, apply oc directly
sway_log(SWAY_DEBUG, "Applying oc directly");
current = new_output_config(oc->name);
merge_output_config(current, oc);
}
apply_output_config(current, sway_output);
free_output_config(current);
if (!wildcard) {
// Stop looking if the output config isn't applicable to all
// outputs
break;
}
}
} }
struct sway_seat *seat; struct sway_seat *seat;
@ -642,14 +458,7 @@ void apply_output_config_to_outputs(struct output_config *oc) {
} }
void reset_outputs(void) { void reset_outputs(void) {
struct output_config *oc = NULL; apply_output_config_to_outputs();
int i = list_seq_find(config->output_configs, output_name_cmp, "*");
if (i >= 0) {
oc = config->output_configs->items[i];
} else {
oc = store_output_config(new_output_config("*"));
}
apply_output_config_to_outputs(oc);
} }
void free_output_config(struct output_config *oc) { void free_output_config(struct output_config *oc) {