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.
510 lines
11 KiB
510 lines
11 KiB
/* Copyright © 2019 Raheman Vaiya. |
|
* |
|
* Permission is hereby granted, free of charge, to any person obtaining a |
|
* copy of this software and associated documentation files (the "Software"), |
|
* to deal in the Software without restriction, including without limitation |
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense, |
|
* and/or sell copies of the Software, and to permit persons to whom the |
|
* Software is furnished to do so, subject to the following conditions: |
|
* |
|
* The above copyright notice and this permission notice (including the next |
|
* paragraph) shall be included in all copies or substantial portions of the |
|
* Software. |
|
* |
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
* DEALINGS IN THE SOFTWARE. |
|
*/ |
|
|
|
#include <stdint.h> |
|
#include <unistd.h> |
|
#include <stdio.h> |
|
#include <time.h> |
|
#include "keyboard.h" |
|
#include "keyd.h" |
|
#include "descriptor.h" |
|
#include "layer.h" |
|
|
|
#define MACRO_REPEAT_TIMEOUT 400 /* In ms */ |
|
#define MACRO_REPEAT_INTERVAL 20 /* In ms */ |
|
|
|
static void kbd_lookup_descriptor(struct keyboard *kbd, |
|
uint16_t code, |
|
int pressed, |
|
struct descriptor *descriptor, |
|
int *descriptor_layer) |
|
{ |
|
size_t i; |
|
struct descriptor d; |
|
int dl; |
|
int found = 0; |
|
|
|
/* Check if the key is active */ |
|
for (i = 0; i < kbd->nr_active_keys; i++) { |
|
struct active_key *ak = &kbd->active_keys[i]; |
|
|
|
if (ak->code == code) { |
|
dl = ak->dl; |
|
d = ak->d; |
|
|
|
found = 1; |
|
} |
|
} |
|
|
|
if (!found) { |
|
struct layer *layer; |
|
|
|
if (!kbd->nr_active_layers) |
|
dl = kbd->layout; |
|
else |
|
dl = kbd->active_layers[kbd->nr_active_layers-1].layer; |
|
|
|
layer = &kbd->config.layers[dl]; |
|
|
|
d = layer->keymap[code]; |
|
|
|
/* |
|
* If the most recently active layer is a modifier layer |
|
* and the key is undefined, use the layout definition. |
|
* If the key is undefined in a normal layer, treat it |
|
* as undefined. |
|
*/ |
|
if (!d.op && layer->mods) { |
|
dl = kbd->layout; |
|
d = kbd->config.layers[dl].keymap[code]; |
|
} |
|
} |
|
|
|
if (pressed) { |
|
struct active_key *ak = &kbd->active_keys[kbd->nr_active_keys++]; |
|
|
|
ak->code = code; |
|
ak->d = d; |
|
ak->dl = dl; |
|
} else { |
|
int n = 0; |
|
|
|
for (i = 0; i < kbd->nr_active_keys; i++) { |
|
if (kbd->active_keys[i].code != code) |
|
kbd->active_keys[n++] = kbd->active_keys[i]; |
|
} |
|
|
|
kbd->nr_active_keys = n; |
|
} |
|
|
|
*descriptor = d; |
|
*descriptor_layer = dl; |
|
} |
|
|
|
/* Compute the current modifier state based on the activated layers. */ |
|
static uint16_t kbd_compute_mods(struct keyboard *kbd) |
|
{ |
|
size_t i; |
|
uint16_t mods = 0; |
|
|
|
for (i = 0; i < kbd->nr_active_layers; i++) { |
|
struct layer *layer = &kbd->config.layers[kbd->active_layers[i].layer]; |
|
mods |= layer->mods; |
|
} |
|
|
|
return mods; |
|
} |
|
|
|
/* Compute and apply the current mod state to the virtual keyboard. */ |
|
static void kbd_reify_mods(struct keyboard *kbd) |
|
{ |
|
set_mods(kbd_compute_mods(kbd)); |
|
} |
|
|
|
/* Returns the active_layer struct associated with the given layer index. */ |
|
static const struct active_layer *kbd_lookup_active_layer(struct keyboard *kbd, int layer) |
|
{ |
|
size_t i; |
|
|
|
for (i = 0; i < kbd->nr_active_layers; i++) { |
|
struct active_layer *al = &kbd->active_layers[i]; |
|
|
|
if (al->layer == layer) |
|
return al; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
static void kbd_deactivate_layer(struct keyboard *kbd, int layer) |
|
{ |
|
int i; |
|
int n = kbd->nr_active_layers; |
|
|
|
dbg("Deactivating layer %s", kbd->config.layers[layer].name); |
|
|
|
kbd->nr_active_layers = 0; |
|
for (i = 0; i < n; i++) { |
|
struct active_layer *al = &kbd->active_layers[i]; |
|
|
|
if (al->layer != layer) |
|
kbd->active_layers[kbd->nr_active_layers++] = *al; |
|
} |
|
|
|
kbd_reify_mods(kbd); |
|
} |
|
|
|
static void kbd_clear_oneshots(struct keyboard *kbd) |
|
{ |
|
size_t i, n; |
|
|
|
n = 0; |
|
for (i = 0; i < kbd->nr_active_layers; i++) { |
|
struct active_layer *al = &kbd->active_layers[i]; |
|
|
|
if (!al->oneshot) |
|
kbd->active_layers[n++] = *al; |
|
else |
|
dbg("Clearing oneshot layer %s", kbd->config.layers[al->layer].name); |
|
} |
|
|
|
kbd->nr_active_layers = n; |
|
} |
|
|
|
|
|
static void kbd_activate_layer(struct keyboard *kbd, int layer, int oneshot) |
|
{ |
|
struct active_layer *al; |
|
size_t i; |
|
|
|
dbg("Activating layer %s", kbd->config.layers[layer].name); |
|
|
|
for (i = 0; i < kbd->nr_active_layers; i++) { |
|
al = &kbd->active_layers[i]; |
|
if(al->layer == layer) { |
|
al->oneshot = oneshot; |
|
return; |
|
} |
|
} |
|
|
|
al = &kbd->active_layers[kbd->nr_active_layers]; |
|
|
|
al->layer = layer; |
|
al->oneshot = oneshot; |
|
|
|
kbd->nr_active_layers++; |
|
kbd_reify_mods(kbd); |
|
} |
|
|
|
static void kbd_process_keyseq(struct keyboard *kbd, |
|
int verbatim, |
|
const struct key_sequence *sequence, |
|
int pressed) |
|
{ |
|
if (sequence->code == 0) |
|
return; |
|
|
|
if (pressed) { |
|
uint16_t mods; |
|
|
|
if (verbatim) /* Temporarily disable layer mods. */ |
|
mods = sequence->mods; |
|
else |
|
mods = kbd_compute_mods(kbd) | sequence->mods; |
|
|
|
set_mods(mods); |
|
|
|
/* Accommodate rolling modified/unmodified keys (e.g [/{). */ |
|
if (keystate[sequence->code]) |
|
send_key(sequence->code, 0); |
|
|
|
send_key(sequence->code, 1); |
|
} else |
|
send_key(sequence->code, 0); |
|
} |
|
|
|
static void kbd_swap_layer(struct keyboard *kbd, |
|
int dl, |
|
int replacment_layer, |
|
struct descriptor new_descriptor) |
|
{ |
|
size_t i; |
|
|
|
/* |
|
* Find the key activating dl and swap out its descriptor with the |
|
* current one to deactivate the replacement layer. |
|
*/ |
|
for (i = 0; i < kbd->nr_active_keys; i++) { |
|
struct active_key *ak = &kbd->active_keys[i]; |
|
|
|
if((ak->d.op == OP_LAYER || ak->d.op == OP_OVERLOAD || ak->d.op == OP_SWAP) && ak->d.args[0].idx == dl) { |
|
kbd_deactivate_layer(kbd, dl); |
|
kbd_activate_layer(kbd, replacment_layer, 0); |
|
|
|
ak->d = new_descriptor; |
|
|
|
return; |
|
} |
|
} |
|
} |
|
|
|
static void kbd_execute_macro(struct keyboard *kbd, |
|
const struct macro *macro) |
|
{ |
|
size_t i; |
|
int hold_start = -1; |
|
|
|
for (i = 0; i < macro->sz; i++) { |
|
const struct macro_entry *ent = ¯o->entries[i]; |
|
|
|
switch (ent->type) { |
|
case MACRO_HOLD: |
|
if (hold_start == -1) { |
|
hold_start = i; |
|
set_mods(0); |
|
} |
|
|
|
send_key(ent->data.sequence.code, 1); |
|
|
|
break; |
|
case MACRO_RELEASE: |
|
if (hold_start != -1) { |
|
size_t j; |
|
|
|
for (j = hold_start; j < i; j++) { |
|
const struct macro_entry *ent = ¯o->entries[j]; |
|
send_key(ent->data.sequence.code, 0); |
|
} |
|
} |
|
break; |
|
case MACRO_KEYSEQUENCE: |
|
set_mods(ent->data.sequence.mods); |
|
|
|
send_key(ent->data.sequence.code, 1); |
|
send_key(ent->data.sequence.code, 0); |
|
break; |
|
case MACRO_TIMEOUT: |
|
usleep(ent->data.timeout*1E3); |
|
break; |
|
} |
|
|
|
} |
|
kbd_reify_mods(kbd); |
|
} |
|
|
|
int kbd_execute_expression(struct keyboard *kbd, const char *exp) |
|
{ |
|
return config_execute_expression(&kbd->config, exp); |
|
} |
|
|
|
void kbd_reset(struct keyboard *kbd) |
|
{ |
|
/* TODO optimize */ |
|
|
|
kbd->config = kbd->original_config; |
|
} |
|
|
|
static long get_time_ms() |
|
{ |
|
struct timespec tv; |
|
clock_gettime(CLOCK_MONOTONIC, &tv); |
|
return (tv.tv_sec*1E3)+tv.tv_nsec/1E6; |
|
} |
|
|
|
/* |
|
* Here be tiny dragons. |
|
* |
|
* 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. |
|
*/ |
|
long kbd_process_key_event(struct keyboard *kbd, |
|
uint16_t code, |
|
int pressed) |
|
{ |
|
int dl; |
|
struct descriptor d; |
|
int timeout = 0; |
|
|
|
static int oneshot_latch = 0; |
|
static int last_pressed_keycode = 0; |
|
static const struct macro *active_macro = NULL; |
|
|
|
static struct { |
|
int layer; |
|
struct key_sequence sequence; |
|
int timeout; |
|
|
|
long start_time; |
|
uint8_t is_active; |
|
} pending_overload = {0}; |
|
|
|
if (active_macro) { |
|
if (!code) { |
|
kbd_execute_macro(kbd, active_macro); |
|
return MACRO_REPEAT_INTERVAL; |
|
} else |
|
active_macro = NULL; |
|
} |
|
|
|
if (pending_overload.is_active) { |
|
if ((get_time_ms() - pending_overload.start_time) >= (long)pending_overload.timeout) { |
|
kbd_activate_layer(kbd, pending_overload.layer, 0); |
|
} else { |
|
kbd_process_keyseq(kbd, 1, &pending_overload.sequence, 1); |
|
kbd_process_keyseq(kbd, 1, &pending_overload.sequence, 0); |
|
|
|
kbd_reify_mods(kbd); |
|
} |
|
|
|
pending_overload.is_active = 0; |
|
} |
|
|
|
if (!code) |
|
return 0; |
|
|
|
kbd_lookup_descriptor(kbd, code, pressed, &d, &dl); |
|
|
|
if(!d.op) { |
|
kbd_clear_oneshots(kbd); |
|
return 0; |
|
} |
|
|
|
switch (d.op) { |
|
int verbatim; |
|
const struct key_sequence *sequence; |
|
int layer; |
|
const struct macro *macro; |
|
|
|
case OP_KEYSEQ: |
|
verbatim = dl != kbd->layout; |
|
|
|
kbd_process_keyseq(kbd, verbatim, &d.args[0].sequence, pressed); |
|
|
|
if (pressed) { |
|
oneshot_latch = 0; |
|
kbd_clear_oneshots(kbd); |
|
} else |
|
kbd_reify_mods(kbd); |
|
|
|
break; |
|
case OP_RESET: |
|
if (!pressed) { |
|
kbd->nr_active_layers = 0; |
|
kbd_reify_mods(kbd); |
|
} |
|
|
|
break; |
|
case OP_TOGGLE: |
|
layer = d.args[0].idx; |
|
|
|
if (!pressed) { |
|
const struct active_layer *al = kbd_lookup_active_layer(kbd, layer); |
|
if (al) { |
|
if (al->oneshot) /* Allow oneshot layers to toggle themselves. */ |
|
kbd_activate_layer(kbd, layer, 0); |
|
else |
|
kbd_deactivate_layer(kbd, layer); |
|
} else |
|
kbd_activate_layer(kbd, layer, 0); |
|
} |
|
|
|
break; |
|
case OP_LAYOUT: |
|
if (!pressed) |
|
kbd->layout = d.args[0].idx; |
|
|
|
break; |
|
case OP_LAYER: |
|
layer = d.args[0].idx; |
|
|
|
if (pressed) |
|
kbd_activate_layer(kbd, layer, 0); |
|
else |
|
kbd_deactivate_layer(kbd, layer); |
|
|
|
break; |
|
case OP_ONESHOT: |
|
layer = d.args[0].idx; |
|
|
|
if (pressed) { |
|
oneshot_latch++; |
|
kbd_activate_layer(kbd, layer, 0); |
|
} else if (oneshot_latch) /* No interposed KEYSEQ since key down (other modifiers don't interfere with this). */ |
|
kbd_activate_layer(kbd, layer, 1); |
|
else |
|
kbd_deactivate_layer(kbd, layer); |
|
|
|
break; |
|
case OP_SWAP: |
|
layer = d.args[0].idx; |
|
sequence = &d.args[1].sequence; |
|
|
|
if (pressed) { |
|
kbd_process_keyseq(kbd, 1, sequence, 1); |
|
kbd_process_keyseq(kbd, 1, sequence, 0); |
|
|
|
kbd_swap_layer(kbd, dl, layer, d); |
|
} else if (last_pressed_keycode != code) { /* We only reach this from the remapped dl activate key. */ |
|
kbd_deactivate_layer(kbd, layer); |
|
} |
|
|
|
break; |
|
case OP_OVERLOAD: |
|
layer = d.args[0].idx; |
|
sequence = &d.args[1].sequence; |
|
|
|
if (pressed) { |
|
kbd_activate_layer(kbd, layer, 0); |
|
} else if (last_pressed_keycode == code) { |
|
kbd_deactivate_layer(kbd, layer); |
|
verbatim = dl != kbd->layout; |
|
|
|
kbd_process_keyseq(kbd, verbatim, sequence, 1); |
|
kbd_process_keyseq(kbd, verbatim, sequence, 0); |
|
|
|
kbd_reify_mods(kbd); |
|
} else { |
|
kbd_deactivate_layer(kbd, layer); |
|
} |
|
|
|
break; |
|
case OP_OVERLOAD_TIMEOUT: |
|
layer = d.args[0].idx; |
|
sequence = &d.args[1].sequence; |
|
|
|
if (pressed) { |
|
pending_overload.layer = d.args[0].idx; |
|
pending_overload.sequence = d.args[1].sequence; |
|
pending_overload.timeout = d.args[2].timeout; |
|
pending_overload.start_time = get_time_ms(); |
|
|
|
pending_overload.is_active = 1; |
|
} else { |
|
kbd_deactivate_layer(kbd, layer); |
|
} |
|
|
|
break; |
|
case OP_MACRO: |
|
macro = &kbd->config.macros[d.args[0].idx]; |
|
|
|
if (pressed) { |
|
active_macro = macro; |
|
kbd_execute_macro(kbd, macro); |
|
|
|
timeout = MACRO_REPEAT_TIMEOUT; |
|
} else |
|
active_macro = NULL; |
|
|
|
break; |
|
default: |
|
printf("Unrecognized op: %d, ignoring...\n", d.op); |
|
break; |
|
} |
|
|
|
if (pressed) |
|
last_pressed_keycode = code; |
|
|
|
return timeout; |
|
}
|
|
|