almost a tree now!
parent
2b98ca5f8d
commit
90bd7c9116
|
@ -324,3 +324,31 @@
|
|||
#define SYSTEM_POWER_DOWN 0x0081
|
||||
#define SYSTEM_SLEEP 0x0082
|
||||
#define SYSTEM_WAKE_UP 0x0083
|
||||
|
||||
/*
|
||||
* MediaCodeLookupTable is used to translate from enumeration in keyboard.h to
|
||||
* consumer key scan code in usb_keyboard.h
|
||||
*/
|
||||
static const uint16_t _media_code_lookup_table[] = {
|
||||
AUDIO_MUTE, // MEDIAKEY_AUDIO_MUTE
|
||||
AUDIO_VOL_UP, // MEDIAKEY_AUDIO_VOL_UP
|
||||
AUDIO_VOL_DOWN, // MEDIAKEY_AUDIO_VOL_DOWN
|
||||
TRANSPORT_NEXT_TRACK, // MEDIAKEY_NEXT_TRACK
|
||||
TRANSPORT_PREV_TRACK, // MEDIAKEY_PREV_TRACK
|
||||
TRANSPORT_STOP, // MEDIAKEY_STOP
|
||||
TRANSPORT_PLAY_PAUSE, // MEDIAKEY_PLAY_PAUSE
|
||||
TRANSPORT_RECORD, // MEDIAKEY_RECORD
|
||||
TRANSPORT_REWIND, // MEDIAKEY_REWIND
|
||||
TRANSPORT_EJECT, // MEDIAKEY_EJECT
|
||||
AL_CC_CONFIG, // MEDIAKEY_CC_CONFIG
|
||||
AL_EMAIL, // MEDIAKEY_EMAIL
|
||||
AL_CALCULATOR, // MEDIAKEY_CALCULATOR
|
||||
AL_LOCAL_BROWSER, // MEDIAKEY_LOCAL_BROWSER
|
||||
AC_SEARCH, // MEDIAKEY_BROWSER_SEARCH
|
||||
AC_HOME, // MEDIAKEY_BROWSER_HOME
|
||||
AC_BACK, // MEDIAKEY_BROWSER_BACK
|
||||
AC_FORWARD, // MEDIAKEY_BROWSER_FORWARD
|
||||
AC_STOP, // MEDIAKEY_BROWSER_STOP
|
||||
AC_REFRESH, // MEDIAKEY_BROWSER_REFRESH
|
||||
AC_BOOKMARKS, // MEDIAKEY_BROWSER_BOOKMARKS
|
||||
};
|
|
@ -11,8 +11,6 @@
|
|||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include "../lib/keyboard.h"
|
||||
#include "../lib/keyfunctions.h"
|
||||
|
||||
typedef void (*void_funptr_t)(void);
|
||||
|
||||
|
@ -22,6 +20,43 @@ typedef void (*void_funptr_t)(void);
|
|||
#define KB_COLUMNS 14 // must match real life
|
||||
#define KB_LAYERS 10
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
uint8_t main_layers_top_layer (void);
|
||||
uint8_t main_layers_top_sticky (void);
|
||||
uint8_t main_layers_sticky (uint8_t layer);
|
||||
void main_layers_enable (uint8_t layer, uint8_t sticky);
|
||||
void main_layers_disable (uint8_t layer);
|
||||
void main_layers_disable_top (void);
|
||||
|
||||
// basic
|
||||
void kbfun_press_release (void);
|
||||
void kbfun_press_release_preserve_sticky (void);
|
||||
void kbfun_toggle (void);
|
||||
void kbfun_transparent (void);
|
||||
|
||||
// layer functions
|
||||
void kbfun_layer_enable (void);
|
||||
void kbfun_layer_sticky (void);
|
||||
void kbfun_layer_disable (void);
|
||||
|
||||
// device
|
||||
void kbfun_jump_to_bootloader (void);
|
||||
|
||||
// special
|
||||
void kbfun_shift_press_release (void);
|
||||
void kbfun_control_press_release (void);
|
||||
void kbfun_2_keys_capslock_press_release (void);
|
||||
void kbfun_mediakey_press_release (void);
|
||||
|
||||
// private
|
||||
void _kbfun_press_release (bool press, uint8_t keycode);
|
||||
bool _kbfun_is_pressed (uint8_t keycode);
|
||||
void _kbfun_mediakey_press_release (bool press, uint8_t keycode);
|
||||
uint8_t _kbfun_get_keycode (void);
|
||||
|
||||
// device
|
||||
void kbfun_jump_to_bootloader(void);
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
|
|
|
@ -1,407 +0,0 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* key functions : private : code
|
||||
* ----------------------------------------------------------------------------
|
||||
* 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 "./keyfunctions.h"
|
||||
|
||||
/*
|
||||
* MediaCodeLookupTable is used to translate from enumeration in keyboard.h to
|
||||
* consumer key scan code in usb_keyboard.h
|
||||
*/
|
||||
static const uint16_t _media_code_lookup_table[] = {
|
||||
AUDIO_MUTE, // MEDIAKEY_AUDIO_MUTE
|
||||
AUDIO_VOL_UP, // MEDIAKEY_AUDIO_VOL_UP
|
||||
AUDIO_VOL_DOWN, // MEDIAKEY_AUDIO_VOL_DOWN
|
||||
TRANSPORT_NEXT_TRACK, // MEDIAKEY_NEXT_TRACK
|
||||
TRANSPORT_PREV_TRACK, // MEDIAKEY_PREV_TRACK
|
||||
TRANSPORT_STOP, // MEDIAKEY_STOP
|
||||
TRANSPORT_PLAY_PAUSE, // MEDIAKEY_PLAY_PAUSE
|
||||
TRANSPORT_RECORD, // MEDIAKEY_RECORD
|
||||
TRANSPORT_REWIND, // MEDIAKEY_REWIND
|
||||
TRANSPORT_EJECT, // MEDIAKEY_EJECT
|
||||
AL_CC_CONFIG, // MEDIAKEY_CC_CONFIG
|
||||
AL_EMAIL, // MEDIAKEY_EMAIL
|
||||
AL_CALCULATOR, // MEDIAKEY_CALCULATOR
|
||||
AL_LOCAL_BROWSER, // MEDIAKEY_LOCAL_BROWSER
|
||||
AC_SEARCH, // MEDIAKEY_BROWSER_SEARCH
|
||||
AC_HOME, // MEDIAKEY_BROWSER_HOME
|
||||
AC_BACK, // MEDIAKEY_BROWSER_BACK
|
||||
AC_FORWARD, // MEDIAKEY_BROWSER_FORWARD
|
||||
AC_STOP, // MEDIAKEY_BROWSER_STOP
|
||||
AC_REFRESH, // MEDIAKEY_BROWSER_REFRESH
|
||||
AC_BOOKMARKS, // MEDIAKEY_BROWSER_BOOKMARKS
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* 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(main_arg_layer, main_arg_row, main_arg_col);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// basic
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void kbfun_press_release() {
|
||||
if (!main_arg_trans_key_pressed) {
|
||||
main_arg_any_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(main_arg_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?
|
||||
main_arg_trans_key_pressed = true;
|
||||
main_arg_layer_offset++;
|
||||
main_arg_layer = main_layers_peek(main_arg_layer_offset);
|
||||
main_layers_pressed[main_arg_row][main_arg_col] = main_arg_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, main_arg_row, main_arg_col);
|
||||
|
||||
if (is_layer_enable(key_function)) {
|
||||
uint8_t enable_layer = kb_layout_get(layer, main_arg_row, main_arg_col);
|
||||
if (enable_layer <= max_layer) {
|
||||
main_layers_enable(enable_layer, eStickyNone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void layer_disable_all() {
|
||||
// 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, main_arg_row, main_arg_col);
|
||||
|
||||
if (is_layer_disable(key_function)) {
|
||||
uint8_t disable_layer = kb_layout_get(layer, main_arg_row, main_arg_col);
|
||||
main_layers_disable(disable_layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// 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 == eStickyOnceDown || topSticky == eStickyOnceUp) { */
|
||||
/* main_layers_disable_top(); */
|
||||
/* } */
|
||||
|
||||
layer_enable_upto(layer);
|
||||
}
|
||||
|
||||
// disable given layer
|
||||
void kbfun_layer_disable() {
|
||||
/* uint8_t layer = _kbfun_get_keycode(); */
|
||||
layer_disable_all();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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();
|
||||
uint8_t topSticky = main_layers_top_sticky();
|
||||
|
||||
if (main_arg_is_pressed) {
|
||||
if (topLayer == layer) {
|
||||
// FIXME
|
||||
/* if (topSticky == eStickyOnceUp) { */
|
||||
/* main_layers_enable(layer, eStickyLock); */
|
||||
/* } */
|
||||
} else {
|
||||
// only the topmost layer on the stack should be in sticky once state
|
||||
if (topSticky == eStickyOnceDown || topSticky == eStickyOnceUp) {
|
||||
main_layers_disable_top();
|
||||
}
|
||||
main_layers_enable(layer, eStickyOnceDown);
|
||||
|
||||
// this should be the only place we care about this flag being cleared
|
||||
main_arg_any_non_trans_key_pressed = false;
|
||||
}
|
||||
} else {
|
||||
if (main_layers_sticky(layer) == eStickyOnceDown) {
|
||||
// When releasing this sticky key, pop the layer always
|
||||
main_layers_disable(layer);
|
||||
|
||||
if (!main_arg_any_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, eStickyOnceUp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// device
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* [name]
|
||||
* Jump to Bootloader
|
||||
*
|
||||
* [description]
|
||||
* For reflashing the controller
|
||||
*/
|
||||
|
||||
// from PJRC (slightly modified)
|
||||
// <http://www.pjrc.com/teensy/jump_to_bootloader.html>
|
||||
void kbfun_jump_to_bootloader(void) {
|
||||
// --- for all Teensy boards ---
|
||||
|
||||
cli();
|
||||
|
||||
// disable watchdog, if enabled
|
||||
// disable all peripherals
|
||||
UDCON = 1;
|
||||
USBCON = (1<<FRZCLK); // disable USB
|
||||
UCSR1B = 0;
|
||||
_delay_ms(5);
|
||||
|
||||
// --- Teensy 2.0 specific ---
|
||||
|
||||
EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0; ADCSRA = 0;
|
||||
TIMSK0 = 0; TIMSK1 = 0; TIMSK3 = 0; TIMSK4 = 0; UCSR1B = 0; TWCR = 0;
|
||||
DDRB = 0; DDRC = 0; DDRD = 0; DDRE = 0; DDRF = 0; TWCR = 0;
|
||||
PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; PORTF = 0;
|
||||
asm volatile("jmp 0x7E00");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// special
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Generate a 'shift' press or release before the normal keypress or release
|
||||
*/
|
||||
void kbfun_shift_press_release(void) {
|
||||
_kbfun_press_release(main_arg_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(main_arg_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 (!main_arg_is_pressed) { keys_pressed--; }
|
||||
|
||||
// take care of the key that was actually pressed
|
||||
_kbfun_press_release(main_arg_is_pressed, keycode);
|
||||
|
||||
// take care of capslock (only on the press of the 2nd key)
|
||||
if (keys_pressed == 1 && main_arg_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 (main_arg_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(main_arg_is_pressed, keycode);
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* key functions : public exports
|
||||
* ----------------------------------------------------------------------------
|
||||
* 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>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <avr/interrupt.h>
|
||||
#include <util/delay.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "../main.h"
|
||||
#include "../keyboard/layout.h"
|
||||
#include "../lib/keyboard.h"
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
// basic
|
||||
void kbfun_press_release (void);
|
||||
void kbfun_press_release_preserve_sticky (void);
|
||||
void kbfun_toggle (void);
|
||||
void kbfun_transparent (void);
|
||||
|
||||
// layer functions
|
||||
void kbfun_layer_enable (void);
|
||||
void kbfun_layer_sticky (void);
|
||||
void kbfun_layer_disable (void);
|
||||
|
||||
// device
|
||||
void kbfun_jump_to_bootloader (void);
|
||||
|
||||
// special
|
||||
void kbfun_shift_press_release (void);
|
||||
void kbfun_control_press_release (void);
|
||||
void kbfun_2_keys_capslock_press_release (void);
|
||||
void kbfun_mediakey_press_release (void);
|
||||
|
||||
// private
|
||||
void _kbfun_press_release (bool press, uint8_t keycode);
|
||||
bool _kbfun_is_pressed (uint8_t keycode);
|
||||
void _kbfun_mediakey_press_release (bool press, uint8_t keycode);
|
||||
uint8_t _kbfun_get_keycode (void);
|
||||
|
||||
// device
|
||||
void kbfun_jump_to_bootloader(void);
|
394
src/main.c
394
src/main.c
|
@ -7,11 +7,23 @@
|
|||
* ------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "./main.h"
|
||||
#include <avr/interrupt.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdint.h>
|
||||
#include <util/delay.h>
|
||||
#include <util/delay.h>
|
||||
#include "./keyboard/controller.h"
|
||||
#include "./keyboard/keyboard.h"
|
||||
#include "./keyboard/layout_gen.h"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#define KB_ROWS 6 // must match real life
|
||||
#define KB_COLUMNS 14 // must match real life
|
||||
#define KB_LAYERS 10
|
||||
|
||||
static bool _main_kb_is_pressed[KB_ROWS][KB_COLUMNS];
|
||||
bool (*main_kb_is_pressed)[KB_ROWS][KB_COLUMNS] = &_main_kb_is_pressed;
|
||||
|
||||
|
@ -31,6 +43,16 @@ bool main_arg_was_pressed;
|
|||
bool main_arg_any_non_trans_key_pressed;
|
||||
bool main_arg_trans_key_pressed;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
typedef enum StickyState
|
||||
{
|
||||
eStickyNone,
|
||||
eStickyOnceDown,
|
||||
eStickyOnceUp,
|
||||
eStickyLock
|
||||
} StickyState;
|
||||
|
||||
// layer data
|
||||
struct layer {
|
||||
bool active;
|
||||
|
@ -198,3 +220,373 @@ void main_exec_key(void) {
|
|||
main_layers_disable_top();
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* 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(main_arg_layer, main_arg_row, main_arg_col);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// basic
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void kbfun_press_release() {
|
||||
if (!main_arg_trans_key_pressed) {
|
||||
main_arg_any_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(main_arg_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?
|
||||
main_arg_trans_key_pressed = true;
|
||||
main_arg_layer_offset++;
|
||||
main_arg_layer = main_layers_peek(main_arg_layer_offset);
|
||||
main_layers_pressed[main_arg_row][main_arg_col] = main_arg_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, main_arg_row, main_arg_col);
|
||||
|
||||
if (is_layer_enable(key_function)) {
|
||||
uint8_t enable_layer = kb_layout_get(layer, main_arg_row, main_arg_col);
|
||||
if (enable_layer <= max_layer) {
|
||||
main_layers_enable(enable_layer, eStickyNone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void layer_disable_all() {
|
||||
// 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, main_arg_row, main_arg_col);
|
||||
|
||||
if (is_layer_disable(key_function)) {
|
||||
uint8_t disable_layer = kb_layout_get(layer, main_arg_row, main_arg_col);
|
||||
main_layers_disable(disable_layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// 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 == eStickyOnceDown || topSticky == eStickyOnceUp) { */
|
||||
/* main_layers_disable_top(); */
|
||||
/* } */
|
||||
|
||||
layer_enable_upto(layer);
|
||||
}
|
||||
|
||||
// disable given layer
|
||||
void kbfun_layer_disable() {
|
||||
/* uint8_t layer = _kbfun_get_keycode(); */
|
||||
layer_disable_all();
|
||||
}
|
||||
|
||||
/*
|
||||
* 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();
|
||||
uint8_t topSticky = main_layers_top_sticky();
|
||||
|
||||
if (main_arg_is_pressed) {
|
||||
if (topLayer == layer) {
|
||||
// FIXME
|
||||
/* if (topSticky == eStickyOnceUp) { */
|
||||
/* main_layers_enable(layer, eStickyLock); */
|
||||
/* } */
|
||||
} else {
|
||||
// only the topmost layer on the stack should be in sticky once state
|
||||
if (topSticky == eStickyOnceDown || topSticky == eStickyOnceUp) {
|
||||
main_layers_disable_top();
|
||||
}
|
||||
main_layers_enable(layer, eStickyOnceDown);
|
||||
|
||||
// this should be the only place we care about this flag being cleared
|
||||
main_arg_any_non_trans_key_pressed = false;
|
||||
}
|
||||
} else {
|
||||
if (main_layers_sticky(layer) == eStickyOnceDown) {
|
||||
// When releasing this sticky key, pop the layer always
|
||||
main_layers_disable(layer);
|
||||
|
||||
if (!main_arg_any_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, eStickyOnceUp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// device
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* [name]
|
||||
* Jump to Bootloader
|
||||
*
|
||||
* [description]
|
||||
* For reflashing the controller
|
||||
*/
|
||||
|
||||
// from PJRC (slightly modified)
|
||||
// <http://www.pjrc.com/teensy/jump_to_bootloader.html>
|
||||
void kbfun_jump_to_bootloader(void) {
|
||||
// --- for all Teensy boards ---
|
||||
|
||||
cli();
|
||||
|
||||
// disable watchdog, if enabled
|
||||
// disable all peripherals
|
||||
UDCON = 1;
|
||||
USBCON = (1<<FRZCLK); // disable USB
|
||||
UCSR1B = 0;
|
||||
_delay_ms(5);
|
||||
|
||||
// --- Teensy 2.0 specific ---
|
||||
|
||||
EIMSK = 0; PCICR = 0; SPCR = 0; ACSR = 0; EECR = 0; ADCSRA = 0;
|
||||
TIMSK0 = 0; TIMSK1 = 0; TIMSK3 = 0; TIMSK4 = 0; UCSR1B = 0; TWCR = 0;
|
||||
DDRB = 0; DDRC = 0; DDRD = 0; DDRE = 0; DDRF = 0; TWCR = 0;
|
||||
PORTB = 0; PORTC = 0; PORTD = 0; PORTE = 0; PORTF = 0;
|
||||
asm volatile("jmp 0x7E00");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// special
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* Generate a 'shift' press or release before the normal keypress or release
|
||||
*/
|
||||
void kbfun_shift_press_release(void) {
|
||||
_kbfun_press_release(main_arg_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(main_arg_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 (!main_arg_is_pressed) { keys_pressed--; }
|
||||
|
||||
// take care of the key that was actually pressed
|
||||
_kbfun_press_release(main_arg_is_pressed, keycode);
|
||||
|
||||
// take care of capslock (only on the press of the 2nd key)
|
||||
if (keys_pressed == 1 && main_arg_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 (main_arg_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(main_arg_is_pressed, keycode);
|
||||
}
|
||||
|
|
49
src/main.h
49
src/main.h
|
@ -1,49 +0,0 @@
|
|||
/* ----------------------------------------------------------------------------
|
||||
* main() : functions and data that may be useful externally
|
||||
* ----------------------------------------------------------------------------
|
||||
* 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>
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <util/delay.h>
|
||||
#include "./lib/keyfunctions.h"
|
||||
#include "./keyboard/controller.h"
|
||||
#include "./keyboard/layout.h"
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
typedef enum StickyState
|
||||
{
|
||||
eStickyNone,
|
||||
eStickyOnceDown,
|
||||
eStickyOnceUp,
|
||||
eStickyLock
|
||||
} StickyState;
|
||||
|
||||
extern bool (*main_kb_is_pressed)[KB_ROWS][KB_COLUMNS];
|
||||
extern bool (*main_kb_was_pressed)[KB_ROWS][KB_COLUMNS];
|
||||
extern uint8_t main_layers_pressed[KB_ROWS][KB_COLUMNS];
|
||||
extern uint8_t main_arg_layer;
|
||||
extern uint8_t main_arg_layer_offset;
|
||||
extern uint8_t main_arg_row;
|
||||
extern uint8_t main_arg_col;
|
||||
extern bool main_arg_is_pressed;
|
||||
extern bool main_arg_was_pressed;
|
||||
extern bool main_arg_any_non_trans_key_pressed;
|
||||
extern bool main_arg_trans_key_pressed;
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
void main_exec_key (void);
|
||||
|
||||
uint8_t main_layers_top_layer (void);
|
||||
uint8_t main_layers_top_sticky (void);
|
||||
uint8_t main_layers_sticky (uint8_t layer);
|
||||
void main_layers_enable (uint8_t layer, uint8_t sticky);
|
||||
void main_layers_disable (uint8_t layer);
|
||||
void main_layers_disable_top (void);
|
Loading…
Reference in New Issue