summary refs log tree commit diff
path: root/users
diff options
context:
space:
mode:
authorTim Schumacher <twschum@gmail.com>2019-11-21 06:07:33 -0500
committerDrashna Jaelre <drashna@live.com>2019-11-21 03:07:33 -0800
commit84065e1d7458ad6b0a04a82fed4dfc69a4ded2b2 (patch)
treec3985a31591fb3f2c3252caf49d7751cd5d8cb10 /users
parent90bb7db48e658624c6c4609c832ac1451e4db98b (diff)
[Keymap] Add DZ60, Levinson keymaps, twschum user files (#7358)
* Adds layout for DZ60rev2 plate B, options 4 and 10

* Use KC_TRANS for layer keys

* Format layer0 with visual key size layout

* Add RGB controls; add start description; add KC_LOCK

* Update comments on rules.mk for DZ60

* Gets keymap compiling

* Fix wiring for shift on row 3

* Hold a with other key to send Ctrl-A before other key

* Adds compile-time defined mac-compatability media keycodes

* Adds logic in place for capturing taps (w/ timeout)

* Add send_keys(...) which can send up to 64 nested keycodes

* Implement send_keys callable with n repeats

* Tweaks some of the keymap

* Add reset keycode; add disable to custom shortcuts

* Adds a special "off" layer where bonus mod goes to layer 0, every other key KC_NO

* Adjust timeout; mousekey settings

* Changes layout of Home,End,PgUp/Dn on the dedicated arrow keys L1/L2

* PoC on rgb lighted layer indication

* Refactors color table defines

* Adds logic for controlling layer lights

* Only change state on one side of the event lol

* Switch back to Tapping Toggle for layer 4

* Add custom config file for keyboard; TT and mousekey settings

* Code cleanup; starting to refactor special ctrl tapping keys functions

* Move defines and reak out functions

* Remove debugging light

* Adjust keymaps; add enter

* Adds a couple custom macros

* Add simplified version of keymap to help debug issues

* Adds basi numpad configuration for levinson keyboard half

* Use ANSI ctrl key as layer 1 for better one hand (`)

* Adapt to new 8bit hue from #5547; layer enum use everywhere

* Move custom code out to users/ space

* Flip col pins, move key assignments to "left hand"

* Update readme

* Implement placeholder macros

* Notes

* Reduce tapping time for SFT_T(/)

* Adds vim features; refactors things

* Adds fork of the vim features written by xton

* Use correct backspace keycode

* Add VIM_ESC

* Add "OFF" to the RGB/HSV definitions

* Clean up rules, use new "OFF" definition

* Add windows KC_CALCULATOR key to numpad

* Reformat layers with better guide; change layer names

* Add sleep key

* Change timeout delay

* Add a "code paste" which surrounds a ^V with ```

* Try removing shift tap on rshift /

* Update macros

* Update the "code paste" macro

* Update keymap with reset, calc, equals

* Update keyboards/dz60/keymaps/twschum_b_4_10_simple/keymap.c

Co-Authored-By: Drashna Jaelre <drashna@live.com>

* Update keyboards/dz60/keymaps/twschum_b_4_10/keymap.c

Co-Authored-By: Drashna Jaelre <drashna@live.com>

* Update users/twschum/twschum.c

Co-Authored-By: Drashna Jaelre <drashna@live.com>

* Update users/twschum/twschum.c

Co-Authored-By: Drashna Jaelre <drashna@live.com>

* Revert quantum/rgblight_list.h

* Better name for split space layout; rename KEYMAP->LAYOUT

* Rename KEYMAP->LAYOUT

* Use simpler :flash command

* Clean up layout files

* Use qmk's MOD_MASK_CTRL

* Sync lufa submodule

* Cleanup from noroadsleft's PR comments

* Modernize vim layer code
Diffstat (limited to 'users')
-rw-r--r--users/twschum/config.h0
-rw-r--r--users/twschum/readme.md14
-rw-r--r--users/twschum/rules.mk5
-rw-r--r--users/twschum/twschum.c257
-rw-r--r--users/twschum/twschum.h131
-rw-r--r--users/twschum/xtonhasvim.c593
-rw-r--r--users/twschum/xtonhasvim.h62
7 files changed, 1062 insertions, 0 deletions
diff --git a/users/twschum/config.h b/users/twschum/config.h
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/users/twschum/config.h
diff --git a/users/twschum/readme.md b/users/twschum/readme.md
new file mode 100644
index 0000000000..b354e4b79c
--- /dev/null
+++ b/users/twschum/readme.md
@@ -0,0 +1,14 @@
+Copyright 2019 Tim Schumacher <twschum@gmail.com> @twschum
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
diff --git a/users/twschum/rules.mk b/users/twschum/rules.mk
new file mode 100644
index 0000000000..9878e6f690
--- /dev/null
+++ b/users/twschum/rules.mk
@@ -0,0 +1,5 @@
+SRC += twschum.c
+SRC += xtonhasvim.c
+ifeq ($(strip $(FLASH_BOOTLOADER)), yes)
+    OPT_DEFS += -DFLASH_BOOTLOADER
+endif
diff --git a/users/twschum/twschum.c b/users/twschum/twschum.c
new file mode 100644
index 0000000000..2d34f95718
--- /dev/null
+++ b/users/twschum/twschum.c
@@ -0,0 +1,257 @@
+#include "twschum.h"
+
+#ifdef TWSCHUM_TAPPING_CTRL_PREFIX
+// state for the great state machine of custom actions!
+#define TIMEOUT_DELAY 200 // ms
+static uint16_t idle_timer;
+static bool timeout_is_active = false;
+
+static bool ctrl_shortcuts_enabled_g = false;
+//static bool B_down = 0; // TODO just use top bit from count
+//static int8_t B_count = 0;
+
+#define N_TAPPING_CTRL_KEYS 2
+static struct Tapping_ctrl_key_t special_keys_g[N_TAPPING_CTRL_KEYS] = {
+    {false, 0, KC_B}, {false, 0, KC_A}
+};
+
+static inline void start_idle_timer(void) {
+    idle_timer = timer_read();
+    timeout_is_active = true;
+}
+static inline void clear_state_after_idle_timeout(void) {
+    idle_timer = 0;
+    timeout_is_active = false;
+
+    // send timed out plain keys from tapping ctrl mod
+    for (int i = 0; i < N_TAPPING_CTRL_KEYS; ++i) {
+        struct Tapping_ctrl_key_t* key = special_keys_g + i;
+        repeat_send_keys(key->count, key->keycode);
+        key->count = 0;
+    }
+}
+
+inline void matrix_scan_user(void) {
+    if (timeout_is_active && timer_elapsed(idle_timer) > TIMEOUT_DELAY) {
+        clear_state_after_idle_timeout();
+    }
+}
+
+static inline bool tap_ctrl_event(struct Tapping_ctrl_key_t* key, keyrecord_t* record) {
+    if (!ctrl_shortcuts_enabled_g) {
+        // normal operation, just send the plain keycode
+        if (record->event.pressed) {
+            register_code(key->keycode);
+        }
+        else {
+            unregister_code(key->keycode);
+        }
+        return false;
+    }
+    key->down = record->event.pressed;
+    // increment count and reset timer when key pressed
+    // start the timeout when released
+    if (key->down) {
+        ++(key->count);
+        timeout_is_active = false;
+        idle_timer = 0;
+    }
+    else {
+        if (key->count) {
+            start_idle_timer();
+        }
+    }
+    return false;
+}
+
+static inline bool tap_ctrl_other_pressed(void) {
+    for (int i = 0; i < N_TAPPING_CTRL_KEYS; ++i) {
+        struct Tapping_ctrl_key_t* key = special_keys_g + i;
+        if (key->count) {
+            if (key->down) {
+                // another key has been pressed while the leader key is down,
+                // so send number of ctrl-KEY combos before the other key
+                repeat_send_keys(key->count, KC_LCTL, key->keycode);
+                key->count = 0;
+            }
+            else {
+                // another key pressed after leader key released,
+                // need to send the plain keycode plus potential mods
+                if (get_mods() & MOD_MASK_CTRL) {
+                    // make sure to send a shift if prssed
+                    repeat_send_keys(key->count, KC_RSHIFT, key->keycode);
+                }
+                else {
+                    repeat_send_keys(key->count, key->keycode);
+                }
+                key->count = 0;
+            }
+            return true; // will send the other keycode
+        }
+    }
+    return true; // safe default
+}
+#endif /* TWSCHUM_TAPPING_CTRL_PREFIX */
+
+
+/* Use RGB underglow to indicate layer
+ * https://docs.qmk.fm/reference/customizing-functionality
+ */
+// add to quantum/rgblight_list.h
+#ifdef RGBLIGHT_ENABLE
+static bool rgb_layers_enabled = true;
+static bool rgb_L0_enabled = false;
+
+layer_state_t layer_state_set_user(layer_state_t state) {
+    if (!rgb_layers_enabled) {
+        return state;
+    }
+    switch (get_highest_layer(state)) {
+    case _Base:
+        if (rgb_L0_enabled) {
+            rgblight_sethsv_noeeprom(_Base_HSV_ON);
+        }
+        else {
+            rgblight_sethsv_noeeprom(_Base_HSV_OFF);
+        }
+        break;
+    case _Vim:
+        rgblight_sethsv_noeeprom(_Vim_HSV);
+        break;
+    case _Fn:
+        rgblight_sethsv_noeeprom(_Fn_HSV);
+        break;
+    case _Nav:
+        rgblight_sethsv_noeeprom(_Nav_HSV);
+        break;
+    case _Num:
+        rgblight_sethsv_noeeprom(_Num_HSV);
+        break;
+    case _Cfg:
+        rgblight_sethsv_noeeprom(_Cfg_HSV);
+        break;
+    case _None:
+        rgblight_sethsv_noeeprom(_None_HSV);
+        break;
+    }
+    return state;
+}
+#endif /* RGBLIGHT_ENABLE */
+
+/* process_record_vimlayer: handles the VIM_ keycodes from xtonhasvim's vim
+ * emulation layer
+ * add process_record_keymap to allow specific keymap to still add keys
+ * Makes the callstack look like:
+ * process_record_
+ *  _quantum
+ *    _kb
+ *      _user
+ *        _keymap
+ *        _vimlayer
+ */
+__attribute__ ((weak))
+bool process_record_keymap(uint16_t keycode, keyrecord_t *record) {
+  return true;
+}
+
+/* Return True to continue processing keycode, false to stop further processing
+ * process_record_keymap to be call by process_record_user in the vim addon */
+bool process_record_user(uint16_t keycode, keyrecord_t *record) {
+
+  /* keymap gets first whack, then vimlayer */
+  if(!process_record_keymap(keycode, record)) return false;
+  if(!process_record_vimlayer(keycode, record)) return false;
+
+    switch (keycode) {
+        /* KC_MAKE is a keycode to be used with any keymap
+         * Outputs `make <keyboard>:<keymap>`
+         * Holding shift will add the appropriate flashing command (:dfu,
+         *   :teensy, :avrdude, :dfu-util) for a majority of keyboards.
+         * Holding control will add some commands that will speed up compiling
+         *   time by processing multiple files at once
+         * For the boards that lack a shift key, or that you want to always
+         *   attempt the flashing part, you can add FLASH_BOOTLOADER = yes to the
+         *   rules.mk of that keymap.
+         */
+        case KC_MAKE:  // Compiles the firmware, and adds the flash command based on keyboard bootloader
+            if (!record->event.pressed) {
+            uint8_t temp_mod = get_mods();
+            uint8_t temp_osm = get_oneshot_mods();
+            clear_mods(); clear_oneshot_mods();
+            SEND_STRING("make " QMK_KEYBOARD ":" QMK_KEYMAP);
+        #ifndef FLASH_BOOTLOADER
+            if ( (temp_mod | temp_osm) & MOD_MASK_SHIFT ) {
+                SEND_STRING(":flash");
+            }
+        #endif
+            if ( (temp_mod | temp_osm) & MOD_MASK_CTRL) {
+                SEND_STRING(" -j8 --output-sync");
+            }
+            SEND_STRING(SS_TAP(X_ENTER));
+            set_mods(temp_mod);
+        }
+        break;
+
+        #ifdef RGBLIGHT_ENABLE
+        case TG_LAYER_RGB:
+            if (record->event.pressed) {
+                rgb_layers_enabled = !rgb_layers_enabled;
+            }
+            return false;
+        case TG_L0_RGB:
+            if (record->event.pressed) {
+                rgb_L0_enabled = !rgb_L0_enabled;
+            }
+            return false;
+        #endif
+
+        case SALT_CMD:
+            if (!record->event.pressed) {
+                SEND_STRING(SALT_CMD_MACRO);
+            }
+            return false;
+        case LESS_PD:
+            if (!record->event.pressed) {
+                SEND_STRING(LESS_PD_MACRO);
+            }
+            return false;
+        case CODE_PASTE:
+            if (!record->event.pressed) {
+                SEND_STRING(CODE_PASTE_MACRO);
+            }
+            return false;
+
+        #ifdef TWSCHUM_TAPPING_CTRL_PREFIX
+        case EN_CTRL_SHORTCUTS:
+            if (record->event.pressed) {
+                ctrl_shortcuts_enabled_g = !ctrl_shortcuts_enabled_g;
+                start_idle_timer(); // need to clear out state in some cases
+            }
+            return false;
+        case CTRL_A:
+            return tap_ctrl_event(&special_keys_g[1], record);
+        case CTRL_B:
+            return tap_ctrl_event(&special_keys_g[0], record);
+        default:
+            if (record->event.pressed) {
+                return tap_ctrl_other_pressed();
+            }
+        #endif
+    }
+    return true;
+}
+
+#ifdef RGBLIGHT_ENABLE
+void matrix_init_user(void) {
+    // called once on board init
+    rgblight_enable();
+}
+#endif
+
+void suspend_power_down_user(void) {
+    // TODO shut off backlighting
+}
+
+void suspend_wakeup_init_user(void) {
+    // TODO turn on backlighting
+}
diff --git a/users/twschum/twschum.h b/users/twschum/twschum.h
new file mode 100644
index 0000000000..e8c9aeffcd
--- /dev/null
+++ b/users/twschum/twschum.h
@@ -0,0 +1,131 @@
+#pragma once
+#include <stdarg.h>
+#include "quantum.h"
+#include "xtonhasvim.h"
+
+/**************************
+ * QMK Features Used
+ **************************
+ * RGBLIGHT_ENABLE
+ *  - Adds layer indication via RGB underglow
+ *  - see the `layer_definitions` enum and following _*_HSV #defines
+ *
+ *
+ *
+ **************************
+ * Custom Feature Flags
+ **************************
+ *
+ * TWSCHUM_TAPPING_CTRL_PREFIX
+ * - Adds feature that makes sending nested sequences of C-a, C-b[, C-b, ...]
+ *   as simple as C-a b [b ...]
+ * - Not necessarily super useful outside specialized nested tmux sessions,
+ *   but it was a fun state-machine to build
+ *
+ * TWSCHUM_VIM_LAYER
+ * - Fork of xtonhasvim, adding vim-emulation
+ *
+ * TWSCHUM_IS_MAC
+ * - Flag for handling media keys and other settings between OSX and Win/Unix
+ *   without having to include bootmagic
+ *
+ **************************
+ * Features Wishlist
+ **************************
+ * use VIM_Q as macro recorder!
+ * Dynamic macros
+ * Leader functions
+ * Uniicode leader commands??? (symbolic unicode)
+ * Mac mode vs not: -probably bootmagic or use default with dynamic swap out here
+ *    KC_MFFD(KC_MEDIA_FAST_FORWARD) and KC_MRWD(KC_MEDIA_REWIND) instead of KC_MNXT and KC_MPRV
+ */
+
+/* Each layer gets a color, overwritable per keyboard */
+enum layers_definitions {
+    _Base,
+    _Vim,
+    _Fn,
+    _Nav,
+    _Num,
+    _Cfg,
+    _None,
+};
+#ifdef RGBLIGHT_ENABLE
+#define _Base_HSV_ON  HSV_WHITE
+#define _Base_HSV_OFF 0, 0, 0
+#define _Vim_HSV      HSV_ORANGE
+#define _Fn_HSV       HSV_GREEN
+#define _Nav_HSV      HSV_AZURE
+#define _Num_HSV      HSV_GOLD
+#define _Cfg_HSV      HSV_RED
+#define _None_HSV     HSV_WHITE
+#endif
+
+enum extra_keycodes {
+    TWSCHUM_START = VIM_SAFE_RANGE,
+    KC_MAKE, // types the make command for this keyboard
+#ifdef TWSCHUM_TAPPING_CTRL_PREFIX
+    CTRL_A,
+    CTRL_B,
+    EN_CTRL_SHORTCUTS,
+#endif
+#ifdef RGBLIGHT_ENABLE
+    TG_LAYER_RGB, // Toggle between standard RGB underglow, and RGB underglow to do layer indication
+    TG_L0_RGB, // Toggle color on or off of layer0
+#endif
+    SALT_CMD, // macro
+    LESS_PD, // macro
+    CODE_PASTE, // macro
+    KEYMAP_SAFE_RANGE, // range to start for the keymap
+};
+#define SALT_CMD_MACRO "sudo salt \\* cmd.run ''"SS_TAP(X_LEFT)
+#define LESS_PD_MACRO "sudo less /pipedream/cache/"
+// TODO mac vs linux
+#define CODE_PASTE_MACRO SS_LSFT("\n")"```"SS_LSFT("\n")SS_LALT("v")SS_LSFT("\n")"```"
+
+
+/* PP_NARG macro returns the number of arguments passed to it.
+ * https://groups.google.com/forum/#!topic/comp.std.c/d-6Mj5Lko_s
+ */
+#define PP_NARG(...) PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
+#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)
+#define PP_MAX_ARGS 64
+#define PP_ARG_N( \
+          _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
+         _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
+         _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
+         _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
+         _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
+         _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
+         _61,_62,_63,N,...) N
+#define PP_RSEQ_N() 63,62,61,60,        \
+         59,58,57,56,55,54,53,52,51,50, \
+         49,48,47,46,45,44,43,42,41,40, \
+         39,38,37,36,35,34,33,32,31,30, \
+         29,28,27,26,25,24,23,22,21,20, \
+         19,18,17,16,15,14,13,12,11,10, \
+         9,8,7,6,5,4,3,2,1,0
+
+#define send_keys(...) send_n_keys(PP_NARG(__VA_ARGS__), __VA_ARGS__)
+static inline void send_n_keys(int n, ...) {
+    uint8_t i = 0;
+    uint16_t keycodes[PP_MAX_ARGS];
+    va_list keys;
+    va_start(keys, n);
+    for (; i < n; ++i) {
+        keycodes[i] = (uint16_t)va_arg(keys, int); // cast suppresses warning
+        register_code(keycodes[i]);
+    }
+    for (; n > 0; --n) {
+        unregister_code(keycodes[n-1]);
+    }
+    va_end(keys);
+}
+#define repeat_send_keys(n, ...) {for (int i=0; i < n; ++i) {send_keys(__VA_ARGS__);}}
+
+/* State functions for nested c-a & c-b leader keystrokes */
+struct Tapping_ctrl_key_t {
+    bool down;
+    int8_t count;
+    const uint16_t keycode;
+};
diff --git a/users/twschum/xtonhasvim.c b/users/twschum/xtonhasvim.c
new file mode 100644
index 0000000000..a1adf39f04
--- /dev/null
+++ b/users/twschum/xtonhasvim.c
@@ -0,0 +1,593 @@
+ /* Copyright 2015-2017 Christon DeWan *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "xtonhasvim.h"
+
+
+uint16_t vstate = VIM_START;
+static bool yank_was_lines = false;
+static bool SHIFTED = false;
+static uint32_t mod_override_layer_state = 0;
+static uint16_t mod_override_triggering_key = 0;
+
+static void edit(void) { vstate = VIM_START; layer_clear(); }
+#define EDIT edit()
+
+
+static void simple_movement(uint16_t keycode) {
+  switch(keycode) {
+    case VIM_B:
+      register_code(KC_LALT);
+        tap_code16(LSFT(KC_LEFT)); // select to start of this word
+      unregister_code(KC_LALT);
+      break;
+    case VIM_E:
+      register_code(KC_LALT);
+        tap_code16(LSFT(KC_RIGHT)); // select to end of this word
+      unregister_code(KC_LALT);
+      break;
+    case VIM_H:
+      tap_code16(LSFT(KC_LEFT));
+      break;
+    case VIM_J:
+      tap_code16(LGUI(KC_LEFT));
+      tap_code16(LSFT(KC_DOWN));
+      tap_code16(LSFT(KC_DOWN));
+      break;
+    case VIM_K:
+      tap_code16(LGUI(KC_LEFT));
+      tap_code(KC_DOWN);
+      tap_code16(LSFT(KC_UP));
+      tap_code16(LSFT(KC_UP));
+      break;
+    case VIM_L:
+      tap_code16(LSFT(KC_RIGHT));
+      break;
+    case VIM_W:
+      register_code(KC_LALT);
+      tap_code16(LSFT(KC_RIGHT)); // select to end of this word
+      tap_code16(LSFT(KC_RIGHT)); // select to end of next word
+      tap_code16(LSFT(KC_LEFT));  // select to start of next word
+      unregister_code(KC_LALT);
+      break;
+  }
+}
+
+static void comma_period(uint16_t keycode) {
+  switch (keycode) {
+  case VIM_COMMA:
+    if (SHIFTED) {
+      // indent
+      tap_code16(LGUI(KC_LBRACKET));
+    } else {
+      // toggle comment
+      tap_code16(LGUI(KC_SLASH));
+    }
+    break;
+  case VIM_PERIOD:
+    if (SHIFTED) {
+      // outdent
+      tap_code16(LGUI(KC_RBRACKET));
+    }
+    break;
+  }
+}
+
+
+bool process_record_vimlayer(uint16_t keycode, keyrecord_t *record) {
+
+  /****** mod passthru *****/
+  if(record->event.pressed && layer_state_is(vim_cmd_layer()) && (IS_MOD(keycode) || keycode == LSFT(KC_LALT))) {
+    mod_override_layer_state = layer_state;
+    mod_override_triggering_key = keycode;
+    // TODO: change this to track key location instead
+    layer_clear();
+    return true; // let the event fall through...
+  }
+  if(mod_override_layer_state && !record->event.pressed && keycode == mod_override_triggering_key) {
+    layer_state_set(mod_override_layer_state);
+    mod_override_layer_state = 0;
+    mod_override_triggering_key = 0;
+    return true;
+  }
+
+  if (VIM_START <= keycode && keycode <= VIM_ESC) {
+    if(keycode == VIM_SHIFT) {
+      SHIFTED = record->event.pressed;
+      return false;
+    }
+
+    if (record->event.pressed) {
+      if(keycode == VIM_START) {
+        // entry from anywhere
+        layer_on(vim_cmd_layer());
+        vstate = VIM_START;
+
+        // reset state
+        yank_was_lines = false;
+        SHIFTED = false;
+        mod_override_layer_state = 0;
+        mod_override_triggering_key = 0;
+
+        return false;
+      }
+      switch(vstate) {
+        case VIM_START:
+          switch(keycode){
+            /*****************************
+             * ground state
+             *****************************/
+            case VIM_A:
+              if(SHIFTED) {
+                // tap_code16(LGUI(KC_RIGHT));
+                tap_code16(LCTL(KC_E));
+              } else {
+                tap_code(KC_RIGHT);
+              }
+              EDIT;
+              break;
+            case VIM_B:
+              register_code(KC_LALT);
+              register_code(KC_LEFT);
+              break;
+            case VIM_C:
+              if(SHIFTED) {
+                register_code(KC_LSHIFT);
+                  tap_code16(LGUI(KC_RIGHT));
+                unregister_code(KC_LSHIFT);
+                tap_code16(LGUI(KC_X));
+                yank_was_lines = false;
+                EDIT;
+              } else {
+                vstate = VIM_C;
+              }
+              break;
+            case VIM_D:
+              if(SHIFTED) {
+                tap_code16(LCTL(KC_K));
+              } else {
+                vstate = VIM_D;
+              }
+              break;
+            case VIM_E:
+              register_code(KC_LALT);
+              register_code(KC_RIGHT);
+              break;
+            case VIM_G:
+              if(SHIFTED) {
+                tap_code(KC_END);
+              } else {
+                vstate = VIM_G;
+              }
+              break;
+            case VIM_H:
+              register_code(KC_LEFT);
+              break;
+            case VIM_I:
+              if(SHIFTED){
+                tap_code16(LCTL(KC_A));
+              }
+              EDIT;
+              break;
+            case VIM_J:
+              if(SHIFTED) {
+                tap_code16(LGUI(KC_RIGHT));
+                tap_code(KC_DEL);
+              } else {
+                register_code(KC_DOWN);
+              }
+              break;
+            case VIM_K:
+              register_code(KC_UP);
+              break;
+            case VIM_L:
+              register_code(KC_RIGHT);
+              break;
+            case VIM_O:
+              if(SHIFTED) {
+                tap_code16(LGUI(KC_LEFT));
+                tap_code(KC_ENTER);
+                tap_code(KC_UP);
+                EDIT;
+              } else {
+                tap_code16(LGUI(KC_RIGHT));
+                tap_code(KC_ENTER);
+                EDIT;
+              }
+              break;
+            case VIM_P:
+              if(SHIFTED) {
+                tap_code16(LGUI(KC_LEFT));
+                tap_code16(LGUI(KC_V));
+              } else {
+                if(yank_was_lines) {
+                  tap_code16(LGUI(KC_RIGHT));
+                  tap_code(KC_RIGHT);
+                  tap_code16(LGUI(KC_V));
+                } else {
+                  tap_code16(LGUI(KC_V));
+                }
+              }
+              break;
+            case VIM_S:
+              // s for substitute?
+              if(SHIFTED) {
+                tap_code16(LGUI(KC_LEFT));
+                register_code(KC_LSHIFT);
+                  tap_code16(LGUI(KC_RIGHT));
+                unregister_code(KC_LSHIFT);
+                tap_code16(LGUI(KC_X));
+                yank_was_lines = false;
+                EDIT;
+              } else {
+                tap_code16(LSFT(KC_RIGHT));
+                tap_code16(LGUI(KC_X));
+                yank_was_lines = false;
+                EDIT;
+              }
+              break;
+            case VIM_U:
+              if(SHIFTED) {
+                register_code(KC_LSFT);
+                  tap_code16(LGUI(KC_Z));
+                unregister_code(KC_LSHIFT);
+              } else {
+                tap_code16(LGUI(KC_Z));
+              }
+              break;
+            case VIM_V:
+              if(SHIFTED) {
+                tap_code16(LGUI(KC_LEFT));
+                tap_code16(LSFT(KC_DOWN));
+                vstate = VIM_VS;
+              } else {
+                vstate = VIM_V;
+              }
+              break;
+            case VIM_W:
+              register_code(KC_LALT);
+                tap_code(KC_RIGHT);
+                tap_code(KC_RIGHT);
+                tap_code(KC_LEFT);
+              unregister_code(KC_LALT);
+              break;
+            case VIM_X:
+              // tap_code16(LSFT(KC_RIGHT));
+              // tap_code16(LGUI(KC_X));
+              register_code(KC_DEL);
+              break;
+            case VIM_Y:
+              if(SHIFTED) {
+                tap_code16(LGUI(KC_LEFT));
+                tap_code16(LSFT(KC_DOWN));
+                tap_code16(LGUI(KC_C));
+                tap_code(KC_RIGHT);
+                yank_was_lines = true;
+              } else {
+                vstate = VIM_Y;
+              }
+              break;
+            case VIM_COMMA:
+            case VIM_PERIOD:
+              comma_period(keycode);
+              break;
+          }
+          break;
+        case VIM_C:
+          /*****************************
+           * c-  ...for change. I never use this...
+           *****************************/
+          switch(keycode) {
+          case VIM_B:
+          case VIM_E:
+          case VIM_H:
+          case VIM_J:
+          case VIM_K:
+          case VIM_L:
+          case VIM_W:
+            simple_movement(keycode);
+            tap_code16(LGUI(KC_X));
+            yank_was_lines = false;
+            EDIT;
+            break;
+
+          case VIM_C:
+            tap_code16(LGUI(KC_LEFT));
+            register_code(KC_LSHIFT);
+              tap_code16(LGUI(KC_RIGHT));
+            unregister_code(KC_LSHIFT);
+            tap_code16(LGUI(KC_X));
+            yank_was_lines = false;
+            EDIT;
+            break;
+          case VIM_I:
+            vstate = VIM_CI;
+            break;
+          default:
+            vstate = VIM_START;
+            break;
+          }
+          break;
+        case VIM_CI:
+          /*****************************
+           * ci-  ...change inner word
+           *****************************/
+          switch(keycode) {
+            case VIM_W:
+              tap_code16(LALT(KC_LEFT));
+              register_code(KC_LSHIFT);
+                tap_code16(LALT(KC_RIGHT));
+              unregister_code(KC_LSHIFT);
+              tap_code16(LGUI(KC_X));
+              yank_was_lines = false;
+              EDIT;
+            default:
+              vstate = VIM_START;
+              break;
+          }
+          break;
+        case VIM_D:
+          /*****************************
+           * d-  ...delete stuff
+           *****************************/
+          switch(keycode) {
+          case VIM_B:
+          case VIM_E:
+          case VIM_H:
+          case VIM_J:
+          case VIM_K:
+          case VIM_L:
+          case VIM_W:
+            simple_movement(keycode);
+            tap_code16(LGUI(KC_X));
+            yank_was_lines = false;
+            vstate = VIM_START;
+            break;
+          case VIM_D:
+            tap_code16(LGUI(KC_LEFT));
+            tap_code16(LSFT(KC_DOWN));
+            tap_code16(LGUI(KC_X));
+            yank_was_lines = true;
+            vstate = VIM_START;
+            break;
+          case VIM_I:
+            vstate = VIM_DI;
+            break;
+          default:
+            vstate = VIM_START;
+            break;
+          }
+          break;
+        case VIM_DI:
+          /*****************************
+           * ci-  ...delete a word... FROM THE INSIDE!
+           *****************************/
+          switch(keycode) {
+            case VIM_W:
+              tap_code16(LALT(KC_LEFT));
+              register_code(KC_LSHIFT);
+                tap_code16(LALT(KC_RIGHT));
+              unregister_code(KC_LSHIFT);
+              tap_code16(LGUI(KC_X));
+              yank_was_lines = false;
+              vstate = VIM_START;
+            default:
+              vstate = VIM_START;
+              break;
+          }
+          break;
+        case VIM_V:
+          /*****************************
+           * visual!
+           *****************************/
+          switch(keycode) {
+            case VIM_D:
+            case VIM_X:
+              tap_code16(LGUI(KC_X));
+              yank_was_lines = false;
+              vstate = VIM_START;
+              break;
+            case VIM_B:
+              register_code(KC_LALT);
+              register_code(KC_LSHIFT);
+              register_code(KC_LEFT);
+              // leave open for key repeat
+              break;
+            case VIM_E:
+              register_code(KC_LALT);
+              register_code(KC_LSHIFT);
+              register_code(KC_RIGHT);
+              // leave open for key repeat
+              break;
+            case VIM_H:
+              register_code(KC_LSHIFT);
+              register_code(KC_LEFT);
+              break;
+            case VIM_I:
+              vstate = VIM_VI;
+              break;
+            case VIM_J:
+              register_code(KC_LSHIFT);
+              register_code(KC_DOWN);
+              break;
+            case VIM_K:
+              register_code(KC_LSHIFT);
+              register_code(KC_UP);
+              break;
+            case VIM_L:
+              register_code(KC_LSHIFT);
+              register_code(KC_RIGHT);
+              break;
+            case VIM_W:
+              register_code(KC_LALT);
+              tap_code16(LSFT(KC_RIGHT)); // select to end of this word
+              tap_code16(LSFT(KC_RIGHT)); // select to end of next word
+              tap_code16(LSFT(KC_LEFT));  // select to start of next word
+              unregister_code(KC_LALT);
+              break;
+            case VIM_P:
+              tap_code16(LGUI(KC_V));
+              vstate = VIM_START;
+              break;
+            case VIM_Y:
+              tap_code16(LGUI(KC_C));
+              tap_code(KC_RIGHT);
+              yank_was_lines = false;
+              vstate = VIM_START;
+              break;
+            case VIM_V:
+            case VIM_ESC:
+              tap_code(KC_RIGHT);
+              vstate = VIM_START;
+              break;
+            case VIM_COMMA:
+            case VIM_PERIOD:
+              comma_period(keycode);
+              break;
+            default:
+              // do nothing
+              break;
+          }
+          break;
+        case VIM_VI:
+          /*****************************
+           * vi-  ...select a word... FROM THE INSIDE!
+           *****************************/
+          switch(keycode) {
+            case VIM_W:
+              tap_code16(LALT(KC_LEFT));
+              register_code(KC_LSHIFT);
+                tap_code16(LALT(KC_RIGHT));
+              unregister_code(KC_LSHIFT);
+              vstate = VIM_V;
+            default:
+              // ignore
+              vstate = VIM_V;
+              break;
+          }
+          break;
+        case VIM_VS:
+          /*****************************
+           * visual line
+           *****************************/
+          switch(keycode) {
+            case VIM_D:
+            case VIM_X:
+              tap_code16(LGUI(KC_X));
+              yank_was_lines = true;
+              vstate = VIM_START;
+              break;
+            case VIM_J:
+              register_code(KC_LSHIFT);
+              register_code(KC_DOWN);
+              break;
+            case VIM_K:
+              register_code(KC_LSHIFT);
+              register_code(KC_UP);
+              break;
+            case VIM_Y:
+              tap_code16(LGUI(KC_C));
+              yank_was_lines = true;
+              tap_code(KC_RIGHT);
+              vstate = VIM_START;
+              break;
+            case VIM_P:
+              tap_code16(LGUI(KC_V));
+              vstate = VIM_START;
+              break;
+            case VIM_V:
+            case VIM_ESC:
+              tap_code(KC_RIGHT);
+              vstate = VIM_START;
+              break;
+            case VIM_COMMA:
+            case VIM_PERIOD:
+              comma_period(keycode);
+              break;
+            default:
+              // do nothing
+              break;
+          }
+          break;
+        case VIM_G:
+          /*****************************
+           * gg, and a grab-bag of other macros i find useful
+           *****************************/
+          switch(keycode) {
+            case VIM_G:
+              tap_code(KC_HOME);
+              break;
+            // codes b
+            case VIM_H:
+              tap_code16(LCTL(KC_A));
+              break;
+            case VIM_J:
+              register_code(KC_PGDN);
+              break;
+            case VIM_K:
+              register_code(KC_PGUP);
+              break;
+            case VIM_L:
+              tap_code16(LCTL(KC_E));
+              break;
+            default:
+              // do nothing
+              break;
+          }
+          vstate = VIM_START;
+          break;
+        case VIM_Y:
+          /*****************************
+           * yoink!
+           *****************************/
+          switch(keycode) {
+          case VIM_B:
+          case VIM_E:
+          case VIM_H:
+          case VIM_J:
+          case VIM_K:
+          case VIM_L:
+          case VIM_W:
+            simple_movement(keycode);
+            tap_code16(LGUI(KC_C));
+            tap_code(KC_RIGHT);
+            yank_was_lines = false;
+            break;
+          case VIM_Y:
+            tap_code16(LGUI(KC_LEFT));
+            tap_code16(LSFT(KC_DOWN));
+            tap_code16(LGUI(KC_C));
+            tap_code(KC_RIGHT);
+            yank_was_lines = true;
+            break;
+          default:
+            // NOTHING
+            break;
+          }
+          vstate = VIM_START;
+          break;
+        }
+    } else {
+      /************************
+       * key unregister_code events
+       ************************/
+      clear_keyboard();
+    }
+    return false;
+  } else {
+    return true;
+  }
+}
+
diff --git a/users/twschum/xtonhasvim.h b/users/twschum/xtonhasvim.h
new file mode 100644
index 0000000000..fd9ebd4f03
--- /dev/null
+++ b/users/twschum/xtonhasvim.h
@@ -0,0 +1,62 @@
+ /* Copyright 2015-2017 Christon DeWan
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef USERSPACE
+#define USERSPACE
+
+#include QMK_KEYBOARD_H
+
+enum xtonhasvim_keycodes {
+  VIM_START = SAFE_RANGE,  // bookend for vim states
+  VIM_A,
+  VIM_B,
+  VIM_C,
+  VIM_CI,
+  VIM_D,
+  VIM_DI,
+  VIM_E,
+  VIM_H,
+  VIM_G,
+  VIM_I,
+  VIM_J,
+  VIM_K,
+  VIM_L,
+  VIM_O,
+  VIM_P,
+  VIM_S,
+  VIM_U,
+  VIM_V,
+  VIM_VS, // visual-line
+  VIM_VI,
+  VIM_W,
+  VIM_X,
+  VIM_Y,
+  VIM_PERIOD, // to support indent/outdent
+  VIM_COMMA,  // and toggle comments
+  VIM_SHIFT, // avoid side-effect of supporting real shift.
+  VIM_ESC, // bookend
+  VIM_SAFE_RANGE // start other keycodes here.
+};
+
+bool process_record_vimlayer(uint16_t keycode, keyrecord_t *record);
+
+// NOTE: Define this in keymap.c to return vim layer
+extern uint8_t vim_cmd_layer(void);
+
+extern uint16_t vstate;
+
+
+#endif