diff --git a/build_keyboard.mk b/build_keyboard.mk
index c8e82cf0e..4a6fc0980 100644
--- a/build_keyboard.mk
+++ b/build_keyboard.mk
@@ -168,16 +168,19 @@ endif
 
 ifeq ($(strip $(UCIS_ENABLE)), yes)
 	OPT_DEFS += -DUCIS_ENABLE
-	UNICODE_ENABLE = yes
+	SRC += $(QUANTUM_DIR)/process_keycode/process_unicode_common.c
+	SRC += $(QUANTUM_DIR)/process_keycode/process_ucis.c
 endif
 
 ifeq ($(strip $(UNICODEMAP_ENABLE)), yes)
 	OPT_DEFS += -DUNICODEMAP_ENABLE
-	UNICODE_ENABLE = yes
+	SRC += $(QUANTUM_DIR)/process_keycode/process_unicode_common.c
+	SRC += $(QUANTUM_DIR)/process_keycode/process_unicodemap.c
 endif
 
 ifeq ($(strip $(UNICODE_ENABLE)), yes)
     OPT_DEFS += -DUNICODE_ENABLE
+	SRC += $(QUANTUM_DIR)/process_keycode/process_unicode_common.c
 	SRC += $(QUANTUM_DIR)/process_keycode/process_unicode.c
 endif
 
diff --git a/quantum/keymap_common.c b/quantum/keymap_common.c
index 54b872d49..002eabd85 100644
--- a/quantum/keymap_common.c
+++ b/quantum/keymap_common.c
@@ -119,6 +119,9 @@ action_t action_for_key(uint8_t layer, keypos_t key)
             mod = keycode & 0xFF;
             action.code = ACTION_MODS_ONESHOT(mod);
             break;
+        case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:
+            action.code = ACTION_LAYER_TAP_TOGGLE(keycode & 0xFF);
+            break;
         case QK_MOD_TAP ... QK_MOD_TAP_MAX:
             action.code = ACTION_MODS_TAP_KEY((keycode >> 0x8) & 0x1F, keycode & 0xFF);
             break;
diff --git a/quantum/process_keycode/process_ucis.c b/quantum/process_keycode/process_ucis.c
new file mode 100644
index 000000000..4ad2533b0
--- /dev/null
+++ b/quantum/process_keycode/process_ucis.c
@@ -0,0 +1,133 @@
+#include "process_ucis.h"
+
+qk_ucis_state_t qk_ucis_state;
+
+void qk_ucis_start(void) {
+  qk_ucis_state.count = 0;
+  qk_ucis_state.in_progress = true;
+
+  qk_ucis_start_user();
+}
+
+__attribute__((weak))
+void qk_ucis_start_user(void) {
+  unicode_input_start();
+  register_hex(0x2328);
+  unicode_input_finish();
+}
+
+static bool is_uni_seq(char *seq) {
+  uint8_t i;
+
+  for (i = 0; seq[i]; i++) {
+    uint16_t code;
+    if (('1' <= seq[i]) && (seq[i] <= '0'))
+      code = seq[i] - '1' + KC_1;
+    else
+      code = seq[i] - 'a' + KC_A;
+
+    if (i > qk_ucis_state.count || qk_ucis_state.codes[i] != code)
+      return false;
+  }
+
+  return (qk_ucis_state.codes[i] == KC_ENT ||
+          qk_ucis_state.codes[i] == KC_SPC);
+}
+
+__attribute__((weak))
+void qk_ucis_symbol_fallback (void) {
+  for (uint8_t i = 0; i < qk_ucis_state.count - 1; i++) {
+    uint8_t code = qk_ucis_state.codes[i];
+    register_code(code);
+    unregister_code(code);
+    wait_ms(UNICODE_TYPE_DELAY);
+  }
+}
+
+void register_ucis(const char *hex) {
+  for(int i = 0; hex[i]; i++) {
+    uint8_t kc = 0;
+    char c = hex[i];
+
+    switch (c) {
+    case '0':
+      kc = KC_0;
+      break;
+    case '1' ... '9':
+      kc = c - '1' + KC_1;
+      break;
+    case 'a' ... 'f':
+      kc = c - 'a' + KC_A;
+      break;
+    case 'A' ... 'F':
+      kc = c - 'A' + KC_A;
+      break;
+    }
+
+    if (kc) {
+      register_code (kc);
+      unregister_code (kc);
+      wait_ms (UNICODE_TYPE_DELAY);
+    }
+  }
+}
+
+bool process_ucis (uint16_t keycode, keyrecord_t *record) {
+  uint8_t i;
+
+  if (!qk_ucis_state.in_progress)
+    return true;
+
+  if (qk_ucis_state.count >= UCIS_MAX_SYMBOL_LENGTH &&
+      !(keycode == KC_BSPC || keycode == KC_ESC || keycode == KC_SPC || keycode == KC_ENT)) {
+    return false;
+  }
+
+  if (!record->event.pressed)
+    return true;
+
+  qk_ucis_state.codes[qk_ucis_state.count] = keycode;
+  qk_ucis_state.count++;
+
+  if (keycode == KC_BSPC) {
+    if (qk_ucis_state.count >= 2) {
+      qk_ucis_state.count -= 2;
+      return true;
+    } else {
+      qk_ucis_state.count--;
+      return false;
+    }
+  }
+
+  if (keycode == KC_ENT || keycode == KC_SPC || keycode == KC_ESC) {
+    bool symbol_found = false;
+
+    for (i = qk_ucis_state.count; i > 0; i--) {
+      register_code (KC_BSPC);
+      unregister_code (KC_BSPC);
+      wait_ms(UNICODE_TYPE_DELAY);
+    }
+
+    if (keycode == KC_ESC) {
+      qk_ucis_state.in_progress = false;
+      return false;
+    }
+
+    unicode_input_start();
+    for (i = 0; ucis_symbol_table[i].symbol; i++) {
+      if (is_uni_seq (ucis_symbol_table[i].symbol)) {
+        symbol_found = true;
+        register_ucis(ucis_symbol_table[i].code + 2);
+        break;
+      }
+    }
+    if (!symbol_found) {
+      qk_ucis_symbol_fallback();
+    }
+    unicode_input_finish();
+
+    qk_ucis_state.in_progress = false;
+    return false;
+  }
+  return true;
+}
\ No newline at end of file
diff --git a/quantum/process_keycode/process_ucis.h b/quantum/process_keycode/process_ucis.h
new file mode 100644
index 000000000..4332f57b3
--- /dev/null
+++ b/quantum/process_keycode/process_ucis.h
@@ -0,0 +1,35 @@
+#ifndef PROCESS_UCIS_H
+#define PROCESS_UCIS_H
+
+#include "quantum.h"
+#include "process_unicode_common.h"
+
+#ifndef UCIS_MAX_SYMBOL_LENGTH
+#define UCIS_MAX_SYMBOL_LENGTH 32
+#endif
+
+typedef struct {
+  char *symbol;
+  char *code;
+} qk_ucis_symbol_t;
+
+typedef struct {
+  uint8_t count;
+  uint16_t codes[UCIS_MAX_SYMBOL_LENGTH];
+  bool in_progress:1;
+} qk_ucis_state_t;
+
+extern qk_ucis_state_t qk_ucis_state;
+
+#define UCIS_TABLE(...) {__VA_ARGS__, {NULL, NULL}}
+#define UCIS_SYM(name, code) {name, #code}
+
+extern const qk_ucis_symbol_t ucis_symbol_table[];
+
+void qk_ucis_start(void);
+void qk_ucis_start_user(void);
+void qk_ucis_symbol_fallback (void);
+void register_ucis(const char *hex);
+bool process_ucis (uint16_t keycode, keyrecord_t *record);
+
+#endif
diff --git a/quantum/process_keycode/process_unicode.c b/quantum/process_keycode/process_unicode.c
index 9d01a592d..ccae6fdca 100644
--- a/quantum/process_keycode/process_unicode.c
+++ b/quantum/process_keycode/process_unicode.c
@@ -1,103 +1,6 @@
 #include "process_unicode.h"
 #include "action_util.h"
 
