From b8e913c8db73ebf890e4604ee41991a34354a600 Mon Sep 17 00:00:00 2001
From: Joel Challis <git@zvecr.com>
Date: Wed, 18 Aug 2021 00:18:58 +0100
Subject: Migrate platform independent code from tmk_core -> quantum (#13673)

* Migrate action|keyboard|keycode|eeconfig from tmk_core -> quantum
---
 quantum/action.c         | 1126 ++++++++++++++++++++++++++++++++++++++++++++++
 quantum/action.h         |  132 ++++++
 quantum/action_code.h    |  308 +++++++++++++
 quantum/action_layer.c   |  279 ++++++++++++
 quantum/action_layer.h   |  147 ++++++
 quantum/action_macro.c   |   93 ++++
 quantum/action_macro.h   |  123 +++++
 quantum/action_tapping.c |  456 +++++++++++++++++++
 quantum/action_tapping.h |   42 ++
 quantum/action_util.c    |  455 +++++++++++++++++++
 quantum/action_util.h    |  105 +++++
 quantum/eeconfig.c       |  211 +++++++++
 quantum/eeconfig.h       |  113 +++++
 quantum/keyboard.c       |  569 +++++++++++++++++++++++
 quantum/keyboard.h       |   90 ++++
 quantum/keycode.h        |  560 +++++++++++++++++++++++
 quantum/via.h            |    2 +-
 17 files changed, 4810 insertions(+), 1 deletion(-)
 create mode 100644 quantum/action.c
 create mode 100644 quantum/action.h
 create mode 100644 quantum/action_code.h
 create mode 100644 quantum/action_layer.c
 create mode 100644 quantum/action_layer.h
 create mode 100644 quantum/action_macro.c
 create mode 100644 quantum/action_macro.h
 create mode 100644 quantum/action_tapping.c
 create mode 100644 quantum/action_tapping.h
 create mode 100644 quantum/action_util.c
 create mode 100644 quantum/action_util.h
 create mode 100644 quantum/eeconfig.c
 create mode 100644 quantum/eeconfig.h
 create mode 100644 quantum/keyboard.c
 create mode 100644 quantum/keyboard.h
 create mode 100644 quantum/keycode.h

(limited to 'quantum')

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