You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
815 lines
17 KiB
815 lines
17 KiB
/* |
|
* keyd - A key remapping daemon. |
|
* |
|
* © 2019 Raheman Vaiya (see also: LICENSE). |
|
*/ |
|
|
|
#include "keyboard.h" |
|
|
|
/* |
|
* Here be tiny dragons. |
|
*/ |
|
|
|
static long get_time() |
|
{ |
|
/* Close enough :/. Using a syscall is unnecessary. */ |
|
static long time = 1; |
|
return time++; |
|
} |
|
|
|
static int cache_set(struct keyboard *kbd, uint8_t code, |
|
const struct descriptor *d, int dl) |
|
{ |
|
size_t i; |
|
int slot = -1; |
|
|
|
for (i = 0; i < CACHE_SIZE; i++) |
|
if (kbd->cache[i].code == code) { |
|
slot = i; |
|
break; |
|
} else if (!kbd->cache[i].code) { |
|
slot = i; |
|
} |
|
|
|
if (slot == -1) |
|
return -1; |
|
|
|
if (d == NULL) { |
|
kbd->cache[slot].code = 0; |
|
} else { |
|
kbd->cache[slot].code = code; |
|
kbd->cache[slot].d = *d; |
|
kbd->cache[slot].dl = dl; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static int cache_get(struct keyboard *kbd, uint8_t code, |
|
struct descriptor *d, int *dl) |
|
{ |
|
size_t i; |
|
|
|
for (i = 0; i < CACHE_SIZE; i++) |
|
if (kbd->cache[i].code == code) { |
|
if (d) |
|
*d = kbd->cache[i].d; |
|
if (dl) |
|
*dl = kbd->cache[i].dl; |
|
|
|
return 0; |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
static void reset_keystate(struct keyboard *kbd) |
|
{ |
|
size_t i; |
|
|
|
for (i = 0; i < 256; i++) { |
|
if (kbd->keystate[i]) { |
|
kbd->output(i, 0); |
|
kbd->keystate[i] = 0; |
|
} |
|
} |
|
|
|
} |
|
|
|
static void send_key(struct keyboard *kbd, uint8_t code, uint8_t pressed) |
|
{ |
|
if (code == KEYD_NOOP || code == KEYD_EXTERNAL_MOUSE_BUTTON) |
|
return; |
|
|
|
if (pressed) |
|
kbd->last_pressed_output_code = code; |
|
|
|
if (kbd->keystate[code] != pressed) { |
|
kbd->keystate[code] = pressed; |
|
kbd->output(code, pressed); |
|
} |
|
} |
|
|
|
static void set_mods(struct keyboard *kbd, uint8_t mods) |
|
{ |
|
size_t i; |
|
|
|
for (i = 0; i < ARRAY_SIZE(modifier_table); i++) { |
|
uint8_t code = modifier_table[i].code1; |
|
uint8_t mask = modifier_table[i].mask; |
|
|
|
if (mask & mods) { |
|
if (!kbd->keystate[code]) |
|
send_key(kbd, code, 1); |
|
} else { |
|
/* |
|
* Some modifiers have a special meaning when used in |
|
* isolation (e.g meta in Gnome, alt in Firefox). |
|
* In order to prevent spurious key presses we |
|
* avoid adjacent down/up pairs by interposing |
|
* additional control sequences. |
|
*/ |
|
int guard = ((((kbd->last_pressed_output_code == KEYD_LEFTMETA) && mask == MOD_SUPER) || |
|
((kbd->last_pressed_output_code == KEYD_LEFTALT) && mask == MOD_ALT)) && |
|
!kbd->inhibit_modifier_guard); |
|
|
|
if (kbd->keystate[code]) { |
|
if (guard && !kbd->keystate[KEYD_LEFTCTRL]) { |
|
send_key(kbd, KEYD_LEFTCTRL, 1); |
|
send_key(kbd, code, 0); |
|
send_key(kbd, KEYD_LEFTCTRL, 0); |
|
} else { |
|
send_key(kbd, code, 0); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
static void update_mods(struct keyboard *kbd, int excluded_layer_idx, uint8_t mods) |
|
{ |
|
size_t i; |
|
struct layer *excluded_layer = excluded_layer_idx == -1 ? |
|
NULL : |
|
&kbd->config.layers[excluded_layer_idx]; |
|
|
|
for (i = 0; i < kbd->config.nr_layers; i++) { |
|
struct layer *layer = &kbd->config.layers[i]; |
|
int excluded = 0; |
|
|
|
if (!kbd->layer_state[i].active) |
|
continue; |
|
|
|
if (layer == excluded_layer) { |
|
excluded = 1; |
|
} else if (excluded_layer && excluded_layer->type == LT_COMPOSITE) { |
|
size_t j; |
|
|
|
for (j = 0; j < excluded_layer->nr_constituents; j++) |
|
if ((size_t)excluded_layer->constituents[j] == i) |
|
excluded = 1; |
|
} |
|
|
|
if (!excluded) |
|
mods |= layer->mods; |
|
} |
|
|
|
set_mods(kbd, mods); |
|
} |
|
|
|
static void execute_macro(struct keyboard *kbd, int dl, const struct macro *macro) |
|
{ |
|
/* Minimize redundant modifier strokes for simple key sequences. */ |
|
if (macro->sz == 1 && macro->entries[0].type == MACRO_KEYSEQUENCE) { |
|
uint8_t code = macro->entries[0].data; |
|
uint8_t mods = macro->entries[0].data >> 8; |
|
|
|
update_mods(kbd, dl, mods); |
|
send_key(kbd, code, 1); |
|
send_key(kbd, code, 0); |
|
} else { |
|
update_mods(kbd, dl, 0); |
|
macro_execute(kbd->output, macro, kbd->config.macro_sequence_timeout); |
|
} |
|
} |
|
|
|
static void lookup_descriptor(struct keyboard *kbd, uint8_t code, |
|
struct descriptor *d, int *dl) |
|
{ |
|
size_t max; |
|
size_t i; |
|
|
|
d->op = 0; |
|
|
|
long maxts = 0; |
|
|
|
for (i = 0; i < kbd->config.nr_layers; i++) { |
|
struct layer *layer = &kbd->config.layers[i]; |
|
|
|
if (kbd->layer_state[i].active) { |
|
long activation_time = kbd->layer_state[i].activation_time; |
|
|
|
if (layer->keymap[code].op && activation_time >= maxts) { |
|
maxts = activation_time; |
|
*d = layer->keymap[code]; |
|
*dl = i; |
|
} |
|
} |
|
} |
|
|
|
max = 0; |
|
/* Scan for any composite matches (which take precedence). */ |
|
for (i = 0; i < kbd->config.nr_layers; i++) { |
|
struct layer *layer = &kbd->config.layers[i]; |
|
|
|
if (layer->type == LT_COMPOSITE) { |
|
size_t j; |
|
int match = 1; |
|
uint8_t mods = 0; |
|
|
|
for (j = 0; j < layer->nr_constituents; j++) { |
|
if (kbd->layer_state[layer->constituents[j]].active) |
|
mods |= kbd->config.layers[layer->constituents[j]].mods; |
|
else |
|
match = 0; |
|
} |
|
|
|
if (match && layer->keymap[code].op && (layer->nr_constituents > max)) { |
|
*d = layer->keymap[code]; |
|
*dl = i; |
|
|
|
max = layer->nr_constituents; |
|
} |
|
} |
|
} |
|
|
|
if (!d->op) { |
|
d->op = OP_KEYSEQUENCE; |
|
d->args[0].code = code; |
|
d->args[1].mods = 0; |
|
} |
|
} |
|
|
|
static void deactivate_layer(struct keyboard *kbd, int idx) |
|
{ |
|
dbg("Deactivating layer %s", kbd->config.layers[idx].name); |
|
|
|
assert(kbd->layer_state[idx].active > 0); |
|
kbd->layer_state[idx].active--; |
|
|
|
if (kbd->layer_observer) |
|
kbd->layer_observer(kbd, kbd->config.layers[idx].name, 0); |
|
} |
|
|
|
/* |
|
* NOTE: Every activation call *must* be paired with a |
|
* corresponding deactivation call. |
|
*/ |
|
|
|
static void activate_layer(struct keyboard *kbd, uint8_t code, int idx) |
|
{ |
|
dbg("Activating layer %s", kbd->config.layers[idx].name); |
|
|
|
kbd->layer_state[idx].activation_time = get_time(); |
|
kbd->layer_state[idx].active++; |
|
kbd->last_layer_code = code; |
|
|
|
if (kbd->layer_observer) |
|
kbd->layer_observer(kbd, kbd->config.layers[idx].name, 1); |
|
} |
|
|
|
static void execute_command(const char *cmd) |
|
{ |
|
int fd; |
|
|
|
dbg("executing command: %s", cmd); |
|
|
|
if (fork()) |
|
return; |
|
if (fork()) |
|
exit(0); |
|
|
|
fd = open("/dev/null", O_RDWR); |
|
|
|
if (fd < 0) { |
|
perror("open"); |
|
exit(-1); |
|
} |
|
|
|
close(0); |
|
close(1); |
|
close(2); |
|
|
|
dup2(fd, 0); |
|
dup2(fd, 1); |
|
dup2(fd, 2); |
|
|
|
execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); |
|
} |
|
|
|
static void clear_oneshot(struct keyboard *kbd) |
|
{ |
|
size_t i = 0; |
|
|
|
for (i = 0; i < kbd->config.nr_layers; i++) |
|
if (kbd->layer_state[i].oneshot) { |
|
kbd->layer_state[i].oneshot = 0; |
|
deactivate_layer(kbd, i); |
|
} |
|
|
|
kbd->oneshot_latch = 0; |
|
} |
|
|
|
static void clear(struct keyboard *kbd) |
|
{ |
|
size_t i; |
|
for (i = 1; i < kbd->config.nr_layers; i++) { |
|
struct layer *layer = &kbd->config.layers[i]; |
|
|
|
if (layer->type != LT_LAYOUT) { |
|
if (kbd->layer_state[i].active && kbd->layer_observer) |
|
kbd->layer_observer(kbd, kbd->config.layers[i].name, 0); |
|
|
|
memset(&kbd->layer_state[i], 0, sizeof kbd->layer_state[0]); |
|
} |
|
} |
|
|
|
/* Neutralize upstroke for active keys. */ |
|
memset(kbd->cache, 0, sizeof(kbd->cache)); |
|
|
|
kbd->oneshot_latch = 0; |
|
kbd->active_macro = NULL; |
|
|
|
reset_keystate(kbd); |
|
} |
|
|
|
static void setlayout(struct keyboard *kbd, uint8_t idx) |
|
{ |
|
clear(kbd); |
|
/* Only only layout may be active at a time */ |
|
size_t i; |
|
for (i = 0; i < kbd->config.nr_layers; i++) { |
|
struct layer *layer = &kbd->config.layers[i]; |
|
|
|
if (layer->type == LT_LAYOUT) |
|
kbd->layer_state[i].active = 0; |
|
} |
|
|
|
kbd->layer_state[idx].activation_time = 1; |
|
kbd->layer_state[idx].active = 1; |
|
} |
|
|
|
|
|
static long process_descriptor(struct keyboard *kbd, uint8_t code, |
|
struct descriptor *d, int dl, |
|
int pressed) |
|
{ |
|
int timeout = 0; |
|
|
|
if (pressed) { |
|
struct macro *macro; |
|
|
|
switch (d->op) { |
|
case OP_LAYERM: |
|
case OP_ONESHOTM: |
|
case OP_TOGGLEM: |
|
macro = &kbd->config.macros[d->args[1].idx]; |
|
execute_macro(kbd, dl, macro); |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
switch (d->op) { |
|
int idx; |
|
struct macro *macro; |
|
struct descriptor *action; |
|
uint8_t mods; |
|
|
|
case OP_KEYSEQUENCE: |
|
code = d->args[0].code; |
|
mods = d->args[1].mods; |
|
|
|
if (pressed) { |
|
/* |
|
* Permit variations of the same key |
|
* to be actuated next to each other |
|
* E.G [/{ |
|
*/ |
|
if (kbd->keystate[code]) |
|
send_key(kbd, code, 0); |
|
|
|
update_mods(kbd, dl, mods); |
|
|
|
send_key(kbd, code, 1); |
|
clear_oneshot(kbd); |
|
} else { |
|
send_key(kbd, code, 0); |
|
update_mods(kbd, -1, 0); |
|
} |
|
|
|
break; |
|
case OP_OVERLOAD_TIMEOUT_TAP: |
|
case OP_OVERLOAD_TIMEOUT: |
|
if (pressed) { |
|
uint8_t layer = d->args[0].idx; |
|
struct descriptor *action = &kbd->config.descriptors[d->args[1].idx]; |
|
timeout = d->args[2].idx; |
|
|
|
kbd->overload2.active = 1; |
|
kbd->overload2.resolve_on_tap = d->op == OP_OVERLOAD_TIMEOUT_TAP ? 1 : 0; |
|
kbd->overload2.code = code; |
|
kbd->overload2.layer = layer; |
|
kbd->overload2.action = action; |
|
kbd->overload2.dl = dl; |
|
kbd->overload2.n = 0; |
|
} else { |
|
deactivate_layer(kbd, d->args[0].idx); |
|
update_mods(kbd, -1, 0); |
|
} |
|
|
|
|
|
break; |
|
case OP_LAYOUT: |
|
if (pressed) |
|
setlayout(kbd, d->args[0].idx); |
|
|
|
break; |
|
case OP_LAYERM: |
|
case OP_LAYER: |
|
idx = d->args[0].idx; |
|
|
|
if (pressed) { |
|
activate_layer(kbd, code, idx); |
|
} else { |
|
deactivate_layer(kbd, idx); |
|
} |
|
|
|
if (kbd->last_pressed_code == code) { |
|
kbd->inhibit_modifier_guard = 1; |
|
update_mods(kbd, -1, 0); |
|
kbd->inhibit_modifier_guard = 0; |
|
} else { |
|
update_mods(kbd, -1, 0); |
|
} |
|
|
|
break; |
|
case OP_CLEARM: |
|
if(pressed) { |
|
clear(kbd); |
|
macro = &kbd->config.macros[d->args[0].idx]; |
|
execute_macro(kbd, dl, macro); |
|
} |
|
break; |
|
case OP_CLEAR: |
|
if(pressed) |
|
clear(kbd); |
|
break; |
|
case OP_OVERLOAD: |
|
idx = d->args[0].idx; |
|
action = &kbd->config.descriptors[d->args[1].idx]; |
|
|
|
if (pressed) { |
|
activate_layer(kbd, code, idx); |
|
update_mods(kbd, -1, 0); |
|
} else { |
|
deactivate_layer(kbd, idx); |
|
|
|
if (kbd->last_pressed_code == code) { |
|
clear_oneshot(kbd); |
|
|
|
process_descriptor(kbd, code, action, dl, 1); |
|
process_descriptor(kbd, code, action, dl, 0); |
|
} |
|
|
|
update_mods(kbd, -1, 0); |
|
} |
|
|
|
break; |
|
case OP_ONESHOTM: |
|
case OP_ONESHOT: |
|
idx = d->args[0].idx; |
|
|
|
if (pressed) { |
|
if (kbd->layer_state[idx].active) { |
|
/* Neutralize the upstroke. */ |
|
cache_set(kbd, code, NULL, -1); |
|
} else { |
|
activate_layer(kbd, code, idx); |
|
update_mods(kbd, dl, 0); |
|
kbd->oneshot_latch = 1; |
|
} |
|
} else { |
|
if (kbd->oneshot_latch) { |
|
kbd->layer_state[idx].oneshot = 1; |
|
} else { |
|
deactivate_layer(kbd, idx); |
|
update_mods(kbd, -1, 0); |
|
} |
|
} |
|
|
|
break; |
|
case OP_MACRO2: |
|
case OP_MACRO: |
|
if (pressed) { |
|
if (d->op == OP_MACRO2) { |
|
macro = &kbd->config.macros[d->args[2].idx]; |
|
|
|
timeout = d->args[0].timeout; |
|
kbd->macro_repeat_timeout = d->args[1].timeout; |
|
} else { |
|
macro = &kbd->config.macros[d->args[0].idx]; |
|
|
|
timeout = kbd->config.macro_timeout; |
|
kbd->macro_repeat_timeout = kbd->config.macro_repeat_timeout; |
|
} |
|
|
|
clear_oneshot(kbd); |
|
|
|
execute_macro(kbd, dl, macro); |
|
kbd->active_macro = macro; |
|
kbd->active_macro_layer = dl; |
|
} |
|
|
|
break; |
|
case OP_TOGGLEM: |
|
case OP_TOGGLE: |
|
idx = d->args[0].idx; |
|
|
|
if (!pressed) { |
|
kbd->layer_state[idx].toggled = !kbd->layer_state[idx].toggled; |
|
|
|
if (kbd->layer_state[idx].toggled) |
|
activate_layer(kbd, code, idx); |
|
else |
|
deactivate_layer(kbd, idx); |
|
|
|
update_mods(kbd, -1, 0); |
|
} else { |
|
clear_oneshot(kbd); |
|
} |
|
|
|
break; |
|
case OP_TIMEOUT: |
|
if (pressed) { |
|
kbd->pending_timeout.d1 = kbd->config.descriptors[d->args[0].idx]; |
|
kbd->pending_timeout.d2 = kbd->config.descriptors[d->args[2].idx]; |
|
|
|
kbd->pending_timeout.code = code; |
|
kbd->pending_timeout.dl = dl; |
|
|
|
kbd->pending_timeout.active = 1; |
|
|
|
timeout = d->args[1].timeout; |
|
} |
|
break; |
|
case OP_COMMAND: |
|
if (pressed) |
|
execute_command(kbd->config.commands[d->args[0].idx].cmd); |
|
break; |
|
case OP_SWAP: |
|
case OP_SWAPM: |
|
idx = d->args[0].idx; |
|
macro = d->op == OP_SWAPM ? &kbd->config.macros[d->args[1].idx] : NULL; |
|
|
|
if (pressed) { |
|
struct descriptor od; |
|
int odl; |
|
|
|
if (kbd->last_layer_code && |
|
!cache_get(kbd, kbd->last_layer_code, &od, &odl)) { |
|
int oldlayer = od.args[0].idx; |
|
od.args[0].idx = d->args[0].idx; |
|
|
|
cache_set(kbd, kbd->last_layer_code, &od, odl); |
|
|
|
deactivate_layer(kbd, oldlayer); |
|
activate_layer(kbd, kbd->last_layer_code, idx); |
|
update_mods(kbd, -1, 0); |
|
|
|
if (macro) |
|
execute_macro(kbd, dl, macro); |
|
} |
|
} else { |
|
if (macro && |
|
macro->sz == 1 && |
|
macro->entries[0].type == MACRO_KEYSEQUENCE) { |
|
uint8_t code = macro->entries[0].data; |
|
|
|
send_key(kbd, code, 0); |
|
update_mods(kbd, -1, 0); |
|
} |
|
} |
|
|
|
break; |
|
} |
|
|
|
if (pressed) |
|
kbd->last_pressed_code = code; |
|
|
|
return timeout; |
|
} |
|
|
|
struct keyboard *new_keyboard(struct config *config, |
|
void (*sink) (uint8_t code, uint8_t pressed), |
|
void (*layer_observer)(struct keyboard *kbd, const char *name, int state)) |
|
{ |
|
size_t i; |
|
struct keyboard *kbd; |
|
|
|
kbd = calloc(1, sizeof(struct keyboard)); |
|
|
|
kbd->original_config = config; |
|
memcpy(&kbd->config, kbd->original_config, sizeof(struct config)); |
|
|
|
kbd->layer_state[0].active = 1; |
|
kbd->layer_state[0].activation_time = 0; |
|
|
|
if (kbd->config.default_layout[0]) { |
|
int found = 0; |
|
for (i = 0; i < kbd->config.nr_layers; i++) { |
|
struct layer *layer = &kbd->config.layers[i]; |
|
|
|
if (layer->type == LT_LAYOUT && |
|
!strcmp(layer->name, |
|
kbd->config.default_layout)) { |
|
kbd->layer_state[i].active = 1; |
|
kbd->layer_state[i].activation_time = 1; |
|
found = 1; |
|
break; |
|
} |
|
} |
|
|
|
if (!found) |
|
fprintf(stderr, |
|
"\tWARNING: could not find default layout %s.\n", |
|
kbd->config.default_layout); |
|
} |
|
|
|
kbd->output = sink; |
|
kbd->layer_observer = layer_observer; |
|
|
|
return kbd; |
|
} |
|
|
|
static int resolve_overload2(struct keyboard *kbd, int time, int hold) |
|
{ |
|
int timeout = 0; |
|
int timeout_ts; |
|
|
|
struct key_event queue[ARRAY_SIZE(kbd->overload2.queued)]; |
|
int n = kbd->overload2.n; |
|
|
|
kbd->overload2.active = 0; |
|
|
|
memcpy(queue, kbd->overload2.queued, sizeof(struct key_event)*n); |
|
|
|
if (hold) { |
|
activate_layer(kbd, kbd->overload2.code, kbd->overload2.layer); |
|
update_mods(kbd, -1, 0); |
|
} else { |
|
process_descriptor(kbd, |
|
kbd->overload2.code, |
|
kbd->overload2.action, |
|
kbd->overload2.dl, 1); |
|
|
|
process_descriptor(kbd, |
|
kbd->overload2.code, |
|
kbd->overload2.action, |
|
kbd->overload2.dl, 0); |
|
|
|
cache_set(kbd, kbd->overload2.code, NULL, -1); |
|
} |
|
|
|
if (!n) |
|
return 0; |
|
|
|
timeout = kbd_process_events(kbd, queue, n); |
|
timeout_ts = queue[n-1].timestamp + timeout; |
|
|
|
if (timeout_ts <= time) { |
|
struct key_event ev = {.code = 0, .timestamp = timeout_ts}; |
|
return kbd_process_events(kbd, &ev, 1); |
|
} |
|
|
|
return timeout_ts - time; |
|
} |
|
|
|
/* |
|
* `code` may be 0 in the event of a timeout. |
|
* |
|
* The return value corresponds to a timeout before which the next invocation |
|
* of kbd_process_key_event must take place. A return value of 0 permits the |
|
* main loop to call at liberty. |
|
*/ |
|
static long process_event(struct keyboard *kbd, uint8_t code, int pressed, int time) |
|
{ |
|
int dl = -1; |
|
struct descriptor d; |
|
|
|
int timeleft = kbd->timeout - (time - kbd->last_event_ts); |
|
kbd->last_event_ts = time; |
|
|
|
/* timeout */ |
|
if (!code) { |
|
if (kbd->overload2.active) |
|
return resolve_overload2(kbd, time, 1); |
|
|
|
if (kbd->active_macro) { |
|
execute_macro(kbd, kbd->active_macro_layer, kbd->active_macro); |
|
return kbd->macro_repeat_timeout; |
|
} |
|
|
|
if (kbd->pending_timeout.active) { |
|
int dl = kbd->pending_timeout.dl; |
|
uint8_t code = kbd->pending_timeout.code; |
|
struct descriptor *d = &kbd->pending_timeout.d2; |
|
|
|
cache_set(kbd, code, d, dl); |
|
|
|
kbd->pending_timeout.active = 0; |
|
return process_descriptor(kbd, code, d, dl, 1); |
|
} |
|
} else { |
|
if (kbd->overload2.active) { |
|
if (kbd->overload2.code == code) { |
|
return resolve_overload2(kbd, time, 0); |
|
} else { |
|
assert(kbd->overload2.n < ARRAY_SIZE(kbd->overload2.queued)); |
|
struct key_event *ev = &kbd->overload2.queued[kbd->overload2.n++]; |
|
|
|
ev->code = code; |
|
ev->pressed = pressed; |
|
ev->timestamp = time; |
|
|
|
//TODO: make this optional (overload3) |
|
if (kbd->overload2.resolve_on_tap && !pressed) { |
|
size_t i; |
|
for (i = 0; i < kbd->overload2.n; i++) |
|
if (kbd->overload2.queued[i].code == code && |
|
kbd->overload2.queued[i].pressed) { |
|
return resolve_overload2(kbd, time, 1); |
|
} |
|
} |
|
|
|
return timeleft; |
|
} |
|
|
|
} |
|
|
|
if (kbd->pending_timeout.active) { |
|
int dl = kbd->pending_timeout.dl; |
|
uint8_t code = kbd->pending_timeout.code; |
|
struct descriptor *d = &kbd->pending_timeout.d1; |
|
|
|
cache_set(kbd, code, d, dl); |
|
process_descriptor(kbd, code, d, dl, 1); |
|
} |
|
|
|
if (kbd->active_macro) { |
|
kbd->active_macro = NULL; |
|
update_mods(kbd, -1, 0); |
|
} |
|
|
|
kbd->pending_timeout.active = 0; |
|
} |
|
|
|
|
|
if (pressed) { |
|
/* |
|
* Guard against successive key down events |
|
* of the same key code. This can be caused |
|
* by unorthodox hardware or by different |
|
* devices mapped to the same config. |
|
*/ |
|
if (cache_get(kbd, code, &d, &dl) == 0) |
|
return 0; |
|
|
|
lookup_descriptor(kbd, code, &d, &dl); |
|
|
|
if (cache_set(kbd, code, &d, dl) < 0) |
|
return 0; |
|
} else { |
|
if (cache_get(kbd, code, &d, &dl) < 0) |
|
return 0; |
|
|
|
cache_set(kbd, code, NULL, -1); |
|
} |
|
|
|
kbd->timeout = process_descriptor(kbd, code, &d, dl, pressed); |
|
return kbd->timeout; |
|
} |
|
|
|
|
|
long kbd_process_events(struct keyboard *kbd, const struct key_event *events, size_t n) |
|
{ |
|
size_t i = 0; |
|
int timeout = 0; |
|
int timeout_ts = 0; |
|
|
|
while (i != n) { |
|
const struct key_event *ev = &events[i]; |
|
|
|
if (timeout > 0 && timeout_ts < ev->timestamp) { |
|
timeout = process_event(kbd, 0, 0, timeout_ts); |
|
} else { |
|
timeout = process_event(kbd, ev->code, ev->pressed, ev->timestamp); |
|
i++; |
|
} |
|
|
|
timeout_ts = ev->timestamp + timeout; |
|
} |
|
|
|
return timeout; |
|
} |
|
|
|
int kbd_eval(struct keyboard *kbd, const char *exp) |
|
{ |
|
if (!strcmp(exp, "reset")) { |
|
memcpy(&kbd->config, kbd->original_config, sizeof(struct config)); |
|
return 0; |
|
} else { |
|
return config_add_entry(&kbd->config, exp); |
|
} |
|
}
|
|
|