-static uint8_t input_mode;
-uint8_t mods;
-
-__attribute__((weak))
-uint16_t hex_to_keycode(uint8_t hex)
-{
-  if (hex == 0x0) {
-    return KC_0;
-  } else if (hex < 0xA) {
-    return KC_1 + (hex - 0x1);
-  } else {
-    return KC_A + (hex - 0xA);
-  }
-}
-
-void set_unicode_input_mode(uint8_t os_target)
-{
-  input_mode = os_target;
-}
-
-uint8_t get_unicode_input_mode(void) {
-  return input_mode;
-}
-
-__attribute__((weak))
-void unicode_input_start (void) {
-  // save current mods
-  mods = keyboard_report->mods;
-
-  // unregister all mods to start from clean state
-  if (mods & MOD_BIT(KC_LSFT)) unregister_code(KC_LSFT);
-  if (mods & MOD_BIT(KC_RSFT)) unregister_code(KC_RSFT);
-  if (mods & MOD_BIT(KC_LCTL)) unregister_code(KC_LCTL);
-  if (mods & MOD_BIT(KC_RCTL)) unregister_code(KC_RCTL);
-  if (mods & MOD_BIT(KC_LALT)) unregister_code(KC_LALT);
-  if (mods & MOD_BIT(KC_RALT)) unregister_code(KC_RALT);
-  if (mods & MOD_BIT(KC_LGUI)) unregister_code(KC_LGUI);
-  if (mods & MOD_BIT(KC_RGUI)) unregister_code(KC_RGUI);
-
-  switch(input_mode) {
-  case UC_OSX:
-    register_code(KC_LALT);
-    break;
-  case UC_LNX:
-    register_code(KC_LCTL);
-    register_code(KC_LSFT);
-    register_code(KC_U);
-    unregister_code(KC_U);
-    unregister_code(KC_LSFT);
-    unregister_code(KC_LCTL);
-    break;
-  case UC_WIN:
-    register_code(KC_LALT);
-    register_code(KC_PPLS);
-    unregister_code(KC_PPLS);
-    break;
-  case UC_WINC:
-    register_code(KC_RALT);
-    unregister_code(KC_RALT);
-    register_code(KC_U);
-    unregister_code(KC_U);
-  }
-  wait_ms(UNICODE_TYPE_DELAY);
-}
-
-__attribute__((weak))
-void unicode_input_finish (void) {
-  switch(input_mode) {
-    case UC_OSX:
-    case UC_WIN:
-      unregister_code(KC_LALT);
-      break;
-    case UC_LNX:
-      register_code(KC_SPC);
-      unregister_code(KC_SPC);
-      break;
-  }
-
-  // reregister previously set mods
-  if (mods & MOD_BIT(KC_LSFT)) register_code(KC_LSFT);
-  if (mods & MOD_BIT(KC_RSFT)) register_code(KC_RSFT);
-  if (mods & MOD_BIT(KC_LCTL)) register_code(KC_LCTL);
-  if (mods & MOD_BIT(KC_RCTL)) register_code(KC_RCTL);
-  if (mods & MOD_BIT(KC_LALT)) register_code(KC_LALT);
-  if (mods & MOD_BIT(KC_RALT)) register_code(KC_RALT);
-  if (mods & MOD_BIT(KC_LGUI)) register_code(KC_LGUI);
-  if (mods & MOD_BIT(KC_RGUI)) register_code(KC_RGUI);
-}
-
-void register_hex(uint16_t hex) {
-  for(int i = 3; i >= 0; i--) {
-    uint8_t digit = ((hex >> (i*4)) & 0xF);
-    register_code(hex_to_keycode(digit));
-    unregister_code(hex_to_keycode(digit));
-  }
-}
-
 bool process_unicode(uint16_t keycode, keyrecord_t *record) {
   if (keycode > QK_UNICODE && record->event.pressed) {
     uint16_t unicode = keycode & 0x7FFF;
@@ -108,191 +11,3 @@ bool process_unicode(uint16_t keycode, keyrecord_t *record) {
   return true;
 }
 
-#ifdef UNICODEMAP_ENABLE
-__attribute__((weak))
-const uint32_t PROGMEM unicode_map[] = {
-};
-
-void register_hex32(uint32_t hex) {
-  uint8_t onzerostart = 1;
-  for(int i = 7; i >= 0; i--) {
-    if (i <= 3) {
-      onzerostart = 0;
-    }
-    uint8_t digit = ((hex >> (i*4)) & 0xF);
-    if (digit == 0) {
-      if (onzerostart == 0) {
-        register_code(hex_to_keycode(digit));
-        unregister_code(hex_to_keycode(digit));
-      }
-    } else {
-      register_code(hex_to_keycode(digit));
-      unregister_code(hex_to_keycode(digit));
-      onzerostart = 0;
-    }
-  }
-}
-
-__attribute__((weak))
-void unicode_map_input_error() {}
-
-bool process_unicode_map(uint16_t keycode, keyrecord_t *record) {
-  if ((keycode & QK_UNICODE_MAP) == QK_UNICODE_MAP && record->event.pressed) {
-    const uint32_t* map = unicode_map;
-    uint16_t index = keycode - QK_UNICODE_MAP;
-    uint32_t code = pgm_read_dword_far(&map[index]);
-    if (code > 0xFFFF && code <= 0x10ffff && input_mode == UC_OSX) {
-      // Convert to UTF-16 surrogate pair
-      code -= 0x10000;
-      uint32_t lo = code & 0x3ff;
-      uint32_t hi = (code & 0xffc00) >> 10;
-      unicode_input_start();
-      register_hex32(hi + 0xd800);
-      register_hex32(lo + 0xdc00);
-      unicode_input_finish();
-    } else if ((code > 0x10ffff && input_mode == UC_OSX) || (code > 0xFFFFF && input_mode == UC_LNX)) {
-      // when character is out of range supported by the OS
-      unicode_map_input_error();
-    } else {
-      unicode_input_start();
-      register_hex32(code);
-      unicode_input_finish();
-    }
-  }
-  return true;
-}
-#endif
-
-#ifdef UCIS_ENABLE
-qk_ucis_state_t qk_ucis_state;
-
-void qk_ucis_start(void) {
-  qk_ucis_state.count = 0;
-  qk_ucis_state.in_progress = true;
-
-  qk_ucis_start_user();
-}
-
-__attribute__((weak))
-void qk_ucis_start_user(void) {
-  unicode_input_start();
-  register_hex(0x2328);
-  unicode_input_finish();
-}
-
-static bool is_uni_seq(char *seq) {
-  uint8_t i;
-
-  for (i = 0; seq[i]; i++) {
-    uint16_t code;
-    if (('1' <= seq[i]) && (seq[i] <= '0'))
-      code = seq[i] - '1' + KC_1;
-    else
-      code = seq[i] - 'a' + KC_A;
-
-    if (i > qk_ucis_state.count || qk_ucis_state.codes[i] != code)
-      return false;
-  }
-
-  return (qk_ucis_state.codes[i] == KC_ENT ||
-          qk_ucis_state.codes[i] == KC_SPC);
-}
-
-__attribute__((weak))
-void qk_ucis_symbol_fallback (void) {
-  for (uint8_t i = 0; i < qk_ucis_state.count - 1; i++) {
-    uint8_t code = qk_ucis_state.codes[i];
-    register_code(code);
-    unregister_code(code);
-    wait_ms(UNICODE_TYPE_DELAY);
-  }
-}
-
-void register_ucis(const char *hex) {
-  for(int i = 0; hex[i]; i++) {
-    uint8_t kc = 0;
-    char c = hex[i];
-
-    switch (c) {
-    case '0':
-      kc = KC_0;
-      break;
-    case '1' ... '9':
-      kc = c - '1' + KC_1;
-      break;
-    case 'a' ... 'f':
-      kc = c - 'a' + KC_A;
-      break;
-    case 'A' ... 'F':
-      kc = c - 'A' + KC_A;
-      break;
-    }
-
-    if (kc) {
-      register_code (kc);
-      unregister_code (kc);
-      wait_ms (UNICODE_TYPE_DELAY);
-    }
-  }
-}
-
-bool process_ucis (uint16_t keycode, keyrecord_t *record) {
-  uint8_t i;
-
-  if (!qk_ucis_state.in_progress)
-    return true;
-
-  if (qk_ucis_state.count >= UCIS_MAX_SYMBOL_LENGTH &&
-      !(keycode == KC_BSPC || keycode == KC_ESC || keycode == KC_SPC || keycode == KC_ENT)) {
-    return false;
-  }
-
-  if (!record->event.pressed)
-    return true;
-
-  qk_ucis_state.codes[qk_ucis_state.count] = keycode;
-  qk_ucis_state.count++;
-
-  if (keycode == KC_BSPC) {
-    if (qk_ucis_state.count >= 2) {
-      qk_ucis_state.count -= 2;
-      return true;
-    } else {
-      qk_ucis_state.count--;
-      return false;
-    }
-  }
-
-  if (keycode == KC_ENT || keycode == KC_SPC || keycode == KC_ESC) {
-    bool symbol_found = false;
-
-    for (i = qk_ucis_state.count; i > 0; i--) {
-      register_code (KC_BSPC);
-      unregister_code (KC_BSPC);
-      wait_ms(UNICODE_TYPE_DELAY);
-    }
-
-    if (keycode == KC_ESC) {
-      qk_ucis_state.in_progress = false;
-      return false;
-    }
-
-    unicode_input_start();
-    for (i = 0; ucis_symbol_table[i].symbol; i++) {
-      if (is_uni_seq (ucis_symbol_table[i].symbol)) {
-        symbol_found = true;
-        register_ucis(ucis_symbol_table[i].code + 2);
-        break;
-      }
-    }
-    if (!symbol_found) {
-      qk_ucis_symbol_fallback();
-    }
-    unicode_input_finish();
-
-    qk_ucis_state.in_progress = false;
-    return false;
-  }
-  return true;
-}
-#endif
diff --git a/quantum/process_keycode/process_unicode.h b/quantum/process_keycode/process_unicode.h
index f17cfa6cf..4c21f11eb 100644
--- a/quantum/process_keycode/process_unicode.h
+++ b/quantum/process_keycode/process_unicode.h
@@ -2,166 +2,8 @@
 #define PROCESS_UNICODE_H
 
 #include "quantum.h"
-
-#define UC_OSX 0  // Mac OS X
-#define UC_LNX 1  // Linux
-#define UC_WIN 2  // Windows 'HexNumpad'
-#define UC_BSD 3  // BSD (not implemented)
-#define UC_WINC 4 // WinCompose https://github.com/samhocevar/wincompose
-
-#ifndef UNICODE_TYPE_DELAY
-#define UNICODE_TYPE_DELAY 10
-#endif
-
-void set_unicode_input_mode(uint8_t os_target);
-uint8_t get_unicode_input_mode(void);
-void unicode_input_start(void);
-void unicode_input_finish(void);
-void register_hex(uint16_t hex);
+#include "process_unicode_common.h"
 
 bool process_unicode(uint16_t keycode, keyrecord_t *record);
 
-#ifdef UNICODEMAP_ENABLE
-void unicode_map_input_error(void);
-bool process_unicode_map(uint16_t keycode, keyrecord_t *record);
-#endif
-
-#ifdef UCIS_ENABLE
-#ifndef UCIS_MAX_SYMBOL_LENGTH
-#define UCIS_MAX_SYMBOL_LENGTH 32
-#endif
-
-typedef struct {
-  char *symbol;
-  char *code;
-} qk_ucis_symbol_t;
-
-typedef struct {
-  uint8_t count;
-  uint16_t codes[UCIS_MAX_SYMBOL_LENGTH];
-  bool in_progress:1;
-} qk_ucis_state_t;
-
-extern qk_ucis_state_t qk_ucis_state;
-
-#define UCIS_TABLE(...) {__VA_ARGS__, {NULL, NULL}}
-#define UCIS_SYM(name, code) {name, #code}
-
-extern const qk_ucis_symbol_t ucis_symbol_table[];
-
-void qk_ucis_start(void);
-void qk_ucis_start_user(void);
-void qk_ucis_symbol_fallback (void);
-void register_ucis(const char *hex);
-bool process_ucis (uint16_t keycode, keyrecord_t *record);
-
-#endif
-
-#define UC_BSPC	UC(0x0008)
-
-#define UC_SPC	UC(0x0020)
-
-#define UC_EXLM	UC(0x0021)
-#define UC_DQUT	UC(0x0022)
-#define UC_HASH	UC(0x0023)
-#define UC_DLR	UC(0x0024)
-#define UC_PERC	UC(0x0025)
-#define UC_AMPR	UC(0x0026)
-#define UC_QUOT	UC(0x0027)
-#define UC_LPRN	UC(0x0028)
-#define UC_RPRN	UC(0x0029)
-#define UC_ASTR	UC(0x002A)
-#define UC_PLUS	UC(0x002B)
-#define UC_COMM	UC(0x002C)
-#define UC_DASH	UC(0x002D)
-#define UC_DOT	UC(0x002E)
-#define UC_SLSH	UC(0x002F)
-
-#define UC_0	UC(0x0030)
-#define UC_1	UC(0x0031)
-#define UC_2	UC(0x0032)
-#define UC_3	UC(0x0033)
-#define UC_4	UC(0x0034)
-#define UC_5	UC(0x0035)
-#define UC_6	UC(0x0036)
-#define UC_7	UC(0x0037)
-#define UC_8	UC(0x0038)
-#define UC_9	UC(0x0039)
-
-#define UC_COLN UC(0x003A)
-#define UC_SCLN UC(0x003B)
-#define UC_LT	UC(0x003C)
-#define UC_EQL	UC(0x003D)
-#define UC_GT	UC(0x003E)
-#define UC_QUES	UC(0x003F)
-#define UC_AT 	UC(0x0040)
-
-#define UC_A 	UC(0x0041)
-#define UC_B 	UC(0x0042)
-#define UC_C 	UC(0x0043)
-#define UC_D 	UC(0x0044)
-#define UC_E 	UC(0x0045)
-#define UC_F 	UC(0x0046)
-#define UC_G 	UC(0x0047)
-#define UC_H 	UC(0x0048)
-#define UC_I 	UC(0x0049)
-#define UC_J 	UC(0x004A)
-#define UC_K 	UC(0x004B)
-#define UC_L 	UC(0x004C)
-#define UC_M 	UC(0x004D)
-#define UC_N 	UC(0x004E)
-#define UC_O 	UC(0x004F)
-#define UC_P 	UC(0x0050)
-#define UC_Q 	UC(0x0051)
-#define UC_R 	UC(0x0052)
-#define UC_S 	UC(0x0053)
-#define UC_T 	UC(0x0054)
-#define UC_U 	UC(0x0055)
-#define UC_V 	UC(0x0056)
-#define UC_W 	UC(0x0057)
-#define UC_X 	UC(0x0058)
-#define UC_Y 	UC(0x0059)
-#define UC_Z 	UC(0x005A)
-
-#define UC_LBRC	UC(0x005B)
-#define UC_BSLS	UC(0x005C)
-#define UC_RBRC	UC(0x005D)
-#define UC_CIRM	UC(0x005E)
-#define UC_UNDR	UC(0x005F)
-
-#define UC_GRV 	UC(0x0060)
-
-#define UC_a 	UC(0x0061)
-#define UC_b 	UC(0x0062)
-#define UC_c 	UC(0x0063)
-#define UC_d 	UC(0x0064)
-#define UC_e 	UC(0x0065)
-#define UC_f 	UC(0x0066)
-#define UC_g 	UC(0x0067)
-#define UC_h 	UC(0x0068)
-#define UC_i 	UC(0x0069)
-#define UC_j 	UC(0x006A)
-#define UC_k 	UC(0x006B)
-#define UC_l 	UC(0x006C)
-#define UC_m 	UC(0x006D)
-#define UC_n 	UC(0x006E)
-#define UC_o 	UC(0x006F)
-#define UC_p 	UC(0x0070)
-#define UC_q 	UC(0x0071)
-#define UC_r 	UC(0x0072)
-#define UC_s 	UC(0x0073)
-#define UC_t 	UC(0x0074)
-#define UC_u 	UC(0x0075)
-#define UC_v 	UC(0x0076)
-#define UC_w 	UC(0x0077)
-#define UC_x 	UC(0x0078)
-#define UC_y 	UC(0x0079)
-#define UC_z 	UC(0x007A)
-
-#define UC_LCBR	UC(0x007B)
-#define UC_PIPE	UC(0x007C)
-#define UC_RCBR	UC(0x007D)
-#define UC_TILD	UC(0x007E)
-#define UC_DEL	UC(0x007F)
-
 #endif
diff --git a/quantum/process_keycode/process_unicode_common.c b/quantum/process_keycode/process_unicode_common.c
new file mode 100644
index 000000000..31bc3b7ab
--- /dev/null
+++ b/quantum/process_keycode/process_unicode_common.c
@@ -0,0 +1,85 @@
+#include "process_unicode_common.h"
+
+uint8_t mods;
+
+void set_unicode_input_mode(uint8_t os_target)
+{
+  input_mode = os_target;
+}
+
+uint8_t get_unicode_input_mode(void) {
+  return input_mode;
+}
+
+__attribute__((weak))
+void unicode_input_start (void) {
+  // save current mods
+  mods = keyboard_report->mods;
+
+  // unregister all mods to start from clean state
+  if (mods & MOD_BIT(KC_LSFT)) unregister_code(KC_LSFT);
+  if (mods & MOD_BIT(KC_RSFT)) unregister_code(KC_RSFT);
+  if (mods & MOD_BIT(KC_LCTL)) unregister_code(KC_LCTL);
+  if (mods & MOD_BIT(KC_RCTL)) unregister_code(KC_RCTL);
+  if (mods & MOD_BIT(KC_LALT)) unregister_code(KC_LALT);
+  if (mods & MOD_BIT(KC_RALT)) unregister_code(KC_RALT);
+  if (mods & MOD_BIT(KC_LGUI)) unregister_code(KC_LGUI);
+  if (mods & MOD_BIT(KC_RGUI)) unregister_code(KC_RGUI);
+
+  switch(input_mode) {
+  case UC_OSX:
+    register_code(KC_LALT);
+    break;
+  case UC_LNX:
+    register_code(KC_LCTL);
+    register_code(KC_LSFT);
+    register_code(KC_U);
+    unregister_code(KC_U);
+    unregister_code(KC_LSFT);
+    unregister_code(KC_LCTL);
+    break;
+  case UC_WIN:
+    register_code(KC_LALT);
+    register_code(KC_PPLS);
+    unregister_code(KC_PPLS);
+    break;
+  case UC_WINC:
+    register_code(KC_RALT);
+    unregister_code(KC_RALT);
+    register_code(KC_U);
+    unregister_code(KC_U);
+  }
+  wait_ms(UNICODE_TYPE_DELAY);
+}
+
+__attribute__((weak))
+void unicode_input_finish (void) {
+  switch(input_mode) {
+    case UC_OSX:
+    case UC_WIN:
+      unregister_code(KC_LALT);
+      break;
+    case UC_LNX:
+      register_code(KC_SPC);
+      unregister_code(KC_SPC);
+      break;
+  }
+
+  // reregister previously set mods
+  if (mods & MOD_BIT(KC_LSFT)) register_code(KC_LSFT);
+  if (mods & MOD_BIT(KC_RSFT)) register_code(KC_RSFT);
+  if (mods & MOD_BIT(KC_LCTL)) register_code(KC_LCTL);
+  if (mods & MOD_BIT(KC_RCTL)) register_code(KC_RCTL);
+  if (mods & MOD_BIT(KC_LALT)) register_code(KC_LALT);
+  if (mods & MOD_BIT(KC_RALT)) register_code(KC_RALT);
+  if (mods & MOD_BIT(KC_LGUI)) register_code(KC_LGUI);
+  if (mods & MOD_BIT(KC_RGUI)) register_code(KC_RGUI);
+}
+
+void register_hex(uint16_t hex) {
+  for(int i = 3; i >= 0; i--) {
+    uint8_t digit = ((hex >> (i*4)) & 0xF);
+    register_code(hex_to_keycode(digit));
+    unregister_code(hex_to_keycode(digit));
+  }
+}
\ No newline at end of file
diff --git a/quantum/process_keycode/process_unicode_common.h b/quantum/process_keycode/process_unicode_common.h
new file mode 100644
index 000000000..1f25eae7d
--- /dev/null
+++ b/quantum/process_keycode/process_unicode_common.h
@@ -0,0 +1,131 @@
+#ifndef PROCESS_UNICODE_COMMON_H
+#define PROCESS_UNICODE_COMMON_H
+
+#include "quantum.h"
+
+#ifndef UNICODE_TYPE_DELAY
+#define UNICODE_TYPE_DELAY 10
+#endif
+
+static uint8_t input_mode;
+
+void set_unicode_input_mode(uint8_t os_target);
+uint8_t get_unicode_input_mode(void);
+void unicode_input_start(void);
+void unicode_input_finish(void);
+void register_hex(uint16_t hex);
+
+#define UC_OSX 0  // Mac OS X
+#define UC_LNX 1  // Linux
+#define UC_WIN 2  // Windows 'HexNumpad'
+#define UC_BSD 3  // BSD (not implemented)
+#define UC_WINC 4 // WinCompose https://github.com/samhocevar/wincompose
+
+#define UC_BSPC	UC(0x0008)
+
+#define UC_SPC	UC(0x0020)
+
+#define UC_EXLM	UC(0x0021)
+#define UC_DQUT	UC(0x0022)
+#define UC_HASH	UC(0x0023)
+#define UC_DLR	UC(0x0024)
+#define UC_PERC	UC(0x0025)
+#define UC_AMPR	UC(0x0026)
+#define UC_QUOT	UC(0x0027)
+#define UC_LPRN	UC(0x0028)
+#define UC_RPRN	UC(0x0029)
+#define UC_ASTR	UC(0x002A)
+#define UC_PLUS	UC(0x002B)
+#define UC_COMM	UC(0x002C)
+#define UC_DASH	UC(0x002D)
+#define UC_DOT	UC(0x002E)
+#define UC_SLSH	UC(0x002F)
+
+#define UC_0	UC(0x0030)
+#define UC_1	UC(0x0031)
+#define UC_2	UC(0x0032)
+#define UC_3	UC(0x0033)
+#define UC_4	UC(0x0034)
+#define UC_5	UC(0x0035)
+#define UC_6	UC(0x0036)
+#define UC_7	UC(0x0037)
+#define UC_8	UC(0x0038)
+#define UC_9	UC(0x0039)
+
+#define UC_COLN UC(0x003A)
+#define UC_SCLN UC(0x003B)
+#define UC_LT	UC(0x003C)
+#define UC_EQL	UC(0x003D)
+#define UC_GT	UC(0x003E)
+#define UC_QUES	UC(0x003F)
+#define UC_AT 	UC(0x0040)
+
+#define UC_A 	UC(0x0041)
+#define UC_B 	UC(0x0042)
+#define UC_C 	UC(0x0043)
+#define UC_D 	UC(0x0044)
+#define UC_E 	UC(0x0045)
+#define UC_F 	UC(0x0046)
+#define UC_G 	UC(0x0047)
+#define UC_H 	UC(0x0048)
+#define UC_I 	UC(0x0049)
+#define UC_J 	UC(0x004A)
+#define UC_K 	UC(0x004B)
+#define UC_L 	UC(0x004C)
+#define UC_M 	UC(0x004D)
+#define UC_N 	UC(0x004E)
+#define UC_O 	UC(0x004F)
+#define UC_P 	UC(0x0050)
+#define UC_Q 	UC(0x0051)
+#define UC_R 	UC(0x0052)
+#define UC_S 	UC(0x0053)
+#define UC_T 	UC(0x0054)
+#define UC_U 	UC(0x0055)
+#define UC_V 	UC(0x0056)
+#define UC_W 	UC(0x0057)
+#define UC_X 	UC(0x0058)
+#define UC_Y 	UC(0x0059)
+#define UC_Z 	UC(0x005A)
+
+#define UC_LBRC	UC(0x005B)
+#define UC_BSLS	UC(0x005C)
+#define UC_RBRC	UC(0x005D)
+#define UC_CIRM	UC(0x005E)
+#define UC_UNDR	UC(0x005F)
+
+#define UC_GRV 	UC(0x0060)
+
+#define UC_a 	UC(0x0061)
+#define UC_b 	UC(0x0062)
+#define UC_c 	UC(0x0063)
+#define UC_d 	UC(0x0064)
+#define UC_e 	UC(0x0065)
+#define UC_f 	UC(0x0066)
+#define UC_g 	UC(0x0067)
+#define UC_h 	UC(0x0068)
+#define UC_i 	UC(0x0069)
+#define UC_j 	UC(0x006A)
+#define UC_k 	UC(0x006B)
+#define UC_l 	UC(0x006C)
+#define UC_m 	UC(0x006D)
+#define UC_n 	UC(0x006E)
+#define UC_o 	UC(0x006F)
+#define UC_p 	UC(0x0070)
+#define UC_q 	UC(0x0071)
+#define UC_r 	UC(0x0072)
+#define UC_s 	UC(0x0073)
+#define UC_t 	UC(0x0074)
+#define UC_u 	UC(0x0075)
+#define UC_v 	UC(0x0076)
+#define UC_w 	UC(0x0077)
+#define UC_x 	UC(0x0078)
+#define UC_y 	UC(0x0079)
+#define UC_z 	UC(0x007A)
+
+#define UC_LCBR	UC(0x007B)
+#define UC_PIPE	UC(0x007C)
+#define UC_RCBR	UC(0x007D)
+#define UC_TILD	UC(0x007E)
+#define UC_DEL	UC(0x007F)
+
+#endif
\ No newline at end of file
diff --git a/quantum/process_keycode/process_unicodemap.c b/quantum/process_keycode/process_unicodemap.c
new file mode 100644
index 000000000..37f10df86
--- /dev/null
+++ b/quantum/process_keycode/process_unicodemap.c
@@ -0,0 +1,54 @@
+#include "process_unicodemap.h"
+
+__attribute__((weak))
+const uint32_t PROGMEM unicode_map[] = {
+};
+
+void register_hex32(uint32_t hex) {
+  uint8_t onzerostart = 1;
+  for(int i = 7; i >= 0; i--) {
+    if (i <= 3) {
+      onzerostart = 0;
+    }
+    uint8_t digit = ((hex >> (i*4)) & 0xF);
+    if (digit == 0) {
+      if (onzerostart == 0) {
+        register_code(hex_to_keycode(digit));
+        unregister_code(hex_to_keycode(digit));
+      }
+    } else {
+      register_code(hex_to_keycode(digit));
+      unregister_code(hex_to_keycode(digit));
+      onzerostart = 0;
+    }
+  }
+}
+
+__attribute__((weak))
+void unicode_map_input_error() {}
+
+bool process_unicode_map(uint16_t keycode, keyrecord_t *record) {
+  if ((keycode & QK_UNICODE_MAP) == QK_UNICODE_MAP && record->event.pressed) {
+    const uint32_t* map = unicode_map;
+    uint16_t index = keycode - QK_UNICODE_MAP;
+    uint32_t code = pgm_read_dword_far(&map[index]);
+    if (code > 0xFFFF && code <= 0x10ffff && input_mode == UC_OSX) {
+      // Convert to UTF-16 surrogate pair
+      code -= 0x10000;
+      uint32_t lo = code & 0x3ff;
+      uint32_t hi = (code & 0xffc00) >> 10;
+      unicode_input_start();
+      register_hex32(hi + 0xd800);
+      register_hex32(lo + 0xdc00);
+      unicode_input_finish();
+    } else if ((code > 0x10ffff && input_mode == UC_OSX) || (code > 0xFFFFF && input_mode == UC_LNX)) {
+      // when character is out of range supported by the OS
+      unicode_map_input_error();
+    } else {
+      unicode_input_start();
+      register_hex32(code);
+      unicode_input_finish();
+    }
+  }
+  return true;
+}
\ No newline at end of file
diff --git a/quantum/process_keycode/process_unicodemap.h b/quantum/process_keycode/process_unicodemap.h
new file mode 100644
index 000000000..64a7a0109
--- /dev/null
+++ b/quantum/process_keycode/process_unicodemap.h
@@ -0,0 +1,9 @@
+#ifndef PROCESS_UNICODEMAP_H
+#define PROCESS_UNICODEMAP_H
+
+#include "quantum.h"
+#include "process_unicode_common.h"
+
+void unicode_map_input_error(void);
+bool process_unicode_map(uint16_t keycode, keyrecord_t *record);
+#endif
\ No newline at end of file
diff --git a/quantum/quantum.c b/quantum/quantum.c
index 2088c10c9..4a6d0355f 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -972,6 +972,19 @@ void send_nibble(uint8_t number) {
     }
 }
 
+
+__attribute__((weak))
+uint16_t hex_to_keycode(uint8_t hex)
+{
+  if (hex == 0x0) {
+    return KC_0;
+  } else if (hex < 0xA) {
+    return KC_1 + (hex - 0x1);
+  } else {
+    return KC_A + (hex - 0xA);
+  }
+}
+
 void api_send_unicode(uint32_t unicode) {
 #ifdef API_ENABLE
     uint8_t chunk[4];
diff --git a/quantum/quantum.h b/quantum/quantum.h
index 18f072189..580d51202 100644
--- a/quantum/quantum.h
+++ b/quantum/quantum.h
@@ -56,6 +56,14 @@ extern uint32_t default_layer_state;
 	#include "process_unicode.h"
 #endif
 
+#ifdef UCIS_ENABLE
+	#include "process_ucis.h"
+#endif
+
+#ifdef UNICODEMAP_ENABLE
+	#include "process_unicodemap.h"
+#endif
+
 #include "process_tap_dance.h"
 
 #ifdef PRINTING_ENABLE
@@ -117,7 +125,7 @@ void send_dword(uint32_t number);
 void send_word(uint16_t number);
 void send_byte(uint8_t number);
 void send_nibble(uint8_t number);
-
+uint16_t hex_to_keycode(uint8_t hex);
 
 void led_set_user(uint8_t usb_led);
 void led_set_kb(uint8_t usb_led);
diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h
index cc7a5013f..63b626926 100644
--- a/quantum/quantum_keycodes.h
+++ b/quantum/quantum_keycodes.h
@@ -41,16 +41,21 @@ enum quantum_keycodes {
 #endif
     QK_TAP_DANCE          = 0x5700,
     QK_TAP_DANCE_MAX      = 0x57FF,
-#ifdef UNICODEMAP_ENABLE
-    QK_UNICODE_MAP        = 0x5800,
-    QK_UNICODE_MAP_MAX    = 0x5BFF,
-#endif
+    QK_LAYER_TAP_TOGGLE   = 0x5800,
+    QK_LAYER_TAP_TOGGLE_MAX = 0x58FF,
     QK_MOD_TAP            = 0x6000,
     QK_MOD_TAP_MAX        = 0x7FFF,
+#if defined(UNICODEMAP_ENABLE) && defined(UNICODE_ENABLE)
+    #error "Cannot enable both UNICODEMAP && UNICODE"
+#endif
 #ifdef UNICODE_ENABLE
     QK_UNICODE            = 0x8000,
     QK_UNICODE_MAX        = 0xFFFF,
 #endif
+#ifdef UNICODEMAP_ENABLE
+    QK_UNICODE_MAP        = 0x8000,
+    QK_UNICODE_MAP_MAX    = 0x83FF,
+#endif
 
     // Loose keycodes - to be used directly
 
@@ -304,6 +309,9 @@ enum quantum_keycodes {
 // One-shot mod
 #define OSM(mod) (mod | QK_ONE_SHOT_MOD)
 
+// Layer tap-toggle
+#define TT(layer) (layer | QK_LAYER_TAP_TOGGLE)
+
 // M-od, T-ap - 256 keycode max
 #define MT(mod, kc) (kc | QK_MOD_TAP | ((mod & 0x1F) << 8))