diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e48977a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,25 @@ +# 0.1.0 + +Major version update: + +- Introduces support for modifier layers. +- Simplifies config format. +- Improves consistency/expected key behaviour. + +This breaks existing configs. Moving forward the config format is expected to +remain backwards compatible. + +Main Changes: + +- Modifiers are now just treated as layers +- The default layer is now called main +- The modifier layout is distinct from the key layout + +- mods_on_hold(C, esc) = overload(C, esc) +- layer_on_hold(layer, esc) = overload(layer, esc) +- layer_toggle(layer) = layout(layer) +- layer(layer) = layer(layer) +- oneshot(mods) = oneshot(mods) +- oneshot_layer(layer) = oneshot(layer) + +See the manpage for details. diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..dce336e --- /dev/null +++ b/CHANGES @@ -0,0 +1,4 @@ +do away with the idea of layer inheritance +different layer types (exclusive, modifier) +modifiers are just layers with empty keymaps +simplified parsing logic to reflect above. diff --git a/CONTRIBUTING b/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING rename to CONTRIBUTING.md diff --git a/Makefile b/Makefile index ae794fe..5affbda 100644 --- a/Makefile +++ b/Makefile @@ -3,31 +3,31 @@ DESTDIR= PREFIX=/usr -VERSION=0.0.2 +VERSION=0.1.0 GIT_HASH=$(shell git describe --no-match --always --abbrev=40 --dirty) CFLAGS=-DVERSION=\"$(VERSION)\" -DGIT_COMMIT_HASH=\"$(GIT_HASH)\" all: - -mkdir bin + mkdir -p bin $(CC) $(CFLAGS) -O3 src/*.c -o bin/keyd -ludev debug: - -mkdir bin + mkdir -p bin $(CC) $(CFLAGS) -Wall -Wextra -pedantic -DDEBUG -g src/*.c -o bin/keyd -ludev man: pandoc -s -t man man.md | gzip > keyd.1.gz clean: -rm -rf bin install: - -mkdir -p $(DESTDIR)/etc/keyd - -mkdir -p $(DESTDIR)$(PREFIX)/lib/systemd/system - -mkdir -p $(DESTDIR)$(PREFIX)/bin - -mkdir -p $(DESTDIR)$(PREFIX)/share/man/man1 + mkdir -p $(DESTDIR)/etc/keyd + mkdir -p $(DESTDIR)$(PREFIX)/lib/systemd/system + mkdir -p $(DESTDIR)$(PREFIX)/bin + mkdir -p $(DESTDIR)$(PREFIX)/share/man/man1 install -m644 keyd.service $(DESTDIR)$(PREFIX)/lib/systemd/system install -m755 bin/keyd $(DESTDIR)$(PREFIX)/bin install -m644 keyd.1.gz $(DESTDIR)$(PREFIX)/share/man/man1 uninstall: rm -f $(DESTDIR)$(PREFIX)/lib/systemd/system/keyd.service\ - bin/keyd $(DESTDIR)$(PREFIX)/bin/keyd \ + bin/keyd $(DESTDIR)$(PREFIX)/bin/keyd\ $(DESTDIR)$(PREFIX)/share/man/man1/keyd.1.gz diff --git a/README.md b/README.md index 62f2d47..32c556f 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,10 @@ result often being tethered to a specified environment (X11). keyd attempts to solve this problem by providing a flexible system wide daemon which remaps keys using kernel level input primitives (evdev, uinput). +# UPDATE + +*Version 0.1.0 has just been released and includes some breaking changes. Please see the [changelog](CHANGELOG.md) for details.* + # Features keyd has several unique features many of which are traditionally only @@ -60,7 +64,7 @@ Some of the more interesting ones include: ``` # Turns capslock into an escape key when pressed and a control key when held. -capslock = mods_on_hold(C, esc) +capslock = overload(C, esc) # Remaps the escape key to capslock esc = capslock @@ -76,58 +80,37 @@ Before proceeding ensure you have some way of killing keyd if things go wrong the man page for keyboard specific configuraiton) so you can plug in another keyboard which is unaffected by the changes. -# Sample Config File - - # Maps escape to the escape layer when held and the escape key when pressed - - esc = layer_on_hold(escape_layer, esc) - - # Creates an escape layer which is activated by pressing the escape key. +# Sample Config - [escape_layer] + leftshift = oneshot(S) + capslock = overload(symbols, esc) - # Esc+1 changes the letter layout to dvorak. - 1 = layer_toggle(dvorak) + [symbols] - # Esc+2 changes the letter layout back to the default. - 2 = layer_toggle(default) + d = S-grave + f = slash + ... - # Creates a dvorak layer which inherits from the main layer (see the section on layer inheritance in the man page). +# Recommended config - [dvorak:default] +Many users will probably not be interested in taking full advantage of keyd. +For those who seek simple quality of life improvements I can recommend the +following config: - q = apostrophe - w = comma - e = dot - r = p - t = y - y = f - u = g - i = c - o = r - p = l + leftshift = oneshot(S) + leftalt = oneshot(A) + rightalt = oneshot(G) + rightshift = oneshot(A) + leftmeta = oneshot(M) + rightmeta = oneshot(M) - a = a - s = o - d = e - f = u - g = i - h = d - j = h - k = t - l = n - semicolon = s + capslock = overload(esc, C) + insert = S-insert - z = semicolon - x = q - c = j - v = k - b = x - n = b - m = m - comma = w - dot = v - slash = z +This remaps all modifiers to 'oneshot' keys and overloads the capslock key to +function as both escape (when tapped) and control (when held). Thus to produce +the letter A you can now simply tap shift and then a instead of having to hold +it. Finally it remaps insert to S-insert (paste on X11). # FAQS @@ -143,11 +126,12 @@ anything that is as flexible as keyd. ## What about [kmonad](https://github.com/kmonad/kmonad)? keyd was written several years ago to allow me to easily experiment with -different layouts on my growing keyboard collection. At the time kmonad did -not exist and custom keyboard firmware like [QMK](https://github.com/qmk/qmk_firmware) (which inspired keyd) was the -only way to get comparable features. I became aware of kmonad after having -published keyd. While kmonad is a fine project with similar goals, it takes -a different approach and has a different design philosophy. +different layouts on my growing keyboard collection. At the time kmonad did not +exist and custom keyboard firmware like +[QMK](https://github.com/qmk/qmk_firmware) (which inspired keyd) was the only +way to get comparable features. I became aware of kmonad after having published +keyd. While kmonad is a fine project with similar goals, it takes a different +approach and has a different design philosophy. Notably keyd was written entirely in C with performance and simplicitly in mind and will likely never be as configurable as kmonad (which is extensible @@ -165,4 +149,4 @@ good candidates for inclusion. # Contributing -See [CONTRIBUTING]. +See [CONTRIBUTING](CONTRIBUTING.md). diff --git a/keyd.1.gz b/keyd.1.gz index 228b21a..41388e0 100644 Binary files a/keyd.1.gz and b/keyd.1.gz differ diff --git a/man.1.gz b/man.1.gz index 7346932..d512efb 100644 Binary files a/man.1.gz and b/man.1.gz differ diff --git a/man.md b/man.md index d58886f..47e0329 100644 --- a/man.md +++ b/man.md @@ -17,32 +17,31 @@ keyd [-m] [-l] **-d**: Fork and run in the background. -# OVERVIEW - keyd is intended to be run as system wide daemon managed by systemd. The default behaviour is to run the forground and print to stderr but it can also be run as a standalone daemon if -d is supplied, in which case log output will -be stored in /var/log/keyd.log. +be stored in */var/log/keyd.log*. # CONFIGURATION -All configuration files are stored in /etc/keyd. The name of each file should +All configuration files are stored in */etc/keyd/*. The name of each file should correspond to the device name to which it is to be applied followed by .cfg (e.g "/etc/keyd/Magic Keyboard.cfg"). Configuration files are loaded upon initialization and can be reified by reloading keyd (e.g sudo systemctl restart keyd). -The monitor flag (-m) can be used to obtain device and key names like so: +A list of valid key names can be produced with **-l**. The monitor flag (**-m**) can +also be used to obtain device and key names like so: -``` -# sudo systemctl stop keyd -# sudo keyd -m + > sudo systemctl stop keyd + > sudo keyd -m -Magic Keyboard: capslock down -Magic Keyboard: capslock up -``` + Magic Keyboard: capslock down + Magic Keyboard: capslock up + ... -If no configuration file exists for a given keyboard 'default.cfg' is used as a fallback (if present). +If no configuration file exists for a given keyboard *default.cfg* is used as a +fallback (if present). Each line in a configuration file consists of a mapping of the following form: @@ -50,9 +49,9 @@ Each line in a configuration file consists of a mapping of the following form: or else represents the beginning of a new layer. E.G: - [] + [] -Where `` has the form: `[-[-]]` +Where `` has the form: `[[-...]-` and each modifier is one of: @@ -63,23 +62,20 @@ and each modifier is one of: \ **G** - AltGr In addition to simple key mappings keyd can remap keys to actions which -can conditionally send keystrokes or transform the state of the keymap. +can conditionally send keystrokes or transform the state of the keymap. It is, for instance, possible to map a key to escape when tapped and control when held -by assigning it to mods_on_hold(C, esc). A complete list of available actions can be -found in ACTIONS. +by assigning it to `overload(C, esc)`. A complete list of available actions can be +found in *ACTIONS*. -## LAYERS +## Layers Each configuration file consists of one or more layers. Each layer is a keymap -unto itself and can be activated by a key mapped to the appropriate -action (see ACTIONS). - -The default layer is called 'default' and is used for mappings which -are not explicitly assigned to a layer. +unto itself and can be transiently activated by a key mapped to the *layer* +action. For example the following configuration creates a new layer called 'symbols' which -is activated by the capslock key on the default layer. +is activated by holding the capslock key. capslock = layer(symbols) @@ -90,100 +86,134 @@ is activated by the capslock key on the default layer. Pressing capslock+f thus produces a tilde. +Any set of valid modifiers is also a valid layer. For example the layer `M-C` +corresponds to a layer which behaves like the modifiers meta and control. These +play nicely with other modifiers and preserve existing stacking semantics. + A layer may optionally have a parent from which mappings are drawn for keys which are not explicitly mapped. By default layers do not have a parent, that is, unmapped keys will have no effect. A parent is specified by appending -`:` to the layer name. This is particularly useful for custom -letter layouts like dvorak which remap a subset of keys but otherwise leave the -default mappings in tact. +`:` to the layer name. -## ACTIONS +The *layout* is a special layer from which mappings are drawn if no other layers +are active. The default layout is called *main* and is the one to which +mappings are assigned if no layer heading is present. By default all keys are +defined as themselves in the main layer. Layers which intend to be used as +layouts will likely want to inherit from main. The default layout can be +changed by including layout() at the top of the config file. -**oneshot(mods)**: If tapped activate a modifier sequence for the next keypress, otherwise act as a normal modifier key when held. +## The Modifier Layout -**mods_on_hold(mods, keyseq)**: Activates the given set of modifiers whilst held and emits keysequence when tapped. +keyd distinguishes between the normal layout and the modifier layout. This +allows the user to use a different letter arrangement for modifiers. It may, +for example, be desireable to use an alternative key layout like dvorak while +preserving standard qwerty modifier shortcuts. This can be achieved by passing +a second argument to the layout function like so: `layout(dvorak, main)`. -**layer_on_hold(layer, keyseq)**: Activates the given layer whilst held and emits keysequence when tapped. +Note that this is different from simply defining a custom layer which reassigns +each key to a modified key sequence (e.g `s = C-s`) since it applies to all +modifiers and preserves expected stacking behaviour. -**layer_toggle(layer)**: Permanently activate a layer when tapped. *Note*: You will need to explicitly map a toggle in the destination layer if you wish to return. +## Modifier Layers -**layer(layer)**: Activate the given layer while the key is held down. +In addition to standard layers, keyd introduces the concept of 'modifier +layers' to accomodate the common use case of remapping a subset of modifier keys. A +modifier layer will have identical behaviour to a set of modifiers unless a key is +explicitly defined within it. To define a modifier layer simply define a layer +which inherits from a valid modifier set. -**oneshot_layer(layer)**: If tapped activate a layer for the duration of the next keypress, otherwise act as a normal layer key when held. +E.G: -### Legend: +``` +capslock = layer(custom_control) + +[custom_control:C] + +1 = C-A-f1 +2 = C-A-f2 +``` - - `` = A set of modifiers of the form: `[-...]` (e.g C-M = control + meta). - - `` = A key sequence consisting of zero or more control characters and a key (e.g C-a = control+a). - - `` = The name of a layer. +Will cause the capslock key to behave as control in all instances except when `C-1` is +pressed, in which case the key sequence C-A-f1 will be emitted. This is not +possible to achieve using standard layers without breaking expected behaviour +like modifier stacking and pointer combos. -## Examples +## TLDR + +1. Use [mylayer] if you want to define a custom shift layer (e.g [symbols]). +2. Use [mylayer:C] if you want a layer which behaves like a custom control key. +3. Use [mylayer:main] for defining custom key layouts (e.g dvorak). + +## ACTIONS -Example 1 +**oneshot(\)**: If tapped activate a layer for the next keypress. If this is a modifier layer then it will cause the key to behave as the corresponding modifiers while held. - # Maps capslock to control when held and escape when tapped. - capslock = mods_on_hold(C, esc) +**layer(\)**: Activates the given layer while held. - # Makes the shift key sticky for one keystroke. +**overload(\,\)**: Activates the given layer while held and emits the given key sequence when tapped. - leftshift = oneshot(S) - rightshift = oneshot(S) +**layout(\)**: Sets the current layout to the given layer. You will likely want +to ensure you have a way to switch layouts within the new one. -Example 2 +## Example - # Maps escape to the escape layer when held and the escape key when pressed + # Makes dvorak the default key layout with + # qwerty (main) as the mod layout. - esc = layer_on_hold(escape_layer, esc) + layout(dvorak, main) - [escape_layer] + esc = layer(esc) - 1 = layer_toggle(dvorak) - 2 = layer_toggle(default) + leftshift = oneshot(S) + rightshift = oneshot(S) - # Creates a dvorak layer which inherits from the default layer. Without - # explicitly inheriting from another layer unmapped keys would be ignored. + [esc] - [dvorak:default] + # esc+q changes the layout to qwerty. + q = layout(main) - q = apostrophe - w = comma - e = dot - r = p - t = y - y = f - u = g - i = c - o = r - p = l + w = layout(dvorak, main) - a = a - s = o - d = e - f = u - g = i - h = d - j = h - k = t - l = n - semicolon = s + # Inherits the escape/shift bindings from the main layer - z = semicolon - x = q - c = j - v = k - b = x - n = b - m = m - comma = w - dot = v - slash = z + [dvorak:main] + q = apostrophe + w = comma + e = dot + r = p + t = y + y = f + u = g + i = c + o = r + p = l + a = a + s = o + d = e + f = u + g = i + h = d + j = h + k = t + l = n + semicolon = s + z = semicolon + x = q + c = j + v = k + b = x + n = b + m = m + comma = w + dot = v + slash = z # NOTES - Because of the way keyd works it is possible to render your machine unusable with a bad config file. This can usually be resolved by plugging in a different keyboard, however - if *default.cfg* has been misconfigured you will have to find an alternate way to kill + if *default.cfg* has been misconfigured you will have to find an alternate way to kill the daemon (e.g SSH). # AUTHOR diff --git a/src/config.c b/src/config.c index 9d5fe14..b2a731e 100644 --- a/src/config.c +++ b/src/config.c @@ -38,16 +38,26 @@ struct keyboard_config *configs = NULL; -static char layers[MAX_LAYERS][MAX_LAYER_NAME_LEN]; //Layer names -static size_t nlayers; -static int parents[MAX_LAYERS]; +struct layer_table_ent { + char name[256]; + char pname[256]; + + uint8_t modsonly; + struct layer *layer; +} layer_table[MAX_LAYERS]; + +int nlayers = 0; static int lnum = 0; static char path[PATH_MAX]; +static int lookup_layer(const char *name); +static int parse_modset(const char *s, uint16_t *mods); + #define err(fmt, ...) fprintf(stderr, "%s: ERROR on line %d: "fmt"\n", path, lnum, ##__VA_ARGS__) -static const char *modseq_to_string(uint16_t mods) { +static const char *modseq_to_string(uint16_t mods) +{ static char s[32]; int i = 0; s[0] = '\0'; @@ -55,7 +65,7 @@ static const char *modseq_to_string(uint16_t mods) { if(mods & MOD_CTRL) { strcpy(s+i, "-C"); i+=2; - } + } if(mods & MOD_SHIFT) { strcpy(s+i, "-S"); i+=2; @@ -64,7 +74,7 @@ static const char *modseq_to_string(uint16_t mods) { if(mods & MOD_SUPER) { strcpy(s+i, "-M"); i+=2; - } + } if(mods & MOD_ALT) { strcpy(s+i, "-A"); @@ -82,7 +92,8 @@ static const char *modseq_to_string(uint16_t mods) { return s; } -static const char *keyseq_to_string(uint32_t keyseq) { +const char *keyseq_to_string(uint32_t keyseq) +{ int i = 0; static char s[256]; @@ -94,7 +105,7 @@ static const char *keyseq_to_string(uint32_t keyseq) { if(mods & MOD_CTRL) { strcpy(s+i, "C-"); i+=2; - } + } if(mods & MOD_SHIFT) { strcpy(s+i, "S-"); i+=2; @@ -103,7 +114,7 @@ static const char *keyseq_to_string(uint32_t keyseq) { if(mods & MOD_SUPER) { strcpy(s+i, "M-"); i+=2; - } + } if(mods & MOD_ALT) { strcpy(s+i, "A-"); @@ -123,7 +134,103 @@ static const char *keyseq_to_string(uint32_t keyseq) { return s; } -static int parse_mods(const char *s, uint16_t *mods) +//Returns the position in the layer table +static struct layer_table_ent *create_layer(const char *name, const char *pname, uint16_t mods) +{ + struct layer_table_ent *ent = &layer_table[nlayers]; + + ent->layer = calloc(1, sizeof(struct layer)); + ent->layer->keymap = calloc(KEY_CNT, sizeof(struct key_descriptor)); + + ent->modsonly = 0; + ent->layer->mods = mods; + + strcpy(ent->pname, pname); + strcpy(ent->name, name); + + nlayers++; + assert(nlayers <= MAX_LAYERS); + return ent; +} + +static struct layer_table_ent *create_mod_layer(uint16_t mods) +{ + struct layer_table_ent *ent = create_layer("", "", mods); + ent->modsonly = 1; + + return ent; +} + +static void keyseq_to_desc(uint32_t seq, struct key_descriptor *desc) +{ + desc->action = ACTION_KEYSEQ; + desc->arg.keyseq = seq; + + //To facilitate simplification of modifier handling convert + //all traditional modifier keys to their internal layer + //representations. + + switch(seq) { + case KEY_LEFTCTRL: + desc->action = ACTION_LAYER; + desc->arg.layer = lookup_layer("C"); + break; + case KEY_LEFTALT: + desc->action = ACTION_LAYER; + desc->arg.layer = lookup_layer("A"); + break; + case KEY_LEFTSHIFT: + desc->action = ACTION_LAYER; + desc->arg.layer = lookup_layer("S"); + break; + case KEY_LEFTMETA: + desc->action = ACTION_LAYER; + desc->arg.layer = lookup_layer("M"); + break; + } +} + +static struct layer_table_ent *create_main_layer() +{ + int i; + struct layer_table_ent *ent = create_layer("main", "", 0); + + for(i = 0;i < KEY_CNT;i++) + keyseq_to_desc(i, &ent->layer->keymap[i]); + + return ent; +} + +//Returns the index in the layer table or -1. +static int lookup_layer(const char *name) +{ + int i; + uint16_t mods; + + for(i = 0;i < nlayers;i++) { + if(!strcmp(layer_table[i].name, name)) + return i; + } + + if(!parse_modset(name, &mods)) { + //Check if a dedicated mod layer already exists. + for(i = 0;i < nlayers;i++) { + struct layer_table_ent *ent = &layer_table[i]; + if(ent->modsonly && ent->layer->mods == mods) + return i; + } + + //Autovivify mod layers which don't exist. + + create_mod_layer(mods); + return nlayers-1; + } + + return -1; +} + + +static int parse_modset(const char *s, uint16_t *mods) { *mods = 0; @@ -151,10 +258,8 @@ static int parse_mods(const char *s, uint16_t *mods) if(s[1] == 0) return 0; - else if(s[1] != '-') { - err("%s is not a valid modifier set", s); + else if(s[1] != '-') return -1; - } s+=2; } @@ -162,32 +267,59 @@ static int parse_mods(const char *s, uint16_t *mods) return 0; } -static int parse_keyseq(const char *s, uint16_t *keycode, uint16_t *mods) { +static int parse_layer_heading(const char *s, char name[256], char parent[256]) +{ + size_t len = strlen(s); + char *c; + + name[0] = 0; + parent[0] = 0; + + if(s[0] != '[' || s[len-1] != ']') + return -1; + + c = strchr(s, ':'); + if(c) { + int sz = c-(s+1); + memcpy(name, s+1, sz); + name[sz] = 0; + + sz = len-2-sz-1; + memcpy(parent, c+1, sz); + parent[sz] = 0; + } else { + int sz = len-2; + memcpy(name, s+1, sz); + name[sz] = 0; + } + + return 0; +} + +static uint32_t parse_keyseq(const char *s) { const char *c = s; size_t i; - - *mods = 0; - *keycode = 0; + uint32_t mods = 0; while(*c && c[1] == '-') { switch(*c) { case 'C': - *mods |= MOD_CTRL; + mods |= MOD_CTRL; break; case 'M': - *mods |= MOD_SUPER; + mods |= MOD_SUPER; break; case 'A': - *mods |= MOD_ALT; + mods |= MOD_ALT; break; case 'S': - *mods |= MOD_SHIFT; + mods |= MOD_SHIFT; break; case 'G': - *mods |= MOD_ALT_GR; + mods |= MOD_ALT_GR; break; default: - return -1; + return 0; break; } @@ -196,12 +328,11 @@ static int parse_keyseq(const char *s, uint16_t *keycode, uint16_t *mods) { for(i = 0;i < sizeof keycode_strings / sizeof keycode_strings[0];i++) { if(keycode_strings[i] && !strcmp(keycode_strings[i], c)) { - *keycode |= i; - return 0; + return (mods << 16) | i; } } - return -1; + return 0; } static int parse_kvp(char *s, char **_k, char **_v) @@ -231,74 +362,6 @@ static int parse_kvp(char *s, char **_k, char **_v) return 0; } -static void parse_layer_names() -{ - size_t i; - char *line = NULL; - size_t n; - ssize_t len; - char strparents[MAX_LAYERS][MAX_LAYER_NAME_LEN] = {0}; - - FILE *fh = fopen(path, "r"); - if(!fh) { - fprintf(stderr, "ERROR: Failed to open %s\n", path); - perror("fopen"); - exit(-1); - } - - - nlayers = 0; - strcpy(layers[nlayers++], "default"); - while((len=getline(&line, &n, fh)) != -1) { - char *s = line; - - while(len && isspace(s[0])) { - s++; - len--; - } - - if(len > 2 && s[0] == '[' && s[len-2] == ']') { - const char *name = s+1; - size_t idx = nlayers; - char *c; - - s[len-2] = '\0'; - - if((c=strchr(name, ':'))) { - const char *parent = c+1; - *c = '\0'; - - strcpy(strparents[idx], parent); - } - - strcpy(layers[idx], name); - nlayers++; - } - - free(line); - line = NULL; - n = 0; - } - - free(line); - fclose(fh); - - for(i = 0;i < nlayers;i++) { - size_t j; - - parents[i] = -1; - - for(j = 0;j < nlayers;j++) { - if(!strcmp(layers[j], strparents[i])) { - parents[i] = j; - } - } - if(strparents[i][0] != 0 && parents[i] == -1) { - err("%s is not a valid parent layer", strparents[i]); - } - } -} - static int parse_fn(char *s, char **fn_name, char *args[MAX_ARGS], size_t *nargs) { int openparen = 0; @@ -313,13 +376,11 @@ static int parse_fn(char *s, char **fn_name, char *args[MAX_ARGS], size_t *nargs *s = '\0'; s++; - while(*s && isspace(*s)) + while(*s && isspace(*s)) s++; - if(!*s) { - err("Missing closing parenthesis."); + if(!*s) return -1; - } if(*s == ')') { //No args *s = '\0'; @@ -332,13 +393,11 @@ static int parse_fn(char *s, char **fn_name, char *args[MAX_ARGS], size_t *nargs case ',': *s = '\0'; s++; - while(*s && isspace(*s)) + while(*s && isspace(*s)) s++; - if(!*s) { - err("Missing closing parenthesis."); + if(!*s) return -1; - } args[(*nargs)++] = s; break; @@ -347,122 +406,102 @@ static int parse_fn(char *s, char **fn_name, char *args[MAX_ARGS], size_t *nargs s++; } - if(*s != ')') { - if(openparen) - err("Missing closing parenthesis."); - else - err("Invalid function or key sequence."); - + if(*s != ')') return -1; - } *s = '\0'; return 0; } -static int parse_val(const char *_s, struct key_descriptor *desc) +static int parse_descriptor(const char *_s, struct key_descriptor *desc) { - uint16_t code; - uint16_t mods; + uint32_t seq; char *s = strdup(_s); char *fn; char *args[MAX_ARGS]; size_t nargs; - if(!parse_keyseq(s, &code, &mods)) { - desc->action = ACTION_KEYSEQ; - desc->arg.keyseq = ((uint32_t)mods << 16) | (uint32_t)code; + if((seq=parse_keyseq(s))) { + keyseq_to_desc(seq, desc); goto cleanup; - } + } - if(parse_fn(s, &fn, args, &nargs)) + if(parse_fn(s, &fn, args, &nargs)) { + err("%s is not a valid key sequence or action.", s); goto fail; + } - if(!strcmp(fn, "oneshot_layer") && nargs == 1) { - size_t i; - - for(i = 0;i < nlayers;i++) - if(!strcmp(args[0], layers[i])) { - desc->action = ACTION_LAYER_ONESHOT; - desc->arg.layer = i; + if(!strcmp(fn, "layer") && nargs == 1) { + int idx = lookup_layer(args[0]); - goto cleanup; - } - - err("%s is not a valid layer name.", args[0]); - goto fail; - } else if(!strcmp(fn, "layer") && nargs == 1) { - size_t i; + if(idx < 0) { + err("%s is not a valid layer.", args[0]); + goto fail; + } - for(i = 0;i < nlayers;i++) - if(!strcmp(args[0], layers[i])) { - desc->action = ACTION_LAYER; - desc->arg.layer = i; + desc->action = ACTION_LAYER; + desc->arg.layer = idx; - goto cleanup; - } + goto cleanup; + } else if(!strcmp(fn, "layout") && nargs > 0) { + int idx; - err("%s is not a valid layer name.", args[0]); - goto fail; - } else if(!strcmp(fn, "layer_toggle") && nargs == 1) { - size_t i; + if((idx = lookup_layer(args[0])) < 0) { + err("%s is not a valid layer.", args[0]); + goto fail; + } - for(i = 0;i < nlayers;i++) - if(!strcmp(args[0], layers[i])) { - desc->action = ACTION_LAYER_TOGGLE; - desc->arg.layer = i; + desc->action = ACTION_LAYOUT; + desc->arg.layer = idx; - goto cleanup; + if(nargs == 1) { + desc->arg2.layer = idx; + } else { + if((idx = lookup_layer(args[1])) < 0) { + err("%s is not a valid layer.", args[1]); + goto fail; } - err("%s is not a valid layer name.", args[0]); - goto fail; - } else if(!strcmp(fn, "oneshot") && nargs == 1) { - if(parse_mods(args[0], &mods)) - goto fail; + desc->action = ACTION_LAYOUT; + desc->arg2.layer = idx; + } - desc->action = ACTION_ONESHOT; - desc->arg.mods = mods; - } else if(!strcmp(fn, "layer_on_hold") && nargs == 2) { - size_t i; + goto cleanup; + } else if(!strcmp(fn, "oneshot") && nargs == 1) { + int idx; - desc->action = ACTION_DOUBLE_LAYER; + if((idx = lookup_layer(args[0])) < 0) { - if(parse_keyseq(args[1], &code, &mods)) { - err("%s is not a vaid keysequence.", args[1]); + err("%s is not a valid layer.", args[0]); goto fail; } - desc->arg2.keyseq = ((uint32_t)mods << 16) | (uint32_t)code; - - for(i = 0;i < nlayers;i++) - if(!strcmp(args[0], layers[i])) { - desc->arg.layer = i; - goto cleanup; - } - + desc->action = ACTION_ONESHOT; + desc->arg.layer = idx; - err("%s is not a valid layer.", args[0]); - goto fail; - } else if(!strcmp(fn, "mods_on_hold") && nargs == 2) { - desc->action = ACTION_DOUBLE_MODIFIER; + goto cleanup; + } else if(!strcmp(fn, "overload") && nargs == 2) { + int idx; - if(parse_mods(args[0], &mods)) + if((idx = lookup_layer(args[0])) < 0) { + err("%s is not a valid layer.", args[0]); goto fail; + } - desc->arg.mods = mods; - - if(parse_keyseq(args[1], &code, &mods)) { - err("%s is not a vaid keysequence.", args[1]); + if(!(seq=parse_keyseq(args[1]))) { + err("%s is not a valid key sequence.", args[1]); goto fail; } - desc->arg2.keyseq = ((uint32_t)mods << 16) | (uint32_t)code; + desc->action = ACTION_OVERLOAD; + desc->arg.keyseq = seq; + desc->arg2.layer = idx; + goto cleanup; } else { err("%s is not a valid action or key.", _s); goto fail; @@ -478,29 +517,156 @@ fail: return -1; } +static void build_layer_table() +{ + ssize_t len; + char *line = NULL; + size_t line_sz = 0; + FILE *fh = fopen(path, "r"); + + if(!fh) { + perror("fopen"); + exit(-1); + } + + lnum = 0; + nlayers = 0; + + create_main_layer(); + while((len=getline(&line, &line_sz, fh)) != -1) { + char name[256]; + char pname[256]; + + lnum++; + if(line[len-1] == '\n') + line[--len] = 0; + + if(!parse_layer_heading(line, name, pname)) { + uint16_t mods; + + if(!parse_modset(pname, &mods)) + create_layer(name, "", mods); + else + create_layer(name, pname, 0); + } + } + + free(line); + fclose(fh); +} + +static int parse_layer_entry(char *s, uint16_t *code, struct key_descriptor *desc) +{ + uint32_t seq; + char *k, *v; + + if(parse_kvp(s, &k, &v)) { + err("Invalid key value pair."); + return -1; + } + + if(!(seq = parse_keyseq(k))) { + err("'%s' is not a valid key.", k); + return -1; + } + + if((seq >> 16) != 0) { + err("key cannot contain modifiers."); + return -1; + } + + if(parse_descriptor(v, desc)) + return -1; + + *code = seq & 0xFF; + return 0; +} + +void post_process_config(struct keyboard_config *cfg, + const char *layout_name, + const char *modlayout_name) +{ + cfg->default_layout = -1; + cfg->default_modlayout = -1; + + int i; + for(i = 0;i < nlayers;i++) { + struct layer_table_ent *ent = &layer_table[i]; + + if(!strcmp(ent->name, layout_name)) + cfg->default_layout = i; + + if(!strcmp(ent->name, modlayout_name)) + cfg->default_modlayout = i; + + if(ent->pname[0]) { + int j; + + for(j = 0;j < nlayers;j++) { + struct layer_table_ent *ent2 = &layer_table[j]; + + if(!strcmp(ent->pname, ent2->name)) { + int k; + + struct layer *dst = ent->layer; + struct layer *src = ent2->layer; + + for(k = 0;k < KEY_CNT;k++) + if(dst->keymap[k].action == ACTION_UNDEFINED) + dst->keymap[k] = src->keymap[k]; + } + } + } + } + + if(cfg->default_layout == -1) { + fprintf(stderr, "%s is not a valid default layout", layout_name); + cfg->default_layout = 0; + } + + if(cfg->default_modlayout == -1) { + fprintf(stderr, "%s is not a valid default modifier layout\n", modlayout_name); + cfg->default_modlayout = 0; + } +} + static void parse(struct keyboard_config *cfg) { - size_t i, j; + int i; char *line = NULL; size_t n = 0; ssize_t len; + struct layer *layer; - lnum = 0; - parse_layer_names(); + char layout_name[256]; + char modlayout_name[256]; - FILE *fh = fopen(path, "r"); + build_layer_table(); + FILE *fh = fopen(path, "r"); if(!fh) { perror("fopen"); exit(-1); } - int current_layer = 0; - struct layer *layer = &cfg->layers[0]; - lnum = 0; + layer = layer_table[0].layer; + + strcpy(layout_name, "main"); + strcpy(modlayout_name, "main"); while((len=getline(&line, &n, fh)) != -1) { + char name[256]; + char type[256]; + + uint16_t code; + struct key_descriptor desc; + + size_t nargs; + char *fnname; + char *args[MAX_ARGS]; + + lnum++; char *s = line; @@ -512,40 +678,30 @@ static void parse(struct keyboard_config *cfg) if(len && s[len-1] == '\n') //Strip tailing newline (not present on EOF) s[--len] = '\0'; - if(len == 0 || s[0] == '#') + if(len == 0 || s[0] == '#') continue; - if(s[0] == '[' && s[len-1] == ']') { - layer = &cfg->layers[++current_layer]; - } else if(layer) { - struct key_descriptor desc; - char *key, *val; - - uint16_t code; - uint16_t mods; - - if(parse_kvp(s, &key, &val)) { - err("Invalid key value pair."); - goto next; - } - - if(parse_keyseq(key, &code, &mods)) { - err("'%s' is not a valid key.", key); - goto next; + if(!parse_layer_heading(s, name, type)) { + int idx = lookup_layer(name); + assert(idx > 0); + layer = layer_table[idx].layer; + } else if(strchr(s, '=')) { + if(!parse_layer_entry(s, &code, &desc)) + layer->keymap[code] = desc; + else + err("Invalid layer entry."); + } else if(!parse_fn(s, &fnname, args, &nargs)) { + if(!strcmp(fnname, "layout")) { + if(nargs == 1) { + strcpy(layout_name, args[0]); + strcpy(modlayout_name, args[0]); + } else if(nargs == 2) { + strcpy(layout_name, args[0]); + strcpy(modlayout_name, args[1]); + } } - - if(mods != 0) { - err("key cannot contain modifiers."); - goto next; - } - - if(parse_val(val, &desc)) - goto next; - - layer->keymap[code] = desc; } -next: free(line); line = NULL; n = 0; @@ -554,15 +710,14 @@ next: free(line); fclose(fh); - for(i = 0;i < nlayers;i++) { - int p = parents[i]; - if(p != -1) { - for(j = 0;j < KEY_CNT;j++) { - if(cfg->layers[i].keymap[j].action == ACTION_DEFAULT) - cfg->layers[i].keymap[j] = cfg->layers[p].keymap[j]; - } - } - } + post_process_config(cfg, layout_name, modlayout_name); + + for(i = 0;i < nlayers;i++) + cfg->layers[i] = layer_table[i].layer; + + cfg->nlayers = nlayers; + + return; } void config_free() @@ -570,8 +725,18 @@ void config_free() struct keyboard_config *cfg = configs; while(cfg) { + size_t i; struct keyboard_config *tmp = cfg; + cfg = cfg->next; + + for(i = 0;i < tmp->nlayers;i++) { + struct layer *layer = tmp->layers[i]; + + free(layer->keymap); + free(layer); + } + free(tmp); }; } @@ -587,7 +752,6 @@ void config_generate() } while((ent=readdir(dh))) { - size_t i; struct keyboard_config *cfg; int len = strlen(ent->d_name); @@ -599,18 +763,12 @@ void config_generate() cfg = calloc(1, sizeof(struct keyboard_config)); - for(i = 0;i < KEY_CNT;i++) { - struct key_descriptor *desc = &cfg->layers[0].keymap[i]; - desc->action = ACTION_KEY; - desc->arg.key = i; - } - strcpy(cfg->name, ent->d_name); cfg->name[len-4] = '\0'; - cfg->next = configs; parse(cfg); + cfg->next = configs; configs = cfg; } diff --git a/src/config.h b/src/config.h index 1b68e1c..1db7915 100644 --- a/src/config.h +++ b/src/config.h @@ -28,20 +28,17 @@ #include #include "keys.h" -#define MAX_LAYERS 8 +#define MAX_LAYERS 32 #define CONFIG_DIR "/etc/keyd" #define MAX_LAYER_NAME_LEN 256 enum action { - ACTION_DEFAULT, - ACTION_DOUBLE_MODIFIER, - ACTION_DOUBLE_LAYER, - ACTION_KEY, + ACTION_UNDEFINED, + ACTION_OVERLOAD, + ACTION_LAYOUT, ACTION_KEYSEQ, - ACTION_LAYER_TOGGLE, - ACTION_LAYER_ONESHOT, ACTION_LAYER, - ACTION_ONESHOT + ACTION_ONESHOT, }; struct key_descriptor @@ -49,20 +46,32 @@ struct key_descriptor enum action action; union { uint32_t keyseq; - uint16_t key; uint16_t mods; - uint8_t layer; + int8_t layer; } arg, arg2; }; + + + +//A layer may optionally have modifiers. If it does it is expected to behave as +//a normal modifier in all instances except when a key is explicitly defined in +//the keymap. + struct layer { - struct key_descriptor keymap[KEY_CNT]; + uint16_t mods; + struct key_descriptor *keymap; }; struct keyboard_config { char name[256]; - struct layer layers[MAX_LAYERS]; + struct layer *layers[MAX_LAYERS]; + size_t nlayers; + + int default_modlayout; + int default_layout; + struct keyboard_config *next; }; @@ -70,5 +79,6 @@ extern struct keyboard_config *configs; void config_generate(); void config_free(); +const char *keyseq_to_string(uint32_t keyseq); #endif diff --git a/src/main.c b/src/main.c index 4c5ed48..679c5ba 100644 --- a/src/main.c +++ b/src/main.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include "keys.h" @@ -61,8 +62,14 @@ struct keyboard { int fd; char devnode[256]; - uint8_t main_layer; - uint8_t active_layer; + uint8_t active_layers[MAX_LAYERS]; + uint16_t mods; //The active modifier set + + //The layer to which modifiers are applied, + //this may be distinct from the main layout + int modlayout; + + int layout; struct keyboard_config *cfg; struct keyboard *next; @@ -72,7 +79,7 @@ static struct keyboard *keyboards = NULL; static void warn(char *fmt, ...) { - va_list args; + va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); @@ -82,7 +89,7 @@ static void warn(char *fmt, ...) static void _die(char *fmt, ...) { - va_list args; + va_list args; va_start(args, fmt); vfprintf(stderr, fmt, args); @@ -93,7 +100,7 @@ static void _die(char *fmt, ...) #define die(fmt, ...) _die("%s:%d: "fmt, __FILE__, __LINE__, ## __VA_ARGS__) -static int is_keyboard(struct udev_device *dev) +static int is_keyboard(struct udev_device *dev) { int is_keyboard = 0; @@ -118,7 +125,7 @@ static int is_keyboard(struct udev_device *dev) return is_keyboard; } -static void get_keyboard_nodes(char *nodes[MAX_KEYBOARDS], int *sz) +static void get_keyboard_nodes(char *nodes[MAX_KEYBOARDS], int *sz) { struct udev *udev; struct udev_enumerate *enumerate; @@ -160,7 +167,7 @@ static void get_keyboard_nodes(char *nodes[MAX_KEYBOARDS], int *sz) udev_unref(udev); } -static int create_uinput_fd() +static int create_uinput_fd() { size_t i; struct uinput_setup usetup; @@ -238,69 +245,99 @@ static void send_key(uint16_t code, int is_pressed) syn(); } -static void send_mods(uint16_t mods, int pressed) +static void setmods(uint16_t mods) { - if(mods & MOD_CTRL) - send_key(KEY_LEFTCTRL, pressed); - if(mods & MOD_SHIFT) - send_key(KEY_LEFTSHIFT, pressed); - if(mods & MOD_SUPER) - send_key(KEY_LEFTMETA, pressed); - if(mods & MOD_ALT) - send_key(KEY_LEFTALT, pressed); - if(mods & MOD_ALT_GR) - send_key(KEY_RIGHTALT, pressed); + if(!!(mods & MOD_CTRL) != keystate[KEY_LEFTCTRL]) + send_key(KEY_LEFTCTRL, !keystate[KEY_LEFTCTRL]); + if(!!(mods & MOD_SHIFT) != keystate[KEY_LEFTSHIFT]) + send_key(KEY_LEFTSHIFT, !keystate[KEY_LEFTSHIFT]); + if(!!(mods & MOD_SUPER) != keystate[KEY_LEFTMETA]) + send_key(KEY_LEFTMETA, !keystate[KEY_LEFTMETA]); + if(!!(mods & MOD_ALT) != keystate[KEY_LEFTALT]) + send_key(KEY_LEFTALT, !keystate[KEY_LEFTALT]); + if(!!(mods & MOD_ALT_GR) != keystate[KEY_RIGHTALT]) + send_key(KEY_RIGHTALT, !keystate[KEY_RIGHTALT]); } -static void send_keyseq(uint32_t keyseq, int pressed) +static struct key_descriptor *kbd_lookup_descriptor(struct keyboard *kbd, uint16_t code, uint16_t *modsp) { - uint16_t mods = keyseq >> 16; - uint16_t code = keyseq; + struct key_descriptor *desc = NULL; + int active_layers = 0; + uint16_t mods = 0; + size_t i; - send_mods(mods, pressed); - send_key(code, pressed); -} + for(i = 0;i < kbd->cfg->nlayers;i++) { + if(kbd->active_layers[i]) { + struct layer *layer = kbd->cfg->layers[i]; + struct key_descriptor *d = &layer->keymap[code]; -static struct key_descriptor *get_descriptor(struct keyboard *kbd, - uint16_t code, - uint8_t ispressed, - uint8_t *tapped) -{ - static struct key_descriptor *pressed[KEY_CNT] = {0}; - static uint16_t last = 0; + if(!desc && d->action != ACTION_UNDEFINED) + desc = d; + else + mods |= layer->mods; + + active_layers++; + } + } - struct key_descriptor *desc; + if(!desc) { + if(mods) { + //If the shift key is the only modifier active, we probably + //want to use the key layout. + if(mods == MOD_SHIFT || mods == MOD_ALT_GR) + desc = &kbd->cfg->layers[kbd->layout]->keymap[code]; + else + desc = &kbd->cfg->layers[kbd->modlayout]->keymap[code]; + } else if(active_layers) + return NULL; + else + desc = &kbd->cfg->layers[kbd->layout]->keymap[code]; + } - *tapped = !ispressed && last == code; - last = code; + *modsp = mods; + return desc; +} - //Ensure that key descriptors are consistent accross key up/down event pairs. - //This is necessary to accomodate layer changes midkey. - if(!ispressed) { - assert(pressed[code]); - desc = pressed[code]; +static void reify_layer_mods(struct keyboard *kbd) +{ + uint16_t mods = 0; + size_t i; - pressed[code] = NULL; - return desc; + for(i = 0;i < kbd->cfg->nlayers;i++) { + struct layer *layer = kbd->cfg->layers[i]; + if(kbd->active_layers[i]) + mods |= layer->mods; } - desc = &kbd->cfg->layers[kbd->active_layer].keymap[code]; - pressed[code] = desc; - return desc; + setmods(mods); +} + +static uint64_t get_time() +{ + struct timespec tv; + clock_gettime(CLOCK_MONOTONIC, &tv); + return (tv.tv_sec*1E9)+tv.tv_nsec; } //Where the magic happens static void process_event(struct keyboard *kbd, struct input_event *ev) { - static uint16_t oneshotmods = 0; - static struct key_descriptor *desc, *last_pressed = NULL; - static uint8_t oneshotlayer = 0; - static uint8_t tapped; - - + size_t i; uint16_t code = ev->code; uint8_t pressed = ev->value; - uint32_t keypressed = 0; + + struct key_descriptor *d; + + static struct key_descriptor *lastd = NULL; + static uint8_t oneshot_layers[MAX_LAYERS] = {0}; + static uint64_t pressed_timestamps[KEY_CNT] = {0}; + static uint64_t last_keyseq_timestamp = 0; + uint16_t mods = 0; + + //Cache the descriptor to ensure consistency upon up/down event pairs since layers can change midkey. + + static struct key_descriptor *dcache[KEY_CNT] ={0}; + static uint16_t mcache[KEY_CNT] ={0}; if(ev->type != EV_KEY) return; @@ -311,94 +348,108 @@ static void process_event(struct keyboard *kbd, struct input_event *ev) return; } - desc = get_descriptor(kbd, code, pressed, &tapped); + if(!pressed) { + d = dcache[code]; + mods = mcache[code]; + + dcache[code] = NULL; + mcache[code] = 0; + } else { + pressed_timestamps[code] = get_time(); + d = kbd_lookup_descriptor(kbd, code, &mods); - if(oneshotlayer) { - kbd->active_layer = kbd->main_layer; - oneshotlayer = 0; + dcache[code] = d; + mcache[code] = mods; } - switch(desc->action) { - uint32_t keyseq; - uint16_t key; + if(!d) + return; - case ACTION_KEY: - key = desc->arg.key; - send_key(key, pressed); - keypressed = key; + switch(d->action) { + int layer; + uint32_t keyseq; - break; - case ACTION_KEYSEQ: - keyseq = desc->arg.keyseq; - send_keyseq(keyseq, pressed); - keypressed = keyseq; + case ACTION_OVERLOAD: + keyseq = d->arg.keyseq; + layer = d->arg2.layer; - break; - case ACTION_LAYER: - if(pressed) - kbd->active_layer = desc->arg.layer; - else - kbd->active_layer = kbd->main_layer; - break; - case ACTION_LAYER_ONESHOT: - if(pressed) - kbd->active_layer = desc->arg.layer; - else if(tapped) - oneshotlayer++; - else - kbd->active_layer = kbd->main_layer; - break; - case ACTION_LAYER_TOGGLE: if(pressed) { - kbd->main_layer = desc->arg.layer; - kbd->active_layer = kbd->main_layer; + kbd->active_layers[layer] = 1; + } else { + kbd->active_layers[layer] = 0; + + if(lastd == d) { //If tapped + uint16_t key = keyseq & 0xFF; + mods |= keyseq >> 16; + + setmods(mods); + send_key(key, 1); + send_key(key, 0); + + goto keyseq_cleanup; + } } + + reify_layer_mods(kbd); + break; + case ACTION_LAYOUT: + kbd->layout = d->arg.layer; + kbd->modlayout = d->arg2.layer; + + dbg("layer: %d, modlayout: %d", kbd->layout, kbd->modlayout); break; case ACTION_ONESHOT: if(pressed) - send_mods(desc->arg.mods, 1); - else if(tapped) //If no key has been interposed make the modifiers transient - oneshotmods |= desc->arg.mods; - else //otherwise treat as a normal modifier. - send_mods(desc->arg.mods, 0); + kbd->active_layers[d->arg.layer] = 1; + else if(pressed_timestamps[code] < last_keyseq_timestamp) + kbd->active_layers[d->arg.layer] = 0; + else //Tapped + oneshot_layers[d->arg.layer] = 1; + reify_layer_mods(kbd); break; - case ACTION_DOUBLE_MODIFIER: + case ACTION_LAYER: if(pressed) - send_mods(desc->arg.mods, 1); - else { - send_mods(desc->arg.mods, 0); - if(tapped) { - send_keyseq(desc->arg2.keyseq, 1); - send_keyseq(desc->arg2.keyseq, 0); - keypressed = desc->arg2.keyseq; - } - } + kbd->active_layers[d->arg.layer] = 1; + else + kbd->active_layers[d->arg.layer] = 0; + + reify_layer_mods(kbd); break; - case ACTION_DOUBLE_LAYER: + case ACTION_KEYSEQ: + mods |= d->arg.keyseq >> 16; if(pressed) { - kbd->active_layer = desc->arg.layer; + setmods(mods); + send_key(d->arg.keyseq & 0xFF, 1); + } else { - kbd->active_layer = kbd->main_layer; - if(tapped) { - send_keyseq(desc->arg2.keyseq, 1); - send_keyseq(desc->arg2.keyseq, 0); - keypressed = desc->arg2.keyseq; - } + reify_layer_mods(kbd); + send_key(d->arg.keyseq & 0xFF, 0); } + + goto keyseq_cleanup; break; - default: - dbg("Ignoring action %d generated by %d", desc->action, code); + case ACTION_UNDEFINED: + goto keyseq_cleanup; break; } - if(keypressed && !ISMOD(keypressed & 0xFFFF)) { - send_mods(oneshotmods, 0); - oneshotmods = 0; - } + lastd = d; + return; + +keyseq_cleanup: + lastd = d; if(pressed) - last_pressed = desc; + last_keyseq_timestamp = get_time(); + + //Clear active oneshot layers. + for(i = 0;i < kbd->cfg->nlayers;i++) { + if(oneshot_layers[i]) { + kbd->active_layers[i] = 0; + oneshot_layers[i] = 0; + } + } } static const char *evdev_device_name(const char *devnode) @@ -441,7 +492,7 @@ static void await_keyboard_neutrality(char **devs, int n) //repeat event is generated within the first 300ms. If we miss the //keydown event and the repeat event is not generated within the first //300ms it is possible for this to yield a false positive. In practice - //this seems to work fine. Given the stateless nature of evdev I am not + //this seems to work fine. Given the stateless nature of evdev I am not //aware of a better way to achieve this. while(1) { @@ -519,17 +570,17 @@ static int manage_keyboard(const char *devnode) exit(1); } - kbd = malloc(sizeof(struct keyboard)); + kbd = calloc(1, sizeof(struct keyboard)); kbd->fd = fd; kbd->cfg = cfg; - kbd->main_layer = 0; - kbd->active_layer = 0; + kbd->modlayout = kbd->cfg->default_modlayout; + kbd->layout = kbd->cfg->default_layout; strcpy(kbd->devnode, devnode); //Grab the keyboard. if(ioctl(fd, EVIOCGRAB, (void *)1) < 0) { - perror("EVIOCGRAB"); + perror("EVIOCGRAB"); exit(-1); } @@ -552,7 +603,7 @@ static int destroy_keyboard(const char *devnode) //Attempt to ungrab the the keyboard (assuming it still exists) if(ioctl(kbd->fd, EVIOCGRAB, (void *)1) < 0) { - perror("EVIOCGRAB"); + perror("EVIOCGRAB"); } close(kbd->fd); @@ -567,7 +618,7 @@ static int destroy_keyboard(const char *devnode) return 0; } -static void evdev_monitor_loop(int *fds, int sz) +static void evdev_monitor_loop(int *fds, int sz) { struct input_event ev; fd_set fdset; @@ -610,7 +661,7 @@ static void evdev_monitor_loop(int *fds, int sz) } } -static int monitor_loop() +static int monitor_loop() { char *devnodes[256]; int sz, i; @@ -634,7 +685,7 @@ static int monitor_loop() return 0; } -static void main_loop() +static void main_loop() { struct keyboard *kbd; int monfd; @@ -661,8 +712,7 @@ static void main_loop() monfd = udev_monitor_get_fd(udevmon); - int exit = 0; - while(!exit) { + while(1) { int maxfd; fd_set fds; struct udev_device *dev;