Merge pull request #678 from algernon/ergodox-ez/algernon
ergodox: Update algernon's keymap to v1.6
This commit is contained in:
		@@ -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)
 | 
			
		||||
 
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB  | 
@@ -2,6 +2,7 @@
 | 
			
		||||
 * algernon's ErgoDox EZ layout, please see the readme.md file!
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#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<<HUN)) {
 | 
			
		||||
      layer_off (HUN);
 | 
			
		||||
      queue = false;
 | 
			
		||||
    }
 | 
			
		||||
    return queue;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (time_travel && !record->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);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -75,9 +75,9 @@
 | 
			
		||||
    "STENO",
 | 
			
		||||
    {
 | 
			
		||||
      "x": 4.5,
 | 
			
		||||
      "f": 6
 | 
			
		||||
      "f": 3
 | 
			
		||||
    },
 | 
			
		||||
    "<i class='mss mss-Unicode-Option-3'></i>",
 | 
			
		||||
    "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<i class='kb kb-Multimedia-Play-Pause'></i>",
 | 
			
		||||
    {
 | 
			
		||||
      "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
 | 
			
		||||
    },
 | 
			
		||||
    "<i class='fa fa-fast-backward'></i>\n\n<i class='fa fa-fast-forward'></i>",
 | 
			
		||||
    "\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<i class='kb kb-Multimedia-Play-Pause'></i>",
 | 
			
		||||
    "",
 | 
			
		||||
    {
 | 
			
		||||
      "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
 | 
			
		||||
    },
 | 
			
		||||
    "<i class='kb kb-Multimedia-Stop'></i>"
 | 
			
		||||
    ""
 | 
			
		||||
  ],
 | 
			
		||||
  [
 | 
			
		||||
    {
 | 
			
		||||
@@ -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"
 | 
			
		||||
  ]
 | 
			
		||||
]
 | 
			
		||||
@@ -75,9 +75,9 @@
 | 
			
		||||
    "STENO",
 | 
			
		||||
    {
 | 
			
		||||
      "x": 4.5,
 | 
			
		||||
      "f": 6
 | 
			
		||||
      "f": 3
 | 
			
		||||
    },
 | 
			
		||||
    "<i class='mss mss-Unicode-Option-3'></i>",
 | 
			
		||||
    "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`",
 | 
			
		||||
    "<i class='fa fa-fast-backward'></i>\n\n<i class='fa fa-fast-forward'></i>",
 | 
			
		||||
    {
 | 
			
		||||
      "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
 | 
			
		||||
    },
 | 
			
		||||
    "<i class='fa fa-fast-backward'></i>\n\n<i class='fa fa-fast-forward'></i>",
 | 
			
		||||
    "\n\n~\n`",
 | 
			
		||||
    {
 | 
			
		||||
      "c": "#ffb2d2",
 | 
			
		||||
      "a": 4,
 | 
			
		||||
@@ -503,7 +512,7 @@
 | 
			
		||||
    },
 | 
			
		||||
    "MEDIA",
 | 
			
		||||
    {},
 | 
			
		||||
    "1HAND"
 | 
			
		||||
    "DEL"
 | 
			
		||||
  ],
 | 
			
		||||
  [
 | 
			
		||||
    {
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user