ergodox-firmware/src/main.c

500 lines
15 KiB
C

/* ----------------------------------------------------------------------------
* main()
* ----------------------------------------------------------------------------
* Copyright (c) 2012 Ben Blazak <benblazak.dev@gmail.com>
* Released under The MIT License (MIT) (see "license.md")
* Project located at <https://github.com/benblazak/ergodox-firmware>
* ------------------------------------------------------------------------- */
#include <stdbool.h>
#include <stdint.h>
// --------------------------------------------------------------------
// hardware
// --------------------------------------------------------------------
// comment out this define to disable the debug interface completely
// however, just not using the functions gets rid of most of the firmware bloat already
#define KBD_DEBUG
#include "./keyboard/controller.c"
#include "./keyboard/keyboard.h"
// --------------------------------------------------------------------
// types and forward declarations
// --------------------------------------------------------------------
typedef int8_t i8;
typedef uint8_t u8;
typedef uint16_t u16;
typedef u8 keycode;
typedef u16 media_keycode;
typedef u8 layer;
typedef void (*keyfunc)(keycode, bool);
#include "./main.h"
// ----------------------------------------------------------------------------
// layout data
// ----------------------------------------------------------------------------
#include "./keyboard/layout.c"
// defines:
// #define KB_LAYERS #{Layers.size}
// static const keycode PROGMEM _kb_layout[KB_LAYERS][KB_ROWS][KB_COLUMNS];
// static const keyfunc PROGMEM _kb_layout_press[KB_LAYERS][KB_ROWS][KB_COLUMNS];
// static const keyfunc PROGMEM _kb_layout_release[KB_LAYERS][KB_ROWS][KB_COLUMNS];
#if KB_LAYERS > 8
#error can only handle 8 layers for now
#endif
// ----------------------------------------------------------------------------
// globals
// ----------------------------------------------------------------------------
static bool _kb_is_pressed[KB_ROWS][KB_COLUMNS];
static bool (*kb_is_pressed)[KB_ROWS][KB_COLUMNS] = &_kb_is_pressed;
static bool _kb_was_pressed[KB_ROWS][KB_COLUMNS];
static bool (*kb_was_pressed)[KB_ROWS][KB_COLUMNS] = &_kb_was_pressed;
static layer layers_pressed[KB_ROWS][KB_COLUMNS];
static i8 layers_active[KB_LAYERS];
static layer layers_top;
static u8 layers_state;
static bool layer_sticky_on;
static bool layer_sticky[KB_LAYERS];
static bool layer_sticky_done;
static u8 mod_sticky;
static bool mod_sticky_done;
// TODO this only exists as a workaround until we handle our own key repeats
static const keyfunc _kb_layer_funcs[] = {
&kbfun_layer_press_release,
&kbfun_layer_sticky,
&kbfun_shift_layer_press_release,
&kbfun_control_layer_press_release,
&kbfun_alt_layer_press_release,
&kbfun_win_layer_press_release,
};
// ----------------------------------------------------------------------------
// utilities
// ----------------------------------------------------------------------------
#define array_length(x) (sizeof(x) / sizeof((x)[0]))
#define set_bit(x, i) (x) |= (1u << (i))
#define unset_bit(x, i) (x) &= ~(1u << (i))
#define toggle_bit(x, i) (x) ^= (1u << (i))
#define is_set(x, i) (((x) >> (i)) & 1u)
#define is_unset(x, i) !is_set((x), (i))
#define set_layer(x, layer) set_bit((x), (layer) - 1)
#define unset_layer(x, layer) unset_bit((x), (layer) - 1)
#define toggle_layer(x, layer) toggle_bit((x), (layer) - 1)
// ----------------------------------------------------------------------------
// main
// ----------------------------------------------------------------------------
int main() {
// initialize
init_hw();
init_layers();
init_sticky();
// never return
main_key_loop();
return 0;
}
void main_key_loop() {
for (;;) {
// swap `kb_is_pressed` and `kb_was_pressed`, then update
bool (*temp)[KB_ROWS][KB_COLUMNS] = kb_was_pressed;
kb_was_pressed = kb_is_pressed;
kb_is_pressed = temp;
kb_update_matrix(*kb_is_pressed);
// - execute key functions when their key changes state
// - keep track of which layers the keys were on when they were pressed
// (so they can be released using the function from that layer)
for (u8 row=0; row<KB_ROWS; row++) {
for (u8 col=0; col<KB_COLUMNS; col++) {
bool is_pressed = (*kb_is_pressed)[row][col];
bool was_pressed = (*kb_was_pressed)[row][col];
if (is_pressed != was_pressed) {
layer layer;
if (is_pressed) {
layer = layers_top;
layers_pressed[row][col] = layer;
} else {
layer = layers_pressed[row][col];
layers_pressed[row][col] = 0;
}
// set remaining vars, and "execute" key
exec_key(layer, row, col, is_pressed);
}
}
}
// send the USB report (even if nothing's changed)
usb_keyboard_send();
usb_extra_consumer_send();
// unset sticky keys if necessary
if (layer_sticky_on && layer_sticky_done) {
for (layer l=1; l < KB_LAYERS; l++) {
if (layer_sticky[l]) {
layer_disable(l);
layer_sticky[l] = false;
}
}
layer_sticky_on = false;
layer_sticky_done = false;
}
if (mod_sticky && mod_sticky_done) {
keyboard_modifier_keys &= ~mod_sticky;
usb_keyboard_send();
mod_sticky = 0;
mod_sticky_done = false;
}
// debounce in ms; see keyswitch spec for necessary value
_delay_ms(5);
}
}
// ----------------------------------------------------------------------------
// init functions
// ----------------------------------------------------------------------------
void init_hw() {
kb_init();
usb_init();
while (!usb_configured());
}
void init_sticky() {
for (layer l=1; l < KB_LAYERS; l++) {
layer_sticky[l] = false;
}
layer_sticky_on = false;
mod_sticky = 0;
layer_sticky_done = false;
mod_sticky_done = false;
}
void init_layers() {
for (layer l=0; l < KB_LAYERS; l++) {
layers_active[l] = 0;
}
layers_active[0] = 1;
layers_top = 0;
layers_state = 1;
for (u8 row=0; row<KB_ROWS; row++) {
for (u8 col=0; col<KB_COLUMNS; col++) {
layers_pressed[row][col] = 0;
}
}
}
// ----------------------------------------------------------------------------
// layer functions
// ----------------------------------------------------------------------------
// find highest active layer
layer highest_active_layer() {
for (layer l = KB_LAYERS - 1; l > 0; l--) {
if (layers_active[l] > 0) { return l; }
/* if (is_set(layers_state, l)) { return l; } */
}
// the base layer is always active
return 0;
}
// enable a layer
void layer_enable(layer l) {
if (l >= KB_LAYERS || l == 0) { return; }
layers_active[l] += 1;
set_layer(layers_state, l);
if (l > layers_top) {
layers_top = l;
}
}
// disable a layer
void layer_disable(layer l) {
// base layer stays always on
if (l >= KB_LAYERS || l == 0) { return; }
if (layers_active[l] > 0) {
layers_active[l] -= 1;
}
if (layers_active[l] == 0) {
unset_layer(layers_state, l);
}
if (l == layers_top) {
layers_top = highest_active_layer();
}
// re-press affected keys
for (u8 row=0; row<KB_ROWS; row++) {
for (u8 col=0; col<KB_COLUMNS; col++) {
if (layers_pressed[row][col] == l) {
keyfunc func = (kb_keyfunc_press(l, row, col));
// FIXME don't re-send normal keys until we have key repeats
if (is_layer_keyfunc(func)) {
// FIXME this kinda shouldn't be here and it privileges layer 0 even more
layers_pressed[row][col] = 0;
exec_key(l, row, col, false);
if (! layer_sticky[l]) { // don't resend for sticky keys
layers_pressed[row][col] = layers_top;
exec_key(layers_top, row, col, true);
}
}
}
}
}
}
bool is_layer_keyfunc(keyfunc f) {
for (int i=0; i<array_length(_kb_layer_funcs); i++) {
if (f == _kb_layer_funcs[i]) {
return true;
}
}
return false;
}
// ----------------------------------------------------------------------------
// layout info
// ----------------------------------------------------------------------------
keycode kb_keycode (layer l, u8 row, u8 col) { return (keycode) pgm_read_byte(&(_kb_layout[l][row][col])); }
keyfunc kb_keyfunc_press (layer l, u8 row, u8 col) { return (keyfunc) pgm_read_word(&(_kb_layout_press[l][row][col])); }
keyfunc kb_keyfunc_release (layer l, u8 row, u8 col) { return (keyfunc) pgm_read_word(&(_kb_layout_release[l][row][col])); }
// ----------------------------------------------------------------------------
// keyfunc primitives
// ----------------------------------------------------------------------------
// basic keypresses
void _kbfun_normal_press_release(keycode key, bool is_pressed) {
if (key == 0) { return; } // noop
if (is_pressed) {
for (u8 i=0; i < sizeof(keyboard_keys); i++) {
if (keyboard_keys[i] == 0) {
keyboard_keys[i] = key;
return;
}
}
} else {
for (u8 i=0; i < sizeof(keyboard_keys); i++) {
if (keyboard_keys[i] == key) {
keyboard_keys[i] = 0;
return;
}
}
}
}
void _kbfun_mediakey_press_release(keycode key, bool is_pressed) {
media_keycode media_key = _media_code_lookup(key);
if (is_pressed) {
consumer_key = media_key;
} else {
// only one media key can be pressed at a time, so only clear most recent one
if (media_key == consumer_key) {
consumer_key = 0;
}
}
}
void _kbfun_modifier_press_release(keycode key, bool is_pressed) {
if (is_pressed) {
set_bit(keyboard_modifier_keys, key);
} else {
unset_bit(keyboard_modifier_keys, key);
}
}
bool _kbfun_normal_is_pressed(keycode key) {
for (u8 i=0; i < sizeof(keyboard_keys); i++) {
if (keyboard_keys[i] == key) {
return true;
}
}
return false;
}
bool _kbfun_mediakey_is_pressed(keycode key) {
return (consumer_key != 0);
}
bool _kbfun_modifier_is_pressed(keycode key) {
return is_set(keyboard_modifier_keys, key);
}
void _kbfun_normal_sticky_done() {
layer_sticky_done = true;
mod_sticky_done = true;
}
// ----------------------------------------------------------------------------
// basic keyfuncs
// ----------------------------------------------------------------------------
// execute the keypress or keyrelease function (if it exists) of the key at the current possition
void exec_key(layer layer, u8 row, u8 col, bool is_pressed) {
keycode key = kb_keycode(layer, row, col);
void (*key_function)(keycode, bool) =
( (is_pressed)
? kb_keyfunc_press(layer, row, col)
: kb_keyfunc_release(layer, row, col) );
if (key_function) { (*key_function)(key, is_pressed); }
}
// normal key
void kbfun_normal_press_release(keycode key, bool is_pressed) {
_kbfun_normal_sticky_done();
_kbfun_normal_press_release(key, is_pressed);
}
// media key
void kbfun_mediakey_press_release(keycode key, bool is_pressed) {
_kbfun_normal_sticky_done();
_kbfun_mediakey_press_release(key, is_pressed);
}
// modifier
void kbfun_modifier_press_release(keycode key, bool is_pressed) {
layer_sticky_done = true;
_kbfun_modifier_press_release(key, is_pressed);
}
// layer key
void kbfun_layer_press_release(keycode key, bool is_pressed) {
layer_sticky_done = true; // don't disable sticky mods!
layer l = (layer) key;
if (is_pressed) {
layer_enable(l);
} else {
layer_disable(l);
}
}
// sticky layer key
void kbfun_layer_sticky(keycode key, bool is_pressed) {
layer l = (layer) key;
if (is_pressed) {
if (! layer_sticky[l]) {
layer_enable(l);
layer_sticky_done = false;
}
} else {
if (layer_sticky_done) {
layer_disable(l);
} else {
layer_sticky[l] = true;
layer_sticky_on = true;
layer_sticky_done = false;
}
}
}
// sticky modifier key
void kbfun_modifier_sticky(keycode key, bool is_pressed) {
// TODO handle: sticky, then same modifier
keycode mod = key;
if (is_pressed) {
kbfun_modifier_press_release(key, true);
mod_sticky_done = false;
} else {
if (mod_sticky_done) {
kbfun_modifier_press_release(key, false);
} else {
set_bit(mod_sticky, mod);
mod_sticky_done = false;
}
}
}
// ----------------------------------------------------------------------------
// combo keyfuncs
// ----------------------------------------------------------------------------
void _kbfun_combo_normal_press_release(keycode combo_key, keycode key, bool is_pressed) {
_kbfun_modifier_press_release(combo_key, is_pressed);
kbfun_normal_press_release(key, is_pressed);
}
void _kbfun_combo_layer_press_release(keycode combo_key, keycode key, bool is_pressed) {
kbfun_layer_press_release(key, is_pressed);
_kbfun_modifier_press_release(combo_key, is_pressed);
}
void kbfun_shift_press_release(keycode key, bool is_pressed) { _kbfun_combo_normal_press_release(MOD_KEY_LeftShift, key, is_pressed); } // +shift
void kbfun_control_press_release(keycode key, bool is_pressed) { _kbfun_combo_normal_press_release(MOD_KEY_LeftControl, key, is_pressed); } // +control
void kbfun_alt_press_release(keycode key, bool is_pressed) { _kbfun_combo_normal_press_release(MOD_KEY_LeftAlt, key, is_pressed); } // +alt
void kbfun_win_press_release(keycode key, bool is_pressed) { _kbfun_combo_normal_press_release(MOD_KEY_LeftGUI, key, is_pressed); } // +win
void kbfun_shift_layer_press_release(keycode key, bool is_pressed) { _kbfun_combo_layer_press_release(MOD_KEY_LeftShift, key, is_pressed); } // +shift + layer
void kbfun_control_layer_press_release(keycode key, bool is_pressed) { _kbfun_combo_layer_press_release(MOD_KEY_LeftControl, key, is_pressed); } // +control + layer
void kbfun_alt_layer_press_release(keycode key, bool is_pressed) { _kbfun_combo_layer_press_release(MOD_KEY_LeftAlt, key, is_pressed); } // +alt + layer
void kbfun_win_layer_press_release(keycode key, bool is_pressed) { _kbfun_combo_layer_press_release(MOD_KEY_LeftGUI, key, is_pressed); } // +win + layer
// capslock
void kbfun_capslock_press_release(keycode key, bool is_pressed) {
static u8 keys_pressed;
static bool lshift_pressed;
static bool rshift_pressed;
if (!is_pressed) { keys_pressed--; }
// take care of the key that was actually pressed
_kbfun_modifier_press_release(key, is_pressed);
// take care of capslock (only on the press of the 2nd key)
if (keys_pressed == 1 && is_pressed) {
// save the state of left and right shift
lshift_pressed = _kbfun_modifier_is_pressed(MOD_KEY_LeftShift);
rshift_pressed = _kbfun_modifier_is_pressed(MOD_KEY_RightShift);
// disable both
_kbfun_modifier_press_release(MOD_KEY_LeftShift, false);
_kbfun_modifier_press_release(MOD_KEY_RightShift, false);
// press capslock, then release it
_kbfun_normal_press_release(KEY_CapsLock, true); usb_keyboard_send();
_kbfun_normal_press_release(KEY_CapsLock, false); usb_keyboard_send();
// restore the state of left and right shift
if (lshift_pressed) { _kbfun_modifier_press_release(MOD_KEY_LeftShift, true); }
if (rshift_pressed) { _kbfun_modifier_press_release(MOD_KEY_RightShift, true); }
}
if (is_pressed) { keys_pressed++; }
}