diff --git a/data/keyd-application-mapper.1.gz b/data/keyd-application-mapper.1.gz index 5661b78..8213c3f 100644 Binary files a/data/keyd-application-mapper.1.gz and b/data/keyd-application-mapper.1.gz differ diff --git a/data/keyd.1.gz b/data/keyd.1.gz index 52c75d7..927d705 100644 Binary files a/data/keyd.1.gz and b/data/keyd.1.gz differ diff --git a/docs/keyd.scdoc b/docs/keyd.scdoc index 50c4332..8446ce3 100644 --- a/docs/keyd.scdoc +++ b/docs/keyd.scdoc @@ -177,7 +177,7 @@ and each modifier is one of: Finally, each layer heading is followed by a set of bindings which take the form: - = || + | = || for a description of and see _ACTIONS_ and _MACROS_. @@ -277,6 +277,58 @@ E.G *Note:* You may have to restart your applications for this to take effect. +## Aliases + +A config file may include *aliases* which can be used as valid left hand values +in place of one or more keycodes. For example, the default alias *meta* is bound +to leftmeta and rightmeta. Thus the binding 'meta = a' is equivalent to the +bindings 'leftmeta = a' and 'rightmeta = a'. + +Aliases are defined in a special section called 'aliases' where each line takes the form: + + = + +and __ must be a valid key name. + +Note that may itself be a valid key, in which case all references to the +key will be replaced with the new key. When used judiciously, aliases can be +used in conjunction with the include directive to share bindings between +keyboards with different physical layouts. + +For example: + +``` +/etc/keyd/common: + meta = oneshot(meta) + alt = oneshot(alt) + + a = a + s = o + # etc.. + +/etc/keyd/default.conf: + [ids] + * + + [main] + include common + +/etc/keyd/magic_keyboard.conf: + [ids] + 004c:0267 + + [aliases] + meta = leftalt + meta = rightalt + alt = leftmeta + alt = rightmeta + + [main] + include common +``` + +Allows the user to define a set of universal bindings in /etc/keyd/common without having to explicitly account for the transposed meta and alt keys on an apple keyboard. + ## File Inclusion Config files may include other files located within the config directory using diff --git a/src/config.c b/src/config.c index 6338f00..a1e8ab6 100644 --- a/src/config.c +++ b/src/config.c @@ -120,39 +120,21 @@ fail: /* Return up to two keycodes associated with the given name. */ -static int lookup_keycodes(const char *name, uint8_t *code1, uint8_t *code2) +static uint8_t lookup_keycode(const char *name) { size_t i; - /* - * If the name is a modifier like 'control' we associate it with both - * corresponding key codes (e.g 'rightcontrol'/'leftcontrol') - */ - for (i = 0; i < MAX_MOD; i++) { - const struct modifier_table_ent *mod = &modifier_table[i]; - - if (!strcmp(mod->name, name)) { - *code1 = mod->code1; - *code2 = mod->code2; - - return 0; - } - } - for (i = 0; i < 256; i++) { const struct keycode_table_ent *ent = &keycode_table[i]; if (ent->name && (!strcmp(ent->name, name) || (ent->alt_name && !strcmp(ent->alt_name, name)))) { - *code1 = i; - *code2 = 0; - - return 0; + return i; } } - return -1; + return 0; } int config_get_layer_index(const struct config *config, const char *name) @@ -166,12 +148,41 @@ int config_get_layer_index(const struct config *config, const char *name) return -1; } - /* * Consumes a string of the form `[.] = ` and adds the * mapping to the corresponding layer in the config. */ +int set_layer_entry(const struct config *config, struct layer *layer, + const char *key, const struct descriptor *d) +{ + size_t i; + int found = 0; + + for (i = 0; i < config->nr_aliases; i++) { + const struct alias *alias = &config->aliases[i]; + + if (!strcmp(alias->name, key)) { + layer->keymap[alias->code] = *d; + found = 1; + } + } + + if (!found) { + uint8_t code; + + if (!(code = lookup_keycode(key))) { + err("%s is not a valid keycode or alias.", key); + return -1; + } + + layer->keymap[code] = *d; + + } + + return 0; +} + int config_add_entry(struct config *config, const char *exp) { uint8_t code1, code2; @@ -210,21 +221,10 @@ int config_add_entry(struct config *config, const char *exp) layer = &config->layers[idx]; - if (lookup_keycodes(keyname, &code1, &code2) < 0) { - err("%s is not a valid key.", keyname); - return -1; - } - if (parse_descriptor(descstr, &d, config) < 0) return -1; - if (code1) - layer->keymap[code1] = d; - - if (code2) - layer->keymap[code2] = d; - - return 0; + return set_layer_entry(config, layer, keyname, &d); } /* @@ -325,6 +325,71 @@ static void parse_globals(const char *path, struct config *config, struct ini_se } } +static void add_alias(struct config *config, const char *name, uint8_t code) +{ + if (config->nr_aliases >= MAX_ALIASES) { + fprintf(stderr, "\tERROR: Max aliases (%d) exceeded\n", + MAX_ALIASES); + return; + } + + struct alias *alias = &config->aliases[config->nr_aliases]; + + alias->name[sizeof(alias->name)-1] = 0; + strncpy(alias->name, name, sizeof(alias->name)-1); + alias->code = code; + + config->nr_aliases++; +} + +void create_modifier_aliases(struct config *config) +{ + uint8_t aliased_mods[MAX_MOD] = { 0 }; + size_t i; + + for (i = 0; i < config->nr_aliases; i++) { + size_t j; + + for (j = 0; j < MAX_MOD; j++) { + const struct modifier_table_ent *mod = &modifier_table[j]; + const struct alias *alias = &config->aliases[i]; + + if (!strcmp(alias->name, mod->name)) + aliased_mods[j] = 1; + } + } + + for (i = 0; i < MAX_MOD; i++) { + const struct modifier_table_ent *mod = &modifier_table[i]; + + // Don't create modifier aliases for modifier names which are explicitly + // redefined by the user. + if (!aliased_mods[i]) { + add_alias(config, mod->name, mod->code1); + add_alias(config, mod->name, mod->code2); + } + } +} + +static void parse_aliases(const char *path, struct config *config, struct ini_section *section) +{ + size_t i; + + for (i = 0; i < section->nr_entries;i++) { + uint8_t code; + struct ini_entry *ent = §ion->entries[i]; + + if ((code = lookup_keycode(ent->val))) { + add_alias(config, ent->key, code); + } else { + fprintf(stderr, + "\tERROR %s:%zd: Failed to define alias %s, %s is not a valid keycode\n", + path, ent->lnum, + ent->key, ent->val); + } + } +} + int config_parse(struct config *config, const char *path) { size_t i; @@ -345,28 +410,29 @@ int config_parse(struct config *config, const char *path) for (i = 0; i < ini->nr_sections; i++) { section = &ini->sections[i]; - if (!strcmp(section->name, "ids") || - !strcmp(section->name, "global")) - continue; - - - if (config_add_layer(config, section->name) < 0) - fprintf(stderr, "\tERROR %s:%zd: %s\n", path, section->lnum, errstr); + if (!strcmp(section->name, "ids")) { + ;; + } else if (!strcmp(section->name, "aliases")) { + parse_aliases(path, config, section); + } else if (!strcmp(section->name, "global")) { + parse_globals(path, config, section); + } else { + if (config_add_layer(config, section->name) < 0) + fprintf(stderr, "\tERROR %s:%zd: %s\n", path, section->lnum, errstr); + } } + create_modifier_aliases(config); /* Populate each layer. */ for (i = 0; i < ini->nr_sections; i++) { size_t j; char *layername; section = &ini->sections[i]; - if (!strcmp(section->name, "ids")) - continue; - - if (!strcmp(section->name, "global")) { - parse_globals(path, config, section); + if (!strcmp(section->name, "ids") || + !strcmp(section->name, "aliases") || + !strcmp(section->name, "globals")) continue; - } layername = strtok(section->name, ":"); diff --git a/src/config.h b/src/config.h index bb6dfbb..6c536d9 100644 --- a/src/config.h +++ b/src/config.h @@ -13,12 +13,20 @@ #define MAX_MACROS 256 #define MAX_AUX_DESCRIPTORS 64 +#define MAX_ALIAS_LEN 32 +#define MAX_ALIASES 64 + #include "layer.h" #include "descriptor.h" #include "macro.h" #include "command.h" +struct alias { + char name[MAX_ALIAS_LEN]; + uint8_t code; +}; + struct config { struct layer layers[MAX_LAYERS]; @@ -26,9 +34,10 @@ struct config { struct descriptor descriptors[MAX_AUX_DESCRIPTORS]; struct macro macros[MAX_MACROS]; struct command commands[MAX_COMMANDS]; + struct alias aliases[MAX_ALIASES]; + size_t nr_aliases; size_t nr_layers; - size_t nr_macros; size_t nr_descriptors; size_t nr_commands;