Version 0.1.0

- 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.
master
Raheman Vaiya 5 years ago
parent f5d0c04773
commit 5ef77730bf
  1. 25
      CHANGELOG.md
  2. 4
      CHANGES
  3. 0
      CONTRIBUTING.md
  4. 16
      Makefile
  5. 86
      README.md
  6. BIN
      keyd.1.gz
  7. BIN
      man.1.gz
  8. 200
      man.md
  9. 616
      src/config.c
  10. 34
      src/config.h
  11. 302
      src/main.c

@ -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.

@ -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.

@ -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

@ -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).

Binary file not shown.

Binary file not shown.

200
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:
[<layer name>]
[<layer>]
Where `<keyseq>` has the form: `[<modifier1>-[<modifier2>-]]<key>`
Where `<keyseq>` has the form: `[<modifier1>[-<modifier2>...]-<key>`
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
`:<parent layer>` 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.
`:<parent>` 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(<layer>) 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
```
- `<mods>` = A set of modifiers of the form: `<mod1>[-<mod2>...]` (e.g C-M = control + meta).
- `<keyseq>` = A key sequence consisting of zero or more control characters and a key (e.g C-a = control+a).
- `<layer>` = 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(\<layer\>)**: 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(\<layer\>)**: Activates the given layer while held.
# Makes the shift key sticky for one keystroke.
**overload(\<keyseq\>,\<layer\>)**: Activates the given layer while held and emits the given key sequence when tapped.
leftshift = oneshot(S)
rightshift = oneshot(S)
**layout(\<layer\>)**: 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

@ -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;
}

@ -28,20 +28,17 @@
#include <stddef.h>
#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

@ -34,6 +34,7 @@
#include <stdint.h>
#include <stdarg.h>
#include <linux/uinput.h>
#include <time.h>
#include <stdlib.h>
#include <fcntl.h>
#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;

Loading…
Cancel
Save