diff --git a/include/sway/commands.h b/include/sway/commands.h index ddee2994d..7ff3b651e 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -257,6 +257,7 @@ sway_cmd input_cmd_scroll_method; sway_cmd input_cmd_tap; sway_cmd input_cmd_tap_button_map; sway_cmd input_cmd_xkb_capslock; +sway_cmd input_cmd_xkb_file; sway_cmd input_cmd_xkb_layout; sway_cmd input_cmd_xkb_model; sway_cmd input_cmd_xkb_numlock; diff --git a/include/sway/config.h b/include/sway/config.h index c5558c0d1..f1426453a 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -138,6 +138,9 @@ struct input_config { char *xkb_options; char *xkb_rules; char *xkb_variant; + char *xkb_file; + + bool xkb_file_is_set; int xkb_numlock; int xkb_capslock; diff --git a/sway/commands/input.c b/sway/commands/input.c index 588ef2d58..2289ede54 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -28,6 +28,7 @@ static struct cmd_handler input_handlers[] = { { "scroll_method", input_cmd_scroll_method }, { "tap", input_cmd_tap }, { "tap_button_map", input_cmd_tap_button_map }, + { "xkb_file", input_cmd_xkb_file }, { "xkb_layout", input_cmd_xkb_layout }, { "xkb_model", input_cmd_xkb_model }, { "xkb_options", input_cmd_xkb_options }, @@ -48,8 +49,8 @@ static struct cmd_handler input_config_handlers[] = { static void retranslate_keysyms(struct input_config *input_config) { for (int i = 0; i < config->input_configs->length; ++i) { struct input_config *ic = config->input_configs->items[i]; - if (ic->xkb_layout) { - // this is the first config with xkb_layout + if (ic->xkb_layout || ic->xkb_file) { + // this is the first config with xkb_layout or xkb_file if (ic->identifier == input_config->identifier) { translate_keysyms(ic); } diff --git a/sway/commands/input/xkb_file.c b/sway/commands/input/xkb_file.c new file mode 100644 index 000000000..063f544df --- /dev/null +++ b/sway/commands/input/xkb_file.c @@ -0,0 +1,28 @@ +#define _POSIX_C_SOURCE 200809L +#include "sway/config.h" +#include "sway/commands.h" +#include "sway/input/input-manager.h" +#include "log.h" + +struct cmd_results *input_cmd_xkb_file(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "xkb_file", EXPECTED_EQUAL_TO, 1))) { + return error; + } + struct input_config *ic = config->handler_context.input_config; + if (!ic) { + return cmd_results_new(CMD_FAILURE, "No input device defined."); + } + + if (strcmp(argv[0], "-") == 0) { + free(ic->xkb_file); + ic->xkb_file = NULL; + } else { + ic->xkb_file = strdup(argv[0]); + } + ic->xkb_file_is_set = true; + + sway_log(SWAY_DEBUG, "set-xkb_file for config: %s file: %s", + ic->identifier, ic->xkb_file); + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/config/input.c b/sway/config/input.c index b5be4f266..ad4a64eef 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -39,6 +39,7 @@ struct input_config *new_input_config(const char* identifier) { input->repeat_rate = INT_MIN; input->xkb_numlock = INT_MIN; input->xkb_capslock = INT_MIN; + input->xkb_file_is_set = false; return input; } @@ -95,6 +96,11 @@ void merge_input_config(struct input_config *dst, struct input_config *src) { if (src->tap_button_map != INT_MIN) { dst->tap_button_map = src->tap_button_map; } + if (src->xkb_file_is_set) { + free(dst->xkb_file); + dst->xkb_file = src->xkb_file ? strdup(src->xkb_file) : NULL; + dst->xkb_file_is_set = dst->xkb_file != NULL; + } if (src->xkb_layout) { free(dst->xkb_layout); dst->xkb_layout = strdup(src->xkb_layout); @@ -284,6 +290,8 @@ struct input_config *store_input_config(struct input_config *ic, ic = current; } + ic->xkb_file_is_set = ic->xkb_file != NULL; + if (!current || new_current) { list_add(config_list, ic); } @@ -307,6 +315,7 @@ void free_input_config(struct input_config *ic) { return; } free(ic->identifier); + free(ic->xkb_file); free(ic->xkb_layout); free(ic->xkb_model); free(ic->xkb_options); diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index ef20a3cfb..1f7ed8494 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -534,11 +534,6 @@ static void handle_xkb_context_log(struct xkb_context *context, struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, char **error) { - struct xkb_rule_names rules = {0}; - if (ic) { - input_config_fill_rule_names(ic, &rules); - } - struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); if (!sway_assert(context, "cannot create XKB context")) { return NULL; @@ -546,8 +541,46 @@ struct xkb_keymap *sway_keyboard_compile_keymap(struct input_config *ic, xkb_context_set_user_data(context, error); xkb_context_set_log_fn(context, handle_xkb_context_log); - struct xkb_keymap *keymap = - xkb_keymap_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); + struct xkb_keymap *keymap = NULL; + + if (ic && ic->xkb_file) { + FILE *keymap_file = fopen(ic->xkb_file, "r"); + if (!keymap_file) { + if (error) { + size_t len = snprintf(NULL, 0, "cannot read XKB file %s: %s", + ic->xkb_file, strerror(errno)) + 1; + *error = malloc(len); + if (*error) { + snprintf(*error, len, "cannot read XKB file %s: %s", + ic->xkb_file, strerror(errno)); + } else { + sway_log_errno(SWAY_ERROR, "cannot read XKB file %s: %s", + ic->xkb_file, strerror(errno)); + } + } else { + sway_log_errno(SWAY_ERROR, "cannot read XKB file %s: %s", + ic->xkb_file, strerror(errno)); + } + goto cleanup; + } + + keymap = xkb_keymap_new_from_file(context, keymap_file, + XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); + + if (!fclose(keymap_file)) { + sway_log_errno(SWAY_ERROR, "cannot close XKB file %s: %s", + ic->xkb_file, strerror(errno)); + } + } else { + struct xkb_rule_names rules = {0}; + if (ic) { + input_config_fill_rule_names(ic, &rules); + } + keymap = xkb_keymap_new_from_names(context, &rules, + XKB_KEYMAP_COMPILE_NO_FLAGS); + } + +cleanup: xkb_context_set_user_data(context, NULL); xkb_context_unref(context); return keymap; diff --git a/sway/meson.build b/sway/meson.build index c60be0085..934dbe685 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -160,6 +160,7 @@ sway_sources = files( 'commands/input/tap.c', 'commands/input/tap_button_map.c', 'commands/input/xkb_capslock.c', + 'commands/input/xkb_file.c', 'commands/input/xkb_layout.c', 'commands/input/xkb_model.c', 'commands/input/xkb_numlock.c', diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index 4fb14fd26..9a9c5c4c8 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -44,6 +44,11 @@ on top of the existing device configurations. For more information on these xkb configuration options, see *xkeyboard-config*(7). +*input* xkb_file + Sets all xkb configurations from a complete .xkb file. This file can be + dumped from _xkbcomp $DISPLAY keymap.xkb_. This setting overrides + xkb_layout, xkb_model, xkb_options, xkb_rules, and xkb_variant settings. + *input* xkb_layout Sets the layout of the keyboard like _us_ or _de_.