diff options
author | Joel Challis <git@zvecr.com> | 2021-08-18 00:18:58 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-18 00:18:58 +0100 |
commit | b8e913c8db73ebf890e4604ee41991a34354a600 (patch) | |
tree | 258035f8fda9f83f08a309f6cd608b9c61476678 /quantum | |
parent | 96e2b13d1de227cdc2b918fb0292bd832d346a25 (diff) | |
download | qmk_firmware-b8e913c8db73ebf890e4604ee41991a34354a600.tar.gz qmk_firmware-b8e913c8db73ebf890e4604ee41991a34354a600.zip |
Migrate platform independent code from tmk_core -> quantum (#13673)
* Migrate action|keyboard|keycode|eeconfig from tmk_core -> quantum
Diffstat (limited to 'quantum')
-rw-r--r-- | quantum/action.c | 1126 | ||||
-rw-r--r-- | quantum/action.h | 132 | ||||
-rw-r--r-- | quantum/action_code.h | 308 | ||||
-rw-r--r-- | quantum/action_layer.c | 279 | ||||
-rw-r--r-- | quantum/action_layer.h | 147 | ||||
-rw-r--r-- | quantum/action_macro.c | 93 | ||||
-rw-r--r-- | quantum/action_macro.h | 123 | ||||
-rw-r--r-- | quantum/action_tapping.c | 456 | ||||
-rw-r--r-- | quantum/action_tapping.h | 42 | ||||
-rw-r--r-- | quantum/action_util.c | 455 | ||||
-rw-r--r-- | quantum/action_util.h | 105 | ||||
-rw-r--r-- | quantum/eeconfig.c | 211 | ||||
-rw-r--r-- | quantum/eeconfig.h | 113 | ||||
-rw-r--r-- | quantum/keyboard.c | 569 | ||||
-rw-r--r-- | quantum/keyboard.h | 90 | ||||
-rw-r--r-- | quantum/keycode.h | 560 | ||||
-rw-r--r-- | quantum/via.h | 2 |
17 files changed, 4810 insertions, 1 deletions
diff --git a/quantum/action.c b/quantum/action.c new file mode 100644 index 0000000000..d19fd2a045 --- /dev/null +++ b/quantum/action.c @@ -0,0 +1,1126 @@ +/* +Copyright 2012,2013 Jun Wako <wakojun@gmail.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "host.h" +#include "keycode.h" +#include "keyboard.h" +#include "mousekey.h" +#include "command.h" +#include "led.h" +#include "action_layer.h" +#include "action_tapping.h" +#include "action_macro.h" +#include "action_util.h" +#include "action.h" +#include "wait.h" + +#ifdef BACKLIGHT_ENABLE +# include "backlight.h" +#endif + +#ifdef DEBUG_ACTION +# include "debug.h" +#else +# include "nodebug.h" +#endif + +#ifdef POINTING_DEVICE_ENABLE +# include "pointing_device.h" +#endif + +int tp_buttons; + +#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) +int retro_tapping_counter = 0; +#endif + +#ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY +__attribute__((weak)) bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record) { return false; } +#endif + +#ifdef RETRO_TAPPING_PER_KEY +__attribute__((weak)) bool get_retro_tapping(uint16_t keycode, keyrecord_t *record) { return false; } +#endif + +__attribute__((weak)) bool pre_process_record_quantum(keyrecord_t *record) { return true; } + +#ifndef TAP_CODE_DELAY +# define TAP_CODE_DELAY 0 +#endif +#ifndef TAP_HOLD_CAPS_DELAY +# define TAP_HOLD_CAPS_DELAY 80 +#endif +/** \brief Called to execute an action. + * + * FIXME: Needs documentation. + */ +void action_exec(keyevent_t event) { + if (!IS_NOEVENT(event)) { + dprint("\n---- action_exec: start -----\n"); + dprint("EVENT: "); + debug_event(event); + dprintln(); +#if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) + retro_tapping_counter++; +#endif + } + + if (event.pressed) { + // clear the potential weak mods left by previously pressed keys + clear_weak_mods(); + } + +#ifdef SWAP_HANDS_ENABLE + if (!IS_NOEVENT(event)) { + process_hand_swap(&event); + } +#endif + + keyrecord_t record = {.event = event}; + +#ifndef NO_ACTION_ONESHOT +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + if (has_oneshot_layer_timed_out()) { + clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); + } + if (has_oneshot_mods_timed_out()) { + clear_oneshot_mods(); + } +# ifdef SWAP_HANDS_ENABLE + if (has_oneshot_swaphands_timed_out()) { + clear_oneshot_swaphands(); + } +# endif +# endif +#endif + +#ifndef NO_ACTION_TAPPING + if (IS_NOEVENT(record.event) || pre_process_record_quantum(&record)) { + action_tapping_process(record); + } +#else + if (IS_NOEVENT(record.event) || pre_process_record_quantum(&record)) { + process_record(&record); + } + if (!IS_NOEVENT(record.event)) { + dprint("processed: "); + debug_record(record); + dprintln(); + } +#endif +} + +#ifdef SWAP_HANDS_ENABLE +bool swap_hands = false; +bool swap_held = false; + +/** \brief Process Hand Swap + * + * FIXME: Needs documentation. + */ +void process_hand_swap(keyevent_t *event) { + static swap_state_row_t swap_state[MATRIX_ROWS]; + + keypos_t pos = event->key; + swap_state_row_t col_bit = (swap_state_row_t)1 << pos.col; + bool do_swap = event->pressed ? swap_hands : swap_state[pos.row] & (col_bit); + + if (do_swap) { + event->key.row = pgm_read_byte(&hand_swap_config[pos.row][pos.col].row); + event->key.col = pgm_read_byte(&hand_swap_config[pos.row][pos.col].col); + swap_state[pos.row] |= col_bit; + } else { + swap_state[pos.row] &= ~(col_bit); + } +} +#endif + +#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE) +bool disable_action_cache = false; + +void process_record_nocache(keyrecord_t *record) { + disable_action_cache = true; + process_record(record); + disable_action_cache = false; +} +#else +void process_record_nocache(keyrecord_t *record) { process_record(record); } +#endif + +__attribute__((weak)) bool process_record_quantum(keyrecord_t *record) { return true; } + +__attribute__((weak)) void post_process_record_quantum(keyrecord_t *record) {} + +#ifndef NO_ACTION_TAPPING +/** \brief Allows for handling tap-hold actions immediately instead of waiting for TAPPING_TERM or another keypress. + * + * FIXME: Needs documentation. + */ +void process_record_tap_hint(keyrecord_t *record) { + action_t action = layer_switch_get_action(record->event.key); + + switch (action.kind.id) { +# ifdef SWAP_HANDS_ENABLE + case ACT_SWAP_HANDS: + switch (action.swap.code) { + case OP_SH_ONESHOT: + break; + case OP_SH_TAP_TOGGLE: + default: + swap_hands = !swap_hands; + swap_held = true; + } + break; +# endif + } +} +#endif + +/** \brief Take a key event (key press or key release) and processes it. + * + * FIXME: Needs documentation. + */ +void process_record(keyrecord_t *record) { + if (IS_NOEVENT(record->event)) { + return; + } + + if (!process_record_quantum(record)) { +#ifndef NO_ACTION_ONESHOT + if (is_oneshot_layer_active() && record->event.pressed) { + clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); + } +#endif + return; + } + + process_record_handler(record); + post_process_record_quantum(record); +} + +void process_record_handler(keyrecord_t *record) { +#ifdef COMBO_ENABLE + action_t action; + if (record->keycode) { + action = action_for_keycode(record->keycode); + } else { + action = store_or_get_action(record->event.pressed, record->event.key); + } +#else + action_t action = store_or_get_action(record->event.pressed, record->event.key); +#endif + dprint("ACTION: "); + debug_action(action); +#ifndef NO_ACTION_LAYER + dprint(" layer_state: "); + layer_debug(); + dprint(" default_layer_state: "); + default_layer_debug(); +#endif + dprintln(); + + process_action(record, action); +} + +#if defined(PS2_MOUSE_ENABLE) || defined(POINTING_DEVICE_ENABLE) +void register_button(bool pressed, enum mouse_buttons button) { +# ifdef PS2_MOUSE_ENABLE + tp_buttons = pressed ? tp_buttons | button : tp_buttons & ~button; +# endif +# ifdef POINTING_DEVICE_ENABLE + report_mouse_t currentReport = pointing_device_get_report(); + currentReport.buttons = pressed ? currentReport.buttons | button : currentReport.buttons & ~button; + pointing_device_set_report(currentReport); +# endif +} +#endif + +/** \brief Take an action and processes it. + * + * FIXME: Needs documentation. + */ +void process_action(keyrecord_t *record, action_t action) { + keyevent_t event = record->event; +#ifndef NO_ACTION_TAPPING + uint8_t tap_count = record->tap.count; +#endif + +#ifndef NO_ACTION_ONESHOT + bool do_release_oneshot = false; + // notice we only clear the one shot layer if the pressed key is not a modifier. + if (is_oneshot_layer_active() && event.pressed && (action.kind.id == ACT_USAGE || !IS_MOD(action.key.code)) +# ifdef SWAP_HANDS_ENABLE + && !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT) +# endif + ) { + clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); + do_release_oneshot = !is_oneshot_layer_active(); + } +#endif + + switch (action.kind.id) { + /* Key and Mods */ + case ACT_LMODS: + case ACT_RMODS: { + uint8_t mods = (action.kind.id == ACT_LMODS) ? action.key.mods : action.key.mods << 4; + if (event.pressed) { + if (mods) { + if (IS_MOD(action.key.code) || action.key.code == KC_NO) { + // e.g. LSFT(KC_LGUI): we don't want the LSFT to be weak as it would make it useless. + // This also makes LSFT(KC_LGUI) behave exactly the same as LGUI(KC_LSFT). + // Same applies for some keys like KC_MEH which are declared as MEH(KC_NO). + add_mods(mods); + } else { + add_weak_mods(mods); + } + send_keyboard_report(); + } + register_code(action.key.code); + } else { + unregister_code(action.key.code); + if (mods) { + if (IS_MOD(action.key.code) || action.key.code == KC_NO) { + del_mods(mods); + } else { + del_weak_mods(mods); + } + send_keyboard_report(); + } + } + } break; +#ifndef NO_ACTION_TAPPING + case ACT_LMODS_TAP: + case ACT_RMODS_TAP: { + uint8_t mods = (action.kind.id == ACT_LMODS_TAP) ? action.key.mods : action.key.mods << 4; + switch (action.layer_tap.code) { +# ifndef NO_ACTION_ONESHOT + case MODS_ONESHOT: + // Oneshot modifier + if (event.pressed) { + if (tap_count == 0) { + dprint("MODS_TAP: Oneshot: 0\n"); + register_mods(mods | get_oneshot_mods()); + } else if (tap_count == 1) { + dprint("MODS_TAP: Oneshot: start\n"); + set_oneshot_mods(mods | get_oneshot_mods()); +# if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1 + } else if (tap_count == ONESHOT_TAP_TOGGLE) { + dprint("MODS_TAP: Toggling oneshot"); + clear_oneshot_mods(); + set_oneshot_locked_mods(mods); + register_mods(mods); +# endif + } else { + register_mods(mods | get_oneshot_mods()); + } + } else { + if (tap_count == 0) { + clear_oneshot_mods(); + unregister_mods(mods); + } else if (tap_count == 1) { + // Retain Oneshot mods +# if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1 + if (mods & get_mods()) { + clear_oneshot_locked_mods(); + clear_oneshot_mods(); + unregister_mods(mods); + } + } else if (tap_count == ONESHOT_TAP_TOGGLE) { + // Toggle Oneshot Layer +# endif + } else { + clear_oneshot_mods(); + unregister_mods(mods); + } + } + break; +# endif + case MODS_TAP_TOGGLE: + if (event.pressed) { + if (tap_count <= TAPPING_TOGGLE) { + register_mods(mods); + } + } else { + if (tap_count < TAPPING_TOGGLE) { + unregister_mods(mods); + } + } + break; + default: + if (event.pressed) { + if (tap_count > 0) { +# if !defined(IGNORE_MOD_TAP_INTERRUPT) || defined(IGNORE_MOD_TAP_INTERRUPT_PER_KEY) + if ( +# ifdef IGNORE_MOD_TAP_INTERRUPT_PER_KEY + !get_ignore_mod_tap_interrupt(get_event_keycode(record->event, false), record) && +# endif + record->tap.interrupted) { + dprint("mods_tap: tap: cancel: add_mods\n"); + // ad hoc: set 0 to cancel tap + record->tap.count = 0; + register_mods(mods); + } else +# endif + { + dprint("MODS_TAP: Tap: register_code\n"); + register_code(action.key.code); + } + } else { + dprint("MODS_TAP: No tap: add_mods\n"); + register_mods(mods); + } + } else { + if (tap_count > 0) { + dprint("MODS_TAP: Tap: unregister_code\n"); + if (action.layer_tap.code == KC_CAPS) { + wait_ms(TAP_HOLD_CAPS_DELAY); + } else { + wait_ms(TAP_CODE_DELAY); + } + unregister_code(action.key.code); + } else { + dprint("MODS_TAP: No tap: add_mods\n"); + unregister_mods(mods); + } + } + break; + } + } break; +#endif +#ifdef EXTRAKEY_ENABLE + /* other HID usage */ + case ACT_USAGE: + switch (action.usage.page) { + case PAGE_SYSTEM: + if (event.pressed) { + host_system_send(action.usage.code); + } else { + host_system_send(0); + } + break; + case PAGE_CONSUMER: + if (event.pressed) { + host_consumer_send(action.usage.code); + } else { + host_consumer_send(0); + } + break; + } + break; +#endif +#ifdef MOUSEKEY_ENABLE + /* Mouse key */ + case ACT_MOUSEKEY: + if (event.pressed) { + mousekey_on(action.key.code); + } else { + mousekey_off(action.key.code); + } + switch (action.key.code) { +# if defined(PS2_MOUSE_ENABLE) || defined(POINTING_DEVICE_ENABLE) +# ifdef POINTING_DEVICE_ENABLE + case KC_MS_BTN1 ... KC_MS_BTN8: +# else + case KC_MS_BTN1 ... KC_MS_BTN3: +# endif + register_button(event.pressed, MOUSE_BTN_MASK(action.key.code - KC_MS_BTN1)); + break; +# endif + default: + mousekey_send(); + break; + } + break; +#endif +#ifndef NO_ACTION_LAYER + case ACT_LAYER: + if (action.layer_bitop.on == 0) { + /* Default Layer Bitwise Operation */ + if (!event.pressed) { + uint8_t shift = action.layer_bitop.part * 4; + layer_state_t bits = ((layer_state_t)action.layer_bitop.bits) << shift; + layer_state_t mask = (action.layer_bitop.xbit) ? ~(((layer_state_t)0xf) << shift) : 0; + switch (action.layer_bitop.op) { + case OP_BIT_AND: + default_layer_and(bits | mask); + break; + case OP_BIT_OR: + default_layer_or(bits | mask); + break; + case OP_BIT_XOR: + default_layer_xor(bits | mask); + break; + case OP_BIT_SET: + default_layer_set(bits | mask); + break; + } + } + } else { + /* Layer Bitwise Operation */ + if (event.pressed ? (action.layer_bitop.on & ON_PRESS) : (action.layer_bitop.on & ON_RELEASE)) { + uint8_t shift = action.layer_bitop.part * 4; + layer_state_t bits = ((layer_state_t)action.layer_bitop.bits) << shift; + layer_state_t mask = (action.layer_bitop.xbit) ? ~(((layer_state_t)0xf) << shift) : 0; + switch (action.layer_bitop.op) { + case OP_BIT_AND: + layer_and(bits | mask); + break; + case OP_BIT_OR: + layer_or(bits | mask); + break; + case OP_BIT_XOR: + layer_xor(bits | mask); + break; + case OP_BIT_SET: + layer_state_set(bits | mask); + break; + } + } + } + break; + case ACT_LAYER_MODS: + if (event.pressed) { + layer_on(action.layer_mods.layer); + register_mods(action.layer_mods.mods); + } else { + unregister_mods(action.layer_mods.mods); + layer_off(action.layer_mods.layer); + } + break; +# ifndef NO_ACTION_TAPPING + case ACT_LAYER_TAP: + case ACT_LAYER_TAP_EXT: + switch (action.layer_tap.code) { + case OP_TAP_TOGGLE: + /* tap toggle */ + if (event.pressed) { + if (tap_count < TAPPING_TOGGLE) { + layer_invert(action.layer_tap.val); + } + } else { + if (tap_count <= TAPPING_TOGGLE) { + layer_invert(action.layer_tap.val); + } + } + break; + case OP_ON_OFF: + event.pressed ? layer_on(action.layer_tap.val) : layer_off(action.layer_tap.val); + break; + case OP_OFF_ON: + event.pressed ? layer_off(action.layer_tap.val) : layer_on(action.layer_tap.val); + break; + case OP_SET_CLEAR: + event.pressed ? layer_move(action.layer_tap.val) : layer_clear(); + break; +# ifndef NO_ACTION_ONESHOT + case OP_ONESHOT: + // Oneshot modifier +# if defined(ONESHOT_TAP_TOGGLE) && ONESHOT_TAP_TOGGLE > 1 + do_release_oneshot = false; + if (event.pressed) { + del_mods(get_oneshot_locked_mods()); + if (get_oneshot_layer_state() == ONESHOT_TOGGLED) { + reset_oneshot_layer(); + layer_off(action.layer_tap.val); + break; + } else if (tap_count < ONESHOT_TAP_TOGGLE) { + layer_on(action.layer_tap.val); + set_oneshot_layer(action.layer_tap.val, ONESHOT_START); + } + } else { + add_mods(get_oneshot_locked_mods()); + if (tap_count >= ONESHOT_TAP_TOGGLE) { + reset_oneshot_layer(); + clear_oneshot_locked_mods(); + set_oneshot_layer(action.layer_tap.val, ONESHOT_TOGGLED); + } else { + clear_oneshot_layer_state(ONESHOT_PRESSED); + } + } +# else + if (event.pressed) { + layer_on(action.layer_tap.val); + set_oneshot_layer(action.layer_tap.val, ONESHOT_START); + } else { + clear_oneshot_layer_state(ONESHOT_PRESSED); + if (tap_count > 1) { + clear_oneshot_layer_state(ONESHOT_OTHER_KEY_PRESSED); + } + } +# endif + break; +# endif + default: + /* tap key */ + if (event.pressed) { + if (tap_count > 0) { + dprint("KEYMAP_TAP_KEY: Tap: register_code\n"); + register_code(action.layer_tap.code); + } else { + dprint("KEYMAP_TAP_KEY: No tap: On on press\n"); + layer_on(action.layer_tap.val); + } + } else { + if (tap_count > 0) { + dprint("KEYMAP_TAP_KEY: Tap: unregister_code\n"); + if (action.layer_tap.code == KC_CAPS) { + wait_ms(TAP_HOLD_CAPS_DELAY); + } else { + wait_ms(TAP_CODE_DELAY); + } + unregister_code(action.layer_tap.code); + } else { + dprint("KEYMAP_TAP_KEY: No tap: Off on release\n"); + layer_off(action.layer_tap.val); + } + } + break; + } + break; +# endif +#endif + /* Extentions */ +#ifndef NO_ACTION_MACRO + case ACT_MACRO: + action_macro_play(action_get_macro(record, action.func.id, action.func.opt)); + break; +#endif +#ifdef SWAP_HANDS_ENABLE + case ACT_SWAP_HANDS: + switch (action.swap.code) { + case OP_SH_TOGGLE: + if (event.pressed) { + swap_hands = !swap_hands; + } + break; + case OP_SH_ON_OFF: + swap_hands = event.pressed; + break; + case OP_SH_OFF_ON: + swap_hands = !event.pressed; + break; + case OP_SH_ON: + if (!event.pressed) { + swap_hands = true; + } + break; + case OP_SH_OFF: + if (!event.pressed) { + swap_hands = false; + } + break; +# ifndef NO_ACTION_ONESHOT + case OP_SH_ONESHOT: + if (event.pressed) { + set_oneshot_swaphands(); + } else { + release_oneshot_swaphands(); + } + break; +# endif + +# ifndef NO_ACTION_TAPPING + case OP_SH_TAP_TOGGLE: + /* tap toggle */ + + if (event.pressed) { + if (swap_held) { + swap_held = false; + } else { + swap_hands = !swap_hands; + } + } else { + if (tap_count < TAPPING_TOGGLE) { + swap_hands = !swap_hands; + } + } + break; + default: + /* tap key */ + if (tap_count > 0) { + if (swap_held) { + swap_hands = !swap_hands; // undo hold set up in _tap_hint + swap_held = false; + } + if (event.pressed) { + register_code(action.swap.code); + } else { + wait_ms(TAP_CODE_DELAY); + unregister_code(action.swap.code); + *record = (keyrecord_t){}; // hack: reset tap mode + } + } else { + if (swap_held && !event.pressed) { + swap_hands = !swap_hands; // undo hold set up in _tap_hint + swap_held = false; + } + } +# endif + } +#endif +#ifndef NO_ACTION_FUNCTION + case ACT_FUNCTION: + action_function(record, action.func.id, action.func.opt); + break; +#endif + default: + break; + } + +#ifndef NO_ACTION_LAYER + // if this event is a layer action, update the leds + switch (action.kind.id) { + case ACT_LAYER: + case ACT_LAYER_MODS: +# ifndef NO_ACTION_TAPPING + case ACT_LAYER_TAP: + case ACT_LAYER_TAP_EXT: +# endif + led_set(host_keyboard_leds()); + break; + default: + break; + } +#endif + +#ifndef NO_ACTION_TAPPING +# if defined(RETRO_TAPPING) || defined(RETRO_TAPPING_PER_KEY) + if (!is_tap_action(action)) { + retro_tapping_counter = 0; + } else { + if (event.pressed) { + if (tap_count > 0) { + retro_tapping_counter = 0; + } + } else { + if (tap_count > 0) { + retro_tapping_counter = 0; + } else { + if ( +# ifdef RETRO_TAPPING_PER_KEY + get_retro_tapping(get_event_keycode(record->event, false), record) && +# endif + retro_tapping_counter == 2) { + tap_code(action.layer_tap.code); + } + retro_tapping_counter = 0; + } + } + } +# endif +#endif + +#ifdef SWAP_HANDS_ENABLE +# ifndef NO_ACTION_ONESHOT + if (event.pressed && !(action.kind.id == ACT_SWAP_HANDS && action.swap.code == OP_SH_ONESHOT)) { + use_oneshot_swaphands(); + } +# endif +#endif + +#ifndef NO_ACTION_ONESHOT + /* Because we switch layers after a oneshot event, we need to release the + * key before we leave the layer or no key up event will be generated. + */ + if (do_release_oneshot && !(get_oneshot_layer_state() & ONESHOT_PRESSED)) { + record->event.pressed = false; + layer_on(get_oneshot_layer()); + process_record(record); + layer_off(get_oneshot_layer()); + } +#endif +} + +/** \brief Utilities for actions. (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +void register_code(uint8_t code) { + if (code == KC_NO) { + return; + } +#ifdef LOCKING_SUPPORT_ENABLE + else if (KC_LOCKING_CAPS == code) { +# ifdef LOCKING_RESYNC_ENABLE + // Resync: ignore if caps lock already is on + if (host_keyboard_leds() & (1 << USB_LED_CAPS_LOCK)) return; +# endif + add_key(KC_CAPSLOCK); + send_keyboard_report(); + wait_ms(100); + del_key(KC_CAPSLOCK); + send_keyboard_report(); + } + + else if (KC_LOCKING_NUM == code) { +# ifdef LOCKING_RESYNC_ENABLE + if (host_keyboard_leds() & (1 << USB_LED_NUM_LOCK)) return; +# endif + add_key(KC_NUMLOCK); + send_keyboard_report(); + wait_ms(100); + del_key(KC_NUMLOCK); + send_keyboard_report(); + } + + else if (KC_LOCKING_SCROLL == code) { +# ifdef LOCKING_RESYNC_ENABLE + if (host_keyboard_leds() & (1 << USB_LED_SCROLL_LOCK)) return; +# endif + add_key(KC_SCROLLLOCK); + send_keyboard_report(); + wait_ms(100); + del_key(KC_SCROLLLOCK); + send_keyboard_report(); + } +#endif + + else if IS_KEY (code) { + // TODO: should push command_proc out of this block? + if (command_proc(code)) return; + +#ifndef NO_ACTION_ONESHOT +/* TODO: remove + if (oneshot_state.mods && !oneshot_state.disabled) { + uint8_t tmp_mods = get_mods(); + add_mods(oneshot_state.mods); + + add_key(code); + send_keyboard_report(); + + set_mods(tmp_mods); + send_keyboard_report(); + oneshot_cancel(); + } else +*/ +#endif + { + // Force a new key press if the key is already pressed + // without this, keys with the same keycode, but different + // modifiers will be reported incorrectly, see issue #1708 + if (is_key_pressed(keyboard_report, code)) { + del_key(code); + send_keyboard_report(); + } + add_key(code); + send_keyboard_report(); + } + } else if IS_MOD (code) { + add_mods(MOD_BIT(code)); + send_keyboard_report(); + } +#ifdef EXTRAKEY_ENABLE + else if IS_SYSTEM (code) { + host_system_send(KEYCODE2SYSTEM(code)); + } else if IS_CONSUMER (code) { + host_consumer_send(KEYCODE2CONSUMER(code)); + } +#endif +#ifdef MOUSEKEY_ENABLE + else if IS_MOUSEKEY (code) { + mousekey_on(code); + mousekey_send(); + } +#endif +} + +/** \brief Utilities for actions. (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +void unregister_code(uint8_t code) { + if (code == KC_NO) { + return; + } +#ifdef LOCKING_SUPPORT_ENABLE + else if (KC_LOCKING_CAPS == code) { +# ifdef LOCKING_RESYNC_ENABLE + // Resync: ignore if caps lock already is off + if (!(host_keyboard_leds() & (1 << USB_LED_CAPS_LOCK))) return; +# endif + add_key(KC_CAPSLOCK); + send_keyboard_report(); + del_key(KC_CAPSLOCK); + send_keyboard_report(); + } + + else if (KC_LOCKING_NUM == code) { +# ifdef LOCKING_RESYNC_ENABLE + if (!(host_keyboard_leds() & (1 << USB_LED_NUM_LOCK))) return; +# endif + add_key(KC_NUMLOCK); + send_keyboard_report(); + del_key(KC_NUMLOCK); + send_keyboard_report(); + } + + else if (KC_LOCKING_SCROLL == code) { +# ifdef LOCKING_RESYNC_ENABLE + if (!(host_keyboard_leds() & (1 << USB_LED_SCROLL_LOCK))) return; +# endif + add_key(KC_SCROLLLOCK); + send_keyboard_report(); + del_key(KC_SCROLLLOCK); + send_keyboard_report(); + } +#endif + + else if IS_KEY (code) { + del_key(code); + send_keyboard_report(); + } else if IS_MOD (code) { + del_mods(MOD_BIT(code)); + send_keyboard_report(); + } else if IS_SYSTEM (code) { + host_system_send(0); + } else if IS_CONSUMER (code) { + host_consumer_send(0); + } +#ifdef MOUSEKEY_ENABLE + else if IS_MOUSEKEY (code) { + mousekey_off(code); + mousekey_send(); + } +#endif +} + +/** \brief Tap a keycode with a delay. + * + * \param code The basic keycode to tap. + * \param delay The amount of time in milliseconds to leave the keycode registered, before unregistering it. + */ +void tap_code_delay(uint8_t code, uint16_t delay) { + register_code(code); + for (uint16_t i = delay; i > 0; i--) { + wait_ms(1); + } + unregister_code(code); +} + +/** \brief Tap a keycode with the default delay. + * + * \param code The basic keycode to tap. If `code` is `KC_CAPS`, the delay will be `TAP_HOLD_CAPS_DELAY`, otherwise `TAP_CODE_DELAY`, if defined. + */ +void tap_code(uint8_t code) { tap_code_delay(code, code == KC_CAPS ? TAP_HOLD_CAPS_DELAY : TAP_CODE_DELAY); } + +/** \brief Adds the given physically pressed modifiers and sends a keyboard report immediately. + * + * \param mods A bitfield of modifiers to register. + */ +void register_mods(uint8_t mods) { + if (mods) { + add_mods(mods); + send_keyboard_report(); + } +} + +/** \brief Removes the given physically pressed modifiers and sends a keyboard report immediately. + * + * \param mods A bitfield of modifiers to unregister. + */ +void unregister_mods(uint8_t mods) { + if (mods) { + del_mods(mods); + send_keyboard_report(); + } +} + +/** \brief Adds the given weak modifiers and sends a keyboard report immediately. + * + * \param mods A bitfield of modifiers to register. + */ +void register_weak_mods(uint8_t mods) { + if (mods) { + add_weak_mods(mods); + send_keyboard_report(); + } +} + +/** \brief Removes the given weak modifiers and sends a keyboard report immediately. + * + * \param mods A bitfield of modifiers to unregister. + */ +void unregister_weak_mods(uint8_t mods) { + if (mods) { + del_weak_mods(mods); + send_keyboard_report(); + } +} + +/** \brief Utilities for actions. (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +void clear_keyboard(void) { + clear_mods(); + clear_keyboard_but_mods(); +} + +/** \brief Utilities for actions. (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +void clear_keyboard_but_mods(void) { + clear_keys(); + clear_keyboard_but_mods_and_keys(); +} + +/** \brief Utilities for actions. (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +void clear_keyboard_but_mods_and_keys() { +#ifdef EXTRAKEY_ENABLE + host_system_send(0); + host_consumer_send(0); +#endif + clear_weak_mods(); + clear_macro_mods(); + send_keyboard_report(); +#ifdef MOUSEKEY_ENABLE + mousekey_clear(); + mousekey_send(); +#endif +} + +/** \brief Utilities for actions. (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +bool is_tap_key(keypos_t key) { + action_t action = layer_switch_get_action(key); + return is_tap_action(action); +} + +/** \brief Utilities for actions. (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +bool is_tap_record(keyrecord_t *record) { +#ifdef COMBO_ENABLE + action_t action; + if (record->keycode) { + action = action_for_keycode(record->keycode); + } else { + action = layer_switch_get_action(record->event.key); + } +#else + action_t action = layer_switch_get_action(record->event.key); +#endif + return is_tap_action(action); +} + +/** \brief Utilities for actions. (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +bool is_tap_action(action_t action) { + switch (action.kind.id) { + case ACT_LMODS_TAP: + case ACT_RMODS_TAP: + case ACT_LAYER_TAP: + case ACT_LAYER_TAP_EXT: + switch (action.layer_tap.code) { + case KC_NO ... KC_RGUI: + case OP_TAP_TOGGLE: + case OP_ONESHOT: + return true; + } + return false; + case ACT_SWAP_HANDS: + switch (action.swap.code) { + case KC_NO ... KC_RGUI: + case OP_SH_TAP_TOGGLE: + return true; + } + return false; + case ACT_MACRO: + case ACT_FUNCTION: + if (action.func.opt & FUNC_TAP) { + return true; + } + return false; + } + return false; +} + +/** \brief Debug print (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +void debug_event(keyevent_t event) { dprintf("%04X%c(%u)", (event.key.row << 8 | event.key.col), (event.pressed ? 'd' : 'u'), event.time); } +/** \brief Debug print (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +void debug_record(keyrecord_t record) { + debug_event(record.event); +#ifndef NO_ACTION_TAPPING + dprintf(":%u%c", record.tap.count, (record.tap.interrupted ? '-' : ' ')); +#endif +} + +/** \brief Debug print (FIXME: Needs better description) + * + * FIXME: Needs documentation. + */ +void debug_action(action_t action) { + switch (action.kind.id) { + case ACT_LMODS: + dprint("ACT_LMODS"); + break; + case ACT_RMODS: + dprint("ACT_RMODS"); + break; + case ACT_LMODS_TAP: + dprint("ACT_LMODS_TAP"); + break; + case ACT_RMODS_TAP: + dprint("ACT_RMODS_TAP"); + break; + case ACT_USAGE: + dprint("ACT_USAGE"); + break; + case ACT_MOUSEKEY: + dprint("ACT_MOUSEKEY"); + break; + case ACT_LAYER: + dprint("ACT_LAYER"); + break; + case ACT_LAYER_MODS: + dprint("ACT_LAYER_MODS"); + break; + case ACT_LAYER_TAP: + dprint("ACT_LAYER_TAP"); + break; + case ACT_LAYER_TAP_EXT: + dprint("ACT_LAYER_TAP_EXT"); + break; + case ACT_MACRO: + dprint("ACT_MACRO"); + break; + case ACT_FUNCTION: + dprint("ACT_FUNCTION"); + break; + case ACT_SWAP_HANDS: + dprint("ACT_SWAP_HANDS"); + break; + default: + dprint("UNKNOWN"); + break; + } + dprintf("[%X:%02X]", action.kind.param >> 8, action.kind.param & 0xff); +} diff --git a/quantum/action.h b/quantum/action.h new file mode 100644 index 0000000000..3d357b33b8 --- /dev/null +++ b/quantum/action.h @@ -0,0 +1,132 @@ +/* +Copyright 2012,2013 Jun Wako <wakojun@gmail.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> +#include "keyboard.h" +#include "keycode.h" +#include "action_code.h" +#include "action_macro.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Disable macro and function features when LTO is enabled, since they break */ +#ifdef LTO_ENABLE +# ifndef NO_ACTION_MACRO +# define NO_ACTION_MACRO +# endif +# ifndef NO_ACTION_FUNCTION +# define NO_ACTION_FUNCTION +# endif +#endif + +/* tapping count and state */ +typedef struct { + bool interrupted : 1; + bool reserved2 : 1; + bool reserved1 : 1; + bool reserved0 : 1; + uint8_t count : 4; +} tap_t; + +/* Key event container for recording */ +typedef struct { + keyevent_t event; +#ifndef NO_ACTION_TAPPING + tap_t tap; +#endif +#ifdef COMBO_ENABLE + uint16_t keycode; +#endif +} keyrecord_t; + +/* Execute action per keyevent */ +void action_exec(keyevent_t event); + +/* action for key */ +action_t action_for_key(uint8_t layer, keypos_t key); +action_t action_for_keycode(uint16_t keycode); + +/* macro */ +const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt); + +/* user defined special function */ +void action_function(keyrecord_t *record, uint8_t id, uint8_t opt); + +/* keyboard-specific key event (pre)processing */ +bool process_record_quantum(keyrecord_t *record); + +/* Utilities for actions. */ +#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE) +extern bool disable_action_cache; +#endif + +/* Code for handling one-handed key modifiers. */ +#ifdef SWAP_HANDS_ENABLE +extern bool swap_hands; +extern const keypos_t PROGMEM hand_swap_config[MATRIX_ROWS][MATRIX_COLS]; +# if (MATRIX_COLS <= 8) +typedef uint8_t swap_state_row_t; +# elif (MATRIX_COLS <= 16) +typedef uint16_t swap_state_row_t; +# elif (MATRIX_COLS <= 32) +typedef uint32_t swap_state_row_t; +# else +# error "MATRIX_COLS: invalid value" +# endif + +void process_hand_swap(keyevent_t *record); +#endif + +void process_record_nocache(keyrecord_t *record); +void process_record(keyrecord_t *record); +void process_record_handler(keyrecord_t *record); +void post_process_record_quantum(keyrecord_t *record); +void process_action(keyrecord_t *record, action_t action); +void register_code(uint8_t code); +void unregister_code(uint8_t code); +void tap_code(uint8_t code); +void tap_code_delay(uint8_t code, uint16_t delay); +void register_mods(uint8_t mods); +void unregister_mods(uint8_t mods); +void register_weak_mods(uint8_t mods); +void unregister_weak_mods(uint8_t mods); +// void set_mods(uint8_t mods); +void clear_keyboard(void); +void clear_keyboard_but_mods(void); +void clear_keyboard_but_mods_and_keys(void); +void layer_switch(uint8_t new_layer); +bool is_tap_key(keypos_t key); +bool is_tap_record(keyrecord_t *record); +bool is_tap_action(action_t action); + +#ifndef NO_ACTION_TAPPING +void process_record_tap_hint(keyrecord_t *record); +#endif + +/* debug */ +void debug_event(keyevent_t event); +void debug_record(keyrecord_t record); +void debug_action(action_t action); + +#ifdef __cplusplus +} +#endif diff --git a/quantum/action_code.h b/quantum/action_code.h new file mode 100644 index 0000000000..eb18c36ae8 --- /dev/null +++ b/quantum/action_code.h @@ -0,0 +1,308 @@ +/* +Copyright 2013 Jun Wako <wakojun@gmail.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +/** \brief Action codes + * + * 16bit code: action_kind(4bit) + action_parameter(12bit) + * + * Key Actions(00xx) + * ----------------- + * ACT_MODS(000r): + * 000r|0000|0000 0000 No action code + * 000r|0000|0000 0001 Transparent code + * 000r|0000| keycode Key + * 000r|mods|0000 0000 Modifiers + * 000r|mods| keycode Modifiers+Key(Modified key) + * r: Left/Right flag(Left:0, Right:1) + * + * ACT_MODS_TAP(001r): + * 001r|mods|0000 0000 Modifiers with OneShot + * 001r|mods|0000 0001 Modifiers with tap toggle + * 001r|mods|0000 00xx (reserved) + * 001r|mods| keycode Modifiers with Tap Key(Dual role) + * + * Other Keys(01xx) + * ---------------- + * ACT_USAGE(0100): TODO: Not needed? + * 0100|00| usage(10) System control(0x80) - General Desktop page(0x01) + * 0100|01| usage(10) Consumer control(0x01) - Consumer page(0x0C) + * 0100|10| usage(10) (reserved) + * 0100|11| usage(10) (reserved) + * + * ACT_MOUSEKEY(0101): TODO: Merge these two actions to conserve space? + * 0101|xxxx| keycode Mouse key + * + * ACT_SWAP_HANDS(0110): + * 0110|xxxx| keycode Swap hands (keycode on tap, or options) + * + * 0111|xxxx xxxx xxxx (reserved) + * + * Layer Actions(10xx) + * ------------------- + * ACT_LAYER(1000): + * 1000|oo00|pppE BBBB Default Layer Bitwise operation + * oo: operation(00:AND, 01:OR, 10:XOR, 11:SET) + * ppp: 4-bit chunk part(0-7) + * EBBBB: bits and extra bit + * 1000|ooee|pppE BBBB Layer Bitwise Operation + * oo: operation(00:AND, 01:OR, 10:XOR, 11:SET) + * ppp: 4-bit chunk part(0-7) + * EBBBB: bits and extra bit + * ee: on event(01:press, 10:release, 11:both) + * + * ACT_LAYER_MODS(1001): + * 1001|LLLL| mods Layer with modifiers held + * + * ACT_LAYER_TAP(101x): + * 101E|LLLL| keycode On/Off with tap key (0x00-DF)[TAP] + * 101E|LLLL|1110 mods On/Off with modifiers (0xE0-EF)[NOT TAP] + * 101E|LLLL|1111 0000 Invert with tap toggle (0xF0) [TAP] + * 101E|LLLL|1111 0001 On/Off (0xF1) [NOT TAP] + * 101E|LLLL|1111 0010 Off/On (0xF2) [NOT TAP] + * 101E|LLLL|1111 0011 Set/Clear (0xF3) [NOT TAP] + * 101E|LLLL|1111 0100 One Shot Layer (0xF4) [TAP] + * 101E|LLLL|1111 xxxx Reserved (0xF5-FF) + * ELLLL: layer 0-31(E: extra bit for layer 16-31) + * + * Extensions(11xx) + * ---------------- + * ACT_MACRO(1100): + * 1100|opt | id(8) Macro play? + * 1100|1111| id(8) Macro record? + * + * 1101|xxxx xxxx xxxx (reserved) + * 1110|xxxx xxxx xxxx (reserved) + * + * ACT_FUNCTION(1111): + * 1111| address(12) Function? + * 1111|opt | id(8) Function? + */ +enum action_kind_id { + /* Key Actions */ + ACT_MODS = 0b0000, + ACT_LMODS = 0b0000, + ACT_RMODS = 0b0001, + ACT_MODS_TAP = 0b0010, + ACT_LMODS_TAP = 0b0010, + ACT_RMODS_TAP = 0b0011, + /* Other Keys */ + ACT_USAGE = 0b0100, + ACT_MOUSEKEY = 0b0101, + /* One-hand Support */ + ACT_SWAP_HANDS = 0b0110, + /* Layer Actions */ + ACT_LAYER = 0b1000, + ACT_LAYER_MODS = 0b1001, + ACT_LAYER_TAP = 0b1010, /* Layer 0-15 */ + ACT_LAYER_TAP_EXT = 0b1011, /* Layer 16-31 */ + /* Extensions */ + ACT_MACRO = 0b1100, + ACT_FUNCTION = 0b1111 +}; + +/** \brief Action Code Struct + * + * NOTE: + * In avr-gcc bit field seems to be assigned from LSB(bit0) to MSB(bit15). + * AVR looks like a little endian in avr-gcc. + * Not portable across compiler/endianness? + * + * Byte order and bit order of 0x1234: + * Big endian: Little endian: + * -------------------- -------------------- + * FEDC BA98 7654 3210 0123 4567 89AB CDEF + * 0001 0010 0011 0100 0010 1100 0100 1000 + * 0x12 0x34 0x34 0x12 + */ +typedef union { + uint16_t code; + struct action_kind { + uint16_t param : 12; + uint8_t id : 4; + } kind; + struct action_key { + uint8_t code : 8; + uint8_t mods : 4; + uint8_t kind : 4; + } key; + struct action_layer_bitop { + uint8_t bits : 4; + uint8_t xbit : 1; + uint8_t part : 3; + uint8_t on : 2; + uint8_t op : 2; + uint8_t kind : 4; + } layer_bitop; + struct action_layer_mods { + uint8_t mods : 8; + uint8_t layer : 4; + uint8_t kind : 4; + } layer_mods; + struct action_layer_tap { + uint8_t code : 8; + uint8_t val : 5; + uint8_t kind : 3; + } layer_tap; + struct action_usage { + uint16_t code : 10; + uint8_t page : 2; + uint8_t kind : 4; + } usage; + struct action_function { + uint8_t id : 8; + uint8_t opt : 4; + uint8_t kind : 4; + } func; + struct action_swap { + uint8_t code : 8; + uint8_t opt : 4; + uint8_t kind : 4; + } swap; +} action_t; + +/* action utility */ +#define ACTION_NO 0 +#define ACTION_TRANSPARENT 1 +#define ACTION(kind, param) ((kind) << 12 | (param)) + +/** \brief Key Actions + * + * Mod bits: 43210 + * bit 0 ||||+- Control + * bit 1 |||+-- Shift + * bit 2 ||+--- Alt + * bit 3 |+---- Gui + * bit 4 +----- LR flag(Left:0, Right:1) + */ +enum mods_bit { + MOD_LCTL = 0x01, + MOD_LSFT = 0x02, + MOD_LALT = 0x04, + MOD_LGUI = 0x08, + MOD_RCTL = 0x11, + MOD_RSFT = 0x12, + MOD_RALT = 0x14, + MOD_RGUI = 0x18, +}; +enum mods_codes { + MODS_ONESHOT = 0x00, + MODS_TAP_TOGGLE = 0x01, +}; +#define ACTION_KEY(key) ACTION(ACT_MODS, (key)) +#define ACTION_MODS(mods) ACTION(ACT_MODS, ((mods)&0x1f) << 8 | 0) +#define ACTION_MODS_KEY(mods, key) ACTION(ACT_MODS, ((mods)&0x1f) << 8 | (key)) +#define ACTION_MODS_TAP_KEY(mods, key) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | (key)) +#define ACTION_MODS_ONESHOT(mods) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | MODS_ONESHOT) +#define ACTION_MODS_TAP_TOGGLE(mods) ACTION(ACT_MODS_TAP, ((mods)&0x1f) << 8 | MODS_TAP_TOGGLE) + +/** \brief Other Keys + */ +enum usage_pages { PAGE_SYSTEM, PAGE_CONSUMER }; +#define ACTION_USAGE_SYSTEM(id) ACTION(ACT_USAGE, PAGE_SYSTEM << 10 | (id)) +#define ACTION_USAGE_CONSUMER(id) ACTION(ACT_USAGE, PAGE_CONSUMER << 10 | (id)) +#define ACTION_MOUSEKEY(key) ACTION(ACT_MOUSEKEY, key) + +/** \brief Layer Actions + */ +enum layer_param_on { + ON_PRESS = 1, + ON_RELEASE = 2, + ON_BOTH = 3, +}; + +/** \brief Layer Actions + */ +enum layer_param_bit_op { + OP_BIT_AND = 0, + OP_BIT_OR = 1, + OP_BIT_XOR = 2, + OP_BIT_SET = 3, +}; + +/** \brief Layer Actions + */ +enum layer_param_tap_op { + OP_TAP_TOGGLE = 0xF0, + OP_ON_OFF, + OP_OFF_ON, + OP_SET_CLEAR, + OP_ONESHOT, +}; +#define ACTION_LAYER_BITOP(op, part, bits, on) ACTION(ACT_LAYER, (op) << 10 | (on) << 8 | (part) << 5 | ((bits)&0x1f)) +#define ACTION_LAYER_TAP(layer, key) ACTION(ACT_LAYER_TAP, (layer) << 8 | (key)) +/* Default Layer */ +#define ACTION_DEFAULT_LAYER_SET(layer) ACTION_DEFAULT_LAYER_BIT_SET((layer) / 4, 1 << ((layer) % 4)) +/* Layer Operation */ +#define ACTION_LAYER_CLEAR(on) ACTION_LAYER_BIT_AND(0, 0, (on)) +#define ACTION_LAYER_MOMENTARY(layer) ACTION_LAYER_ON_OFF(layer) +#define ACTION_LAYER_TOGGLE(layer) ACTION_LAYER_INVERT(layer, ON_RELEASE) +#define ACTION_LAYER_INVERT(layer, on) ACTION_LAYER_BIT_XOR((layer) / 4, 1 << ((layer) % 4), (on)) +#define ACTION_LAYER_ON(layer, on) ACTION_LAYER_BIT_OR((layer) / 4, 1 << ((layer) % 4), (on)) +#define ACTION_LAYER_OFF(layer, on) ACTION_LAYER_BIT_AND((layer) / 4, ~(1 << ((layer) % 4)), (on)) +#define ACTION_LAYER_SET(layer, on) ACTION_LAYER_BIT_SET((layer) / 4, 1 << ((layer) % 4), (on)) +#define ACTION_LAYER_ON_OFF(layer) ACTION_LAYER_TAP((layer), OP_ON_OFF) +#define ACTION_LAYER_OFF_ON(layer) ACTION_LAYER_TAP((layer), OP_OFF_ON) +#define ACTION_LAYER_SET_CLEAR(layer) ACTION_LAYER_TAP((layer), OP_SET_CLEAR) +#define ACTION_LAYER_ONESHOT(layer) ACTION_LAYER_TAP((layer), OP_ONESHOT) +#define ACTION_LAYER_MODS(layer, mods) ACTION(ACT_LAYER_MODS, (layer) << 8 | (mods)) +/* With Tapping */ +#define ACTION_LAYER_TAP_KEY(layer, key) ACTION_LAYER_TAP((layer), (key)) +#define ACTION_LAYER_TAP_TOGGLE(layer) ACTION_LAYER_TAP((layer), OP_TAP_TOGGLE) +/* Bitwise Operation */ +#define ACTION_LAYER_BIT_AND(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_AND, (part), (bits), (on)) +#define ACTION_LAYER_BIT_OR(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_OR, (part), (bits), (on)) +#define ACTION_LAYER_BIT_XOR(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_XOR, (part), (bits), (on)) +#define ACTION_LAYER_BIT_SET(part, bits, on) ACTION_LAYER_BITOP(OP_BIT_SET, (part), (bits), (on)) +/* Default Layer Bitwise Operation */ +#define ACTION_DEFAULT_LAYER_BIT_AND(part, bits) ACTION_LAYER_BITOP(OP_BIT_AND, (part), (bits), 0) +#define ACTION_DEFAULT_LAYER_BIT_OR(part, bits) ACTION_LAYER_BITOP(OP_BIT_OR, (part), (bits), 0) +#define ACTION_DEFAULT_LAYER_BIT_XOR(part, bits) ACTION_LAYER_BITOP(OP_BIT_XOR, (part), (bits), 0) +#define ACTION_DEFAULT_LAYER_BIT_SET(part, bits) ACTION_LAYER_BITOP(OP_BIT_SET, (part), (bits), 0) + +/* Macro */ +#define ACTION_MACRO(id) ACTION(ACT_MACRO, (id)) +#define ACTION_MACRO_TAP(id) ACTION(ACT_MACRO, FUNC_TAP << 8 | (id)) +#define ACTION_MACRO_OPT(id, opt) ACTION(ACT_MACRO, (opt) << 8 | (id)) +/* Function */ +enum function_opts { + FUNC_TAP = 0x8, /* indciates function is tappable */ +}; +#define ACTION_FUNCTION(id) ACTION(ACT_FUNCTION, (id)) +#define ACTION_FUNCTION_TAP(id) ACTION(ACT_FUNCTION, FUNC_TAP << 8 | (id)) +#define ACTION_FUNCTION_OPT(id, opt) ACTION(ACT_FUNCTION, (opt) << 8 | (id)) +/* OneHand Support */ +enum swap_hands_param_tap_op { + OP_SH_TOGGLE = 0xF0, + OP_SH_TAP_TOGGLE, + OP_SH_ON_OFF, + OP_SH_OFF_ON, + OP_SH_OFF, + OP_SH_ON, + OP_SH_ONESHOT, +}; + +#define ACTION_SWAP_HANDS() ACTION_SWAP_HANDS_ON_OFF() +#define ACTION_SWAP_HANDS_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TOGGLE) +#define ACTION_SWAP_HANDS_TAP_TOGGLE() ACTION(ACT_SWAP_HANDS, OP_SH_TAP_TOGGLE) +#define ACTION_SWAP_HANDS_ONESHOT() ACTION(ACT_SWAP_HANDS, OP_SH_ONESHOT) +#define ACTION_SWAP_HANDS_TAP_KEY(key) ACTION(ACT_SWAP_HANDS, key) +#define ACTION_SWAP_HANDS_ON_OFF() ACTION(ACT_SWAP_HANDS, OP_SH_ON_OFF) +#define ACTION_SWAP_HANDS_OFF_ON() ACTION(ACT_SWAP_HANDS, OP_SH_OFF_ON) +#define ACTION_SWAP_HANDS_ON() ACTION(ACT_SWAP_HANDS, OP_SH_ON) +#define ACTION_SWAP_HANDS_OFF() ACTION(ACT_SWAP_HANDS, OP_SH_OFF) diff --git a/quantum/action_layer.c b/quantum/action_layer.c new file mode 100644 index 0000000000..ed1a4bd20d --- /dev/null +++ b/quantum/action_layer.c @@ -0,0 +1,279 @@ +#include <stdint.h> +#include "keyboard.h" +#include "action.h" +#include "util.h" +#include "action_layer.h" + +#ifdef DEBUG_ACTION +# include "debug.h" +#else +# include "nodebug.h" +#endif + +/** \brief Default Layer State + */ +layer_state_t default_layer_state = 0; + +/** \brief Default Layer State Set At user Level + * + * Run user code on default layer state change + */ +__attribute__((weak)) layer_state_t default_layer_state_set_user(layer_state_t state) { return state; } + +/** \brief Default Layer State Set At Keyboard Level + * + * Run keyboard code on default layer state change + */ +__attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t state) { return default_layer_state_set_user(state); } + +/** \brief Default Layer State Set + * + * Static function to set the default layer state, prints debug info and clears keys + */ +static void default_layer_state_set(layer_state_t state) { + state = default_layer_state_set_kb(state); + debug("default_layer_state: "); + default_layer_debug(); + debug(" to "); + default_layer_state = state; + default_layer_debug(); + debug("\n"); +#ifdef STRICT_LAYER_RELEASE + clear_keyboard_but_mods(); // To avoid stuck keys +#else + clear_keyboard_but_mods_and_keys(); // Don't reset held keys +#endif +} + +/** \brief Default Layer Print + * + * Print out the hex value of the 32-bit default layer state, as well as the value of the highest bit. + */ +void default_layer_debug(void) { dprintf("%08lX(%u)", default_layer_state, get_highest_layer(default_layer_state)); } + +/** \brief Default Layer Set + * + * Sets the default layer state. + */ +void default_layer_set(layer_state_t state) { default_layer_state_set(state); } + +#ifndef NO_ACTION_LAYER +/** \brief Default Layer Or + * + * Turns on the default layer based on matching bits between specifed layer and existing layer state + */ +void default_layer_or(layer_state_t state) { default_layer_state_set(default_layer_state | state); } +/** \brief Default Layer And + * + * Turns on default layer based on matching enabled bits between specifed layer and existing layer state + */ +void default_layer_and(layer_state_t state) { default_layer_state_set(default_layer_state & state); } +/** \brief Default Layer Xor + * + * Turns on default layer based on non-matching bits between specifed layer and existing layer state + */ +void default_layer_xor(layer_state_t state) { default_layer_state_set(default_layer_state ^ state); } +#endif + +#ifndef NO_ACTION_LAYER +/** \brief Keymap Layer State + */ +layer_state_t layer_state = 0; + +/** \brief Layer state set user + * + * Runs user code on layer state change + */ +__attribute__((weak)) layer_state_t layer_state_set_user(layer_state_t state) { return state; } + +/** \brief Layer state set keyboard + * + * Runs keyboard code on layer state change + */ +__attribute__((weak)) layer_state_t layer_state_set_kb(layer_state_t state) { return layer_state_set_user(state); } + +/** \brief Layer state set + * + * Sets the layer to match the specifed state (a bitmask) + */ +void layer_state_set(layer_state_t state) { + state = layer_state_set_kb(state); + dprint("layer_state: "); + layer_debug(); + dprint(" to "); + layer_state = state; + layer_debug(); + dprintln(); +# ifdef STRICT_LAYER_RELEASE + clear_keyboard_but_mods(); // To avoid stuck keys +# else + clear_keyboard_but_mods_and_keys(); // Don't reset held keys +# endif +} + +/** \brief Layer clear + * + * Turn off all layers + */ +void layer_clear(void) { layer_state_set(0); } + +/** \brief Layer state is + * + * Return whether the given state is on (it might still be shadowed by a higher state, though) + */ +bool layer_state_is(uint8_t layer) { return layer_state_cmp(layer_state, layer); } + +/** \brief Layer state compare + * + * Used for comparing layers {mostly used for unit testing} + */ +bool layer_state_cmp(layer_state_t cmp_layer_state, uint8_t layer) { + if (!cmp_layer_state) { + return layer == 0; + } + return (cmp_layer_state & ((layer_state_t)1 << layer)) != 0; +} + +/** \brief Layer move + * + * Turns on the given layer and turn off all other layers + */ +void layer_move(uint8_t layer) { layer_state_set((layer_state_t)1 << layer); } + +/** \brief Layer on + * + * Turns on given layer + */ +void layer_on(uint8_t layer) { layer_state_set(layer_state | ((layer_state_t)1 << layer)); } + +/** \brief Layer off + * + * Turns off given layer + */ +void layer_off(uint8_t layer) { layer_state_set(layer_state & ~((layer_state_t)1 << layer)); } + +/** \brief Layer invert + * + * Toggle the given layer (set it if it's unset, or unset it if it's set) + */ +void layer_invert(uint8_t layer) { layer_state_set(layer_state ^ ((layer_state_t)1 << layer)); } + +/** \brief Layer or + * + * Turns on layers based on matching bits between specifed layer and existing layer state + */ +void layer_or(layer_state_t state) { layer_state_set(layer_state | state); } +/** \brief Layer and + * + * Turns on layers based on matching enabled bits between specifed layer and existing layer state + */ +void layer_and(layer_state_t state) { layer_state_set(layer_state & state); } +/** \brief Layer xor + * + * Turns on layers based on non-matching bits between specifed layer and existing layer state + */ +void layer_xor(layer_state_t state) { layer_state_set(layer_state ^ state); } + +/** \brief Layer debug printing + * + * Print out the hex value of the 32-bit layer state, as well as the value of the highest bit. + */ +void layer_debug(void) { dprintf("%08lX(%u)", layer_state, get_highest_layer(layer_state)); } +#endif + +#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE) +/** \brief source layer cache + */ + +uint8_t source_layers_cache[(MATRIX_ROWS * MATRIX_COLS + 7) / 8][MAX_LAYER_BITS] = {{0}}; + +/** \brief update source layers cache + * + * Updates the cached keys when changing layers + */ +void update_source_layers_cache(keypos_t key, uint8_t layer) { + const uint8_t key_number = key.col + (key.row * MATRIX_COLS); + const uint8_t storage_row = key_number / 8; + const uint8_t storage_bit = key_number % 8; + + for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) { + source_layers_cache[storage_row][bit_number] ^= (-((layer & (1U << bit_number)) != 0) ^ source_layers_cache[storage_row][bit_number]) & (1U << storage_bit); + } +} + +/** \brief read source layers cache + * + * reads the cached keys stored when the layer was changed + */ +uint8_t read_source_layers_cache(keypos_t key) { + const uint8_t key_number = key.col + (key.row * MATRIX_COLS); + const uint8_t storage_row = key_number / 8; + const uint8_t storage_bit = key_number % 8; + uint8_t layer = 0; + + for (uint8_t bit_number = 0; bit_number < MAX_LAYER_BITS; bit_number++) { + layer |= ((source_layers_cache[storage_row][bit_number] & (1U << storage_bit)) != 0) << bit_number; + } + + return layer; +} +#endif + +/** \brief Store or get action (FIXME: Needs better summary) + * + * Make sure the action triggered when the key is released is the same + * one as the one triggered on press. It's important for the mod keys + * when the layer is switched after the down event but before the up + * event as they may get stuck otherwise. + */ +action_t store_or_get_action(bool pressed, keypos_t key) { +#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE) + if (disable_action_cache) { + return layer_switch_get_action(key); + } + + uint8_t layer; + + if (pressed) { + layer = layer_switch_get_layer(key); + update_source_layers_cache(key, layer); + } else { + layer = read_source_layers_cache(key); + } + return action_for_key(layer, key); +#else + return layer_switch_get_action(key); +#endif +} + +/** \brief Layer switch get layer + * + * Gets the layer based on key info + */ +uint8_t layer_switch_get_layer(keypos_t key) { +#ifndef NO_ACTION_LAYER + action_t action; + action.code = ACTION_TRANSPARENT; + + layer_state_t layers = layer_state | default_layer_state; + /* check top layer first */ + for (int8_t i = MAX_LAYER - 1; i >= 0; i--) { + if (layers & ((layer_state_t)1 << i)) { + action = action_for_key(i, key); + if (action.code != ACTION_TRANSPARENT) { + return i; + } + } + } + /* fall back to layer 0 */ + return 0; +#else + return get_highest_layer(default_layer_state); +#endif +} + +/** \brief Layer switch get layer + * + * Gets action code based on key position + */ +action_t layer_switch_get_action(keypos_t key) { return action_for_key(layer_switch_get_layer(key), key); } diff --git a/quantum/action_layer.h b/quantum/action_layer.h new file mode 100644 index 0000000000..b87d096eed --- /dev/null +++ b/quantum/action_layer.h @@ -0,0 +1,147 @@ +/* +Copyright 2013 Jun Wako <wakojun@gmail.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdint.h> +#include "keyboard.h" +#include "action.h" + +#ifdef DYNAMIC_KEYMAP_ENABLE +# ifndef DYNAMIC_KEYMAP_LAYER_COUNT +# define DYNAMIC_KEYMAP_LAYER_COUNT 4 +# endif +# if DYNAMIC_KEYMAP_LAYER_COUNT <= 8 +# ifndef LAYER_STATE_8BIT +# define LAYER_STATE_8BIT +# endif +# elif DYNAMIC_KEYMAP_LAYER_COUNT <= 16 +# ifndef LAYER_STATE_16BIT +# define LAYER_STATE_16BIT +# endif +# else +# ifndef LAYER_STATE_32BIT +# define LAYER_STATE_32BIT +# endif +# endif +#endif + +#if !defined(LAYER_STATE_8BIT) && !defined(LAYER_STATE_16BIT) && !defined(LAYER_STATE_32BIT) +# define LAYER_STATE_32BIT +#endif + +#if defined(LAYER_STATE_8BIT) +typedef uint8_t layer_state_t; +# define MAX_LAYER_BITS 3 +# ifndef MAX_LAYER +# define MAX_LAYER 8 +# endif +# define get_highest_layer(state) biton(state) +#elif defined(LAYER_STATE_16BIT) +typedef uint16_t layer_state_t; +# define MAX_LAYER_BITS 4 +# ifndef MAX_LAYER +# define MAX_LAYER 16 +# endif +# define get_highest_layer(state) biton16(state) +#elif defined(LAYER_STATE_32BIT) +typedef uint32_t layer_state_t; +# define MAX_LAYER_BITS 5 +# ifndef MAX_LAYER +# define MAX_LAYER 32 +# endif +# define get_highest_layer(state) biton32(state) +#else +# error Layer Mask size not specified. HOW?! +#endif + +/* + * Default Layer + */ +extern layer_state_t default_layer_state; +void default_layer_debug(void); +void default_layer_set(layer_state_t state); + +__attribute__((weak)) layer_state_t default_layer_state_set_kb(layer_state_t state); +__attribute__((weak)) layer_state_t default_layer_state_set_user(layer_state_t state); + +#ifndef NO_ACTION_LAYER +/* bitwise operation */ +void default_layer_or(layer_state_t state); +void default_layer_and(layer_state_t state); +void default_layer_xor(layer_state_t state); +#else +# define default_layer_or(state) +# define default_layer_and(state) +# define default_layer_xor(state) +#endif + +/* + * Keymap Layer + */ +#ifndef NO_ACTION_LAYER +extern layer_state_t layer_state; + +void layer_state_set(layer_state_t state); +bool layer_state_is(uint8_t layer); +bool layer_state_cmp(layer_state_t layer1, uint8_t layer2); + +void layer_debug(void); +void layer_clear(void); +void layer_move(uint8_t layer); +void layer_on(uint8_t layer); +void layer_off(uint8_t layer); +void layer_invert(uint8_t layer); +/* bitwise operation */ +void layer_or(layer_state_t state); +void layer_and(layer_state_t state); +void layer_xor(layer_state_t state); +layer_state_t layer_state_set_user(layer_state_t state); +layer_state_t layer_state_set_kb(layer_state_t state); +#else +# define layer_state 0 + +# define layer_state_set(layer) +# define layer_state_is(layer) (layer == 0) +# define layer_state_cmp(state, layer) (state == 0 ? layer == 0 : (state & (layer_state_t)1 << layer) != 0) + +# define layer_debug() +# define layer_clear() +# define layer_move(layer) (void)layer +# define layer_on(layer) (void)layer +# define layer_off(layer) (void)layer +# define layer_invert(layer) (void)layer +# define layer_or(state) (void)state +# define layer_and(state) (void)state +# define layer_xor(state) (void)state +# define layer_state_set_kb(state) (void)state +# define layer_state_set_user(state) (void)state +#endif + +/* pressed actions cache */ +#if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE) + +void update_source_layers_cache(keypos_t key, uint8_t layer); +uint8_t read_source_layers_cache(keypos_t key); +#endif +action_t store_or_get_action(bool pressed, keypos_t key); + +/* return the topmost non-transparent layer currently associated with key */ +uint8_t layer_switch_get_layer(keypos_t key); + +/* return action depending on current layer status */ +action_t layer_switch_get_action(keypos_t key); diff --git a/quantum/action_macro.c b/quantum/action_macro.c new file mode 100644 index 0000000000..92228c0ba8 --- /dev/null +++ b/quantum/action_macro.c @@ -0,0 +1,93 @@ +/* +Copyright 2013 Jun Wako <wakojun@gmail.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "action.h" +#include "action_util.h" +#include "action_macro.h" +#include "wait.h" + +#ifdef DEBUG_ACTION +# include "debug.h" +#else +# include "nodebug.h" +#endif + +#ifndef NO_ACTION_MACRO + +# define MACRO_READ() (macro = MACRO_GET(macro_p++)) +/** \brief Action Macro Play + * + * FIXME: Needs doc + */ +void action_macro_play(const macro_t *macro_p) { + macro_t macro = END; + uint8_t interval = 0; + + if (!macro_p) return; + while (true) { + switch (MACRO_READ()) { + case KEY_DOWN: + MACRO_READ(); + dprintf("KEY_DOWN(%02X)\n", macro); + if (IS_MOD(macro)) { + add_macro_mods(MOD_BIT(macro)); + send_keyboard_report(); + } else { + register_code(macro); + } + break; + case KEY_UP: + MACRO_READ(); + dprintf("KEY_UP(%02X)\n", macro); + if (IS_MOD(macro)) { + del_macro_mods(MOD_BIT(macro)); + send_keyboard_report(); + } else { + unregister_code(macro); + } + break; + case WAIT: + MACRO_READ(); + dprintf("WAIT(%u)\n", macro); + { + uint8_t ms = macro; + while (ms--) wait_ms(1); + } + break; + case INTERVAL: + interval = MACRO_READ(); + dprintf("INTERVAL(%u)\n", interval); + break; + case 0x04 ... 0x73: + dprintf("DOWN(%02X)\n", macro); + register_code(macro); + break; + case 0x84 ... 0xF3: + dprintf("UP(%02X)\n", macro); + unregister_code(macro & 0x7F); + break; + case END: + default: + return; + } + // interval + { + uint8_t ms = interval; + while (ms--) wait_ms(1); + } + } +} +#endif diff --git a/quantum/action_macro.h b/quantum/action_macro.h new file mode 100644 index 0000000000..685e2c6ffc --- /dev/null +++ b/quantum/action_macro.h @@ -0,0 +1,123 @@ +/* +Copyright 2013 Jun Wako <wakojun@gmail.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdint.h> +#include "progmem.h" + +typedef uint8_t macro_t; + +#define MACRO_NONE (macro_t *)0 +#define MACRO(...) \ + ({ \ + static const macro_t __m[] PROGMEM = {__VA_ARGS__}; \ + &__m[0]; \ + }) +#define MACRO_GET(p) pgm_read_byte(p) + +// Sends press when the macro key is pressed, release when release, or tap_macro when the key has been tapped +#define MACRO_TAP_HOLD(record, press, release, tap_macro) (((record)->event.pressed) ? (((record)->tap.count <= 0 || (record)->tap.interrupted) ? (press) : MACRO_NONE) : (((record)->tap.count > 0 && !((record)->tap.interrupted)) ? (tap_macro) : (release))) + +// Holds down the modifier mod when the macro key is held, or sends macro instead when tapped +#define MACRO_TAP_HOLD_MOD(record, macro, mod) MACRO_TAP_HOLD(record, (MACRO(D(mod), END)), MACRO(U(mod), END), macro) + +// Holds down the modifier mod when the macro key is held, or pressed a shifted key when tapped (eg: shift+3 for #) +#define MACRO_TAP_SHFT_KEY_HOLD_MOD(record, key, mod) MACRO_TAP_HOLD_MOD(record, (MACRO(I(10), D(LSFT), T(key), U(LSFT), END)), mod) + +// Momentary switch layer when held, sends macro if tapped +#define MACRO_TAP_HOLD_LAYER(record, macro, layer) \ + (((record)->event.pressed) ? (((record)->tap.count <= 0 || (record)->tap.interrupted) ? ({ \ + layer_on((layer)); \ + MACRO_NONE; \ + }) \ + : MACRO_NONE) \ + : (((record)->tap.count > 0 && !((record)->tap.interrupted)) ? (macro) : ({ \ + layer_off((layer)); \ + MACRO_NONE; \ + }))) + +// Momentary switch layer when held, presses a shifted key when tapped (eg: shift+3 for #) +#define MACRO_TAP_SHFT_KEY_HOLD_LAYER(record, key, layer) MACRO_TAP_HOLD_LAYER(record, MACRO(I(10), D(LSFT), T(key), U(LSFT), END), layer) + +#ifndef NO_ACTION_MACRO +void action_macro_play(const macro_t *macro_p); +#else +# define action_macro_play(macro) +#endif + +/* Macro commands + * code(0x04-73) // key down(1byte) + * code(0x04-73) | 0x80 // key up(1byte) + * { KEY_DOWN, code(0x04-0xff) } // key down(2bytes) + * { KEY_UP, code(0x04-0xff) } // key up(2bytes) + * WAIT // wait milli-seconds + * INTERVAL // set interval between macro commands + * END // stop macro execution + * + * Ideas(Not implemented): + * modifiers + * system usage + * consumer usage + * unicode usage + * function call + * conditionals + * loop + */ +enum macro_command_id { + /* 0x00 - 0x03 */ + END = 0x00, + KEY_DOWN, + KEY_UP, + + /* 0x04 - 0x73 (reserved for keycode down) */ + + /* 0x74 - 0x83 */ + WAIT = 0x74, + INTERVAL, + + /* 0x84 - 0xf3 (reserved for keycode up) */ + + /* 0xf4 - 0xff */ +}; + +/* TODO: keycode:0x04-0x73 can be handled by 1byte command else 2bytes are needed + * if keycode between 0x04 and 0x73 + * keycode / (keycode|0x80) + * else + * {KEY_DOWN, keycode} / {KEY_UP, keycode} + */ +#define DOWN(key) KEY_DOWN, (key) +#define UP(key) KEY_UP, (key) +#define TYPE(key) DOWN(key), UP(key) +#define WAIT(ms) WAIT, (ms) +#define INTERVAL(ms) INTERVAL, (ms) + +/* key down */ +#define D(key) DOWN(KC_##key) +/* key up */ +#define U(key) UP(KC_##key) +/* key type */ +#define T(key) TYPE(KC_##key) +/* wait */ +#define W(ms) WAIT(ms) +/* interval */ +#define I(ms) INTERVAL(ms) + +/* for backward comaptibility */ +#define MD(key) DOWN(KC_##key) +#define MU(key) UP(KC_##key) diff --git a/quantum/action_tapping.c b/quantum/action_tapping.c new file mode 100644 index 0000000000..36839f9faf --- /dev/null +++ b/quantum/action_tapping.c @@ -0,0 +1,456 @@ +#include <stdint.h> +#include <stdbool.h> +#include "action.h" +#include "action_layer.h" +#include "action_tapping.h" +#include "keycode.h" +#include "timer.h" + +#ifdef DEBUG_ACTION +# include "debug.h" +#else +# include "nodebug.h" +#endif + +#ifndef NO_ACTION_TAPPING + +# define IS_TAPPING() !IS_NOEVENT(tapping_key.event) +# define IS_TAPPING_PRESSED() (IS_TAPPING() && tapping_key.event.pressed) +# define IS_TAPPING_RELEASED() (IS_TAPPING() && !tapping_key.event.pressed) +# define IS_TAPPING_KEY(k) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (k))) +#ifndef COMBO_ENABLE +# define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key))) +#else +# define IS_TAPPING_RECORD(r) (IS_TAPPING() && KEYEQ(tapping_key.event.key, (r->event.key)) && tapping_key.keycode == r->keycode) +#endif + +__attribute__((weak)) uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) { return TAPPING_TERM; } + +# ifdef TAPPING_TERM_PER_KEY +# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < get_tapping_term(get_record_keycode(&tapping_key, false), &tapping_key)) +# else +# define WITHIN_TAPPING_TERM(e) (TIMER_DIFF_16(e.time, tapping_key.event.time) < TAPPING_TERM) +# endif + +# ifdef TAPPING_FORCE_HOLD_PER_KEY +__attribute__((weak)) bool get_tapping_force_hold(uint16_t keycode, keyrecord_t *record) { return false; } +# endif + +# ifdef PERMISSIVE_HOLD_PER_KEY +__attribute__((weak)) bool get_permissive_hold(uint16_t keycode, keyrecord_t *record) { return false; } +# endif + +# ifdef HOLD_ON_OTHER_KEY_PRESS_PER_KEY +__attribute__((weak)) bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) { return false; } +# endif + +static keyrecord_t tapping_key = {}; +static keyrecord_t waiting_buffer[WAITING_BUFFER_SIZE] = {}; +static uint8_t waiting_buffer_head = 0; +static uint8_t waiting_buffer_tail = 0; + +static bool process_tapping(keyrecord_t *record); +static bool waiting_buffer_enq(keyrecord_t record); +static void waiting_buffer_clear(void); +static bool waiting_buffer_typed(keyevent_t event); +static bool waiting_buffer_has_anykey_pressed(void); +static void waiting_buffer_scan_tap(void); +static void debug_tapping_key(void); +static void debug_waiting_buffer(void); + +/** \brief Action Tapping Process + * + * FIXME: Needs doc + */ +void action_tapping_process(keyrecord_t record) { + if (process_tapping(&record)) { + if (!IS_NOEVENT(record.event)) { + debug("processed: "); + debug_record(record); + debug("\n"); + } + } else { + if (!waiting_buffer_enq(record)) { + // clear all in case of overflow. + debug("OVERFLOW: CLEAR ALL STATES\n"); + clear_keyboard(); + waiting_buffer_clear(); + tapping_key = (keyrecord_t){}; + } + } + + // process waiting_buffer + if (!IS_NOEVENT(record.event) && waiting_buffer_head != waiting_buffer_tail) { + debug("---- action_exec: process waiting_buffer -----\n"); + } + for (; waiting_buffer_tail != waiting_buffer_head; waiting_buffer_tail = (waiting_buffer_tail + 1) % WAITING_BUFFER_SIZE) { + if (process_tapping(&waiting_buffer[waiting_buffer_tail])) { + debug("processed: waiting_buffer["); + debug_dec(waiting_buffer_tail); + debug("] = "); + debug_record(waiting_buffer[waiting_buffer_tail]); + debug("\n\n"); + } else { + break; + } + } + if (!IS_NOEVENT(record.event)) { + debug("\n"); + } +} + +/** \brief Tapping + * + * Rule: Tap key is typed(pressed and released) within TAPPING_TERM. + * (without interfering by typing other key) + */ +/* return true when key event is processed or consumed. */ +bool process_tapping(keyrecord_t *keyp) { + keyevent_t event = keyp->event; + + // if tapping + if (IS_TAPPING_PRESSED()) { + if (WITHIN_TAPPING_TERM(event)) { + if (tapping_key.tap.count == 0) { + if (IS_TAPPING_RECORD(keyp) && !event.pressed) { + // first tap! + debug("Tapping: First tap(0->1).\n"); + tapping_key.tap.count = 1; + debug_tapping_key(); + process_record(&tapping_key); + + // copy tapping state + keyp->tap = tapping_key.tap; + // enqueue + return false; + } + /* Process a key typed within TAPPING_TERM + * This can register the key before settlement of tapping, + * useful for long TAPPING_TERM but may prevent fast typing. + */ +# if defined(TAPPING_TERM_PER_KEY) || (TAPPING_TERM >= 500) || defined(PERMISSIVE_HOLD) || defined(PERMISSIVE_HOLD_PER_KEY) + else if ((( +# ifdef TAPPING_TERM_PER_KEY + get_tapping_term(get_record_keycode(&tapping_key, false), keyp) +# else + TAPPING_TERM +# endif + >= 500) + +# ifdef PERMISSIVE_HOLD_PER_KEY + || get_permissive_hold(get_record_keycode(&tapping_key, false), keyp) +# elif defined(PERMISSIVE_HOLD) + || true +# endif + ) && + IS_RELEASED(event) && waiting_buffer_typed(event)) { + debug("Tapping: End. No tap. Interfered by typing key\n"); + process_record(&tapping_key); + tapping_key = (keyrecord_t){}; + debug_tapping_key(); + // enqueue + return false; + } +# endif + /* Process release event of a key pressed before tapping starts + * Without this unexpected repeating will occur with having fast repeating setting + * https://github.com/tmk/tmk_keyboard/issues/60 + */ + else if (IS_RELEASED(event) && !waiting_buffer_typed(event)) { + // Modifier should be retained till end of this tapping. + action_t action = layer_switch_get_action(event.key); + switch (action.kind.id) { + case ACT_LMODS: + case ACT_RMODS: + if (action.key.mods && !action.key.code) return false; + if (IS_MOD(action.key.code)) return false; + break; + case ACT_LMODS_TAP: + case ACT_RMODS_TAP: + if (action.key.mods && keyp->tap.count == 0) return false; + if (IS_MOD(action.key.code)) return false; + break; + } + // Release of key should be process immediately. + debug("Tapping: release event of a key pressed before tapping\n"); + process_record(keyp); + return true; + } else { + // set interrupted flag when other key preesed during tapping + if (event.pressed) { + tapping_key.tap.interrupted = true; +# if defined(HOLD_ON_OTHER_KEY_PRESS) || defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY) +# if defined(HOLD_ON_OTHER_KEY_PRESS_PER_KEY) + if (get_hold_on_other_key_press(get_record_keycode(&tapping_key, false), keyp)) +# endif + { + debug("Tapping: End. No tap. Interfered by pressed key\n"); + process_record(&tapping_key); + tapping_key = (keyrecord_t){}; + debug_tapping_key(); + // enqueue + return false; + } +# endif + } + // enqueue + return false; + } + } + // tap_count > 0 + else { + if (IS_TAPPING_RECORD(keyp) && !event.pressed) { + debug("Tapping: Tap release("); + debug_dec(tapping_key.tap.count); + debug(")\n"); + keyp->tap = tapping_key.tap; + process_record(keyp); + tapping_key = *keyp; + debug_tapping_key(); + return true; + } else if (is_tap_record(keyp) && event.pressed) { + if (tapping_key.tap.count > 1) { + debug("Tapping: Start new tap with releasing last tap(>1).\n"); + // unregister key + process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false, +#ifdef COMBO_ENABLE + .keycode = tapping_key.keycode, +#endif + }); + } else { + debug("Tapping: Start while last tap(1).\n"); + } + tapping_key = *keyp; + waiting_buffer_scan_tap(); + debug_tapping_key(); + return true; + } else { + if (!IS_NOEVENT(event)) { + debug("Tapping: key event while last tap(>0).\n"); + } + process_record(keyp); + return true; + } + } + } + // after TAPPING_TERM + else { + if (tapping_key.tap.count == 0) { + debug("Tapping: End. Timeout. Not tap(0): "); + debug_event(event); + debug("\n"); + process_record(&tapping_key); + tapping_key = (keyrecord_t){}; + debug_tapping_key(); + return false; + } else { + if (IS_TAPPING_RECORD(keyp) && !event.pressed) { + debug("Tapping: End. last timeout tap release(>0)."); + keyp->tap = tapping_key.tap; + process_record(keyp); + tapping_key = (keyrecord_t){}; + return true; + } else if (is_tap_record(keyp) && event.pressed) { + if (tapping_key.tap.count > 1) { + debug("Tapping: Start new tap with releasing last timeout tap(>1).\n"); + // unregister key + process_record(&(keyrecord_t){.tap = tapping_key.tap, .event.key = tapping_key.event.key, .event.time = event.time, .event.pressed = false, +#ifdef COMBO_ENABLE + .keycode = tapping_key.keycode, +#endif + }); + } else { + debug("Tapping: Start while last timeout tap(1).\n"); + } + tapping_key = *keyp; + waiting_buffer_scan_tap(); + debug_tapping_key(); + return true; + } else { + if (!IS_NOEVENT(event)) { + debug("Tapping: key event while last timeout tap(>0).\n"); + } + process_record(keyp); + return true; + } + } + } + } else if (IS_TAPPING_RELEASED()) { + if (WITHIN_TAPPING_TERM(event)) { + if (event.pressed) { + if (IS_TAPPING_RECORD(keyp)) { +//# ifndef TAPPING_FORCE_HOLD +# if !defined(TAPPING_FORCE_HOLD) || defined(TAPPING_FORCE_HOLD_PER_KEY) + if ( +# ifdef TAPPING_FORCE_HOLD_PER_KEY + !get_tapping_force_hold(get_record_keycode(&tapping_key, false), keyp) && +# endif + !tapping_key.tap.interrupted && tapping_key.tap.count > 0) { + // sequential tap. + keyp->tap = tapping_key.tap; + if (keyp->tap.count < 15) keyp->tap.count += 1; + debug("Tapping: Tap press("); + debug_dec(keyp->tap.count); + debug(")\n"); + process_record(keyp); + tapping_key = *keyp; + debug_tapping_key(); + return true; + } +# endif + // FIX: start new tap again + tapping_key = *keyp; + return true; + } else if (is_tap_record(keyp)) { + // Sequential tap can be interfered with other tap key. + debug("Tapping: Start with interfering other tap.\n"); + tapping_key = *keyp; + waiting_buffer_scan_tap(); + debug_tapping_key(); + return true; + } else { + // should none in buffer + // FIX: interrupted when other key is pressed + tapping_key.tap.interrupted = true; + process_record(keyp); + return true; + } + } else { + if (!IS_NOEVENT(event)) debug("Tapping: other key just after tap.\n"); + process_record(keyp); + return true; + } + } else { + // FIX: process_action here? + // timeout. no sequential tap. + debug("Tapping: End(Timeout after releasing last tap): "); + debug_event(event); + debug("\n"); + tapping_key = (keyrecord_t){}; + debug_tapping_key(); + return false; + } + } + // not tapping state + else { + if (event.pressed && is_tap_record(keyp)) { + debug("Tapping: Start(Press tap key).\n"); + tapping_key = *keyp; + process_record_tap_hint(&tapping_key); + waiting_buffer_scan_tap(); + debug_tapping_key(); + return true; + } else { + process_record(keyp); + return true; + } + } +} + +/** \brief Waiting buffer enq + * + * FIXME: Needs docs + */ +bool waiting_buffer_enq(keyrecord_t record) { + if (IS_NOEVENT(record.event)) { + return true; + } + + if ((waiting_buffer_head + 1) % WAITING_BUFFER_SIZE == waiting_buffer_tail) { + debug("waiting_buffer_enq: Over flow.\n"); + return false; + } + + waiting_buffer[waiting_buffer_head] = record; + waiting_buffer_head = (waiting_buffer_head + 1) % WAITING_BUFFER_SIZE; + + debug("waiting_buffer_enq: "); + debug_waiting_buffer(); + return true; +} + +/** \brief Waiting buffer clear + * + * FIXME: Needs docs + */ +void waiting_buffer_clear(void) { + waiting_buffer_head = 0; + waiting_buffer_tail = 0; +} + +/** \brief Waiting buffer typed + * + * FIXME: Needs docs + */ +bool waiting_buffer_typed(keyevent_t event) { + for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { + if (KEYEQ(event.key, waiting_buffer[i].event.key) && event.pressed != waiting_buffer[i].event.pressed) { + return true; + } + } + return false; +} + +/** \brief Waiting buffer has anykey pressed + * + * FIXME: Needs docs + */ +__attribute__((unused)) bool waiting_buffer_has_anykey_pressed(void) { + for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { + if (waiting_buffer[i].event.pressed) return true; + } + return false; +} + +/** \brief Scan buffer for tapping + * + * FIXME: Needs docs + */ +void waiting_buffer_scan_tap(void) { + // tapping already is settled + if (tapping_key.tap.count > 0) return; + // invalid state: tapping_key released && tap.count == 0 + if (!tapping_key.event.pressed) return; + + for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { + if (IS_TAPPING_KEY(waiting_buffer[i].event.key) && !waiting_buffer[i].event.pressed && WITHIN_TAPPING_TERM(waiting_buffer[i].event)) { + tapping_key.tap.count = 1; + waiting_buffer[i].tap.count = 1; + process_record(&tapping_key); + + debug("waiting_buffer_scan_tap: found at ["); + debug_dec(i); + debug("]\n"); + debug_waiting_buffer(); + return; + } + } +} + +/** \brief Tapping key debug print + * + * FIXME: Needs docs + */ +static void debug_tapping_key(void) { + debug("TAPPING_KEY="); + debug_record(tapping_key); + debug("\n"); +} + +/** \brief Waiting buffer debug print + * + * FIXME: Needs docs + */ +static void debug_waiting_buffer(void) { + debug("{ "); + for (uint8_t i = waiting_buffer_tail; i != waiting_buffer_head; i = (i + 1) % WAITING_BUFFER_SIZE) { + debug("["); + debug_dec(i); + debug("]="); + debug_record(waiting_buffer[i]); + debug(" "); + } + debug("}\n"); +} + +#endif diff --git a/quantum/action_tapping.h b/quantum/action_tapping.h new file mode 100644 index 0000000000..7de8049c7f --- /dev/null +++ b/quantum/action_tapping.h @@ -0,0 +1,42 @@ +/* +Copyright 2013 Jun Wako <wakojun@gmail.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +/* period of tapping(ms) */ +#ifndef TAPPING_TERM +# define TAPPING_TERM 200 +#endif + +/* tap count needed for toggling a feature */ +#ifndef TAPPING_TOGGLE +# define TAPPING_TOGGLE 5 +#endif + +#define WAITING_BUFFER_SIZE 8 + +#ifndef NO_ACTION_TAPPING +uint16_t get_record_keycode(keyrecord_t *record, bool update_layer_cache); +uint16_t get_event_keycode(keyevent_t event, bool update_layer_cache); +void action_tapping_process(keyrecord_t record); + +uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record); +bool get_permissive_hold(uint16_t keycode, keyrecord_t *record); +bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record); +bool get_tapping_force_hold(uint16_t keycode, keyrecord_t *record); +bool get_retro_tapping(uint16_t keycode, keyrecord_t *record); +#endif diff --git a/quantum/action_util.c b/quantum/action_util.c new file mode 100644 index 0000000000..2b3c00cba0 --- /dev/null +++ b/quantum/action_util.c @@ -0,0 +1,455 @@ +/* +Copyright 2013 Jun Wako <wakojun@gmail.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "host.h" +#include "report.h" +#include "debug.h" +#include "action_util.h" +#include "action_layer.h" +#include "timer.h" +#include "keycode_config.h" + +extern keymap_config_t keymap_config; + +static uint8_t real_mods = 0; +static uint8_t weak_mods = 0; +static uint8_t macro_mods = 0; +#ifdef KEY_OVERRIDE_ENABLE +static uint8_t weak_override_mods = 0; +static uint8_t suppressed_mods = 0; +#endif + +#ifdef USB_6KRO_ENABLE +# define RO_ADD(a, b) ((a + b) % KEYBOARD_REPORT_KEYS) +# define RO_SUB(a, b) ((a - b + KEYBOARD_REPORT_KEYS) % KEYBOARD_REPORT_KEYS) +# define RO_INC(a) RO_ADD(a, 1) +# define RO_DEC(a) RO_SUB(a, 1) +static int8_t cb_head = 0; +static int8_t cb_tail = 0; +static int8_t cb_count = 0; +#endif + +// TODO: pointer variable is not needed +// report_keyboard_t keyboard_report = {}; +report_keyboard_t *keyboard_report = &(report_keyboard_t){}; + +extern inline void add_key(uint8_t key); +extern inline void del_key(uint8_t key); +extern inline void clear_keys(void); + +#ifndef NO_ACTION_ONESHOT +static uint8_t oneshot_mods = 0; +static uint8_t oneshot_locked_mods = 0; +uint8_t get_oneshot_locked_mods(void) { return oneshot_locked_mods; } +void set_oneshot_locked_mods(uint8_t mods) { + if (mods != oneshot_locked_mods) { + oneshot_locked_mods = mods; + oneshot_locked_mods_changed_kb(oneshot_locked_mods); + } +} +void clear_oneshot_locked_mods(void) { + if (oneshot_locked_mods) { + oneshot_locked_mods = 0; + oneshot_locked_mods_changed_kb(oneshot_locked_mods); + } +} +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) +static uint16_t oneshot_time = 0; +bool has_oneshot_mods_timed_out(void) { return TIMER_DIFF_16(timer_read(), oneshot_time) >= ONESHOT_TIMEOUT; } +# else +bool has_oneshot_mods_timed_out(void) { return false; } +# endif +#endif + +/* oneshot layer */ +#ifndef NO_ACTION_ONESHOT +/** \brief oneshot_layer_data bits + * LLLL LSSS + * where: + * L => are layer bits + * S => oneshot state bits + */ +static int8_t oneshot_layer_data = 0; + +inline uint8_t get_oneshot_layer(void) { return oneshot_layer_data >> 3; } +inline uint8_t get_oneshot_layer_state(void) { return oneshot_layer_data & 0b111; } + +# ifdef SWAP_HANDS_ENABLE +enum { + SHO_OFF, + SHO_ACTIVE, // Swap hands button was pressed, and we didn't send any swapped keys yet + SHO_PRESSED, // Swap hands button is currently pressed + SHO_USED, // Swap hands button is still pressed, and we already sent swapped keys +} swap_hands_oneshot = SHO_OFF; +# endif + +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) +static uint16_t oneshot_layer_time = 0; +inline bool has_oneshot_layer_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_layer_time) >= ONESHOT_TIMEOUT && !(get_oneshot_layer_state() & ONESHOT_TOGGLED); } +# ifdef SWAP_HANDS_ENABLE +static uint16_t oneshot_swaphands_time = 0; +inline bool has_oneshot_swaphands_timed_out() { return TIMER_DIFF_16(timer_read(), oneshot_swaphands_time) >= ONESHOT_TIMEOUT && (swap_hands_oneshot == SHO_ACTIVE); } +# endif +# endif + +# ifdef SWAP_HANDS_ENABLE + +void set_oneshot_swaphands(void) { + swap_hands_oneshot = SHO_PRESSED; + swap_hands = true; +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_swaphands_time = timer_read(); + if (oneshot_layer_time != 0) { + oneshot_layer_time = oneshot_swaphands_time; + } +# endif +} + +void release_oneshot_swaphands(void) { + if (swap_hands_oneshot == SHO_PRESSED) { + swap_hands_oneshot = SHO_ACTIVE; + } + if (swap_hands_oneshot == SHO_USED) { + clear_oneshot_swaphands(); + } +} + +void use_oneshot_swaphands(void) { + if (swap_hands_oneshot == SHO_PRESSED) { + swap_hands_oneshot = SHO_USED; + } + if (swap_hands_oneshot == SHO_ACTIVE) { + clear_oneshot_swaphands(); + } +} + +void clear_oneshot_swaphands(void) { + swap_hands_oneshot = SHO_OFF; + swap_hands = false; +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_swaphands_time = 0; +# endif +} + +# endif + +/** \brief Set oneshot layer + * + * FIXME: needs doc + */ +void set_oneshot_layer(uint8_t layer, uint8_t state) { + if (!keymap_config.oneshot_disable) { + oneshot_layer_data = layer << 3 | state; + layer_on(layer); +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_layer_time = timer_read(); +# endif + oneshot_layer_changed_kb(get_oneshot_layer()); + } else { + layer_on(layer); + } +} +/** \brief Reset oneshot layer + * + * FIXME: needs doc + */ +void reset_oneshot_layer(void) { + oneshot_layer_data = 0; +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_layer_time = 0; +# endif + oneshot_layer_changed_kb(get_oneshot_layer()); +} +/** \brief Clear oneshot layer + * + * FIXME: needs doc + */ +void clear_oneshot_layer_state(oneshot_fullfillment_t state) { + uint8_t start_state = oneshot_layer_data; + oneshot_layer_data &= ~state; + if ((!get_oneshot_layer_state() && start_state != oneshot_layer_data) || keymap_config.oneshot_disable) { + layer_off(get_oneshot_layer()); + reset_oneshot_layer(); + } +} +/** \brief Is oneshot layer active + * + * FIXME: needs doc + */ +bool is_oneshot_layer_active(void) { return get_oneshot_layer_state(); } + +/** \brief set oneshot + * + * FIXME: needs doc + */ +void oneshot_set(bool active) { + if (keymap_config.oneshot_disable != active) { + keymap_config.oneshot_disable = active; + eeconfig_update_keymap(keymap_config.raw); + dprintf("Oneshot: active: %d\n", active); + } +} + +/** \brief toggle oneshot + * + * FIXME: needs doc + */ +void oneshot_toggle(void) { oneshot_set(!keymap_config.oneshot_disable); } + +/** \brief enable oneshot + * + * FIXME: needs doc + */ +void oneshot_enable(void) { oneshot_set(true); } + +/** \brief disable oneshot + * + * FIXME: needs doc + */ +void oneshot_disable(void) { oneshot_set(false); } + +bool is_oneshot_enabled(void) { return keymap_config.oneshot_disable; } + +#endif + +/** \brief Send keyboard report + * + * FIXME: needs doc + */ +void send_keyboard_report(void) { + keyboard_report->mods = real_mods; + keyboard_report->mods |= weak_mods; + keyboard_report->mods |= macro_mods; + +#ifndef NO_ACTION_ONESHOT + if (oneshot_mods) { +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + if (has_oneshot_mods_timed_out()) { + dprintf("Oneshot: timeout\n"); + clear_oneshot_mods(); + } +# endif + keyboard_report->mods |= oneshot_mods; + if (has_anykey(keyboard_report)) { + clear_oneshot_mods(); + } + } + +#endif + +#ifdef KEY_OVERRIDE_ENABLE + // These need to be last to be able to properly control key overrides + keyboard_report->mods &= ~suppressed_mods; + keyboard_report->mods |= weak_override_mods; +#endif + + host_keyboard_send(keyboard_report); +} + +/** \brief Get mods + * + * FIXME: needs doc + */ +uint8_t get_mods(void) { return real_mods; } +/** \brief add mods + * + * FIXME: needs doc + */ +void add_mods(uint8_t mods) { real_mods |= mods; } +/** \brief del mods + * + * FIXME: needs doc + */ +void del_mods(uint8_t mods) { real_mods &= ~mods; } +/** \brief set mods + * + * FIXME: needs doc + */ +void set_mods(uint8_t mods) { real_mods = mods; } +/** \brief clear mods + * + * FIXME: needs doc + */ +void clear_mods(void) { real_mods = 0; } + +/** \brief get weak mods + * + * FIXME: needs doc + */ +uint8_t get_weak_mods(void) { return weak_mods; } +/** \brief add weak mods + * + * FIXME: needs doc + */ +void add_weak_mods(uint8_t mods) { weak_mods |= mods; } +/** \brief del weak mods + * + * FIXME: needs doc + */ +void del_weak_mods(uint8_t mods) { weak_mods &= ~mods; } +/** \brief set weak mods + * + * FIXME: needs doc + */ +void set_weak_mods(uint8_t mods) { weak_mods = mods; } +/** \brief clear weak mods + * + * FIXME: needs doc + */ +void clear_weak_mods(void) { weak_mods = 0; } + +#ifdef KEY_OVERRIDE_ENABLE +/** \brief set weak mods used by key overrides. DO not call this manually + */ +void set_weak_override_mods(uint8_t mods) { weak_override_mods = mods; } +/** \brief clear weak mods used by key overrides. DO not call this manually + */ +void clear_weak_override_mods(void) { weak_override_mods = 0; } + +/** \brief set suppressed mods used by key overrides. DO not call this manually + */ +void set_suppressed_override_mods(uint8_t mods) { suppressed_mods = mods; } +/** \brief clear suppressed mods used by key overrides. DO not call this manually + */ +void clear_suppressed_override_mods(void) { suppressed_mods = 0; } +#endif + +/* macro modifier */ +/** \brief get macro mods + * + * FIXME: needs doc + */ +uint8_t get_macro_mods(void) { return macro_mods; } +/** \brief add macro mods + * + * FIXME: needs doc + */ +void add_macro_mods(uint8_t mods) { macro_mods |= mods; } +/** \brief del macro mods + * + * FIXME: needs doc + */ +void del_macro_mods(uint8_t mods) { macro_mods &= ~mods; } +/** \brief set macro mods + * + * FIXME: needs doc + */ +void set_macro_mods(uint8_t mods) { macro_mods = mods; } +/** \brief clear macro mods + * + * FIXME: needs doc + */ +void clear_macro_mods(void) { macro_mods = 0; } + +#ifndef NO_ACTION_ONESHOT +/** \brief get oneshot mods + * + * FIXME: needs doc + */ +uint8_t get_oneshot_mods(void) { return oneshot_mods; } + +void add_oneshot_mods(uint8_t mods) { + if ((oneshot_mods & mods) != mods) { +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_time = timer_read(); +# endif + oneshot_mods |= mods; + oneshot_mods_changed_kb(mods); + } +} + +void del_oneshot_mods(uint8_t mods) { + if (oneshot_mods & mods) { + oneshot_mods &= ~mods; +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_time = oneshot_mods ? timer_read() : 0; +# endif + oneshot_mods_changed_kb(oneshot_mods); + } +} + +/** \brief set oneshot mods + * + * FIXME: needs doc + */ +void set_oneshot_mods(uint8_t mods) { + if (!keymap_config.oneshot_disable) { + if (oneshot_mods != mods) { +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_time = timer_read(); +# endif + oneshot_mods = mods; + oneshot_mods_changed_kb(mods); + } + } +} + +/** \brief clear oneshot mods + * + * FIXME: needs doc + */ +void clear_oneshot_mods(void) { + if (oneshot_mods) { + oneshot_mods = 0; +# if (defined(ONESHOT_TIMEOUT) && (ONESHOT_TIMEOUT > 0)) + oneshot_time = 0; +# endif + oneshot_mods_changed_kb(oneshot_mods); + } +} +#endif + +/** \brief Called when the one shot modifiers have been changed. + * + * \param mods Contains the active modifiers active after the change. + */ +__attribute__((weak)) void oneshot_locked_mods_changed_user(uint8_t mods) {} + +/** \brief Called when the locked one shot modifiers have been changed. + * + * \param mods Contains the active modifiers active after the change. + */ +__attribute__((weak)) void oneshot_locked_mods_changed_kb(uint8_t mods) { oneshot_locked_mods_changed_user(mods); } + +/** \brief Called when the one shot modifiers have been changed. + * + * \param mods Contains the active modifiers active after the change. + */ +__attribute__((weak)) void oneshot_mods_changed_user(uint8_t mods) {} + +/** \brief Called when the one shot modifiers have been changed. + * + * \param mods Contains the active modifiers active after the change. + */ +__attribute__((weak)) void oneshot_mods_changed_kb(uint8_t mods) { oneshot_mods_changed_user(mods); } + +/** \brief Called when the one shot layers have been changed. + * + * \param layer Contains the layer that is toggled on, or zero when toggled off. + */ +__attribute__((weak)) void oneshot_layer_changed_user(uint8_t layer) {} + +/** \brief Called when the one shot layers have been changed. + * + * \param layer Contains the layer that is toggled on, or zero when toggled off. + */ +__attribute__((weak)) void oneshot_layer_changed_kb(uint8_t layer) { oneshot_layer_changed_user(layer); } + +/** \brief inspect keyboard state + * + * FIXME: needs doc + */ +uint8_t has_anymod(void) { return bitpop(real_mods); } diff --git a/quantum/action_util.h b/quantum/action_util.h new file mode 100644 index 0000000000..f2b3897ae5 --- /dev/null +++ b/quantum/action_util.h @@ -0,0 +1,105 @@ +/* +Copyright 2013 Jun Wako <wakojun@gmail.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdint.h> +#include "report.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern report_keyboard_t *keyboard_report; + +void send_keyboard_report(void); + +/* key */ +inline void add_key(uint8_t key) { add_key_to_report(keyboard_report, key); } + +inline void del_key(uint8_t key) { del_key_from_report(keyboard_report, key); } + +inline void clear_keys(void) { clear_keys_from_report(keyboard_report); } + +/* modifier */ +uint8_t get_mods(void); +void add_mods(uint8_t mods); +void del_mods(uint8_t mods); +void set_mods(uint8_t mods); +void clear_mods(void); + +/* weak modifier */ +uint8_t get_weak_mods(void); +void add_weak_mods(uint8_t mods); +void del_weak_mods(uint8_t mods); +void set_weak_mods(uint8_t mods); +void clear_weak_mods(void); + +/* macro modifier */ +uint8_t get_macro_mods(void); +void add_macro_mods(uint8_t mods); +void del_macro_mods(uint8_t mods); +void set_macro_mods(uint8_t mods); +void clear_macro_mods(void); + +/* oneshot modifier */ +uint8_t get_oneshot_mods(void); +void add_oneshot_mods(uint8_t mods); +void del_oneshot_mods(uint8_t mods); +void set_oneshot_mods(uint8_t mods); +void clear_oneshot_mods(void); +bool has_oneshot_mods_timed_out(void); + +uint8_t get_oneshot_locked_mods(void); +void set_oneshot_locked_mods(uint8_t mods); +void clear_oneshot_locked_mods(void); + +typedef enum { ONESHOT_PRESSED = 0b01, ONESHOT_OTHER_KEY_PRESSED = 0b10, ONESHOT_START = 0b11, ONESHOT_TOGGLED = 0b100 } oneshot_fullfillment_t; +void set_oneshot_layer(uint8_t layer, uint8_t state); +uint8_t get_oneshot_layer(void); +void clear_oneshot_layer_state(oneshot_fullfillment_t state); +void reset_oneshot_layer(void); +bool is_oneshot_layer_active(void); +uint8_t get_oneshot_layer_state(void); +bool has_oneshot_layer_timed_out(void); +bool has_oneshot_swaphands_timed_out(void); + +void oneshot_locked_mods_changed_user(uint8_t mods); +void oneshot_locked_mods_changed_kb(uint8_t mods); +void oneshot_mods_changed_user(uint8_t mods); +void oneshot_mods_changed_kb(uint8_t mods); +void oneshot_layer_changed_user(uint8_t layer); +void oneshot_layer_changed_kb(uint8_t layer); + +void oneshot_toggle(void); +void oneshot_enable(void); +void oneshot_disable(void); +bool is_oneshot_enabled(void); + +/* inspect */ +uint8_t has_anymod(void); + +#ifdef SWAP_HANDS_ENABLE +void set_oneshot_swaphands(void); +void release_oneshot_swaphands(void); +void use_oneshot_swaphands(void); +void clear_oneshot_swaphands(void); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/quantum/eeconfig.c b/quantum/eeconfig.c new file mode 100644 index 0000000000..ffa56ab56d --- /dev/null +++ b/quantum/eeconfig.c @@ -0,0 +1,211 @@ +#include <stdint.h> +#include <stdbool.h> +#include "eeprom.h" +#include "eeconfig.h" +#include "action_layer.h" + +#ifdef STM32_EEPROM_ENABLE +# include <hal.h> +# include "eeprom_stm32.h" +#endif + +#if defined(EEPROM_DRIVER) +# include "eeprom_driver.h" +#endif + +#if defined(HAPTIC_ENABLE) +# include "haptic.h" +#endif + +/** \brief eeconfig enable + * + * FIXME: needs doc + */ +__attribute__((weak)) void eeconfig_init_user(void) { + // Reset user EEPROM value to blank, rather than to a set value + eeconfig_update_user(0); +} + +__attribute__((weak)) void eeconfig_init_kb(void) { + // Reset Keyboard EEPROM value to blank, rather than to a set value + eeconfig_update_kb(0); + + eeconfig_init_user(); +} + +/* + * FIXME: needs doc + */ +void eeconfig_init_quantum(void) { +#ifdef STM32_EEPROM_ENABLE + EEPROM_Erase(); +#endif +#if defined(EEPROM_DRIVER) + eeprom_driver_erase(); +#endif + eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER); + eeprom_update_byte(EECONFIG_DEBUG, 0); + eeprom_update_byte(EECONFIG_DEFAULT_LAYER, 0); + default_layer_state = 0; + eeprom_update_byte(EECONFIG_KEYMAP_LOWER_BYTE, 0); + eeprom_update_byte(EECONFIG_KEYMAP_UPPER_BYTE, 0); + eeprom_update_byte(EECONFIG_MOUSEKEY_ACCEL, 0); + eeprom_update_byte(EECONFIG_BACKLIGHT, 0); + eeprom_update_byte(EECONFIG_AUDIO, 0xFF); // On by default + eeprom_update_dword(EECONFIG_RGBLIGHT, 0); + eeprom_update_byte(EECONFIG_STENOMODE, 0); + eeprom_update_dword(EECONFIG_HAPTIC, 0); + eeprom_update_byte(EECONFIG_VELOCIKEY, 0); + eeprom_update_dword(EECONFIG_RGB_MATRIX, 0); + eeprom_update_word(EECONFIG_RGB_MATRIX_EXTENDED, 0); + + // TODO: Remove once ARM has a way to configure EECONFIG_HANDEDNESS + // within the emulated eeprom via dfu-util or another tool +#if defined INIT_EE_HANDS_LEFT +# pragma message "Faking EE_HANDS for left hand" + eeprom_update_byte(EECONFIG_HANDEDNESS, 1); +#elif defined INIT_EE_HANDS_RIGHT +# pragma message "Faking EE_HANDS for right hand" + eeprom_update_byte(EECONFIG_HANDEDNESS, 0); +#endif + +#if defined(HAPTIC_ENABLE) + haptic_reset(); +#else + // this is used in case haptic is disabled, but we still want sane defaults + // in the haptic configuration eeprom. All zero will trigger a haptic_reset + // when a haptic-enabled firmware is loaded onto the keyboard. + eeprom_update_dword(EECONFIG_HAPTIC, 0); +#endif + + eeconfig_init_kb(); +} + +/** \brief eeconfig initialization + * + * FIXME: needs doc + */ +void eeconfig_init(void) { eeconfig_init_quantum(); } + +/** \brief eeconfig enable + * + * FIXME: needs doc + */ +void eeconfig_enable(void) { eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER); } + +/** \brief eeconfig disable + * + * FIXME: needs doc + */ +void eeconfig_disable(void) { +#ifdef STM32_EEPROM_ENABLE + EEPROM_Erase(); +#endif +#if defined(EEPROM_DRIVER) + eeprom_driver_erase(); +#endif + eeprom_update_word(EECONFIG_MAGIC, EECONFIG_MAGIC_NUMBER_OFF); +} + +/** \brief eeconfig is enabled + * + * FIXME: needs doc + */ +bool eeconfig_is_enabled(void) { return (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER); } + +/** \brief eeconfig is disabled + * + * FIXME: needs doc + */ +bool eeconfig_is_disabled(void) { return (eeprom_read_word(EECONFIG_MAGIC) == EECONFIG_MAGIC_NUMBER_OFF); } + +/** \brief eeconfig read debug + * + * FIXME: needs doc + */ +uint8_t eeconfig_read_debug(void) { return eeprom_read_byte(EECONFIG_DEBUG); } +/** \brief eeconfig update debug + * + * FIXME: needs doc + */ +void eeconfig_update_debug(uint8_t val) { eeprom_update_byte(EECONFIG_DEBUG, val); } + +/** \brief eeconfig read default layer + * + * FIXME: needs doc + */ +uint8_t eeconfig_read_default_layer(void) { return eeprom_read_byte(EECONFIG_DEFAULT_LAYER); } +/** \brief eeconfig update default layer + * + * FIXME: needs doc + */ +void eeconfig_update_default_layer(uint8_t val) { eeprom_update_byte(EECONFIG_DEFAULT_LAYER, val); } + +/** \brief eeconfig read keymap + * + * FIXME: needs doc + */ +uint16_t eeconfig_read_keymap(void) { return (eeprom_read_byte(EECONFIG_KEYMAP_LOWER_BYTE) | (eeprom_read_byte(EECONFIG_KEYMAP_UPPER_BYTE) << 8)); } +/** \brief eeconfig update keymap + * + * FIXME: needs doc + */ +void eeconfig_update_keymap(uint16_t val) { + eeprom_update_byte(EECONFIG_KEYMAP_LOWER_BYTE, val & 0xFF); + eeprom_update_byte(EECONFIG_KEYMAP_UPPER_BYTE, (val >> 8) & 0xFF); +} + +/** \brief eeconfig read audio + * + * FIXME: needs doc + */ +uint8_t eeconfig_read_audio(void) { return eeprom_read_byte(EECONFIG_AUDIO); } +/** \brief eeconfig update audio + * + * FIXME: needs doc + */ +void eeconfig_update_audio(uint8_t val) { eeprom_update_byte(EECONFIG_AUDIO, val); } + +/** \brief eeconfig read kb + * + * FIXME: needs doc + */ +uint32_t eeconfig_read_kb(void) { return eeprom_read_dword(EECONFIG_KEYBOARD); } +/** \brief eeconfig update kb + * + * FIXME: needs doc + */ +void eeconfig_update_kb(uint32_t val) { eeprom_update_dword(EECONFIG_KEYBOARD, val); } + +/** \brief eeconfig read user + * + * FIXME: needs doc + */ +uint32_t eeconfig_read_user(void) { return eeprom_read_dword(EECONFIG_USER); } +/** \brief eeconfig update user + * + * FIXME: needs doc + */ +void eeconfig_update_user(uint32_t val) { eeprom_update_dword(EECONFIG_USER, val); } + +/** \brief eeconfig read haptic + * + * FIXME: needs doc + */ +uint32_t eeconfig_read_haptic(void) { return eeprom_read_dword(EECONFIG_HAPTIC); } +/** \brief eeconfig update haptic + * + * FIXME: needs doc + */ +void eeconfig_update_haptic(uint32_t val) { eeprom_update_dword(EECONFIG_HAPTIC, val); } + +/** \brief eeconfig read split handedness + * + * FIXME: needs doc + */ +bool eeconfig_read_handedness(void) { return !!eeprom_read_byte(EECONFIG_HANDEDNESS); } +/** \brief eeconfig update split handedness + * + * FIXME: needs doc + */ +void eeconfig_update_handedness(bool val) { eeprom_update_byte(EECONFIG_HANDEDNESS, !!val); } diff --git a/quantum/eeconfig.h b/quantum/eeconfig.h new file mode 100644 index 0000000000..a88071729d --- /dev/null +++ b/quantum/eeconfig.h @@ -0,0 +1,113 @@ +/* +Copyright 2013 Jun Wako <wakojun@gmail.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +#ifndef EECONFIG_MAGIC_NUMBER +# define EECONFIG_MAGIC_NUMBER (uint16_t)0xFEEA // When changing, decrement this value to avoid future re-init issues +#endif +#define EECONFIG_MAGIC_NUMBER_OFF (uint16_t)0xFFFF + +/* EEPROM parameter address */ +#define EECONFIG_MAGIC (uint16_t *)0 +#define EECONFIG_DEBUG (uint8_t *)2 +#define EECONFIG_DEFAULT_LAYER (uint8_t *)3 +#define EECONFIG_KEYMAP (uint8_t *)4 +#define EECONFIG_MOUSEKEY_ACCEL (uint8_t *)5 +#define EECONFIG_BACKLIGHT (uint8_t *)6 +#define EECONFIG_AUDIO (uint8_t *)7 +#define EECONFIG_RGBLIGHT (uint32_t *)8 +#define EECONFIG_UNICODEMODE (uint8_t *)12 +#define EECONFIG_STENOMODE (uint8_t *)13 +// EEHANDS for two handed boards +#define EECONFIG_HANDEDNESS (uint8_t *)14 +#define EECONFIG_KEYBOARD (uint32_t *)15 +#define EECONFIG_USER (uint32_t *)19 +#define EECONFIG_VELOCIKEY (uint8_t *)23 + +#define EECONFIG_HAPTIC (uint32_t *)24 + +// Mutually exclusive +#define EECONFIG_LED_MATRIX (uint32_t *)28 +#define EECONFIG_RGB_MATRIX (uint32_t *)28 +// Speed & Flags +#define EECONFIG_LED_MATRIX_EXTENDED (uint16_t *)32 +#define EECONFIG_RGB_MATRIX_EXTENDED (uint16_t *)32 + +// TODO: Combine these into a single word and single block of EEPROM +#define EECONFIG_KEYMAP_UPPER_BYTE (uint8_t *)34 +// Size of EEPROM being used, other code can refer to this for available EEPROM +#define EECONFIG_SIZE 35 +/* debug bit */ +#define EECONFIG_DEBUG_ENABLE (1 << 0) +#define EECONFIG_DEBUG_MATRIX (1 << 1) +#define EECONFIG_DEBUG_KEYBOARD (1 << 2) +#define EECONFIG_DEBUG_MOUSE (1 << 3) + +/* keyconf bit */ +#define EECONFIG_KEYMAP_SWAP_CONTROL_CAPSLOCK (1 << 0) +#define EECONFIG_KEYMAP_CAPSLOCK_TO_CONTROL (1 << 1) +#define EECONFIG_KEYMAP_SWAP_LALT_LGUI (1 << 2) +#define EECONFIG_KEYMAP_SWAP_RALT_RGUI (1 << 3) +#define EECONFIG_KEYMAP_NO_GUI (1 << 4) +#define EECONFIG_KEYMAP_SWAP_GRAVE_ESC (1 << 5) +#define EECONFIG_KEYMAP_SWAP_BACKSLASH_BACKSPACE (1 << 6) +#define EECONFIG_KEYMAP_NKRO (1 << 7) + +#define EECONFIG_KEYMAP_LOWER_BYTE EECONFIG_KEYMAP + +bool eeconfig_is_enabled(void); +bool eeconfig_is_disabled(void); + +void eeconfig_init(void); +void eeconfig_init_quantum(void); +void eeconfig_init_kb(void); +void eeconfig_init_user(void); + +void eeconfig_enable(void); + +void eeconfig_disable(void); + +uint8_t eeconfig_read_debug(void); +void eeconfig_update_debug(uint8_t val); + +uint8_t eeconfig_read_default_layer(void); +void eeconfig_update_default_layer(uint8_t val); + +uint16_t eeconfig_read_keymap(void); +void eeconfig_update_keymap(uint16_t val); + +#ifdef AUDIO_ENABLE +uint8_t eeconfig_read_audio(void); +void eeconfig_update_audio(uint8_t val); +#endif + +uint32_t eeconfig_read_kb(void); +void eeconfig_update_kb(uint32_t val); +uint32_t eeconfig_read_user(void); +void eeconfig_update_user(uint32_t val); + +#ifdef HAPTIC_ENABLE +uint32_t eeconfig_read_haptic(void); +void eeconfig_update_haptic(uint32_t val); +#endif + +bool eeconfig_read_handedness(void); +void eeconfig_update_handedness(bool val); diff --git a/quantum/keyboard.c b/quantum/keyboard.c new file mode 100644 index 0000000000..0eb41e7d30 --- /dev/null +++ b/quantum/keyboard.c @@ -0,0 +1,569 @@ +/* +Copyright 2011, 2012, 2013 Jun Wako <wakojun@gmail.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdint.h> +#include "keyboard.h" +#include "matrix.h" +#include "keymap.h" +#include "host.h" +#include "led.h" +#include "keycode.h" +#include "timer.h" +#include "sync_timer.h" +#include "print.h" +#include "debug.h" +#include "command.h" +#include "util.h" +#include "sendchar.h" +#include "eeconfig.h" +#include "action_layer.h" +#ifdef BACKLIGHT_ENABLE +# include "backlight.h" +#endif +#ifdef MOUSEKEY_ENABLE +# include "mousekey.h" +#endif +#ifdef PS2_MOUSE_ENABLE +# include "ps2_mouse.h" +#endif +#ifdef SERIAL_MOUSE_ENABLE +# include "serial_mouse.h" +#endif +#ifdef ADB_MOUSE_ENABLE +# include "adb.h" +#endif +#ifdef RGBLIGHT_ENABLE +# include "rgblight.h" +#endif +#ifdef LED_MATRIX_ENABLE +# include "led_matrix.h" +#endif +#ifdef RGB_MATRIX_ENABLE +# include "rgb_matrix.h" +#endif +#ifdef ENCODER_ENABLE +# include "encoder.h" +#endif +#ifdef STENO_ENABLE +# include "process_steno.h" +#endif +#ifdef SERIAL_LINK_ENABLE +# include "serial_link/system/serial_link.h" +#endif +#ifdef VISUALIZER_ENABLE +# include "visualizer/visualizer.h" +#endif +#ifdef POINTING_DEVICE_ENABLE +# include "pointing_device.h" +#endif +#ifdef MIDI_ENABLE +# include "process_midi.h" +#endif +#ifdef JOYSTICK_ENABLE +# include "process_joystick.h" +#endif +#ifdef HD44780_ENABLE +# include "hd44780.h" +#endif +#ifdef QWIIC_ENABLE +# include "qwiic.h" +#endif +#ifdef OLED_DRIVER_ENABLE +# include "oled_driver.h" +#endif +#ifdef ST7565_ENABLE +# include "st7565.h" +#endif +#ifdef VELOCIKEY_ENABLE +# include "velocikey.h" +#endif +#ifdef VIA_ENABLE +# include "via.h" +#endif +#ifdef DIP_SWITCH_ENABLE +# include "dip_switch.h" +#endif +#ifdef STM32_EEPROM_ENABLE +# include "eeprom_stm32.h" +#endif +#ifdef EEPROM_DRIVER +# include "eeprom_driver.h" +#endif +#if defined(CRC_ENABLE) +# include "crc.h" +#endif +#ifdef DIGITIZER_ENABLE +# include "digitizer.h" +#endif + +static uint32_t last_input_modification_time = 0; +uint32_t last_input_activity_time(void) { return last_input_modification_time; } +uint32_t last_input_activity_elapsed(void) { return timer_elapsed32(last_input_modification_time); } + +static uint32_t last_matrix_modification_time = 0; +uint32_t last_matrix_activity_time(void) { return last_matrix_modification_time; } +uint32_t last_matrix_activity_elapsed(void) { return timer_elapsed32(last_matrix_modification_time); } +void last_matrix_activity_trigger(void) { last_matrix_modification_time = last_input_modification_time = timer_read32(); } + +static uint32_t last_encoder_modification_time = 0; +uint32_t last_encoder_activity_time(void) { return last_encoder_modification_time; } +uint32_t last_encoder_activity_elapsed(void) { return timer_elapsed32(last_encoder_modification_time); } +void last_encoder_activity_trigger(void) { last_encoder_modification_time = last_input_modification_time = timer_read32(); } + +// Only enable this if console is enabled to print to +#if defined(DEBUG_MATRIX_SCAN_RATE) +static uint32_t matrix_timer = 0; +static uint32_t matrix_scan_count = 0; +static uint32_t last_matrix_scan_count = 0; + +void matrix_scan_perf_task(void) { + matrix_scan_count++; + + uint32_t timer_now = timer_read32(); + if (TIMER_DIFF_32(timer_now, matrix_timer) > 1000) { +# if defined(CONSOLE_ENABLE) + dprintf("matrix scan frequency: %lu\n", matrix_scan_count); +# endif + last_matrix_scan_count = matrix_scan_count; + matrix_timer = timer_now; + matrix_scan_count = 0; + } +} + +uint32_t get_matrix_scan_rate(void) { return last_matrix_scan_count; } +#else +# define matrix_scan_perf_task() +#endif + +#ifdef MATRIX_HAS_GHOST +extern const uint16_t keymaps[][MATRIX_ROWS][MATRIX_COLS]; +static matrix_row_t get_real_keys(uint8_t row, matrix_row_t rowdata) { + matrix_row_t out = 0; + for (uint8_t col = 0; col < MATRIX_COLS; col++) { + // read each key in the row data and check if the keymap defines it as a real key + if (pgm_read_byte(&keymaps[0][row][col]) && (rowdata & (1 << col))) { + // this creates new row data, if a key is defined in the keymap, it will be set here + out |= 1 << col; + } + } + return out; +} + +static inline bool popcount_more_than_one(matrix_row_t rowdata) { + rowdata &= rowdata - 1; // if there are less than two bits (keys) set, rowdata will become zero + return rowdata; +} + +static inline bool has_ghost_in_row(uint8_t row, matrix_row_t rowdata) { + /* No ghost exists when less than 2 keys are down on the row. + If there are "active" blanks in the matrix, the key can't be pressed by the user, + there is no doubt as to which keys are really being pressed. + The ghosts will be ignored, they are KC_NO. */ + rowdata = get_real_keys(row, rowdata); + if ((popcount_more_than_one(rowdata)) == 0) { + return false; + } + /* Ghost occurs when the row shares a column line with other row, + and two columns are read on each row. Blanks in the matrix don't matter, + so they are filtered out. + If there are two or more real keys pressed and they match columns with + at least two of another row's real keys, the row will be ignored. Keep in mind, + we are checking one row at a time, not all of them at once. + */ + for (uint8_t i = 0; i < MATRIX_ROWS; i++) { + if (i != row && popcount_more_than_one(get_real_keys(i, matrix_get_row(i)) & rowdata)) { + return true; + } + } + return false; +} + +#endif + +void disable_jtag(void) { +// To use PF4-7 (PC2-5 on ATmega32A), disable JTAG by writing JTD bit twice within four cycles. +#if (defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega16U4__) || defined(__AVR_ATmega32U4__)) + MCUCR |= _BV(JTD); + MCUCR |= _BV(JTD); +#elif defined(__AVR_ATmega32A__) + MCUCSR |= _BV(JTD); + MCUCSR |= _BV(JTD); +#endif +} + +/** \brief matrix_setup + * + * FIXME: needs doc + */ +__attribute__((weak)) void matrix_setup(void) {} + +/** \brief keyboard_pre_init_user + * + * FIXME: needs doc + */ +__attribute__((weak)) void keyboard_pre_init_user(void) {} + +/** \brief keyboard_pre_init_kb + * + * FIXME: needs doc + */ +__attribute__((weak)) void keyboard_pre_init_kb(void) { keyboard_pre_init_user(); } + +/** \brief keyboard_post_init_user + * + * FIXME: needs doc + */ + +__attribute__((weak)) void keyboard_post_init_user() {} + +/** \brief keyboard_post_init_kb + * + * FIXME: needs doc + */ + +__attribute__((weak)) void keyboard_post_init_kb(void) { keyboard_post_init_user(); } + +/** \brief keyboard_setup + * + * FIXME: needs doc + */ +void keyboard_setup(void) { +#ifndef NO_JTAG_DISABLE + disable_jtag(); +#endif + print_set_sendchar(sendchar); +#ifdef STM32_EEPROM_ENABLE + EEPROM_Init(); +#endif +#ifdef EEPROM_DRIVER + eeprom_driver_init(); +#endif + matrix_setup(); + keyboard_pre_init_kb(); +} + +#ifndef SPLIT_KEYBOARD + +/** \brief is_keyboard_master + * + * FIXME: needs doc + */ +__attribute__((weak)) bool is_keyboard_master(void) { return true; } + +/** \brief is_keyboard_left + * + * FIXME: needs doc + */ +__attribute__((weak)) bool is_keyboard_left(void) { return true; } + +#endif + +/** \brief should_process_keypress + * + * Override this function if you have a condition where keypresses processing should change: + * - splits where the slave side needs to process for rgb/oled functionality + */ +__attribute__((weak)) bool should_process_keypress(void) { return is_keyboard_master(); } + +/** \brief housekeeping_task_kb + * + * Override this function if you have a need to execute code for every keyboard main loop iteration. + * This is specific to keyboard-level functionality. + */ +__attribute__((weak)) void housekeeping_task_kb(void) {} + +/** \brief housekeeping_task_user + * + * Override this function if you have a need to execute code for every keyboard main loop iteration. + * This is specific to user/keymap-level functionality. + */ +__attribute__((weak)) void housekeeping_task_user(void) {} + +/** \brief housekeeping_task + * + * Invokes hooks for executing code after QMK is done after each loop iteration. + */ +void housekeeping_task(void) { + housekeeping_task_kb(); + housekeeping_task_user(); +} + +/** \brief keyboard_init + * + * FIXME: needs doc + */ +void keyboard_init(void) { + timer_init(); + sync_timer_init(); + matrix_init(); +#if defined(CRC_ENABLE) + crc_init(); +#endif +#ifdef VIA_ENABLE + via_init(); +#endif +#ifdef QWIIC_ENABLE + qwiic_init(); +#endif +#ifdef OLED_DRIVER_ENABLE + oled_init(OLED_ROTATION_0); +#endif +#ifdef ST7565_ENABLE + st7565_init(DISPLAY_ROTATION_0); +#endif +#ifdef PS2_MOUSE_ENABLE + ps2_mouse_init(); +#endif +#ifdef SERIAL_MOUSE_ENABLE + serial_mouse_init(); +#endif +#ifdef ADB_MOUSE_ENABLE + adb_mouse_init(); +#endif +#ifdef BACKLIGHT_ENABLE + backlight_init(); +#endif +#ifdef RGBLIGHT_ENABLE + rgblight_init(); +#endif +#ifdef ENCODER_ENABLE + encoder_init(); +#endif +#ifdef STENO_ENABLE + steno_init(); +#endif +#ifdef POINTING_DEVICE_ENABLE + pointing_device_init(); +#endif +#if defined(NKRO_ENABLE) && defined(FORCE_NKRO) + keymap_config.nkro = 1; + eeconfig_update_keymap(keymap_config.raw); +#endif +#ifdef DIP_SWITCH_ENABLE + dip_switch_init(); +#endif + +#if defined(DEBUG_MATRIX_SCAN_RATE) && defined(CONSOLE_ENABLE) + debug_enable = true; +#endif + + keyboard_post_init_kb(); /* Always keep this last */ +} + +/** \brief key_event_task + * + * This function is responsible for calling into other systems when they need to respond to electrical switch press events. + * This is differnet than keycode events as no layer processing, or filtering occurs. + */ +void switch_events(uint8_t row, uint8_t col, bool pressed) { +#if defined(LED_MATRIX_ENABLE) + process_led_matrix(row, col, pressed); +#endif +#if defined(RGB_MATRIX_ENABLE) + process_rgb_matrix(row, col, pressed); +#endif +} + +/** \brief Keyboard task: Do keyboard routine jobs + * + * Do routine keyboard jobs: + * + * * scan matrix + * * handle mouse movements + * * run visualizer code + * * handle midi commands + * * light LEDs + * + * This is repeatedly called as fast as possible. + */ +void keyboard_task(void) { + static matrix_row_t matrix_prev[MATRIX_ROWS]; + static uint8_t led_status = 0; + matrix_row_t matrix_row = 0; + matrix_row_t matrix_change = 0; +#ifdef QMK_KEYS_PER_SCAN + uint8_t keys_processed = 0; +#endif +#ifdef ENCODER_ENABLE + bool encoders_changed = false; +#endif + + uint8_t matrix_changed = matrix_scan(); + if (matrix_changed) last_matrix_activity_trigger(); + + for (uint8_t r = 0; r < MATRIX_ROWS; r++) { + matrix_row = matrix_get_row(r); + matrix_change = matrix_row ^ matrix_prev[r]; + if (matrix_change) { +#ifdef MATRIX_HAS_GHOST + if (has_ghost_in_row(r, matrix_row)) { + continue; + } +#endif + if (debug_matrix) matrix_print(); + matrix_row_t col_mask = 1; + for (uint8_t c = 0; c < MATRIX_COLS; c++, col_mask <<= 1) { + if (matrix_change & col_mask) { + if (should_process_keypress()) { + action_exec((keyevent_t){ + .key = (keypos_t){.row = r, .col = c}, .pressed = (matrix_row & col_mask), .time = (timer_read() | 1) /* time should not be 0 */ + }); + } + // record a processed key + matrix_prev[r] ^= col_mask; + + switch_events(r, c, (matrix_row & col_mask)); + +#ifdef QMK_KEYS_PER_SCAN + // only jump out if we have processed "enough" keys. + if (++keys_processed >= QMK_KEYS_PER_SCAN) +#endif + // process a key per task call + goto MATRIX_LOOP_END; + } + } + } + } + // call with pseudo tick event when no real key event. +#ifdef QMK_KEYS_PER_SCAN + // we can get here with some keys processed now. + if (!keys_processed) +#endif + action_exec(TICK); + +MATRIX_LOOP_END: + +#ifdef DEBUG_MATRIX_SCAN_RATE + matrix_scan_perf_task(); +#endif + +#if defined(RGBLIGHT_ENABLE) + rgblight_task(); +#endif + +#ifdef LED_MATRIX_ENABLE + led_matrix_task(); +#endif +#ifdef RGB_MATRIX_ENABLE + rgb_matrix_task(); +#endif + +#if defined(BACKLIGHT_ENABLE) +# if defined(BACKLIGHT_PIN) || defined(BACKLIGHT_PINS) + backlight_task(); +# endif +#endif + +#ifdef ENCODER_ENABLE + encoders_changed = encoder_read(); + if (encoders_changed) last_encoder_activity_trigger(); +#endif + +#ifdef QWIIC_ENABLE + qwiic_task(); +#endif + +#ifdef OLED_DRIVER_ENABLE + oled_task(); +# ifndef OLED_DISABLE_TIMEOUT + // Wake up oled if user is using those fabulous keys or spinning those encoders! +# ifdef ENCODER_ENABLE + if (matrix_changed || encoders_changed) oled_on(); +# else + if (matrix_changed) oled_on(); +# endif +# endif +#endif + +#ifdef ST7565_ENABLE + st7565_task(); +# ifndef ST7565_DISABLE_TIMEOUT + // Wake up display if user is using those fabulous keys or spinning those encoders! +# ifdef ENCODER_ENABLE + if (matrix_changed || encoders_changed) st7565_on(); +# else + if (matrix_changed) st7565_on(); +# endif +# endif +#endif + +#ifdef MOUSEKEY_ENABLE + // mousekey repeat & acceleration + mousekey_task(); +#endif + +#ifdef PS2_MOUSE_ENABLE + ps2_mouse_task(); +#endif + +#ifdef SERIAL_MOUSE_ENABLE + serial_mouse_task(); +#endif + +#ifdef ADB_MOUSE_ENABLE + adb_mouse_task(); +#endif + +#ifdef SERIAL_LINK_ENABLE + serial_link_update(); +#endif + +#ifdef VISUALIZER_ENABLE + visualizer_update(default_layer_state, layer_state, visualizer_get_mods(), host_keyboard_leds()); +#endif + +#ifdef POINTING_DEVICE_ENABLE + pointing_device_task(); +#endif + +#ifdef MIDI_ENABLE + midi_task(); +#endif + +#ifdef VELOCIKEY_ENABLE + if (velocikey_enabled()) { + velocikey_decelerate(); + } +#endif + +#ifdef JOYSTICK_ENABLE + joystick_task(); +#endif + +#ifdef DIGITIZER_ENABLE + digitizer_task(); +#endif + + // update LED + if (led_status != host_keyboard_leds()) { + led_status = host_keyboard_leds(); + keyboard_set_leds(led_status); + } +} + +/** \brief keyboard set leds + * + * FIXME: needs doc + */ +void keyboard_set_leds(uint8_t leds) { + if (debug_keyboard) { + debug("keyboard_set_led: "); + debug_hex8(leds); + debug("\n"); + } + led_set(leds); +} diff --git a/quantum/keyboard.h b/quantum/keyboard.h new file mode 100644 index 0000000000..08f4e84f94 --- /dev/null +++ b/quantum/keyboard.h @@ -0,0 +1,90 @@ +/* +Copyright 2011,2012,2013 Jun Wako <wakojun@gmail.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#pragma once + +#include <stdbool.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* key matrix position */ +typedef struct { + uint8_t col; + uint8_t row; +} keypos_t; + +/* key event */ +typedef struct { + keypos_t key; + bool pressed; + uint16_t time; +} keyevent_t; + +/* equivalent test of keypos_t */ +#define KEYEQ(keya, keyb) ((keya).row == (keyb).row && (keya).col == (keyb).col) + +/* Rules for No Event: + * 1) (time == 0) to handle (keyevent_t){} as empty event + * 2) Matrix(255, 255) to make TICK event available + */ +static inline bool IS_NOEVENT(keyevent_t event) { return event.time == 0 || (event.key.row == 255 && event.key.col == 255); } +static inline bool IS_PRESSED(keyevent_t event) { return (!IS_NOEVENT(event) && event.pressed); } +static inline bool IS_RELEASED(keyevent_t event) { return (!IS_NOEVENT(event) && !event.pressed); } + +/* Tick event */ +#define TICK \ + (keyevent_t) { .key = (keypos_t){.row = 255, .col = 255}, .pressed = false, .time = (timer_read() | 1) } + +/* it runs once at early stage of startup before keyboard_init. */ +void keyboard_setup(void); +/* it runs once after initializing host side protocol, debug and MCU peripherals. */ +void keyboard_init(void); +/* it runs repeatedly in main loop */ +void keyboard_task(void); +/* it runs when host LED status is updated */ +void keyboard_set_leds(uint8_t leds); +/* it runs whenever code has to behave differently on a slave */ +bool is_keyboard_master(void); +/* it runs whenever code has to behave differently on left vs right split */ +bool is_keyboard_left(void); + +void keyboard_pre_init_kb(void); +void keyboard_pre_init_user(void); +void keyboard_post_init_kb(void); +void keyboard_post_init_user(void); + +void housekeeping_task(void); // To be executed by the main loop in each backend TMK protocol +void housekeeping_task_kb(void); // To be overridden by keyboard-level code +void housekeeping_task_user(void); // To be overridden by user/keymap-level code + +uint32_t last_input_activity_time(void); // Timestamp of the last matrix or encoder activity +uint32_t last_input_activity_elapsed(void); // Number of milliseconds since the last matrix or encoder activity + +uint32_t last_matrix_activity_time(void); // Timestamp of the last matrix activity +uint32_t last_matrix_activity_elapsed(void); // Number of milliseconds since the last matrix activity + +uint32_t last_encoder_activity_time(void); // Timestamp of the last encoder activity +uint32_t last_encoder_activity_elapsed(void); // Number of milliseconds since the last encoder activity + +uint32_t get_matrix_scan_rate(void); + +#ifdef __cplusplus +} +#endif diff --git a/quantum/keycode.h b/quantum/keycode.h new file mode 100644 index 0000000000..8facabd818 --- /dev/null +++ b/quantum/keycode.h @@ -0,0 +1,560 @@ +/* +Copyright 2011,2012 Jun Wako <wakojun@gmail.com> + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Keycodes based on HID Keyboard/Keypad Usage Page (0x07) plus media keys from Generic Desktop Page (0x01) and Consumer Page (0x0C) + * + * See https://web.archive.org/web/20060218214400/http://www.usb.org/developers/devclass_docs/Hut1_12.pdf + * or http://www.usb.org/developers/hidpage/Hut1_12v2.pdf (older) + */ + +#pragma once + +/* FIXME: Add doxygen comments here */ + +#define IS_ERROR(code) (KC_ROLL_OVER <= (code) && (code) <= KC_UNDEFINED) +#define IS_ANY(code) (KC_A <= (code) && (code) <= 0xFF) +#define IS_KEY(code) (KC_A <= (code) && (code) <= KC_EXSEL) +#define IS_MOD(code) (KC_LCTRL <= (code) && (code) <= KC_RGUI) + +#define IS_SPECIAL(code) ((0xA5 <= (code) && (code) <= 0xDF) || (0xE8 <= (code) && (code) <= 0xFF)) +#define IS_SYSTEM(code) (KC_PWR <= (code) && (code) <= KC_WAKE) +#define IS_CONSUMER(code) (KC_MUTE <= (code) && (code) <= KC_BRID) + +#define IS_FN(code) (KC_FN0 <= (code) && (code) <= KC_FN31) + +#define IS_MOUSEKEY(code) (KC_MS_UP <= (code) && (code) <= KC_MS_ACCEL2) +#define IS_MOUSEKEY_MOVE(code) (KC_MS_UP <= (code) && (code) <= KC_MS_RIGHT) +#define IS_MOUSEKEY_BUTTON(code) (KC_MS_BTN1 <= (code) && (code) <= KC_MS_BTN8) +#define IS_MOUSEKEY_WHEEL(code) (KC_MS_WH_UP <= (code) && (code) <= KC_MS_WH_RIGHT) +#define IS_MOUSEKEY_ACCEL(code) (KC_MS_ACCEL0 <= (code) && (code) <= KC_MS_ACCEL2) + +#define MOD_BIT(code) (1 << MOD_INDEX(code)) +#define MOD_INDEX(code) ((code)&0x07) + +#define MOD_MASK_CTRL (MOD_BIT(KC_LCTRL) | MOD_BIT(KC_RCTRL)) +#define MOD_MASK_SHIFT (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)) +#define MOD_MASK_ALT (MOD_BIT(KC_LALT) | MOD_BIT(KC_RALT)) +#define MOD_MASK_GUI (MOD_BIT(KC_LGUI) | MOD_BIT(KC_RGUI)) +#define MOD_MASK_CS (MOD_MASK_CTRL | MOD_MASK_SHIFT) +#define MOD_MASK_CA (MOD_MASK_CTRL | MOD_MASK_ALT) +#define MOD_MASK_CG (MOD_MASK_CTRL | MOD_MASK_GUI) +#define MOD_MASK_SA (MOD_MASK_SHIFT | MOD_MASK_ALT) +#define MOD_MASK_SG (MOD_MASK_SHIFT | MOD_MASK_GUI) +#define MOD_MASK_AG (MOD_MASK_ALT | MOD_MASK_GUI) +#define MOD_MASK_CSA (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_ALT) +#define MOD_MASK_CSG (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_GUI) +#define MOD_MASK_CAG (MOD_MASK_CTRL | MOD_MASK_ALT | MOD_MASK_GUI) +#define MOD_MASK_SAG (MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI) +#define MOD_MASK_CSAG (MOD_MASK_CTRL | MOD_MASK_SHIFT | MOD_MASK_ALT | MOD_MASK_GUI) + +#define FN_BIT(code) (1 << FN_INDEX(code)) +#define FN_INDEX(code) ((code)-KC_FN0) +#define FN_MIN KC_FN0 +#define FN_MAX KC_FN31 + +/* + * Short names for ease of definition of keymap + */ +/* Transparent */ +#define KC_TRANSPARENT 0x01 +#define KC_TRNS KC_TRANSPARENT + +/* Punctuation */ +#define KC_ENT KC_ENTER +#define KC_ESC KC_ESCAPE +#define KC_BSPC KC_BSPACE +#define KC_SPC KC_SPACE +#define KC_MINS KC_MINUS +#define KC_EQL KC_EQUAL +#define KC_LBRC KC_LBRACKET +#define KC_RBRC KC_RBRACKET +#define KC_BSLS KC_BSLASH +#define KC_NUHS KC_NONUS_HASH +#define KC_SCLN KC_SCOLON +#define KC_QUOT KC_QUOTE +#define KC_GRV KC_GRAVE +#define KC_COMM KC_COMMA +#define KC_SLSH KC_SLASH +#define KC_NUBS KC_NONUS_BSLASH + +/* Lock Keys */ +#define KC_CLCK KC_CAPSLOCK +#define KC_CAPS KC_CAPSLOCK +#define KC_SLCK KC_SCROLLLOCK +#define KC_NLCK KC_NUMLOCK +#define KC_LCAP KC_LOCKING_CAPS +#define KC_LNUM KC_LOCKING_NUM +#define KC_LSCR KC_LOCKING_SCROLL + +/* Commands */ +#define KC_PSCR KC_PSCREEN +#define KC_PAUS KC_PAUSE +#define KC_BRK KC_PAUSE +#define KC_INS KC_INSERT +#define KC_DEL KC_DELETE +#define KC_PGDN KC_PGDOWN +#define KC_RGHT KC_RIGHT +#define KC_APP KC_APPLICATION +#define KC_EXEC KC_EXECUTE +#define KC_SLCT KC_SELECT +#define KC_AGIN KC_AGAIN +#define KC_PSTE KC_PASTE +#define KC_ERAS KC_ALT_ERASE +#define KC_CLR KC_CLEAR + +/* Keypad */ +#define KC_PSLS KC_KP_SLASH +#define KC_PAST KC_KP_ASTERISK +#define KC_PMNS KC_KP_MINUS +#define KC_PPLS KC_KP_PLUS +#define KC_PENT KC_KP_ENTER +#define KC_P1 KC_KP_1 +#define KC_P2 KC_KP_2 +#define KC_P3 KC_KP_3 +#define KC_P4 KC_KP_4 +#define KC_P5 KC_KP_5 +#define KC_P6 KC_KP_6 +#define KC_P7 KC_KP_7 +#define KC_P8 KC_KP_8 +#define KC_P9 KC_KP_9 +#define KC_P0 KC_KP_0 +#define KC_PDOT KC_KP_DOT +#define KC_PEQL KC_KP_EQUAL +#define KC_PCMM KC_KP_COMMA + +/* Japanese specific */ +#define KC_ZKHK KC_GRAVE +#define KC_RO KC_INT1 +#define KC_KANA KC_INT2 +#define KC_JYEN KC_INT3 +#define KC_HENK KC_INT4 +#define KC_MHEN KC_INT5 + +/* Korean specific */ +#define KC_HAEN KC_LANG1 +#define KC_HANJ KC_LANG2 + +/* Modifiers */ +#define KC_LCTL KC_LCTRL +#define KC_LSFT KC_LSHIFT +#define KC_LOPT KC_LALT +#define KC_LCMD KC_LGUI +#define KC_LWIN KC_LGUI +#define KC_RCTL KC_RCTRL +#define KC_RSFT KC_RSHIFT +#define KC_ALGR KC_RALT +#define KC_ROPT KC_RALT +#define KC_RCMD KC_RGUI +#define KC_RWIN KC_RGUI + +/* Generic Desktop Page (0x01) */ +#define KC_PWR KC_SYSTEM_POWER +#define KC_SLEP KC_SYSTEM_SLEEP +#define KC_WAKE KC_SYSTEM_WAKE + +/* Consumer Page (0x0C) */ +#define KC_MUTE KC_AUDIO_MUTE +#define KC_VOLU KC_AUDIO_VOL_UP +#define KC_VOLD KC_AUDIO_VOL_DOWN +#define KC_MNXT KC_MEDIA_NEXT_TRACK +#define KC_MPRV KC_MEDIA_PREV_TRACK +#define KC_MSTP KC_MEDIA_STOP +#define KC_MPLY KC_MEDIA_PLAY_PAUSE +#define KC_MSEL KC_MEDIA_SELECT +#define KC_EJCT KC_MEDIA_EJECT +#define KC_CALC KC_CALCULATOR +#define KC_MYCM KC_MY_COMPUTER +#define KC_WSCH KC_WWW_SEARCH +#define KC_WHOM KC_WWW_HOME +#define KC_WBAK KC_WWW_BACK +#define KC_WFWD KC_WWW_FORWARD +#define KC_WSTP KC_WWW_STOP +#define KC_WREF KC_WWW_REFRESH +#define KC_WFAV KC_WWW_FAVORITES +#define KC_MFFD KC_MEDIA_FAST_FORWARD +#define KC_MRWD KC_MEDIA_REWIND +#define KC_BRIU KC_BRIGHTNESS_UP +#define KC_BRID KC_BRIGHTNESS_DOWN + +/* System Specific */ +#define KC_BRMU KC_PAUSE +#define KC_BRMD KC_SCROLLLOCK + +/* Mouse Keys */ +#define KC_MS_U KC_MS_UP +#define KC_MS_D KC_MS_DOWN +#define KC_MS_L KC_MS_LEFT +#define KC_MS_R KC_MS_RIGHT +#define KC_BTN1 KC_MS_BTN1 +#define KC_BTN2 KC_MS_BTN2 +#define KC_BTN3 KC_MS_BTN3 +#define KC_BTN4 KC_MS_BTN4 +#define KC_BTN5 KC_MS_BTN5 +#define KC_BTN6 KC_MS_BTN6 +#define KC_BTN7 KC_MS_BTN7 +#define KC_BTN8 KC_MS_BTN8 +#define KC_WH_U KC_MS_WH_UP +#define KC_WH_D KC_MS_WH_DOWN +#define KC_WH_L KC_MS_WH_LEFT +#define KC_WH_R KC_MS_WH_RIGHT +#define KC_ACL0 KC_MS_ACCEL0 +#define KC_ACL1 KC_MS_ACCEL1 +#define KC_ACL2 KC_MS_ACCEL2 + +/* Keyboard/Keypad Page (0x07) */ +enum hid_keyboard_keypad_usage { + KC_NO = 0x00, + KC_ROLL_OVER, + KC_POST_FAIL, + KC_UNDEFINED, + KC_A, + KC_B, + KC_C, + KC_D, + KC_E, + KC_F, + KC_G, + KC_H, + KC_I, + KC_J, + KC_K, + KC_L, + KC_M, // 0x10 + KC_N, + KC_O, + KC_P, + KC_Q, + KC_R, + KC_S, + KC_T, + KC_U, + KC_V, + KC_W, + KC_X, + KC_Y, + KC_Z, + KC_1, + KC_2, + KC_3, // 0x20 + KC_4, + KC_5, + KC_6, + KC_7, + KC_8, + KC_9, + KC_0, + KC_ENTER, + KC_ESCAPE, + KC_BSPACE, + KC_TAB, + KC_SPACE, + KC_MINUS, + KC_EQUAL, + KC_LBRACKET, + KC_RBRACKET, // 0x30 + KC_BSLASH, + KC_NONUS_HASH, + KC_SCOLON, + KC_QUOTE, + KC_GRAVE, + KC_COMMA, + KC_DOT, + KC_SLASH, + KC_CAPSLOCK, + KC_F1, + KC_F2, + KC_F3, + KC_F4, + KC_F5, + KC_F6, + KC_F7, // 0x40 + KC_F8, + KC_F9, + KC_F10, + KC_F11, + KC_F12, + KC_PSCREEN, + KC_SCROLLLOCK, + KC_PAUSE, + KC_INSERT, + KC_HOME, + KC_PGUP, + KC_DELETE, + KC_END, + KC_PGDOWN, + KC_RIGHT, + KC_LEFT, // 0x50 + KC_DOWN, + KC_UP, + KC_NUMLOCK, + KC_KP_SLASH, + KC_KP_ASTERISK, + KC_KP_MINUS, + KC_KP_PLUS, + KC_KP_ENTER, + KC_KP_1, + KC_KP_2, + KC_KP_3, + KC_KP_4, + KC_KP_5, + KC_KP_6, + KC_KP_7, + KC_KP_8, // 0x60 + KC_KP_9, + KC_KP_0, + KC_KP_DOT, + KC_NONUS_BSLASH, + KC_APPLICATION, + KC_POWER, + KC_KP_EQUAL, + KC_F13, + KC_F14, + KC_F15, + KC_F16, + KC_F17, + KC_F18, + KC_F19, + KC_F20, + KC_F21, // 0x70 + KC_F22, + KC_F23, + KC_F24, + KC_EXECUTE, + KC_HELP, + KC_MENU, + KC_SELECT, + KC_STOP, + KC_AGAIN, + KC_UNDO, + KC_CUT, + KC_COPY, + KC_PASTE, + KC_FIND, + KC__MUTE, + KC__VOLUP, // 0x80 + KC__VOLDOWN, + KC_LOCKING_CAPS, + KC_LOCKING_NUM, + KC_LOCKING_SCROLL, + KC_KP_COMMA, + KC_KP_EQUAL_AS400, + KC_INT1, + KC_INT2, + KC_INT3, + KC_INT4, + KC_INT5, + KC_INT6, + KC_INT7, + KC_INT8, + KC_INT9, + KC_LANG1, // 0x90 + KC_LANG2, + KC_LANG3, + KC_LANG4, + KC_LANG5, + KC_LANG6, + KC_LANG7, + KC_LANG8, + KC_LANG9, + KC_ALT_ERASE, + KC_SYSREQ, + KC_CANCEL, + KC_CLEAR, + KC_PRIOR, + KC_RETURN, + KC_SEPARATOR, + KC_OUT, // 0xA0 + KC_OPER, + KC_CLEAR_AGAIN, + KC_CRSEL, + KC_EXSEL, + +#if 0 + // *************************************************************** + // These keycodes are present in the HID spec, but are * + // nonfunctional on modern OSes. QMK uses this range (0xA5-0xDF) * + // for the media and function keys instead - see below. * + // *************************************************************** + + KC_KP_00 = 0xB0, + KC_KP_000, + KC_THOUSANDS_SEPARATOR, + KC_DECIMAL_SEPARATOR, + KC_CURRENCY_UNIT, + KC_CURRENCY_SUB_UNIT, + KC_KP_LPAREN, + KC_KP_RPAREN, + KC_KP_LCBRACKET, + KC_KP_RCBRACKET, + KC_KP_TAB, + KC_KP_BSPACE, + KC_KP_A, + KC_KP_B, + KC_KP_C, + KC_KP_D, + KC_KP_E, //0xC0 + KC_KP_F, + KC_KP_XOR, + KC_KP_HAT, + KC_KP_PERC, + KC_KP_LT, + KC_KP_GT, + KC_KP_AND, + KC_KP_LAZYAND, + KC_KP_OR, + KC_KP_LAZYOR, + KC_KP_COLON, + KC_KP_HASH, + KC_KP_SPACE, + KC_KP_ATMARK, + KC_KP_EXCLAMATION, + KC_KP_MEM_STORE, //0xD0 + KC_KP_MEM_RECALL, + KC_KP_MEM_CLEAR, + KC_KP_MEM_ADD, + KC_KP_MEM_SUB, + KC_KP_MEM_MUL, + KC_KP_MEM_DIV, + KC_KP_PLUS_MINUS, + KC_KP_CLEAR, + KC_KP_CLEAR_ENTRY, + KC_KP_BINARY, + KC_KP_OCTAL, + KC_KP_DECIMAL, + KC_KP_HEXADECIMAL, +#endif + + /* Modifiers */ + KC_LCTRL = 0xE0, + KC_LSHIFT, + KC_LALT, + KC_LGUI, + KC_RCTRL, + KC_RSHIFT, + KC_RALT, + KC_RGUI + + // ********************************************** + // * 0xF0-0xFF are unallocated in the HID spec. * + // * QMK uses these for Mouse Keys - see below. * + // ********************************************** +}; + +/* Media and Function keys */ +enum internal_special_keycodes { + /* Generic Desktop Page (0x01) */ + KC_SYSTEM_POWER = 0xA5, + KC_SYSTEM_SLEEP, + KC_SYSTEM_WAKE, + + /* Consumer Page (0x0C) */ + KC_AUDIO_MUTE, + KC_AUDIO_VOL_UP, + KC_AUDIO_VOL_DOWN, + KC_MEDIA_NEXT_TRACK, + KC_MEDIA_PREV_TRACK, + KC_MEDIA_STOP, + KC_MEDIA_PLAY_PAUSE, + KC_MEDIA_SELECT, + KC_MEDIA_EJECT, // 0xB0 + KC_MAIL, + KC_CALCULATOR, + KC_MY_COMPUTER, + KC_WWW_SEARCH, + KC_WWW_HOME, + KC_WWW_BACK, + KC_WWW_FORWARD, + KC_WWW_STOP, + KC_WWW_REFRESH, + KC_WWW_FAVORITES, + KC_MEDIA_FAST_FORWARD, + KC_MEDIA_REWIND, + KC_BRIGHTNESS_UP, + KC_BRIGHTNESS_DOWN, + + /* Fn keys */ + KC_FN0 = 0xC0, + KC_FN1, + KC_FN2, + KC_FN3, + KC_FN4, + KC_FN5, + KC_FN6, + KC_FN7, + KC_FN8, + KC_FN9, + KC_FN10, + KC_FN11, + KC_FN12, + KC_FN13, + KC_FN14, + KC_FN15, + KC_FN16, // 0xD0 + KC_FN17, + KC_FN18, + KC_FN19, + KC_FN20, + KC_FN21, + KC_FN22, + KC_FN23, + KC_FN24, + KC_FN25, + KC_FN26, + KC_FN27, + KC_FN28, + KC_FN29, + KC_FN30, + KC_FN31 +}; + +enum mouse_keys { +/* Mouse Buttons */ +#ifdef VIA_ENABLE + KC_MS_UP = 0xF0, +#else + KC_MS_UP = 0xED, +#endif + KC_MS_DOWN, + KC_MS_LEFT, + KC_MS_RIGHT, // 0xF0 + KC_MS_BTN1, + KC_MS_BTN2, + KC_MS_BTN3, + KC_MS_BTN4, + KC_MS_BTN5, +#ifdef VIA_ENABLE + KC_MS_BTN6 = KC_MS_BTN5, + KC_MS_BTN7 = KC_MS_BTN5, + KC_MS_BTN8 = KC_MS_BTN5, +#else + KC_MS_BTN6, + KC_MS_BTN7, + KC_MS_BTN8, +#endif + + /* Mouse Wheel */ + KC_MS_WH_UP, + KC_MS_WH_DOWN, + KC_MS_WH_LEFT, + KC_MS_WH_RIGHT, + + /* Acceleration */ + KC_MS_ACCEL0, + KC_MS_ACCEL1, + KC_MS_ACCEL2 // 0xFF +}; diff --git a/quantum/via.h b/quantum/via.h index d0510fcabd..de0a21da20 100644 --- a/quantum/via.h +++ b/quantum/via.h @@ -16,7 +16,7 @@ #pragma once -#include "tmk_core/common/eeconfig.h" // for EECONFIG_SIZE +#include "eeconfig.h" // for EECONFIG_SIZE // Keyboard level code can change where VIA stores the magic. // The magic is the build date YYMMDD encoded as BCD in 3 bytes, |