ergodox-firmware/src/lib/key-functions.c

414 lines
12 KiB
C

/* ----------------------------------------------------------------------------
* key functions: code
*
* These functions may do.. pretty much anything rational that thay like. If
* they want keycodes to be sent to the host in an aggrate report, they're
* responsible for modifying the appropriate report variables.
* ----------------------------------------------------------------------------
* 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 "lib-other/pjrc/usb_keyboard/usb_keyboard.h"
#include "lib/data-types.h"
#include "lib/usb/usage-page/keyboard.h"
#include "keyboard.h"
#include "key-functions.h"
#include "key-functions--private.h"
// ----------------------------------------------------------------------------
// public functions (not for keys)
// ----------------------------------------------------------------------------
/*
* Exec key
* - Execute the keypress or keyrelease function (if it exists) of the key at
* the current possition. Pass the keycode at the current position, and pass
* all other arguments as received
*/
void _kbfun_exec_key( KBFUN_FUNCTION_ARGS ) {
kbfun_funptr_t key_function =
( (pressed_)
? kb_layout_press_get(layer_, *row_, *col_)
: kb_layout_release_get(layer_, *row_, *col_) );
if (key_function)
(*key_function)(
pressed_,
kb_layout_get(layer_, *row_, *col_),
layer_,
row_,
col_,
current_layer_,
current_layers_,
pressed_layers_ );
}
// ----------------------------------------------------------------------------
// private functions
// ----------------------------------------------------------------------------
/*
* Generate a normal keypress or keyrelease
*
* Arguments
* - keycode: the keycode to use
* - pressed: whether to generate a keypress (true) or keyrelease (false)
*
* 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 _press_release(bool pressed, uint8_t keycode) {
// no-op
if (keycode == 0)
return;
// modifier keys
switch (keycode) {
case KEY_LeftControl: (pressed)
? (keyboard_modifier_keys |= (1<<0))
: (keyboard_modifier_keys &= ~(1<<0));
return;
case KEY_LeftShift: (pressed)
? (keyboard_modifier_keys |= (1<<1))
: (keyboard_modifier_keys &= ~(1<<1));
return;
case KEY_LeftAlt: (pressed)
? (keyboard_modifier_keys |= (1<<2))
: (keyboard_modifier_keys &= ~(1<<2));
return;
case KEY_LeftGUI: (pressed)
? (keyboard_modifier_keys |= (1<<3))
: (keyboard_modifier_keys &= ~(1<<3));
return;
case KEY_RightControl: (pressed)
? (keyboard_modifier_keys |= (1<<4))
: (keyboard_modifier_keys &= ~(1<<4));
return;
case KEY_RightShift: (pressed)
? (keyboard_modifier_keys |= (1<<5))
: (keyboard_modifier_keys &= ~(1<<5));
return;
case KEY_RightAlt: (pressed)
? (keyboard_modifier_keys |= (1<<6))
: (keyboard_modifier_keys &= ~(1<<6));
return;
case KEY_RightGUI: (pressed)
? (keyboard_modifier_keys |= (1<<7))
: (keyboard_modifier_keys &= ~(1<<7));
return;
}
// all others
for (uint8_t i=0; i<6; i++) {
if (pressed) {
if (keyboard_keys[i] == 0) {
keyboard_keys[i] = keycode;
return;
}
} else {
if (keyboard_keys[i] == keycode) {
keyboard_keys[i] = 0;
return;
}
}
}
}
/*
* Set current layer
* - Sets any keys currently set to the overall current layer to the new layer,
* and then sets the overall current layer
*
* Arguments
* - layer: the new layer value
* - current_layer: (a pointer to) the overall current layer (see main.c)
* - current_layers: (a pointer to a matrix of) the current layer for each key
* (see main.c and lib/key-functions.h)
*
* Note
* - Leaving all non-current layer values alone allows changing layers while
* maintaining a possibly enabled layer mask (as might be used to implement
* firmware enabled numlock)
*/
void _layer_set_current(
uint8_t layer,
uint8_t * current_layer,
uint8_t (*current_layers)[KB_ROWS][KB_COLUMNS] ) {
// don't switch to out-of-bounds layers
if ( layer < 0 || layer >= KB_LAYERS )
return;
for (uint8_t row=0; row<KB_ROWS; row++)
for (uint8_t col=0; col<KB_COLUMNS; col++)
// if a key is set to a non-current layer, leave it
if ((*current_layers)[row][col] == *current_layer)
(*current_layers)[row][col] = layer;
(*current_layer) = layer;
}
/*
* Set layer mask
* - Sets the specified key positions to the specified layer
*/
void _layer_set_mask(
uint8_t layer,
bool positions[KB_ROWS][KB_COLUMNS],
uint8_t (*current_layers)[KB_ROWS][KB_COLUMNS] ) {
// don't switch to out-of-bounds layers
if ( layer < 0 || layer >= KB_LAYERS )
return;
for (uint8_t row=0; row<KB_ROWS; row++)
for (uint8_t col=0; col<KB_COLUMNS; col++)
if (positions[row][col])
(*current_layers)[row][col] = layer;
}
/*
* Is the given keycode pressed?
*/
bool _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;
}
// ----------------------------------------------------------------------------
// public functions
// ----------------------------------------------------------------------------
/*
* Press|Release
* - Generate a normal keypress or keyrelease
*/
void kbfun_press_release( KBFUN_FUNCTION_ARGS ) {
_press_release(pressed_, keycode_);
}
/*
* Toggle
* - Toggle the key pressed or unpressed
*/
void kbfun_toggle( KBFUN_FUNCTION_ARGS ) {
if (_is_pressed(keycode_))
_press_release(false, keycode_);
else
_press_release(true, keycode_);
}
/*
* Increase layer
* - Increment the current layer by the value specified in the keymap (for all
* non-masked keys)
*/
void kbfun_layer_inc( KBFUN_FUNCTION_ARGS ) {
_layer_set_current(
(*current_layer_) + keycode_,
current_layer_,
current_layers_ );
}
/*
* Decrease layer
* - Decrement the current layer by the value specified in the keymap (for all
* non-masked keys)
*/
void kbfun_layer_dec( KBFUN_FUNCTION_ARGS ) {
_layer_set_current(
(*current_layer_) - keycode_,
current_layer_,
current_layers_ );
}
/*
* Increase layer, Execute key
* - Increment the current layer by the value specified in the keymap (for all
* non-masked keys), and execute (usually press|release) the key in the same
* position on that new layer
*
* Note
* - Meant to be paired with `kbfun_layer_dec_exec()`
*/
void kbfun_layer_inc_exec( KBFUN_FUNCTION_ARGS ) {
// switch layers
_layer_set_current(
(*current_layer_) + keycode_,
current_layer_,
current_layers_ );
// exececute second key (in the same position)
// - `layer_+keycode_` will be constant (under normal circumstances)
// between the press and release
_kbfun_exec_key(
pressed_, 0, layer_+keycode_,
row_, col_, current_layer_,
current_layers_, pressed_layers_ );
}
/*
* Decrease layer, Execute key
* - Decrement the current layer by the value specified in the keymap (for all
* non-masked keys), and execute (usually press|release) the key in the same
* position on that new layer
*
* Note
* - Meant to be paired with `kbfun_layer_inc_exec()`
*/
void kbfun_layer_dec_exec( KBFUN_FUNCTION_ARGS ) {
// switch layers
_layer_set_current(
(*current_layer_) - keycode_,
current_layer_,
current_layers_ );
// exececute second key (in the same position)
// - `layer_+keycode_` will be constant (under normal circumstances)
// between the press and release
_kbfun_exec_key(
pressed_, 0, layer_+keycode_,
row_, col_, current_layer_,
current_layers_, pressed_layers_ );
}
/*
* Two keys => capslock
* - 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
*
* Note
* - 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( KBFUN_FUNCTION_ARGS ) {
static uint8_t keys_pressed;
static bool lshift_pressed;
static bool rshift_pressed;
if (!pressed_) keys_pressed--;
// take care of the key that was actually pressed
_press_release(pressed_, keycode_);
// take care of capslock (only on the press of the 2nd key)
if (keys_pressed == 1 && pressed_) {
// save the state of left and right shift
lshift_pressed = _is_pressed(KEY_LeftShift);
rshift_pressed = _is_pressed(KEY_RightShift);
// disable both
_press_release(false, KEY_LeftShift);
_press_release(false, KEY_RightShift);
// press capslock, then release it
_press_release(true, KEY_CapsLock);
usb_keyboard_send();
_press_release(false, KEY_CapsLock);
usb_keyboard_send();
// restore the state of left and right shift
if (lshift_pressed)
_press_release(true, KEY_LeftShift);
if (rshift_pressed)
_press_release(true, KEY_RightShift);
}
if (pressed_) keys_pressed++;
}
/*
* Activate Numpad
* - Sets num-lock (on for press, off for release) and shifts (without changing
* the overall current layer) the layer of the keys specified in this
* function to the value specified in the keymap
*
* Note
* - If a more than one layer mask of this type is used at the same time, the
* second will override the first, and any keys covered by both will be reset
* to the overall current layer when the second is released (even if the
* first is still pressed)
*/
#include "usb/usage-page/keyboard.h"
void kbfun_layermask_numpad_press_release( KBFUN_FUNCTION_ARGS ) {
// define layer mask
bool layer_mask[KB_ROWS][KB_COLUMNS] = MATRIX_LAYER(
// unused
0,
// left hand
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0,
0, 0,
0, 0, 0,
// right hand
0, 0, 1, 1, 1, 1, 0,
0, 0, 1, 1, 1, 1, 0,
0, 1, 1, 1, 1, 0,
0, 0, 1, 1, 1, 1, 0,
0, 0, 0, 0, 0,
0,
0, 0,
0, 0, 0 );
// set numlock
if ( // if pressed and numlock off
(pressed_ && !(keyboard_leds & (1<<0))) ||
// if released and numlock on
(!pressed_ && (keyboard_leds & (1<<0))) ) {
// toggle numlock
_press_release(true, KEYPAD_NumLock_Clear);
usb_keyboard_send();
_press_release(false, KEYPAD_NumLock_Clear);
usb_keyboard_send();
}
// set layer mask
if (pressed_)
_layer_set_mask(keycode_, layer_mask, current_layers_);
else
_layer_set_mask(*current_layer_, layer_mask, current_layers_);
}