/* ---------------------------------------------------------------------------- * main() * ---------------------------------------------------------------------------- * Copyright (c) 2012 Ben Blazak * Released under The MIT License (MIT) (see "license.md") * Project located at * ------------------------------------------------------------------------- */ #include #include // -------------------------------------------------------------------- // hardware // -------------------------------------------------------------------- #define KBD_DEBUG // comment out to disable the debug interface completely #include "./keyboard/controller.c" #include "./keyboard/keyboard.h" // -------------------------------------------------------------------- // types and forward declarations // -------------------------------------------------------------------- typedef uint8_t u8; typedef uint16_t u16; typedef enum StickyState { StickyNone, StickyOnceDown, StickyOnceUp, } StickyState; typedef u8 keycode; typedef u16 media_keycode; typedef u8 layer; typedef void (*keyfunc)(void); #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]; // ---------------------------------------------------------------------------- // 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 u8 current_row; static u8 current_col; static layer current_layer; static keycode current_keycode; static bool current_is_pressed; static bool sticky_done; static bool layers_active[KB_LAYERS]; static StickyState layers_sticky[KB_LAYERS]; static layer layers_top = 0; // ---------------------------------------------------------------------------- int main() { kb_init(); usb_init(); while (!usb_configured()); // initialize layers init_layers(); // 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 0 && l < KB_LAYERS; l--) { if (layers_active[l]) { return l; } } } // the base layer is always active return 0; } // return if highest active layer is sticky StickyState layer_top_sticky() { return layer_sticky(layers_top); } // return if layer is sticky StickyState layer_sticky(layer l) { if (l < KB_LAYERS) { return layers_sticky[l]; } return StickyNone; } // enable a layer void layer_enable(layer l, StickyState sticky) { // FIXME split off sticky part if (l >= KB_LAYERS) { return; } layers_active[l] = true; layers_sticky[l] = sticky; 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; } layers_active[l] = false; if (layers_sticky[l] != StickyNone) { debug_printf("sticky %d up!\n", l); } layers_sticky[l] = StickyNone; if (l == layers_top) { layers_top = highest_active_layer(1); } } // disable the highest active layer void layer_disable_top() { layer_disable(layers_top); } // return layer offset elements below the top layer layer_peek(layer offset) { return highest_active_layer(offset); } bool is_layer_enable(keyfunc f) { if (f == &kbfun_layer_enable || f == &kbfun_layer_sticky) { return true; } return false; } bool is_layer_disable(keyfunc f) { if (f == &kbfun_layer_disable || f == &kbfun_layer_sticky) { return true; } return false; } void layer_enable_upto(layer max_layer) { // FIXME clean this up // pressing a key implicitly activates all lower layers as well for (layer l=0; l <= KB_LAYERS; l++) { void (*key_function)(void) = kb_keyfunc_press(l, current_row, current_col); if (is_layer_enable(key_function)) { layer enable_layer = (layer) kb_keycode(l, current_row, current_col); if (enable_layer <= max_layer) { layer_enable(enable_layer, StickyNone); } } } } // ---------------------------------------------------------------------------- // 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_press_release(bool press, keycode key) { // no-op if (key == 0) { return; } if (press) { _kbfun_press(key); } else { _kbfun_release(key); } } void _kbfun_press(keycode key) { // modifier keys switch (key) { case KEY_LeftControl: keyboard_modifier_keys |= (1<<0); return; case KEY_LeftShift: keyboard_modifier_keys |= (1<<1); return; case KEY_LeftAlt: keyboard_modifier_keys |= (1<<2); return; case KEY_LeftGUI: keyboard_modifier_keys |= (1<<3); return; case KEY_RightControl: keyboard_modifier_keys |= (1<<4); return; case KEY_RightShift: keyboard_modifier_keys |= (1<<5); return; case KEY_RightAlt: keyboard_modifier_keys |= (1<<6); return; case KEY_RightGUI: keyboard_modifier_keys |= (1<<7); return; } // all others for (u8 i=0; i < sizeof(keyboard_keys); i++) { if (keyboard_keys[i] == 0) { keyboard_keys[i] = key; return; } } } void _kbfun_release(keycode key) { // modifier keys switch (key) { case KEY_LeftControl: keyboard_modifier_keys &= ~(1<<0); return; case KEY_LeftShift: keyboard_modifier_keys &= ~(1<<1); return; case KEY_LeftAlt: keyboard_modifier_keys &= ~(1<<2); return; case KEY_LeftGUI: keyboard_modifier_keys &= ~(1<<3); return; case KEY_RightControl: keyboard_modifier_keys &= ~(1<<4); return; case KEY_RightShift: keyboard_modifier_keys &= ~(1<<5); return; case KEY_RightAlt: keyboard_modifier_keys &= ~(1<<6); return; case KEY_RightGUI: keyboard_modifier_keys &= ~(1<<7); return; } // all others for (u8 i=0; i < sizeof(keyboard_keys); i++) { if (keyboard_keys[i] == key) { keyboard_keys[i] = 0; return; } } } bool _kbfun_is_pressed(keycode key) { // modifier keys switch (key) { case KEY_LeftControl: return (keyboard_modifier_keys & (1<<0)); case KEY_LeftShift: return (keyboard_modifier_keys & (1<<1)); case KEY_LeftAlt: return (keyboard_modifier_keys & (1<<2)); case KEY_LeftGUI: return (keyboard_modifier_keys & (1<<3)); case KEY_RightControl: return (keyboard_modifier_keys & (1<<4)); case KEY_RightShift: return (keyboard_modifier_keys & (1<<5)); case KEY_RightAlt: return (keyboard_modifier_keys & (1<<6)); case KEY_RightGUI: return (keyboard_modifier_keys & (1<<7)); } // all others for (u8 i=0; i < sizeof(keyboard_keys); i++) { if (keyboard_keys[i] == key) { return true; } } return false; } void _kbfun_mediakey_press_release(bool press, keycode key) { media_keycode media_key = _media_code_lookup_table[key]; if (press) { 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; } } } // ---------------------------------------------------------------------------- // basic keyfuncs // ---------------------------------------------------------------------------- bool key_is_modifier(keycode key) { switch (key) { case KEY_LeftControl: return true; case KEY_LeftShift: return true; case KEY_LeftAlt: return true; case KEY_LeftGUI: return true; case KEY_RightControl: return true; case KEY_RightShift: return true; case KEY_RightAlt: return true; case KEY_RightGUI: return true; default: return false; } } // normal key void kbfun_press_release() { sticky_done = ! key_is_modifier(current_keycode); _kbfun_press_release(current_is_pressed, current_keycode); } // media key void kbfun_mediakey_press_release() { sticky_done = true; keycode key = current_keycode; _kbfun_mediakey_press_release(current_is_pressed, key); } // enable layer void kbfun_layer_enable() { layer l = (layer) current_keycode; layer_enable_upto(l); } // disable layer void kbfun_layer_disable() { // letting go off a key releases *all* layers on that key for (layer l=0; l <= KB_LAYERS; l++) { void (*key_function)(void) = kb_keyfunc_release(l, current_row, current_col); if (is_layer_disable(key_function)) { layer disable_layer = (layer) kb_keycode(l, current_row, current_col); layer_disable(disable_layer); } } } // sticky layer key void kbfun_layer_sticky() { layer l = (layer) current_keycode; StickyState top_sticky = layer_top_sticky(); if (current_is_pressed) { if (l != layers_top) { // only the topmost layer on the stack should be in sticky once state if (top_sticky == StickyOnceDown || top_sticky == StickyOnceUp) { layer_disable_top(); } layer_enable(l, StickyOnceDown); debug_printf("sticky %d down!\n", l); // this should be the only place we care about this flag being cleared sticky_done = false; } } else { if (layer_sticky(l) == StickyOnceDown) { // When releasing this sticky key, pop the layer always layer_disable(l); if (!sticky_done) { // re-enable the sticky key if we didn't actually use it yet layer_enable(l, StickyOnceUp); debug_printf("sticky %d still down!\n", l); } } } } // ---------------------------------------------------------------------------- // combo keyfuncs // ---------------------------------------------------------------------------- void _kbfun_combo_press_release(keycode combo_key) { _kbfun_press_release(current_is_pressed, combo_key); kbfun_press_release(); } void kbfun_shift_press_release() { _kbfun_combo_press_release(KEY_LeftShift); } // +shift void kbfun_control_press_release() { _kbfun_combo_press_release(KEY_LeftControl); } // +control void kbfun_alt_press_release() { _kbfun_combo_press_release(KEY_LeftAlt); } // +alt void kbfun_win_press_release() { _kbfun_combo_press_release(KEY_LeftGUI); } // +win // capslock void kbfun_2_keys_capslock_press_release() { static u8 keys_pressed; static bool lshift_pressed; static bool rshift_pressed; keycode key = current_keycode; if (!current_is_pressed) { keys_pressed--; } // take care of the key that was actually pressed _kbfun_press_release(current_is_pressed, key); // take care of capslock (only on the press of the 2nd key) if (keys_pressed == 1 && current_is_pressed) { // save the state of left and right shift lshift_pressed = _kbfun_is_pressed(KEY_LeftShift); rshift_pressed = _kbfun_is_pressed(KEY_RightShift); // disable both _kbfun_press_release(false, KEY_LeftShift); _kbfun_press_release(false, KEY_RightShift); // press capslock, then release it _kbfun_press_release(true, KEY_CapsLock); usb_keyboard_send(); _kbfun_press_release(false, KEY_CapsLock); usb_keyboard_send(); // restore the state of left and right shift if (lshift_pressed) { _kbfun_press_release(true, KEY_LeftShift); } if (rshift_pressed) { _kbfun_press_release(true, KEY_RightShift); } } if (current_is_pressed) { keys_pressed++; } }