diff --git a/include/sway/config.h b/include/sway/config.h index 311adb16..57ae3c63 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -581,7 +581,7 @@ struct input_config *new_input_config(const char* identifier); void merge_input_config(struct input_config *dst, struct input_config *src); -struct input_config *store_input_config(struct input_config *ic); +struct input_config *store_input_config(struct input_config *ic, char **error); void input_config_fill_rule_names(struct input_config *ic, struct xkb_rule_names *rules); diff --git a/sway/commands/input.c b/sway/commands/input.c index 23a6644f..d95c4baf 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -88,9 +88,10 @@ struct cmd_results *cmd_input(int argc, char **argv) { if (!res || res->status == CMD_SUCCESS) { char *error = NULL; - struct xkb_keymap *keymap = sway_keyboard_compile_keymap( - config->handler_context.input_config, &error); - if (!keymap) { + struct input_config *ic = + store_input_config(config->handler_context.input_config, &error); + if (!ic) { + free_input_config(config->handler_context.input_config); if (res) { free_cmd_results(res); } @@ -99,10 +100,6 @@ struct cmd_results *cmd_input(int argc, char **argv) { free(error); return res; } - xkb_keymap_unref(keymap); - - struct input_config *ic = - store_input_config(config->handler_context.input_config); input_manager_apply_input_config(ic); retranslate_keysyms(ic); diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c index 44bc9e74..cd2985ee 100644 --- a/sway/commands/input/events.c +++ b/sway/commands/input/events.c @@ -101,7 +101,7 @@ static void toggle_wildcard_send_events(int argc, char **argv) { } else { toggle_supported_send_events_for_device(ic, input_device); } - store_input_config(ic); + store_input_config(ic, NULL); } } diff --git a/sway/config/input.c b/sway/config/input.c index 9c533e71..c4f64eb8 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -3,6 +3,7 @@ #include #include #include "sway/config.h" +#include "sway/input/keyboard.h" #include "log.h" struct input_config *new_input_config(const char* identifier) { @@ -133,6 +134,49 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { } } +static bool validate_xkb_merge(struct input_config *dest, + struct input_config *src, char **xkb_error) { + struct input_config *temp = new_input_config("temp"); + if (dest) { + merge_input_config(temp, dest); + } + merge_input_config(temp, src); + + struct xkb_keymap *keymap = sway_keyboard_compile_keymap(temp, xkb_error); + free_input_config(temp); + if (!keymap) { + return false; + } + + xkb_keymap_unref(keymap); + return true; +} + +static bool validate_wildcard_on_all(struct input_config *wildcard, + char **error) { + for (int i = 0; i < config->input_configs->length; i++) { + struct input_config *ic = config->input_configs->items[i]; + if (strcmp(wildcard->identifier, ic->identifier) != 0) { + sway_log(SWAY_DEBUG, "Validating xkb merge of * on %s", + ic->identifier); + if (!validate_xkb_merge(ic, wildcard, error)) { + return false; + } + } + } + + for (int i = 0; i < config->input_type_configs->length; i++) { + struct input_config *ic = config->input_type_configs->items[i]; + sway_log(SWAY_DEBUG, "Validating xkb merge of * config on %s", + ic->identifier); + if (!validate_xkb_merge(ic, wildcard, error)) { + return false; + } + } + + return true; +} + static void merge_wildcard_on_all(struct input_config *wildcard) { for (int i = 0; i < config->input_configs->length; i++) { struct input_config *ic = config->input_configs->items[i]; @@ -144,11 +188,28 @@ static void merge_wildcard_on_all(struct input_config *wildcard) { for (int i = 0; i < config->input_type_configs->length; i++) { struct input_config *ic = config->input_type_configs->items[i]; - if (strcmp(wildcard->identifier, ic->identifier) != 0) { - sway_log(SWAY_DEBUG, "Merging input * config on %s", ic->identifier); - merge_input_config(ic, wildcard); + sway_log(SWAY_DEBUG, "Merging input * config on %s", ic->identifier); + merge_input_config(ic, wildcard); + } +} + +static bool validate_type_on_existing(struct input_config *type_wildcard, + char **error) { + for (int i = 0; i < config->input_configs->length; i++) { + struct input_config *ic = config->input_configs->items[i]; + if (ic->input_type == NULL) { + continue; + } + + if (strcmp(ic->input_type, type_wildcard->identifier + 5) == 0) { + sway_log(SWAY_DEBUG, "Validating merge of %s on %s", + type_wildcard->identifier, ic->identifier); + if (!validate_xkb_merge(ic, type_wildcard, error)) { + return false; + } } } + return true; } static void merge_type_on_existing(struct input_config *type_wildcard) { @@ -167,44 +228,59 @@ static void merge_type_on_existing(struct input_config *type_wildcard) { } } -struct input_config *store_input_config(struct input_config *ic) { +struct input_config *store_input_config(struct input_config *ic, + char **error) { bool wildcard = strcmp(ic->identifier, "*") == 0; + if (wildcard && error && !validate_wildcard_on_all(ic, error)) { + return NULL; + } + + bool type = strncmp(ic->identifier, "type:", strlen("type:")) == 0; + if (type && error && !validate_type_on_existing(ic, error)) { + return NULL; + } + + list_t *config_list = type ? config->input_type_configs + : config->input_configs; + + struct input_config *current = NULL; + bool new_current = false; + + int i = list_seq_find(config_list, input_identifier_cmp, ic->identifier); + if (i >= 0) { + current = config_list->items[i]; + } + + i = list_seq_find(config->input_configs, input_identifier_cmp, "*"); + if (!current && i >= 0) { + current = new_input_config(ic->identifier); + merge_input_config(current, config->input_configs->items[i]); + new_current = true; + } + + if (error && !validate_xkb_merge(current, ic, error)) { + if (new_current) { + free_input_config(current); + } + return NULL; + } + if (wildcard) { merge_wildcard_on_all(ic); } - list_t *config_list = NULL; - if (strncmp(ic->identifier, "type:", 5) == 0) { - config_list = config->input_type_configs; + if (type) { merge_type_on_existing(ic); - } else { - config_list = config->input_configs; } - int i = list_seq_find(config_list, input_identifier_cmp, - ic->identifier); - if (i >= 0) { - sway_log(SWAY_DEBUG, "Merging on top of existing input config"); - struct input_config *current = config_list->items[i]; + if (current) { merge_input_config(current, ic); free_input_config(ic); ic = current; - } else if (!wildcard) { - sway_log(SWAY_DEBUG, "Adding non-wildcard input config"); - i = list_seq_find(config->input_configs, input_identifier_cmp, "*"); - if (i >= 0) { - sway_log(SWAY_DEBUG, "Merging on top of input * config"); - struct input_config *current = new_input_config(ic->identifier); - merge_input_config(current, config->input_configs->items[i]); - merge_input_config(current, ic); - free_input_config(ic); - ic = current; - } + } + + if (!current || new_current) { list_add(config_list, ic); - } else { - // New wildcard config. Just add it - sway_log(SWAY_DEBUG, "Adding input * config"); - list_add(config->input_configs, ic); } sway_log(SWAY_DEBUG, "Config stored for input %s", ic->identifier);