/* ---------------------------------------------------------------------------- * main() * ---------------------------------------------------------------------------- * Copyright (c) 2012 Ben Blazak * Released under The MIT License (MIT) (see "license.md") * Project located at * ------------------------------------------------------------------------- */ #include #include #include "./keyboard/controller.c" #include "./keyboard/keyboard.h" // -------------------------------------------------------------------- // types and forward declarations // -------------------------------------------------------------------- typedef void (*void_funptr_t)(void); typedef enum StickyState { StickyNone, StickyOnceDown, StickyOnceUp, StickyLock, } StickyState; #include "./main.h" // ---------------------------------------------------------------------------- // layout data // ---------------------------------------------------------------------------- #include "./keyboard/layout.c" // defines: // #define KB_Layers #{Layers.size} // static const uint8_t PROGMEM _kb_layout[KB_LAYERS][KB_ROWS][KB_COLUMNS]; // static const void_funptr_t PROGMEM _kb_layout_press[KB_LAYERS][KB_ROWS][KB_COLUMNS]; // static const void_funptr_t 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 bool kb_was_transparent[KB_ROWS][KB_COLUMNS]; static uint8_t layers_pressed[KB_ROWS][KB_COLUMNS]; static uint8_t current_layer; static uint8_t layer_offset; static uint8_t current_row; static uint8_t current_col; static bool current_is_pressed; static bool non_trans_key_pressed; static bool trans_key_pressed; static bool layers_active[KB_LAYERS]; static StickyState layers_sticky[KB_LAYERS]; static uint8_t layers_top = 0; // ---------------------------------------------------------------------------- /* * main() */ int main(void) { kb_init(); // does controller initialization too usb_init(); while (!usb_configured()); // initialize layers main_init_layers(); // never return main_key_loop(); return 0; } // ---------------------------------------------------------------------------- // layer functions // ---------------------------------------------------------------------------- void main_init_layers() { for (uint8_t layer=0; layer < KB_LAYERS; layer++) { layers_active[layer] = false; layers_sticky[layer] = StickyNone; } layers_active[0] = true; } // find highest active layer uint8_t _highest_active_layer(uint8_t offset) { if (offset < layers_top) { for (uint8_t l = layers_top - offset; l > 0 && l < KB_LAYERS; l--) { if (layers_active[l]) { return l; } } } // the base layer is always active return 0; } // return the highest active layer uint8_t main_layers_top_layer() { return layers_top; } // return if highest active layer is sticky StickyState main_layers_top_sticky() { return main_layers_sticky(layers_top); } // return if layer is sticky StickyState main_layers_sticky(uint8_t layer) { if (layer < KB_LAYERS) { return layers_sticky[layer]; } return StickyNone; } // enable a layer void main_layers_enable(uint8_t layer, StickyState sticky) { if (layer >= KB_LAYERS) { return; } layers_active[layer] = true; layers_sticky[layer] = sticky; if (layer > layers_top) { layers_top = layer; } } // disable a layer void main_layers_disable(uint8_t layer) { // base layer stays always on if (layer >= KB_LAYERS || layer == 0) { return; } layers_active[layer] = false; layers_sticky[layer] = StickyNone; if (layer == layers_top) { layers_top = _highest_active_layer(1); } } // disable the highest active layer void main_layers_disable_top() { main_layers_disable(layers_top); } // return layer offset elements below the top uint8_t main_layers_peek(uint8_t offset) { return _highest_active_layer(offset); } // execute the keypress or keyrelease function (if it exists) of the key at the current possition void main_exec_key(void) { void (*key_function)(void) = ( (current_is_pressed) ? kb_layout_press_get(current_layer, current_row, current_col) : kb_layout_release_get(current_layer, current_row, current_col) ); if (key_function) { (*key_function)(); } // If the current layer is in the sticky once up state and a key defined // for this layer (a non-transparent key) was pressed, pop the layer if (main_layers_top_sticky() == StickyOnceUp && non_trans_key_pressed) { main_layers_disable_top(); } } // ---------------------------------------------------------------------------- uint8_t kb_layout_get(uint8_t layer, uint8_t row, uint8_t column) { return (uint8_t) pgm_read_byte(&(_kb_layout[layer][row][column] )); } void_funptr_t kb_layout_press_get(uint8_t layer, uint8_t row, uint8_t column) { return (void_funptr_t) pgm_read_word(&(_kb_layout_press[layer][row][column] )); } void_funptr_t kb_layout_release_get(uint8_t layer, uint8_t row, uint8_t column) { return (void_funptr_t) pgm_read_word(&(_kb_layout_release[layer][row][column])); } /* * Generate a normal keypress or keyrelease * * Arguments * - press: whether to generate a keypress (true) or keyrelease (false) * - keycode: the keycode to use * * Note * - Because of the way USB does things, what this actually does is either add * or remove 'keycode' from the list of currently pressed keys, to be sent at * the end of the current cycle (see main.c) */ void _kbfun_press_release(bool press, uint8_t keycode) { // no-op if (keycode == 0) { return; } // modifier keys switch (keycode) { case KEY_LeftControl: (press) ? (keyboard_modifier_keys |= (1<<0)) : (keyboard_modifier_keys &= ~(1<<0)); return; case KEY_LeftShift: (press) ? (keyboard_modifier_keys |= (1<<1)) : (keyboard_modifier_keys &= ~(1<<1)); return; case KEY_LeftAlt: (press) ? (keyboard_modifier_keys |= (1<<2)) : (keyboard_modifier_keys &= ~(1<<2)); return; case KEY_LeftGUI: (press) ? (keyboard_modifier_keys |= (1<<3)) : (keyboard_modifier_keys &= ~(1<<3)); return; case KEY_RightControl: (press) ? (keyboard_modifier_keys |= (1<<4)) : (keyboard_modifier_keys &= ~(1<<4)); return; case KEY_RightShift: (press) ? (keyboard_modifier_keys |= (1<<5)) : (keyboard_modifier_keys &= ~(1<<5)); return; case KEY_RightAlt: (press) ? (keyboard_modifier_keys |= (1<<6)) : (keyboard_modifier_keys &= ~(1<<6)); return; case KEY_RightGUI: (press) ? (keyboard_modifier_keys |= (1<<7)) : (keyboard_modifier_keys &= ~(1<<7)); return; } // all others for (uint8_t i=0; i<6; i++) { if (press) { if (keyboard_keys[i] == 0) { keyboard_keys[i] = keycode; return; } } else { if (keyboard_keys[i] == keycode) { keyboard_keys[i] = 0; return; } } } } /* * Is the given keycode pressed? */ bool _kbfun_is_pressed(uint8_t keycode) { // modifier keys switch (keycode) { case KEY_LeftControl: if (keyboard_modifier_keys & (1<<0)) { return true; } case KEY_LeftShift: if (keyboard_modifier_keys & (1<<1)) { return true; } case KEY_LeftAlt: if (keyboard_modifier_keys & (1<<2)) { return true; } case KEY_LeftGUI: if (keyboard_modifier_keys & (1<<3)) { return true; } case KEY_RightControl: if (keyboard_modifier_keys & (1<<4)) { return true; } case KEY_RightShift: if (keyboard_modifier_keys & (1<<5)) { return true; } case KEY_RightAlt: if (keyboard_modifier_keys & (1<<6)) { return true; } case KEY_RightGUI: if (keyboard_modifier_keys & (1<<7)) { return true; } } // all others for (uint8_t i=0; i<6; i++) if (keyboard_keys[i] == keycode) { return true; } return false; } void _kbfun_mediakey_press_release(bool press, uint8_t keycode) { uint16_t mediakey_code = _media_code_lookup_table[keycode]; if (press) { consumer_key = mediakey_code; } else { // Only one key can be pressed at a time so only clear the keypress for // active key (most recently pressed) if (mediakey_code == consumer_key) { consumer_key = 0; } } } uint8_t _kbfun_get_keycode() { return kb_layout_get(current_layer, current_row, current_col); } // ---------------------------------------------------------------------------- // basic // ---------------------------------------------------------------------------- void kbfun_press_release() { if (!trans_key_pressed) { non_trans_key_pressed = true; } kbfun_press_release_preserve_sticky(); } /* * Generate a normal keypress or keyrelease * While basing the sticky key state transition on whether * kbfun_press_release() was called after kbfun_transparent() generally * works in practice, it is not always the desired behavior. One of the * benefits of sticky keys is avoiding key chording, so we want to make sure * that standard modifiers do not interrupt the sticky key cycle. Use * kbfun_press_release_preserve_sticky() if you want to define a standard * modifier key (shift, control, alt, gui) on the sticky layer instead of * defining the key to be transparent for the layer. */ void kbfun_press_release_preserve_sticky() { uint8_t keycode = _kbfun_get_keycode(); _kbfun_press_release(current_is_pressed, keycode); } /* * Toggle the key pressed or unpressed */ void kbfun_toggle(void) { uint8_t keycode = _kbfun_get_keycode(); bool is_pressed = _kbfun_is_pressed(keycode); _kbfun_press_release(!is_pressed, keycode); } /* * Execute the key that would have been executed if the current layer was not * active */ void kbfun_transparent(void) { // TODO maybe re-implement this cleaner? trans_key_pressed = true; layer_offset++; current_layer = main_layers_peek(layer_offset); layers_pressed[current_row][current_col] = current_layer; main_exec_key(); } // ---------------------------------------------------------------------------- // layer helper functions // ---------------------------------------------------------------------------- static bool is_layer_enable(void_funptr_t f) { if (f == &kbfun_layer_enable || f == &kbfun_layer_sticky) { return true; } return false; } static bool is_layer_disable(void_funptr_t f) { if (f == &kbfun_layer_disable || f == &kbfun_layer_sticky) { return true; } return false; } static void layer_enable_upto(uint8_t max_layer) { // FIXME clean this up // pressing a key implicitly activates all lower layers as well for (uint8_t layer=0; layer <= KB_LAYERS; layer++) { void (*key_function)(void) = kb_layout_press_get(layer, current_row, current_col); if (is_layer_enable(key_function)) { uint8_t enable_layer = kb_layout_get(layer, current_row, current_col); if (enable_layer <= max_layer) { main_layers_enable(enable_layer, StickyNone); } } } } // ---------------------------------------------------------------------------- // layer functions // ---------------------------------------------------------------------------- // enable given layer void kbfun_layer_enable() { uint8_t layer = _kbfun_get_keycode(); // FIXME useful for anything? // Only the topmost layer on the stack should be in sticky once state, pop // the top layer if it is in sticky once state /* uint8_t topSticky = main_layers_top_sticky(); */ /* if (topSticky == StickyOnceDown || topSticky == StickyOnceUp) { */ /* main_layers_disable_top(); */ /* } */ layer_enable_upto(layer); } // disable given layer void kbfun_layer_disable() { /* uint8_t layer = _kbfun_get_keycode(); */ // FIXME clean this up // letting go off a key releases *all* layers on that key for (uint8_t layer=0; layer <= KB_LAYERS; layer++) { void (*key_function)(void) = kb_layout_release_get(layer, current_row, current_col); if (is_layer_disable(key_function)) { uint8_t disable_layer = kb_layout_get(layer, current_row, current_col); main_layers_disable(disable_layer); } } } /* * This function gives similar behavior to sticky keys for modifiers available * on most operating systems. * 1) One time down (set on key press) - The layer was not active and the key * has been pressed but not yet released. The layer is pushed in the one * time down state. * 2) One time up (set on key release) - The layer was active when the layer * sticky key was released. If a key on this layer (not set to * transparent) was pressed before the key was released, the layer will be * popped. If a non-transparent key was not pressed, the layer is popped * and pushed again in the one time up state. * 3) Locked (set on key press) - The layer was active and in the one time up * state when the layer sticky key was pressed again. The layer will be * popped if the function is invoked on a subsequent keypress. */ void kbfun_layer_sticky() { uint8_t layer = _kbfun_get_keycode(); uint8_t topLayer = main_layers_top_layer(); StickyState topSticky = main_layers_top_sticky(); if (current_is_pressed) { if (topLayer == layer) { // FIXME /* if (topSticky == StickyOnceUp) { */ /* main_layers_enable(layer, StickyLock); */ /* } */ } else { // only the topmost layer on the stack should be in sticky once state if (topSticky == StickyOnceDown || topSticky == StickyOnceUp) { main_layers_disable_top(); } main_layers_enable(layer, StickyOnceDown); // this should be the only place we care about this flag being cleared non_trans_key_pressed = false; } } else { if (main_layers_sticky(layer) == StickyOnceDown) { // When releasing this sticky key, pop the layer always main_layers_disable(layer); if (!non_trans_key_pressed) { // If no key defined for this layer (a non-transparent key) // was pressed, push the layer again, but in the // StickyOnceUp state main_layers_enable(layer, StickyOnceUp); } } } } // ---------------------------------------------------------------------------- // special // ---------------------------------------------------------------------------- /* * Generate a 'shift' press or release before the normal keypress or release */ void kbfun_shift_press_release(void) { _kbfun_press_release(current_is_pressed, KEY_LeftShift); kbfun_press_release(); } /* * Generate a 'control' press or release before the normal keypress or release */ void kbfun_control_press_release(void) { _kbfun_press_release(current_is_pressed, KEY_LeftControl); kbfun_press_release(); } /* * When assigned to two keys (e.g. the physical left and right shift keys) * (in both the press and release matrices), pressing and holding down one of * the keys will make the second key toggle capslock * * If either of the shifts are pressed when the second key is pressed, they * wil be released so that capslock will register properly when pressed. * Capslock will then be pressed and released, and the original state of the * shifts will be restored */ void kbfun_2_keys_capslock_press_release(void) { static uint8_t keys_pressed; static bool lshift_pressed; static bool rshift_pressed; uint8_t keycode = _kbfun_get_keycode(); if (!current_is_pressed) { keys_pressed--; } // take care of the key that was actually pressed _kbfun_press_release(current_is_pressed, keycode); // 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++; } } /* * Generate a keypress for a media key */ void kbfun_mediakey_press_release(void) { uint8_t keycode = _kbfun_get_keycode(); _kbfun_mediakey_press_release(current_is_pressed, keycode); } // -------------------------------------------------------------------------------------- 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); // this loop is responsible to // - "execute" keys when they change 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) // // note // - everything else is the key function's responsibility // - see the keyboard layout file ("keyboard/layout/*.c") for // which key is assigned which function (per layer) // - see "lib/key-functions/public/*.c" for the function definitions for (uint8_t row=0; row