ergodox-firmware/src/main.c

204 lines
6.0 KiB
C
Raw Normal View History

/* ----------------------------------------------------------------------------
* 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>
#include <util/delay.h>
#include "./lib-other/pjrc/usb_keyboard/usb_keyboard.h"
#include "./lib/key-functions/public.h"
#include "./keyboard/controller.h"
#include "./keyboard/layout.h"
#include "./keyboard/matrix.h"
#include "./main.h"
// ----------------------------------------------------------------------------
2016-02-04 05:38:18 +01:00
// TODO remove this; for now we'll just limit it to the number of layers
#define MAX_ACTIVE_LAYERS KB_LAYERS
// ----------------------------------------------------------------------------
static bool _main_kb_is_pressed[KB_ROWS][KB_COLUMNS];
bool (*main_kb_is_pressed)[KB_ROWS][KB_COLUMNS] = &_main_kb_is_pressed;
static bool _main_kb_was_pressed[KB_ROWS][KB_COLUMNS];
bool (*main_kb_was_pressed)[KB_ROWS][KB_COLUMNS] = &_main_kb_was_pressed;
static bool main_kb_was_transparent[KB_ROWS][KB_COLUMNS];
uint8_t main_layers_pressed[KB_ROWS][KB_COLUMNS];
uint8_t main_arg_layer;
uint8_t main_arg_layer_offset;
uint8_t main_arg_row;
uint8_t main_arg_col;
bool main_arg_is_pressed;
bool main_arg_was_pressed;
adding sticky key functionality This function gives similar behavior to sticky keys for modifiers available on most operating systems. It is considered an accessibility feature because it alleviates the user from having to hold down modifiers while pressing a key to produce the modified key function. It is useful for fast touch typing because you can avoid chording motions which both strain your hands and take your hands out of home-row position while pressing normal alpha keys. This function emulates the 3-state behavior which is default on OS X and optional in Windows where the modifier cycles between Off->Once->Locked states. This is particularly handy for symbol layers where you typically only type one symbol before you want to return to unmodified typing (layer 0), e.g. 'if (condition) { a = "b" + "c"; }'. If you assign a symbol layer to a thumb key as a layer sticky cycle, you can type the entire line of code without taking your hands out of home row position and you do not need to toggle off the layer after each symbol is pressed, only immediately before keying the symbol. The exact behavior of the layer sticky cycle function is defined as follows for each state: 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.
2013-04-08 09:49:35 +02:00
bool main_arg_any_non_trans_key_pressed;
bool main_arg_trans_key_pressed;
2016-02-04 15:19:06 +01:00
// layer data
struct layer {
bool active;
uint8_t sticky;
};
struct layer layers[KB_LAYERS];
uint8_t layers_top = 0;
// ----------------------------------------------------------------------------
/*
* main()
*/
int main(void) {
2016-02-04 05:38:18 +01:00
kb_init(); // does controller initialization too
usb_init();
while (!usb_configured());
2016-02-04 15:19:06 +01:00
// initialize layers
for (uint8_t layer=0; layer < KB_LAYERS; layer++) {
layers[layer].active = false;
layers[layer].sticky = eStickyNone;
}
layers[0].active = true;
2016-02-04 05:38:18 +01:00
for (;;) {
// swap `main_kb_is_pressed` and `main_kb_was_pressed`, then update
bool (*temp)[KB_ROWS][KB_COLUMNS] = main_kb_was_pressed;
main_kb_was_pressed = main_kb_is_pressed;
main_kb_is_pressed = temp;
kb_update_matrix(*main_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/ergodox/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<KB_ROWS; row++) {
for (uint8_t col=0; col<KB_COLUMNS; col++) {
main_arg_is_pressed = (*main_kb_is_pressed)[row][col];
main_arg_was_pressed = (*main_kb_was_pressed)[row][col];
if (main_arg_is_pressed != main_arg_was_pressed) {
if (main_arg_is_pressed) {
main_arg_layer = main_layers_top_layer();
2016-02-04 05:38:18 +01:00
main_layers_pressed[row][col] = main_arg_layer;
main_arg_trans_key_pressed = false;
} else {
main_arg_layer = main_layers_pressed[row][col];
main_arg_trans_key_pressed = main_kb_was_transparent[row][col];
}
// set remaining vars, and "execute" key
main_arg_row = row;
main_arg_col = col;
main_arg_layer_offset = 0;
main_exec_key();
main_kb_was_transparent[row][col] = main_arg_trans_key_pressed;
}
}
}
// send the USB report (even if nothing's changed)
usb_keyboard_send();
usb_extra_consumer_send();
_delay_ms(MAKEFILE_DEBOUNCE_TIME);
}
return 0;
}
2016-02-04 15:19:06 +01:00
// ----------------------------------------------------------------------------
2016-02-04 10:38:07 +01:00
// layer functions
2016-02-04 10:59:15 +01:00
// ----------------------------------------------------------------------------
2016-02-04 15:19:06 +01:00
2016-02-04 10:59:15 +01:00
// find highest active layer
uint8_t _highest_active_layer(uint8_t offset) {
2016-02-04 15:52:43 +01:00
if (offset < layers_top) {
for (uint8_t l = layers_top - offset; l > 0 && l < KB_LAYERS; l--) {
if (layers[l].active) { return l; }
2016-02-04 10:59:15 +01:00
}
}
2016-02-04 10:59:15 +01:00
// the base layer is always active
return 0;
}
// return the highest active layer
uint8_t main_layers_top_layer() {
2016-02-04 10:59:15 +01:00
return layers_top;
}
// return if highest active layer is sticky
uint8_t main_layers_top_sticky() {
2016-02-04 10:59:15 +01:00
return layers[layers_top].sticky;
2016-02-04 10:22:40 +01:00
}
2016-02-04 10:22:40 +01:00
// enable a layer
void main_layers_enable(uint8_t layer, uint8_t sticky) {
2016-02-04 15:52:43 +01:00
if (layer >= KB_LAYERS) { return; }
2016-02-04 10:59:15 +01:00
layers[layer].active = true;
layers[layer].sticky = sticky;
2016-02-04 10:22:40 +01:00
if (layer > layers_top) {
layers_top = layer;
}
}
2016-02-04 10:22:40 +01:00
// disable a layer
void main_layers_disable(uint8_t layer) {
2016-02-04 15:52:43 +01:00
if (layer >= KB_LAYERS) { return; }
// base layer stays always on
if (layer > 0) { layers[layer].active = false; }
2016-02-04 15:52:43 +01:00
if (layer >= layers_top) {
2016-02-04 10:59:15 +01:00
layers_top = _highest_active_layer(1);
2016-02-04 10:22:40 +01:00
}
}
2016-02-04 10:59:15 +01:00
// disable the highest active layer
void main_layers_disable_top() {
main_layers_disable(layers_top);
}
2016-02-04 10:38:07 +01:00
// return layer offset elements below the top
uint8_t main_layers_peek(uint8_t offset) {
2016-02-04 10:59:15 +01:00
return _highest_active_layer(offset);
2016-02-04 10:38:07 +01:00
}
2016-02-04 10:22:40 +01:00
// execute the keypress or keyrelease function (if it exists) of the key at the current possition
adding sticky key functionality This function gives similar behavior to sticky keys for modifiers available on most operating systems. It is considered an accessibility feature because it alleviates the user from having to hold down modifiers while pressing a key to produce the modified key function. It is useful for fast touch typing because you can avoid chording motions which both strain your hands and take your hands out of home-row position while pressing normal alpha keys. This function emulates the 3-state behavior which is default on OS X and optional in Windows where the modifier cycles between Off->Once->Locked states. This is particularly handy for symbol layers where you typically only type one symbol before you want to return to unmodified typing (layer 0), e.g. 'if (condition) { a = "b" + "c"; }'. If you assign a symbol layer to a thumb key as a layer sticky cycle, you can type the entire line of code without taking your hands out of home row position and you do not need to toggle off the layer after each symbol is pressed, only immediately before keying the symbol. The exact behavior of the layer sticky cycle function is defined as follows for each state: 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.
2013-04-08 09:49:35 +02:00
void main_exec_key(void) {
2016-02-04 05:38:18 +01:00
void (*key_function)(void) =
( (main_arg_is_pressed)
? kb_layout_press_get(main_arg_layer, main_arg_row, main_arg_col)
: kb_layout_release_get(main_arg_layer, main_arg_row, main_arg_col) );
if (key_function) {
2016-02-04 05:38:18 +01:00
(*key_function)();
}
2016-02-04 05:38:18 +01:00
// 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() == eStickyOnceUp && main_arg_any_non_trans_key_pressed) {
main_layers_disable_top();
}
adding sticky key functionality This function gives similar behavior to sticky keys for modifiers available on most operating systems. It is considered an accessibility feature because it alleviates the user from having to hold down modifiers while pressing a key to produce the modified key function. It is useful for fast touch typing because you can avoid chording motions which both strain your hands and take your hands out of home-row position while pressing normal alpha keys. This function emulates the 3-state behavior which is default on OS X and optional in Windows where the modifier cycles between Off->Once->Locked states. This is particularly handy for symbol layers where you typically only type one symbol before you want to return to unmodified typing (layer 0), e.g. 'if (condition) { a = "b" + "c"; }'. If you assign a symbol layer to a thumb key as a layer sticky cycle, you can type the entire line of code without taking your hands out of home row position and you do not need to toggle off the layer after each symbol is pressed, only immediately before keying the symbol. The exact behavior of the layer sticky cycle function is defined as follows for each state: 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.
2013-04-08 09:49:35 +02:00
}