diff --git a/keyboards/ergodox/keymaps/algernon/Makefile b/keyboards/ergodox/keymaps/algernon/Makefile index 00194857a..7c2b3ad97 100644 --- a/keyboards/ergodox/keymaps/algernon/Makefile +++ b/keyboards/ergodox/keymaps/algernon/Makefile @@ -1,22 +1,23 @@ BOOTMAGIC_ENABLE=no COMMAND_ENABLE=no SLEEP_LED_ENABLE=no -UNICODE_ENABLE=no FORCE_NKRO ?= yes DEBUG_ENABLE = no CONSOLE_ENABLE = no TAP_DANCE_ENABLE = yes KEYLOGGER_ENABLE ?= yes +UCIS_ENABLE = yes +MOUSEKEY_ENABLE ?= yes -ADORE_AUTOLOG ?= no +AUTOLOG_ENABLE ?= no ifeq (${FORCE_NKRO},yes) OPT_DEFS += -DFORCE_NKRO endif -ifeq (${ADORE_AUTOLOG},yes) +ifeq (${AUTOLOG_ENABLE},yes) KEYLOGGER_ENABLE = yes -OPT_DEFS += -DADORE_AUTOLOG +OPT_DEFS += -DAUTOLOG_ENABLE endif ifeq (${KEYLOGGER_ENABLE},yes) diff --git a/keyboards/ergodox/keymaps/algernon/images/adore-layer.png b/keyboards/ergodox/keymaps/algernon/images/adore-layer.png index 44ad8a1eb..3d0a5fb20 100644 Binary files a/keyboards/ergodox/keymaps/algernon/images/adore-layer.png and b/keyboards/ergodox/keymaps/algernon/images/adore-layer.png differ diff --git a/keyboards/ergodox/keymaps/algernon/images/base-layer.png b/keyboards/ergodox/keymaps/algernon/images/base-layer.png index da159fbe2..7ea57aae3 100644 Binary files a/keyboards/ergodox/keymaps/algernon/images/base-layer.png and b/keyboards/ergodox/keymaps/algernon/images/base-layer.png differ diff --git a/keyboards/ergodox/keymaps/algernon/keymap.c b/keyboards/ergodox/keymaps/algernon/keymap.c index 144030e2e..9f476d8a6 100644 --- a/keyboards/ergodox/keymaps/algernon/keymap.c +++ b/keyboards/ergodox/keymaps/algernon/keymap.c @@ -2,6 +2,7 @@ * algernon's ErgoDox EZ layout, please see the readme.md file! */ +#include #include "ergodox.h" #include "led.h" #include "debug.h" @@ -33,7 +34,6 @@ enum { // Buttons that do extra stuff A_GUI, A_PLVR, - A_ESC, A_MPN, // Function / number keys @@ -47,7 +47,6 @@ enum { KF_8, KF_9, KF_10, - KF_11, // =, F11 // Application select keys APP_SLK, // Slack @@ -62,6 +61,11 @@ enum { A_MDL, A_MDR, + // Mouse acceleration + A_ACL0, + A_ACL1, + A_ACL2, + // Hungarian layer keys HU_AA, // Á HU_OO, // Ó @@ -89,7 +93,6 @@ enum { enum { CT_CLN = 0, - CT_MNS, CT_TA, CT_LBP, CT_RBP @@ -102,7 +105,11 @@ uint16_t gui_timer = 0; uint16_t kf_timers[12]; #if KEYLOGGER_ENABLE +# ifdef AUTOLOG_ENABLE +bool log_enable = true; +# else bool log_enable = false; +# endif #endif bool time_travel = false; @@ -114,12 +121,12 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { /* Keymap 0: Base Layer * * ,-----------------------------------------------------. ,-----------------------------------------------------. - * | Next/Prev | 1 F1 | 2 F2 | 3 F3 | 4 F4 | 5 F5 | Plvr | | Apps | 6 F6 | 7 F7 | 8 F8 | 9 F9 | 0 F10| F11 | + * | Next/Prev | 1 F1 | 2 F2 | 3 F3 | 4 F4 | 5 F5 | Plvr | | F12 | 6 F6 | 7 F7 | 8 F8 | 9 F9 | 0 F10| F11 | * |-----------+------+------+------+------+-------------| |------+------+------+------+------+------+-----------| - * | ~ | ' | , | . | P | Y | [ | | ] | F | G | C | R | L | \ | - * |-----------+------+------+------+------+------| | | |------+------+------+------+------+-----------| + * | ~ | ' | , | . | P | Y | ( | | ) | F | G | C | R | L | \ | + * |-----------+------+------+------+------+------| [ | | ] |------+------+------+------+------+-----------| * | Tab/ARROW | A | O | E | U | I |------| |------| D | H | T | N | S | = / Arrow | - * |-----------+------+------+------+------+------| ( | | ) |------+------+------+------+------+-----------| + * |-----------+------+------+------+------+------| : | | - |------+------+------+------+------+-----------| * | Play/Pause| / | Q | J | K | X | | | | B | M | W | V | Z | Stop | * `-----------+------+------+------+------+-------------' `-------------+------+------+------+------+-----------' * | | | | | : | | - | | | | | @@ -135,21 +142,21 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { [BASE] = KEYMAP( // left hand M(A_MPN) ,M(KF_1) ,M(KF_2) ,M(KF_3),M(KF_4),M(KF_5),M(A_PLVR) -,KC_GRV ,KC_QUOT ,KC_COMM ,KC_DOT ,KC_P ,KC_Y ,KC_LBRC +,KC_GRV ,KC_QUOT ,KC_COMM ,KC_DOT ,KC_P ,KC_Y ,TD(CT_LBP) ,TD(CT_TA) ,KC_A ,KC_O ,KC_E ,KC_U ,KC_I -,KC_MPLY ,KC_SLSH ,KC_Q ,KC_J ,KC_K ,KC_X ,KC_LPRN +,KC_MPLY ,KC_SLSH ,KC_Q ,KC_J ,KC_K ,KC_X ,TD(CT_CLN) ,KC_NO ,KC_NO ,KC_NO ,KC_NO ,TD(CT_CLN) ,F(F_ALT),F(F_GUI) ,F(F_CTRL) - ,KC_BSPC,F(F_SFT),M(A_ESC) + ,KC_BSPC,F(F_SFT),KC_ESC // right hand - ,KC_APP ,M(KF_6),M(KF_7) ,M(KF_8),M(KF_9) ,M(KF_10) ,KC_F11 - ,KC_RBRC ,KC_F ,KC_G ,KC_C ,KC_R ,KC_L ,KC_BSLS + ,KC_F12 ,M(KF_6),M(KF_7) ,M(KF_8),M(KF_9) ,M(KF_10) ,KC_F11 + ,TD(CT_RBP),KC_F ,KC_G ,KC_C ,KC_R ,KC_L ,KC_BSLS ,KC_D ,KC_H ,KC_T ,KC_N ,KC_S ,KC_EQL - ,KC_RPRN ,KC_B ,KC_M ,KC_W ,KC_V ,KC_Z ,KC_MSTP - ,TD(CT_MNS),KC_NO ,KC_NO ,KC_NO ,KC_NO + ,KC_MINS ,KC_B ,KC_M ,KC_W ,KC_V ,KC_Z ,KC_MSTP + ,KC_MINS ,KC_NO ,KC_NO ,KC_NO ,KC_NO ,OSL(NMDIA),KC_DEL ,KC_LEAD @@ -159,13 +166,13 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { /* Keymap 1: Adore layer * * ,-----------------------------------------------------. ,-----------------------------------------------------. - * | Play/Pause| 1 F1 | 2 F2 | 3 F3 | 4 F4 | 5 F5 | Plvr | | Apps | 6 F6 | 7 F7 | 8 F8 | 9 F9 | 0 F10| F11 | + * | Play/Pause| 1 F1 | 2 F2 | 3 F3 | 4 F4 | 5 F5 | Plvr | | F12 | 6 F6 | 7 F7 | 8 F8 | 9 F9 | 0 F10| F11 | * |-----------+------+------+------+------+-------------| |------+------+------+------+------+------+-----------| - * | `~ | X | W | K | L | M | ( | | ) | F | H | C | P | Y | \ | + * | `~ | Y | W | G | L | M | ( | | ) | F | H | C | P | X | \ | * |-----------+------+------+------+------+------| [ | | ] |------+------+------+------+------+-----------| * | Tab/Arrow | A | O | E | I | U |------| |------| D | R | T | N | S | = | * |-----------+------+------+------+------+------| | | |------+------+------+------+------+-----------| - * | | Z | Q | ' | , | . | : | | - | B | G | V | J | / | | + * | | Z | Q | ' | , | . | : | | - | B | K | V | J | / | | * `-----------+------+------+------+------+-------------' `-------------+------+------+------+------+-----------' * | | | | | | | | | | | | * `-----------------------------------' `-----------------------------------' @@ -180,20 +187,20 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { [ADORE] = KEYMAP( // left hand KC_MPLY ,M(KF_1) ,M(KF_2) ,M(KF_3),M(KF_4),M(KF_5),M(A_PLVR) -,KC_GRV ,KC_X ,KC_W ,KC_K ,KC_L ,KC_M ,TD(CT_LBP) +,KC_GRV ,KC_Y ,KC_W ,KC_G ,KC_L ,KC_M ,TD(CT_LBP) ,TD(CT_TA) ,KC_A ,KC_O ,KC_E ,KC_I ,KC_U ,KC_NO ,KC_Z ,KC_Q ,KC_QUOT,KC_COMM,KC_DOT ,TD(CT_CLN) ,KC_NO ,KC_NO ,KC_NO ,KC_NO ,KC_NO ,F(F_ALT),F(F_GUI) ,F(F_CTRL) - ,KC_BSPC,F(F_SFT),M(A_ESC) + ,KC_BSPC,F(F_SFT),KC_ESC // right hand - ,KC_APP ,M(KF_6),M(KF_7),M(KF_8),M(KF_9) ,M(KF_10) ,KC_F11 - ,TD(CT_RBP),KC_F ,KC_H ,KC_C ,KC_P ,KC_Y ,KC_BSLS + ,KC_F12 ,M(KF_6),M(KF_7),M(KF_8),M(KF_9) ,M(KF_10) ,KC_F11 + ,TD(CT_RBP),KC_F ,KC_H ,KC_C ,KC_P ,KC_X ,KC_BSLS ,KC_D ,KC_R ,KC_T ,KC_N ,KC_S ,KC_EQL - ,TD(CT_MNS),KC_B ,KC_G ,KC_V ,KC_J ,KC_SLSH ,KC_NO + ,KC_MINS ,KC_B ,KC_K ,KC_V ,KC_J ,KC_SLSH ,KC_NO ,KC_NO ,KC_NO ,KC_NO ,KC_NO ,KC_NO ,OSL(NMDIA),KC_DEL @@ -363,14 +370,14 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { */ [NMDIA] = KEYMAP( // left hand - KC_ACL0 ,KC_NO ,KC_NO ,KC_NO ,KC_NO ,KC_NO ,LGUI(KC_L) -,KC_ACL1 ,KC_NO ,KC_HOME ,KC_UP ,KC_PGUP ,KC_NO ,KC_NO -,KC_ACL2 ,KC_NO ,KC_LEFT ,KC_DOWN ,KC_RIGHT,KC_NO + M(A_ACL0) ,KC_NO ,KC_NO ,KC_NO ,KC_NO ,KC_NO ,LGUI(KC_L) +,M(A_ACL1) ,KC_NO ,KC_HOME ,KC_UP ,KC_PGUP ,KC_NO ,KC_NO +,M(A_ACL2) ,KC_NO ,KC_LEFT ,KC_DOWN ,KC_RIGHT,KC_NO ,KC_MPLY ,KC_NO ,KC_END ,KC_DOWN ,KC_PGDN ,KC_NO ,KC_NO ,KC_NO ,KC_NO ,KC_NO ,KC_NO ,KC_NO ,KC_MUTE ,KC_VOLU ,KC_VOLD - ,KC_SPC,KC_ENTER,M(A_ESC) + ,KC_SPC,KC_ENTER,KC_ESC // right hand ,LGUI(KC_L),KC_NO ,KC_NO ,KC_NO ,KC_NO ,KC_NO ,KC_NO @@ -439,7 +446,7 @@ const uint16_t PROGMEM fn_actions[] = { ,[F_CTRL] = ACTION_MODS_ONESHOT (MOD_LCTL) }; -void toggle_steno(int pressed) +static void toggle_steno(int pressed) { uint8_t layer = biton32(layer_state); @@ -462,7 +469,7 @@ void toggle_steno(int pressed) } } -macro_t *ang_do_hun (keyrecord_t *record, uint16_t accent, uint16_t hun_char) +static macro_t *ang_do_hun (keyrecord_t *record, uint16_t accent, uint16_t hun_char) { uint8_t need_shift = 0; uint8_t hold_shift = 0; @@ -507,46 +514,43 @@ macro_t *ang_do_hun (keyrecord_t *record, uint16_t accent, uint16_t hun_char) return MACRO_NONE; } -void ang_handle_kf (keyrecord_t *record, uint8_t id) +static bool from_appsel; + +static void ang_handle_kf (keyrecord_t *record, uint8_t id) { uint8_t code = id - KF_1; if (record->event.pressed) { kf_timers[code] = timer_read (); } else { - uint8_t kc; + uint8_t kc_base; - if (timer_elapsed (kf_timers[code]) > TAPPING_TERM) { - // Long press - kc = KC_F1 + code; - } else { - if (id == KF_11) - kc = KC_EQL; - else - kc = KC_1 + code; + if (from_appsel) { + from_appsel = false; + return; } - register_code (kc); - unregister_code (kc); + if (kf_timers[code] && timer_elapsed (kf_timers[code]) > TAPPING_TERM) { + // Long press + kc_base = KC_F1; + } else { + kc_base = KC_1; + } + kf_timers[code] = 0; + code += kc_base; + + register_code (code); + unregister_code (code); } } +static struct { + uint8_t idx; +} m_accel_state; + const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) { switch(id) { - case A_ESC: - if (record->event.pressed) { - if ((get_oneshot_mods ()) && !has_oneshot_mods_timed_out ()) { - clear_oneshot_mods (); - } else { - register_code (KC_ESC); - } - layer_off (HUN); - } else { - unregister_code (KC_ESC); - } - break; - case A_MPN: if (record->event.pressed) { if (keyboard_report->mods & MOD_BIT(KC_LSFT) || @@ -588,6 +592,7 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) case HU_UEE: return ang_do_hun (record, KC_EQL, KC_U); +#if MOUSEKEY_ENABLE /* Mouse movement */ case A_MUL: if (record->event.pressed) { @@ -633,6 +638,24 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) mousekey_send(); break; + case A_ACL0 ... A_ACL2: + if (record->event.pressed) { + uint8_t idx = id - A_ACL0; + if (m_accel_state.idx == id) { + mousekey_off(m_accel_state.idx - A_ACL0 + KC_ACL0); + m_accel_state.idx = 0; + } else { + if (m_accel_state.idx) { + mousekey_off(m_accel_state.idx - A_ACL0 + KC_ACL0); + m_accel_state.idx = 0; + } + mousekey_on(KC_ACL0 + idx); + m_accel_state.idx = id; + } + } + break; +#endif + /* Plover base */ case A_PLVR: toggle_steno(record->event.pressed); @@ -663,22 +686,34 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) break; case APP_SLK: + from_appsel = true; return MACRODOWN(T(S), T(L), T(A), T(C), T(K), T(ENT), END); case APP_EMCS: + from_appsel = true; return MACRODOWN(T(G), T(N), T(U), T(SPC), T(E), T(M), T(A), T(C), T(S), T(SPC), T(2), T(4), T(ENT), END); case APP_TERM: - return MACRODOWN(T(T), T(E), T(R), T(M), T(ENT), END); + from_appsel = true; + if (!record->event.pressed) { + register_code(KC_ESC); + unregister_code(KC_ESC); + wait_ms(TAPPING_TERM + 25); + register_code(KC_DEL); + unregister_code(KC_DEL); + } + break; case APP_CHRM: + from_appsel = true; return MACRODOWN(T(C), T(H), T(R), T(O), T(M), T(ENT), END); case APP_MSIC: + from_appsel = true; return MACRODOWN(T(R), T(H), T(Y), T(T), T(H), T(M), T(B), T(O), T(X), T(ENT), END); /* Function keys */ - case KF_1 ... KF_11: + case KF_1 ... KF_10: ang_handle_kf (record, id); break; } @@ -686,12 +721,14 @@ const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) return MACRO_NONE; }; -uint8_t is_adore = 0; +static uint8_t is_adore = 0; // Runs just one time when the keyboard initializes. void matrix_init_user(void) { uint8_t dl; + set_unicode_input_mode(UC_LNX); + ergodox_led_all_on(); for (int i = LED_BRIGHTNESS_HI; i > LED_BRIGHTNESS_LO; i--) { ergodox_led_all_set (i); @@ -709,120 +746,38 @@ void matrix_init_user(void) { dl = eeconfig_read_default_layer (); if (dl == (1UL << ADORE)) { is_adore = 1; -#if ADORE_AUTOLOG - log_enable = true; -#endif } }; LEADER_EXTERNS(); -void ang_do_unicode (void) { - register_code (KC_RCTL); - register_code (KC_RSFT); - register_code (KC_U); - unregister_code (KC_U); - unregister_code (KC_RSFT); - unregister_code (KC_RCTL); - wait_ms (100); -} +static void ang_tap (uint8_t code, ...) { + uint8_t kc = code; + va_list ap; -void ang_tap (uint16_t codes[]) { - for (int i = 0; codes[i] != 0; i++) { - register_code (codes[i]); - unregister_code (codes[i]); - wait_ms (50); - } + va_start(ap, code); + + do { + register_code(kc); + unregister_code(kc); + wait_ms(50); + kc = va_arg(ap, int); + } while (kc != 0); + va_end(ap); } #define TAP_ONCE(code) \ register_code (code); \ unregister_code (code) -void ang_tap_dance_bp_finished (qk_tap_dance_state_t *state, void *user_data) { - bool left, parens; - - if (state->count > 2) { - state->count = 0; - return; - } - - if (state->keycode == TD(CT_LBP)) - left = true; - else - left = false; - - if (state->count == 1) - parens = false; - else - parens = true; - - if (parens) { - register_code (KC_RSFT); - if (left) { - TAP_ONCE(KC_9); - } else { - TAP_ONCE(KC_0); - } - unregister_code (KC_RSFT); - } else { - if (left) { - TAP_ONCE (KC_LBRC); - } else { - TAP_ONCE (KC_RBRC); - } - } -} - -void ang_tap_dance_cln_finished (qk_tap_dance_state_t *state, void *user_data) { - if (state->count == 1) { - register_code (KC_RSFT); - register_code (KC_SCLN); - } else if (state->count == 2) { - register_code (KC_SCLN); - } -} - -void ang_tap_dance_cln_reset (qk_tap_dance_state_t *state, void *user_data) { - if (state->count == 1) { - unregister_code (KC_SCLN); - unregister_code (KC_RSFT); - } else if (state->count == 2) { - unregister_code (KC_SCLN); - } -} - -void ang_tap_dance_mns_finished (qk_tap_dance_state_t *state, void *user_data) { - if (state->count == 1) { - register_code (KC_MINS); - } else if (state->count == 2) { - register_code (KC_RSFT); - register_code (KC_MINS); - } -} - -void ang_tap_dance_mns_reset (qk_tap_dance_state_t *state, void *user_data) { - if (state->count == 1) { - unregister_code (KC_MINS); - } else if (state->count == 2) { - unregister_code (KC_RSFT); - unregister_code (KC_MINS); - } -} - typedef struct { bool layer_toggle; bool sticky; - bool finished_once; } td_ta_state_t; -void ang_tap_dance_ta_finished (qk_tap_dance_state_t *state, void *user_data) { +static void ang_tap_dance_ta_finished (qk_tap_dance_state_t *state, void *user_data) { td_ta_state_t *td_ta = (td_ta_state_t *) user_data; - if (td_ta->finished_once) { - return; - } - if (td_ta->sticky) { td_ta->sticky = false; td_ta->layer_toggle = false; @@ -830,7 +785,6 @@ void ang_tap_dance_ta_finished (qk_tap_dance_state_t *state, void *user_data) { return; } - td_ta->finished_once = true; if (state->count == 1 && !state->pressed) { register_code (KC_TAB); td_ta->sticky = false; @@ -842,35 +796,29 @@ void ang_tap_dance_ta_finished (qk_tap_dance_state_t *state, void *user_data) { } } -void ang_tap_dance_ta_reset (qk_tap_dance_state_t *state, void *user_data) { +static void ang_tap_dance_ta_reset (qk_tap_dance_state_t *state, void *user_data) { td_ta_state_t *td_ta = (td_ta_state_t *) user_data; if (!td_ta->layer_toggle) unregister_code (KC_TAB); if (!td_ta->sticky) layer_off (ARRW); - - td_ta->finished_once = false; } qk_tap_dance_action_t tap_dance_actions[] = { - [CT_CLN] = ACTION_TAP_DANCE_FN_ADVANCED (NULL, ang_tap_dance_cln_finished, ang_tap_dance_cln_reset) - ,[CT_MNS] = ACTION_TAP_DANCE_FN_ADVANCED (NULL, ang_tap_dance_mns_finished, ang_tap_dance_mns_reset) + [CT_CLN] = ACTION_TAP_DANCE_DOUBLE (KC_COLN, KC_SCLN) ,[CT_TA] = { .fn = { NULL, ang_tap_dance_ta_finished, ang_tap_dance_ta_reset }, - .user_data = (void *)&((td_ta_state_t) { false, false, false }) + .user_data = (void *)&((td_ta_state_t) { false, false }) } - ,[CT_LBP] = ACTION_TAP_DANCE_FN (ang_tap_dance_bp_finished) - ,[CT_RBP] = ACTION_TAP_DANCE_FN (ang_tap_dance_bp_finished) + ,[CT_LBP] = ACTION_TAP_DANCE_DOUBLE (KC_LBRC, KC_LPRN) + ,[CT_RBP] = ACTION_TAP_DANCE_DOUBLE (KC_RBRC, KC_RPRN) }; -static uint16_t uni[32]; -static uint8_t unicnt; -static bool unimagic = false; - // Runs constantly in the background, in a loop. void matrix_scan_user(void) { uint8_t layer = biton32(layer_state); + bool is_arrow = false; if (gui_timer && timer_elapsed (gui_timer) > TAPPING_TERM) unregister_code (KC_LGUI); @@ -893,13 +841,19 @@ void matrix_scan_user(void) { ergodox_right_led_2_set (LED_BRIGHTNESS_HI); } + if (layer_state & (1UL << ARRW)) { + ergodox_right_led_1_on (); + ergodox_right_led_3_on (); + is_arrow = true; + } + if (keyboard_report->mods & MOD_BIT(KC_LSFT) || ((get_oneshot_mods() & MOD_BIT(KC_LSFT)) && !has_oneshot_mods_timed_out())) { ergodox_right_led_1_set (LED_BRIGHTNESS_HI); ergodox_right_led_1_on (); } else { ergodox_right_led_1_set (LED_BRIGHTNESS_LO); - if (layer != NMDIA && layer != PLVR && layer != ADORE) + if (layer != NMDIA && layer != PLVR && layer != ADORE && !is_arrow) ergodox_right_led_1_off (); } @@ -919,7 +873,7 @@ void matrix_scan_user(void) { ergodox_right_led_3_on (); } else { ergodox_right_led_3_set (LED_BRIGHTNESS_LO); - if (layer != HUN && layer != PLVR && layer != ADORE) + if (layer != HUN && layer != PLVR && layer != ADORE && !is_arrow) ergodox_right_led_3_off (); } @@ -936,20 +890,17 @@ void matrix_scan_user(void) { } #endif + SEQ_ONE_KEY (KC_Q) { + register_code16 (LCTL(KC_1)); + unregister_code16 (LCTL(KC_1)); + } + SEQ_ONE_KEY (KC_T) { time_travel = !time_travel; } SEQ_ONE_KEY (KC_U) { - ang_do_unicode (); - } - - SEQ_TWO_KEYS (KC_LEAD, KC_U) { - unicnt = 0; - unimagic = true; - register_code(KC_RSFT); - TAP_ONCE(KC_U); - unregister_code(KC_RSFT); + qk_ucis_start(); } SEQ_ONE_KEY (KC_V) { @@ -958,25 +909,23 @@ void matrix_scan_user(void) { SEQ_ONE_KEY (KC_L) { /* λ */ - ang_do_unicode (); - - uint16_t codes[] = {KC_0, KC_3, KC_B, KC_B, KC_ENT, 0}; - ang_tap (codes); + unicode_input_start(); + register_hex(0x03bb); + unicode_input_finish(); } SEQ_ONE_KEY (KC_Y) { - uint16_t codes[] = {KC_BSLS, KC_O, KC_SLSH, 0}; - ang_tap (codes); + ang_tap (KC_BSLS, KC_O, KC_SLSH, 0); } SEQ_ONE_KEY (KC_S) { - ang_do_unicode (); TAP_ONCE (KC_A); TAP_ONCE (KC_F); TAP_ONCE (KC_SPC); + unicode_input_start(); register_hex(0xaf); unicode_input_finish(); TAP_ONCE (KC_BSLS); register_code (KC_RSFT); TAP_ONCE (KC_MINS); TAP_ONCE (KC_9); unregister_code (KC_RSFT); - ang_do_unicode (); TAP_ONCE (KC_3); TAP_ONCE (KC_0); TAP_ONCE (KC_C); TAP_ONCE (KC_4); TAP_ONCE (KC_SPC); + unicode_input_start (); register_hex(0x30c4); unicode_input_finish(); register_code (KC_RSFT); TAP_ONCE (KC_0); TAP_ONCE (KC_MINS); unregister_code (KC_RSFT); TAP_ONCE (KC_SLSH); - ang_do_unicode (); TAP_ONCE (KC_A); TAP_ONCE (KC_F); TAP_ONCE (KC_SPC); + unicode_input_start (); register_hex(0xaf); unicode_input_finish(); } SEQ_TWO_KEYS (KC_W, KC_M) { @@ -987,8 +936,7 @@ void matrix_scan_user(void) { wait_ms (1000); - uint16_t codes[] = {KC_M, KC_A, KC_X, KC_MINS, KC_F, KC_O, KC_C, KC_U, KC_S, KC_E, KC_D, KC_ENT, 0}; - ang_tap (codes); + ang_tap (KC_M, KC_A, KC_X, KC_MINS, KC_F, KC_O, KC_C, KC_U, KC_S, KC_E, KC_D, KC_ENT, 0); register_code (KC_LGUI); register_code (KC_UP); unregister_code (KC_UP); @@ -1013,13 +961,7 @@ void matrix_scan_user(void) { ergodox_right_led_2_off (); wait_ms (100); ergodox_right_led_1_off (); -#if ADORE_AUTOLOG - log_enable = true; -#endif } else { -#if ADORE_AUTOLOG - log_enable = false; -#endif is_adore = 0; default_layer_and (0); default_layer_or (1UL << BASE); @@ -1043,140 +985,38 @@ void matrix_scan_user(void) { static uint16_t last4[4]; -bool is_uni_seq(char *seq) { - uint8_t i; - - for (i = 0; seq[i]; i++) { - uint16_t code; - if (('1' <= seq[i]) && (seq[i] <= '9')) - code = seq[i] - '1' + KC_1; - else if (seq[i] == '0') - code = KC_0; - else - code = seq[i] - 'a' + KC_A; - - if (i > unicnt) - return false; - if (uni[i] != code) - return false; - } - - if (uni[i] == KC_ENT || uni[i] == KC_SPC) - return true; - - return false; -} - -uint16_t hex_to_keycode(uint8_t hex) -{ - if (hex == 0x0) { - return KC_0; - } else if (hex < 0xA) { - return KC_1 + (hex - 0x1); - } else { - return KC_A + (hex - 0xA); - } -} - -void register_hex(uint16_t hex) { - bool leading_zeros = true; - - for(int i = 3; i >= 0; i--) { - uint8_t digit = ((hex >> (i*4)) & 0xF); - if (digit != 0) - leading_zeros = false; - else if (leading_zeros) - continue; - register_code(hex_to_keycode(digit)); - unregister_code(hex_to_keycode(digit)); - wait_ms(10); - } -} - -typedef struct { - char *symbol; - uint16_t codes[4]; -} qk_ucis_symbol_t; - -static qk_ucis_symbol_t ucis_symbol_table[] = { - {"poop", {0x1, 0xf4a9, 0}}, - {"rofl", {0x1, 0xf923, 0}}, - {"kiss", {0x1, 0xf619, 0}}, - {"snowman", {0x2603, 0}}, - {NULL, {}} -}; - -bool process_record_ucis (uint16_t keycode, keyrecord_t *record) { - uint8_t i; - - if (!unimagic) - return true; - - if (!record->event.pressed) - return true; - - uni[unicnt] = keycode; - unicnt++; - - if (keycode == KC_BSPC) { - if (unicnt >= 2) { - unicnt-= 2; - return true; - } else { - unicnt--; - return false; - } - } - - if (keycode == KC_ENT || keycode == KC_SPC) { - bool symbol_found = false; - - for (i = unicnt; i > 0; i--) { - register_code (KC_BSPC); - unregister_code (KC_BSPC); - wait_ms(10); - } - - ang_do_unicode(); - wait_ms(10); - for (i = 0; ucis_symbol_table[i].symbol; i++) { - if (is_uni_seq (ucis_symbol_table[i].symbol)) { - symbol_found = true; - for (uint8_t j = 0; ucis_symbol_table[i].codes[j]; j++) { - register_hex(ucis_symbol_table[i].codes[j]); - } - break; - } - } - if (!symbol_found) { - for (i = 0; i < unicnt - 1; i++) { - uint8_t code; - - if (uni[i] > KF_1) - code = uni[i] - KF_1 + KC_1; - else - code = uni[i]; - TAP_ONCE(code); - wait_ms (10); - } - } - - unimagic = false; - return true; - } - return true; -} +const qk_ucis_symbol_t ucis_symbol_table[] = UCIS_TABLE +( + UCIS_SYM("poop", 0x1f4a9), + UCIS_SYM("rofl", 0x1f923), + UCIS_SYM("kiss", 0x1f619), + UCIS_SYM("snowman", 0x2603), + UCIS_SYM("coffee", 0x2615), + UCIS_SYM("heart", 0x2764), + UCIS_SYM("bolt", 0x26a1) +); bool process_record_user (uint16_t keycode, keyrecord_t *record) { #if KEYLOGGER_ENABLE if (log_enable) { - xprintf ("KL: col=%d, row=%d\n", record->event.key.col, - record->event.key.row); + xprintf ("KL: col=%02d, row=%02d, pressed=%d, layer=%s\n", record->event.key.col, + record->event.key.row, record->event.pressed, (is_adore) ? "ADORE" : "Dvorak"); } #endif - if (!process_record_ucis (keycode, record)) - return false; + if (keycode == KC_ESC && record->event.pressed) { + bool queue = true; + + if ((get_oneshot_mods ()) && !has_oneshot_mods_timed_out ()) { + clear_oneshot_mods (); + queue = false; + } + if (layer_state & (1UL<event.pressed) { uint8_t p; @@ -1188,15 +1028,13 @@ bool process_record_user (uint16_t keycode, keyrecord_t *record) { last4[3] = keycode; if (last4[0] == KC_D && last4[1] == KC_A && last4[2] == KC_T && last4[3] == KC_E) { - uint16_t codes[] = {KC_E, KC_SPC, KC_MINS, KC_D, KC_SPC, KC_QUOT, 0}; - ang_tap (codes); + ang_tap (KC_E, KC_SPC, KC_MINS, KC_D, KC_SPC, KC_QUOT, 0); register_code (KC_RSFT); register_code (KC_EQL); unregister_code (KC_EQL); unregister_code (KC_RSFT); - uint16_t codes2[] = {KC_4, KC_SPC, KC_D, KC_A, KC_Y, KC_S, KC_QUOT, 0}; - ang_tap (codes2); + ang_tap (KC_4, KC_SPC, KC_D, KC_A, KC_Y, KC_S, KC_QUOT, 0); return false; } @@ -1204,3 +1042,17 @@ bool process_record_user (uint16_t keycode, keyrecord_t *record) { return true; } + +void qk_ucis_symbol_fallback (void) { + for (uint8_t i = 0; i < qk_ucis_state.count - 1; i++) { + uint8_t code; + + if (qk_ucis_state.codes[i] > KF_1) + code = qk_ucis_state.codes[i] - KF_1 + KC_1; + else + code = qk_ucis_state.codes[i]; + register_code(code); + unregister_code(code); + wait_ms (10); + } +} diff --git a/keyboards/ergodox/keymaps/algernon/readme.md b/keyboards/ergodox/keymaps/algernon/readme.md index 26dfddbe6..5e7adb332 100644 --- a/keyboards/ergodox/keymaps/algernon/readme.md +++ b/keyboards/ergodox/keymaps/algernon/readme.md @@ -3,11 +3,15 @@ algernon's layout ======================= -This is an unconventional layout for the ErgoDox EZ. For more details about the history of the layout, see my [blog posts about my ErgoDox journey][blog-ergodox]. +This is an unconventional layout for the [ErgoDox EZ][ez]. For more details about the history of the layout, see my [blog posts about my ErgoDox journey][blog-ergodox]. + [ez]: https://ergodox-ez.com/ [blog-ergodox]: https://asylum.madhouse-project.org/blog/tags/ergodox/ -Some of the things in the layout only work when one uses Spacemacs and GNOME under Linux. Your mileage may vary. +Some of the things in the layout only work when one uses [Spacemacs][spacemacs] and [GNOME][gnome] under Linux. Your mileage may vary. + + [spacemacs]: http://spacemacs.org/ + [gnome]: https://www.gnome.org/ ## Table of Contents @@ -40,10 +44,10 @@ At its core, this is a Dvorak layout, with some minor changes. The more interest * The `GUI` key is special, because when I double-tap it, it sends `GUI + w`, which pops up an application selector. It also switches to a one-shot layer, where the number row on the left half turns into app selector macros, for the most common things I usually want to switch to. Otherwise it behaves as on a normal layout. * The `ESC` key also doubles as a one-shot cancel key: if tapped while any of the one-shot modifiers are in-flight (as in, single-tapped, and not expired yet), it cancels all one-shot modifiers. It also cancels the **Hun** layer, if active. Otherwise it sends the usual keycode. * The **Media** and **Hun** layer keys are one-shot, the **STENO** key is a toggle. -* When holding any of the **Arrow** layer keys, the arrow layer activates while the layer key is held. Tapping the key produces the normal key. +* When holding the `Tab`/**Arrow** key, the arrow layer activates while the key is held. Tapping the key produces the normal, `Tab` key. Double-tapping it toggles the **Arrow** layer on until a third tap. * Tapping the `:` key once yields `:`, tapping it twice yields `;`. +* Tapping the `[{(`/`)}]` keys once yields `[` (or `{` when shifted), tapping them twice yields `(`. * The **Lead** key allows me to type in a sequence of keys, and trigger some actions: - - `LEAD u` enters unicode input mode, by sending the GTK+ key sequence that does this. - `LEAD l` uses the unicode input method to enter a `λ`. - `LEAD s` does a lot of magic to type in a shruggie: `¯\_(ツ)_/¯` - `LEAD y` types `\o/`. @@ -52,7 +56,7 @@ At its core, this is a Dvorak layout, with some minor changes. The more interest - `LEAD v` prints the firmware version, the keyboard and the keymap. - `LEAD d` toggles logging keypress positions to the HID console. - `LEAD t` toggles time travel. Figuring out the current `date` is left as an exercise to the reader. - - `LEAD LEAD u` enters the [Unicode symbol input][#unicode-symbol-input] mode. + - `LEAD u` enters the [Unicode symbol input](#unicode-symbol-input) mode. ## ADORE layer @@ -91,6 +95,7 @@ For the layers, the following rules apply: * When the [ADORE layer](#adore-layer) is toggled on, LEDs will light up from left to right in a sequence, then turn off. When the layer is toggled off, the LEDs light up and turn off in the other direction. No LEDs are on while the layer is active. * When the [Hungarian layer](#hungarian-layer) is active, the *green* and *blue* LEDs are on. * When the [Navigation and media layer](#navigation-and-media-layer) is active, the *red* and *green* ones are on. +* When the **ARROW** layer is active, the *red* and *blue* ones are on. * For the [Steno layer](#steno-layer), all LEDs will be turned on. Unless noted otherwise, the layers use a dim light for the LEDs, while modifiers use a stronger one, and modifiers override any layer preferences. For example, when on the one-handed layer, with the left side active (*red* light blinking), if `Shift` is on, the *red* light will be constantly on. @@ -116,7 +121,7 @@ This is an experimental feature, and may or may not work reliably. When the keypress logging functionality is enabled (by `LEAD d`), the keyboard will output a line every time a key is pressed, containing the position of the key in the matrix. This allows one to collect this information, and build analytics over it, such as a heat map, including dead keys too. -Included with the firmware is a small tool that can parse these logs, and create a heatmap that one can import into [KLE][kle]. To use it, simply point `tools/log-to-heatmap.py` to a base layout file (one is included in the `tools/` directory), and the key position log. The latter one can create by running `hid-listen`, and redirecting its output to a file. +Included with the firmware is a small tool that can parse these logs, and create a heatmap that one can import into [KLE][kle]. To use it, either pipe the output of `hid_listen` into it, or pipe it an already saved log, and it will save the results into files in an output directory (given on the command-line). See the output of `tools/log-to-heatmap.py --help` for more information. [kle]: http://www.keyboard-layout-editor.com/ @@ -151,7 +156,35 @@ The keymap default to forcing NKRO, which seems to upset Windows, and except the # Changelog -## v1.5 - 2016-08-12 +## v1.6 + +*2016-08-24* + +### Base layer changes + +* The parentheses & bracket keys have been merged: tapping them results in `[` or `{` (if it was shifted), double tapping leads to `(`. +* The `:;` and `-_` keys are now available on the base layer, on their [ADORE](#adore-layer) location, too, just below `[{(`/`]})`. +* The `Apps` key has been replaced by `F12`. +* The `-`/`_` is no longer a tap-dance key. + +### ADORE layer changes + +* Adjustments were made to the [ADORE](#adore-layer) layer, to separate some inconvenient combinations. + +### Miscellaneous changes + +* `LEAD u` now starts the symbolic unicode input system, instead of the OS-one. +* The mouse acceleration keys on the [Navigation and Media](#navigation-and-media-layer) layer have been turned into toggles: tap them once to turn them on, until tapped again. Tapping an accelerator button will turn all the others off. +* When the **ARROW** layer is on, the *red* and *blue* LEDs light up now. + +### Heatmap + +* The built-in keylogger has been greatly enhanced, it now outputs the pressed state, and the layer (Dvorak or ADORE). As such, the `ADORE_AUTOLOG` option has been removed, instead there is `AUTOLOG_ENABLE` now, which when enabled, makes the keylogger start when the keyboard boots. It defaults to off. +* The heatmap generator received a lot of updates. + +## v1.5 + +*2016-08-12* * The **1HAND** layer has been removed. * A `Delete` key is now available on the right thumb cluster. @@ -161,7 +194,9 @@ The keymap default to forcing NKRO, which seems to upset Windows, and except the * On the **ARROW** layer, `Backspace` has been replaced by `Enter`. * There is some experimental support for entering Unicode symbols. -## v1.4 - 2016-07-29 +## v1.4 + +*2016-07-29* * When toggling the key logging on or off, the LEDs will do a little dance. * The keylogger is now optional, but enabled by default. Use `KEYLOGGER_ENABLE=no` on the `make` command line to disable it. @@ -169,14 +204,18 @@ The keymap default to forcing NKRO, which seems to upset Windows, and except the * The `-`/`_` key was turned into a tap-dance key too. * There is now a way to travel time with the keyboard, toggle the feature on by hitting `LEAD t`. -## v1.3 - 2016-07-06 +## v1.3 + +*2016-07-06* * Added support for logging keys, by pressing `LEAD d`. Also included is a tool to generate a [heatmap](#heatmap) out of the logs. * The arrow and navigation keys were rearranged again, and now require an additional key being held to activate. See the [base layer](#base-layer) for an image that shows where arrows are. * The **experimental** layer has been redone, and is now called [ADORE](#adore-layer), and as such, can be enabled by `LEAD a` now. * Switching between Dvorak and ADORE is now persisted into EEPROM, and survives a reboot. -## v1.2 - 2016-06-22 +## v1.2 + +*2016-06-22* * The forced NKRO mode can be easily toggled off at compile-time, to make the firmware compatible with [certain operating systems](#using-on-windows). * The `:;` key has changed behaviour: to access the `;` symbol, the key needs to be double-tapped, instead of shifted. @@ -187,7 +226,9 @@ The keymap default to forcing NKRO, which seems to upset Windows, and except the * On the **experimental** layer, the `L` and `Q`, and the `K` and `G` keys were swapped. * The [Steno](#steno-layer) layer gained a few more `#` and `*` keys, to make it easier on my fingers. -## v1.1 - 2016-06-14 +## v1.1 + +*2016-06-14* * The keyboard starts in NKRO mode, bootmagic and other things are disabled. * A [Steno](#steno-layer) layer was added, to be used with Plover. @@ -201,7 +242,9 @@ The keymap default to forcing NKRO, which seems to upset Windows, and except the - `:` now inputs `;` when shifted. * `ESC` cancels the [Hungarian](#hungarian-layer) layer too, not just modifiers. -## v1.0 - 2016-05-26 +## v1.0 + +*2016-05-26* Initial version. diff --git a/keyboards/ergodox/keymaps/algernon/tools/heatmap-adore-layout.json b/keyboards/ergodox/keymaps/algernon/tools/heatmap-layout.ADORE.json similarity index 91% rename from keyboards/ergodox/keymaps/algernon/tools/heatmap-adore-layout.json rename to keyboards/ergodox/keymaps/algernon/tools/heatmap-layout.ADORE.json index 544f61ce8..4dfa877a7 100644 --- a/keyboards/ergodox/keymaps/algernon/tools/heatmap-adore-layout.json +++ b/keyboards/ergodox/keymaps/algernon/tools/heatmap-layout.ADORE.json @@ -75,9 +75,9 @@ "STENO", { "x": 4.5, - "f": 6 + "f": 3 }, - "", + "F12", { "c": "#7adabd", "a": 4, @@ -94,12 +94,20 @@ { "y": -0.875, "c": "#ffb2d2", - "f": 3, + "f": 9, + "a": 6, "w": 1.5 }, - "\n\n~\n`", + "\n\n", { - "t": "#0d0d0b" + "t": "#0d0d0b", + "f": 3, + "a": 4, + "fa": [ + 0, + 0, + 2 + ] }, "!\n1\nF1", { @@ -120,7 +128,7 @@ "t": "#000000", "a": 6 }, - "L", + "G", { "x": 10.5 }, @@ -132,16 +140,16 @@ "x": 2.5, "c": "#bfbad1", "t": "#0d0d0b", - "a": 4 + "a": 6 }, - ">\n.", + "W", { "x": 1, "c": "#7adabd", "t": "#000000", "a": 6 }, - "W", + "L", { "x": 8.5 }, @@ -164,14 +172,15 @@ { "c": "#93c9b7", "a": 4, + "fa": [0, 0, 0], "h": 1.5 }, - "{\n[", + "{\n(\n[", { "x": 4.5, "h": 1.5 }, - "}\n]", + "}\n)\n]", { "c": "#7adabd", "a": 6 @@ -183,21 +192,22 @@ "y": -0.875, "c": "#ffb07b", "t": "#0d0d0b", - "f": 6, + "f": 3, + "a": 4, "w": 1.5 }, - "\n\n", + "\n\n~\n`", { "c": "#ffb2d2", - "a": 4, + "a": 6, "f": 3 }, - "<\n,", + "Y", { "x": 14.5, "a": 6 }, - "Y", + "X", { "a": 4, "w": 1.5 @@ -300,15 +310,14 @@ "x": 6.5, "c": "#93c9b7", "t": "#000000", - "a": 7, "h": 1.5 }, - "(", + ";\n:", { "x": 4.5, "h": 1.5 }, - ")" + "_\n-" ], [ { @@ -320,7 +329,9 @@ }, "\"\n'", { - "x": 10.5 + "x": 10.5, + "a": 6, + "f": 3 }, "V" ], @@ -329,19 +340,22 @@ "y": -0.875, "x": 2.5, "c": "#bfbad1", - "t": "#0d0d0b" + "t": "#0d0d0b", + "a": 6 }, - "Z", + "Q", { "x": 1, "c": "#7adabd", - "t": "#000000" + "t": "#000000", + "a": 4 + }, + "<\n,", + { + "x": 8.5, + "a": 6 }, "K", - { - "x": 8.5 - }, - "G", { "x": 1, "c": "#bfbad1", @@ -354,11 +368,13 @@ "y": -0.875, "x": 5.5, "c": "#7adabd", - "t": "#000000" + "t": "#000000", + "a": 4 }, - "X", + ">\n.", { - "x": 6.5 + "x": 6.5, + "a": 6 }, "B" ], @@ -367,28 +383,32 @@ "y": -0.875, "c": "#ffb07b", "f": 9, - "w": 1.5 + "w": 1.5, + "g": true }, - "\n\n", + "", { "c": "#ffb2d2", "t": "#0d0d0b", - "a": 4, - "f": 3 + "a": 6, + "f": 3, + "g": false }, - "?\n/", + "Z", { "x": 14.5, - "a": 6 + "a": 4 }, - "Q", + "?\n/", { "c": "#ffb07b", "t": "#000000", "f": 9, - "w": 1.5 + "g": true, + "w": 1.5, + "a": 4 }, - "" + "" ], [ { @@ -414,14 +434,14 @@ { "x": 1, "c": "#d4872a", - "g": false, + "g": true, "a": 5 }, - ";\n:", + "", { "x": 8.5 }, - "_\n-", + "", { "x": 1, "c": "#d9dae0", @@ -503,13 +523,13 @@ }, "MEDIA", {}, - "1HAND" + "DEL" ], [ { "x": -3 }, - "LEAD", + "HUN", { "c": "#d4872a", "f": 9, @@ -528,6 +548,6 @@ "c": "#f9cd31", "f": 2 }, - "HUN" + "LEAD" ] ] diff --git a/keyboards/ergodox/keymaps/algernon/tools/heatmap-base-layout.json b/keyboards/ergodox/keymaps/algernon/tools/heatmap-layout.Dvorak.json similarity index 97% rename from keyboards/ergodox/keymaps/algernon/tools/heatmap-base-layout.json rename to keyboards/ergodox/keymaps/algernon/tools/heatmap-layout.Dvorak.json index 148bb1f23..1248d36af 100644 --- a/keyboards/ergodox/keymaps/algernon/tools/heatmap-base-layout.json +++ b/keyboards/ergodox/keymaps/algernon/tools/heatmap-layout.Dvorak.json @@ -75,9 +75,9 @@ "STENO", { "x": 4.5, - "f": 6 + "f": 3 }, - "", + "F12", { "c": "#7adabd", "a": 4, @@ -94,12 +94,21 @@ { "y": -0.875, "c": "#ffb2d2", - "f": 3, + "f": 6, + "a": 6, "w": 1.5 }, - "\n\n~\n`", + "\n\n", { - "t": "#0d0d0b" + "f": 3, + "t": "#0d0d0b", + "a": 4, + "fa": [ + 0, + 0, + 2 + ] + }, "!\n1\nF1", { @@ -167,12 +176,12 @@ "a": 4, "h": 1.5 }, - "{\n[", + "{\n(\n[", { "x": 4.5, "h": 1.5 }, - "}\n]", + "}\n)\n]", { "c": "#7adabd", "a": 6 @@ -184,10 +193,10 @@ "y": -0.875, "c": "#ffb07b", "t": "#0d0d0b", - "f": 6, + "f": 3, "w": 1.5 }, - "\n\n", + "\n\n~\n`", { "c": "#ffb2d2", "a": 4, @@ -503,7 +512,7 @@ }, "MEDIA", {}, - "1HAND" + "DEL" ], [ { diff --git a/keyboards/ergodox/keymaps/algernon/tools/log-to-heatmap.py b/keyboards/ergodox/keymaps/algernon/tools/log-to-heatmap.py index a13d12e81..68585843b 100755 --- a/keyboards/ergodox/keymaps/algernon/tools/log-to-heatmap.py +++ b/keyboards/ergodox/keymaps/algernon/tools/log-to-heatmap.py @@ -3,143 +3,247 @@ import json import os import sys import re +import argparse from math import floor +from os.path import dirname -cr_coord_map = [ - [ - # Row 0 - [ 4, 0], [ 4, 2], [ 2, 0], [ 1, 0], [ 2, 2], [ 3, 0], [ 3, 2], - [ 3, 4], [ 3, 6], [ 2, 4], [ 1, 2], [ 2, 6], [ 4, 4], [ 4, 6], - ], - [ - # Row 1 - [ 8, 0], [ 8, 2], [ 6, 0], [ 5, 0], [ 6, 2], [ 7, 0], [ 7, 2], - [ 7, 4], [ 7, 6], [ 6, 4], [ 5, 2], [ 6, 6], [ 8, 4], [ 8, 6], - ], - [ - # Row 2 - [12, 0], [12, 2], [10, 0], [ 9, 0], [10, 2], [11, 0], [ ], - [ ], [11, 2], [10, 4], [ 9, 2], [10, 6], [12, 4], [12, 6], - ], - [ - # Row 3 - [17, 0], [17, 2], [15, 0], [14, 0], [15, 2], [16, 0], [13, 0], - [13, 2], [16, 2], [15, 4], [14, 2], [15, 6], [17, 4], [17, 6], - ], - [ - # Row 4 - [20, 0], [20, 2], [19, 0], [18, 0], [19, 2], [], [], [], [], - [19, 4], [18, 2], [19, 6], [20, 4], [20, 6], - ], - [ - # Row 5 - [ ], [23, 0], [22, 2], [22, 0], [22, 4], [21, 0], [21, 2], - [24, 0], [24, 2], [25, 0], [25, 4], [25, 2], [26, 0], [ ], - ], -] +class Heatmap(object): + coords = [ + [ + # Row 0 + [ 4, 0], [ 4, 2], [ 2, 0], [ 1, 0], [ 2, 2], [ 3, 0], [ 3, 2], + [ 3, 4], [ 3, 6], [ 2, 4], [ 1, 2], [ 2, 6], [ 4, 4], [ 4, 6], + ], + [ + # Row 1 + [ 8, 0], [ 8, 2], [ 6, 0], [ 5, 0], [ 6, 2], [ 7, 0], [ 7, 2], + [ 7, 4], [ 7, 6], [ 6, 4], [ 5, 2], [ 6, 6], [ 8, 4], [ 8, 6], + ], + [ + # Row 2 + [12, 0], [12, 2], [10, 0], [ 9, 0], [10, 2], [11, 0], [ ], + [ ], [11, 2], [10, 4], [ 9, 2], [10, 6], [12, 4], [12, 6], + ], + [ + # Row 3 + [17, 0], [17, 2], [15, 0], [14, 0], [15, 2], [16, 0], [13, 0], + [13, 2], [16, 2], [15, 4], [14, 2], [15, 6], [17, 4], [17, 6], + ], + [ + # Row 4 + [20, 0], [20, 2], [19, 0], [18, 0], [19, 2], [], [], [], [], + [19, 4], [18, 2], [19, 6], [20, 4], [20, 6], + ], + [ + # Row 5 + [ ], [23, 0], [22, 2], [22, 0], [22, 4], [21, 0], [21, 2], + [24, 0], [24, 2], [25, 0], [25, 4], [25, 2], [26, 0], [ ], + ], + ] -def set_attr_at(j, b, n, attr, fn, val): - blk = j[b][n] - if attr in blk: - blk[attr] = fn(blk[attr], val) - else: - blk[attr] = fn(None, val) + def set_attr_at(self, block, n, attr, fn, val): + blk = self.heatmap[block][n] + if attr in blk: + blk[attr] = fn(blk[attr], val) + else: + blk[attr] = fn(None, val) -def coord(col, row): - return cr_coord_map[row][col] + def coord(self, col, row): + return self.coords[row][col] -def set_attr(orig, new): - return new + @staticmethod + def set_attr(orig, new): + return new -def set_bg(j, (b, n), color): - set_attr_at(j, b, n, "c", set_attr, color) - #set_attr_at(j, b, n, "g", set_attr, False) + def set_bg(self, (block, n), color): + self.set_attr_at(block, n, "c", self.set_attr, color) + #self.set_attr_at(block, n, "g", self.set_attr, False) -def _set_tap_info(o, count, cap): - ns = 4 - o.count ("\n") - return o + "\n" * ns + "%.02f%%" % (float(count) / float(cap) * 100) + def set_tap_info(self, (block, n), count, cap): + def _set_tap_info(o, _count, _cap): + ns = 4 - o.count ("\n") + return o + "\n" * ns + "%.02f%%" % (float(_count) / float(_cap) * 100) -def set_tap_info(j, (b, n), count, cap): - j[b][n + 1] = _set_tap_info (j[b][n + 1], count, cap) + if not cap: + cap = 1 + self.heatmap[block][n + 1] = _set_tap_info (self.heatmap[block][n + 1], count, cap) -def heatmap_color (v): - colors = [ [0.3, 0.3, 1], [0.3, 1, 0.3], [1, 1, 0.3], [1, 0.3, 0.3]] - fb = 0 - if v <= 0: - idx1, idx2 = 0, 0 - elif v >= 1: - idx1, idx2 = len(colors) - 1, len(colors) - 1 - else: - val = v * (len(colors) - 1) - idx1 = int(floor(val)) - idx2 = idx1 + 1 - fb = val - float(idx1) + @staticmethod + def heatmap_color (v): + colors = [ [0.3, 0.3, 1], [0.3, 1, 0.3], [1, 1, 0.3], [1, 0.3, 0.3]] + fb = 0 + if v <= 0: + idx1, idx2 = 0, 0 + elif v >= 1: + idx1, idx2 = len(colors) - 1, len(colors) - 1 + else: + val = v * (len(colors) - 1) + idx1 = int(floor(val)) + idx2 = idx1 + 1 + fb = val - float(idx1) - r = (colors[idx2][0] - colors[idx1][0]) * fb + colors[idx1][0] - g = (colors[idx2][1] - colors[idx1][1]) * fb + colors[idx1][1] - b = (colors[idx2][2] - colors[idx1][2]) * fb + colors[idx1][2] + r = (colors[idx2][0] - colors[idx1][0]) * fb + colors[idx1][0] + g = (colors[idx2][1] - colors[idx1][1]) * fb + colors[idx1][1] + b = (colors[idx2][2] - colors[idx1][2]) * fb + colors[idx1][2] - r, g, b = [x * 255 for x in r, g, b] - return "#%02x%02x%02x" % (r, g, b) + r, g, b = [x * 255 for x in r, g, b] + return "#%02x%02x%02x" % (r, g, b) -# Load the keylog -def load_keylog(fname, restrict_row): - keylog = {} - total = 0 - with open(fname, "r") as f: - lines = f.readlines() - for line in lines: - m = re.search ('KL: col=(\d+), row=(\d+)', line) + def __init__(self, layout): + self.log = {} + self.total = 0 + self.max_cnt = 0 + self.layout = layout + + def update_log(self, (c, r)): + if not (c, r) in self.log: + self.log[(c, r)] = 0 + self.log[(c, r)] = self.log[(c, r)] + 1 + self.total = self.total + 1 + if self.max_cnt < self.log[(c, r)]: + self.max_cnt = self.log[(c, r)] + + def get_heatmap(self): + with open("%s/heatmap-layout.%s.json" % (dirname(sys.argv[0]), self.layout), "r") as f: + self.heatmap = json.load (f) + + ## Reset colors + for row in self.coords: + for coord in row: + if coord != []: + self.set_bg (coord, "#d9dae0") + + for (c, r) in self.log: + coords = self.coord(c, r) + b, n = coords + cap = self.max_cnt + if cap == 0: + cap = 1 + v = float(self.log[(c, r)]) / cap + self.set_bg (coords, self.heatmap_color (v)) + self.set_tap_info (coords, self.log[(c, r)], self.total) + return self.heatmap + + def get_stats(self): + usage = [ + # left hand + [0, 0, 0, 0, 0], + # right hand + [0, 0, 0, 0, 0] + ] + finger_map = [0, 0, 1, 2, 3, 4, 4] + for (c, r) in self.log: + if r == 5: # thumb cluster + if c <= 6: # left side + usage[0][4] = usage[0][4] + self.log[(c, r)] + else: + usage[1][4] = usage[1][4] + self.log[(c, r)] + else: + fc = c + hand = 0 + if fc >= 7: + fc = fc - 7 + hand = 1 + fm = finger_map[fc] + usage[hand][fm] = usage[hand][fm] + self.log[(c, r)] + hand_usage = [0, 0] + for f in usage[0]: + hand_usage[0] = hand_usage[0] + f + for f in usage[1]: + hand_usage[1] = hand_usage[1] + f + + total = self.total + if total == 0: + total = 1 + stats = { + "hands": { + "left": { + "usage": float(hand_usage[0]) / total * 100, + "fingers": { + "0 - pinky": 0, + "1 - ring": 0, + "2 - middle": 0, + "3 - index": 0, + "4 - thumb": 0, + } + }, + "right": { + "usage": float(hand_usage[1]) / total * 100, + "fingers": { + "0 - thumb": 0, + "1 - index": 0, + "2 - middle": 0, + "3 - ring": 0, + "4 - pinky": 0, + } + }, + } + } + + hmap = ['left', 'right'] + fmap = ['0 - pinky', '1 - ring', '2 - middle', '3 - index', '4 - thumb', + '0 - thumb', '1 - index', '2 - middle', '3 - ring', '4 - pinky'] + for hand_idx in range(len(usage)): + hand = usage[hand_idx] + for finger_idx in range(len(hand)): + stats['hands'][hmap[hand_idx]]['fingers'][fmap[finger_idx + hand_idx * 5]] = float(hand[finger_idx]) / total * 100 + return stats + +def dump_all(out_dir, heatmaps): + for layer in heatmaps.keys(): + if len(heatmaps[layer].log) == 0: + continue + + with open ("%s/%s.json" % (out_dir, layer), "w") as f: + json.dump(heatmaps[layer].get_heatmap(), f) + print >>sys.stderr, "%s stats:" % (layer) + json.dump (heatmaps[layer].get_stats(), sys.stderr, + indent = 4, sort_keys = True) + print >>sys.stderr, "" + print >>sys.stderr, "" + +def main(opts): + + heatmaps = {"Dvorak": Heatmap("Dvorak"), + "ADORE": Heatmap("ADORE") + } + cnt = 0 + restrict_row = opts.restrict_row + out_dir = opts.outdir + + while True: + line = sys.stdin.readline() + if not line: + break + m = re.search ('KL: col=(\d+), row=(\d+), pressed=(\d+), layer=(.*)', line) if not m: continue - (c, r) = (int(m.group (2)), int(m.group (1))) - if restrict_row != None and r != int(restrict_row): + + cnt = cnt + 1 + (c, r, l) = (int(m.group (2)), int(m.group (1)), m.group (4)) + if restrict_row != -1 and r != restrict_row: + continue + if c in opts.ignore_columns: continue - if (c, r) in keylog: - keylog[(c, r)] = keylog[(c, r)] + 1 - else: - keylog[(c, r)] = 1 - total = total + 1 - return total / 2, keylog -def l_flat(s): - f = s.split("\n") - return ", ".join (f) + heatmaps[l].update_log ((c, r)) -def main(base_fn, log_fn, restrict_row = None): + if opts.dump_interval != -1 and cnt >= opts.dump_interval: + cnt = 0 + dump_all(out_dir, heatmaps) - with open(base_fn, "r") as f: - layout = json.load (f) - - ## Reset colors - for row in cr_coord_map: - for col in row: - if col != []: - set_bg (layout, col, "#d9dae0") - #set_attr_at (layout, col[0], col[1], "g", set_attr, True) - - total, log = load_keylog (log_fn, restrict_row) - max_cnt = 0 - for (c, r) in log: - max_cnt = max(max_cnt, log[(c, r)]) - - # Create the heatmap - for (c, r) in log: - coords = coord(c, r) - b, n = coords - cap = max_cnt - v = float(log[(c, r)]) / cap - print >> sys.stderr, "%s => %d/%d => %f = %s" % (l_flat(layout[b][n+1]), log[(c,r)], cap, v, heatmap_color(v)) - set_bg (layout, coord(c, r), heatmap_color (v)) - set_tap_info (layout, coord (c, r), log[(c, r)], total) - - print json.dumps(layout) + dump_all (out_dir, heatmaps) if __name__ == "__main__": - if len(sys.argv) < 3: - print """Log to Heatmap -- creates a heatmap out of keyboard logs - -Usage: log-to-heatmap.py base-layout.json logfile [row] >layout.json""" - sys.exit (1) - main(*sys.argv[1:]) + parser = argparse.ArgumentParser (description = "keylog to heatmap processor") + parser.add_argument ('outdir', action = 'store', + help = 'Output directory') + parser.add_argument ('--row', dest = 'restrict_row', action = 'store', type = int, + default = -1, help = 'Restrict processing to this row only') + parser.add_argument ('--dump-interval', dest = 'dump_interval', action = 'store', type = int, + default = 100, help = 'Dump stats and heatmap at every Nth event, -1 for dumping at EOF only') + parser.add_argument ('--ignore-column', dest = 'ignore_columns', action = 'append', type = int, + default = [], help = 'Ignore the specified columns') + args = parser.parse_args() + main(args)