summary refs log tree commit diff
path: root/quantum/process_keycode
diff options
context:
space:
mode:
Diffstat (limited to 'quantum/process_keycode')
-rw-r--r--quantum/process_keycode/process_combo.c134
-rw-r--r--quantum/process_keycode/process_combo.h43
-rw-r--r--quantum/process_keycode/process_midi.c28
-rw-r--r--quantum/process_keycode/process_music.c23
-rw-r--r--quantum/process_keycode/process_printer.c254
-rw-r--r--quantum/process_keycode/process_printer.h8
-rw-r--r--quantum/process_keycode/process_printer_bb.c260
-rw-r--r--quantum/process_keycode/process_tap_dance.c149
-rw-r--r--quantum/process_keycode/process_tap_dance.h53
-rw-r--r--quantum/process_keycode/process_ucis.c133
-rw-r--r--quantum/process_keycode/process_ucis.h35
-rw-r--r--quantum/process_keycode/process_unicode.c56
-rw-r--r--quantum/process_keycode/process_unicode.h117
-rw-r--r--quantum/process_keycode/process_unicode_common.c85
-rw-r--r--quantum/process_keycode/process_unicode_common.h132
-rw-r--r--quantum/process_keycode/process_unicodemap.c54
-rw-r--r--quantum/process_keycode/process_unicodemap.h9
17 files changed, 1321 insertions, 252 deletions
diff --git a/quantum/process_keycode/process_combo.c b/quantum/process_keycode/process_combo.c
new file mode 100644
index 0000000000..e2189ad98b
--- /dev/null
+++ b/quantum/process_keycode/process_combo.c
@@ -0,0 +1,134 @@
+#include "process_combo.h"
+#include "print.h"
+
+
+#define COMBO_TIMER_ELAPSED -1
+
+
+__attribute__ ((weak))
+combo_t key_combos[] = {
+
+};
+
+__attribute__ ((weak))
+void process_combo_event(uint8_t combo_index, bool pressed) {
+
+}
+
+static uint8_t current_combo_index = 0;
+
+static inline void send_combo(uint16_t action, bool pressed)
+{
+    if (action) {
+        if (pressed) {
+            register_code16(action);
+        } else {
+            unregister_code16(action);
+        }
+    } else {
+        process_combo_event(current_combo_index, pressed);
+    }
+}
+
+#define ALL_COMBO_KEYS_ARE_DOWN     (((1<<count)-1) == combo->state)
+#define NO_COMBO_KEYS_ARE_DOWN      (0 == combo->state)
+#define KEY_STATE_DOWN(key)         do{ combo->state |= (1<<key); } while(0)
+#define KEY_STATE_UP(key)           do{ combo->state &= ~(1<<key); } while(0)
+static bool process_single_combo(combo_t *combo, uint16_t keycode, keyrecord_t *record) 
+{
+    uint8_t count = 0;
+    uint8_t index = -1;
+    /* Find index of keycode and number of combo keys */
+    for (const uint16_t *keys = combo->keys; ;++count) {
+        uint16_t key = pgm_read_word(&keys[count]);
+        if (keycode == key) index = count;
+        if (COMBO_END == key) break;
+    }
+
+    /* Return if not a combo key */
+    if (-1 == (int8_t)index) return false;
+
+    /* The combos timer is used to signal whether the combo is active */
+    bool is_combo_active = COMBO_TIMER_ELAPSED == combo->timer ? false : true;
+
+    if (record->event.pressed) {
+        KEY_STATE_DOWN(index);
+
+        if (is_combo_active) {
+            if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was pressed */
+                send_combo(combo->keycode, true);
+                combo->timer = COMBO_TIMER_ELAPSED;
+            } else { /* Combo key was pressed */
+                combo->timer = timer_read();
+#ifdef COMBO_ALLOW_ACTION_KEYS
+                combo->prev_record = *record;
+#else
+                combo->prev_key = keycode;
+#endif
+            }
+        }
+    } else {
+        if (ALL_COMBO_KEYS_ARE_DOWN) { /* Combo was released */
+            send_combo(combo->keycode, false);
+        }
+
+        if (is_combo_active) { /* Combo key was tapped */
+#ifdef COMBO_ALLOW_ACTION_KEYS
+            record->event.pressed = true;
+            process_action(record, store_or_get_action(record->event.pressed, record->event.key));
+            record->event.pressed = false;
+            process_action(record, store_or_get_action(record->event.pressed, record->event.key));
+#else
+            register_code16(keycode);
+            send_keyboard_report();
+            unregister_code16(keycode);
+#endif
+            combo->timer = 0;            
+        }
+
+        KEY_STATE_UP(index);        
+    }
+
+    if (NO_COMBO_KEYS_ARE_DOWN) {
+        combo->timer = 0;
+    }
+
+    return is_combo_active;
+}
+
+bool process_combo(uint16_t keycode, keyrecord_t *record)
+{
+    bool is_combo_key = false;
+
+    for (current_combo_index = 0; current_combo_index < COMBO_COUNT; ++current_combo_index) {
+        combo_t *combo = &key_combos[current_combo_index];
+        is_combo_key |= process_single_combo(combo, keycode, record);
+    }    
+
+    return !is_combo_key;
+}
+
+void matrix_scan_combo(void)
+{
+    for (int i = 0; i < COMBO_COUNT; ++i) {
+        combo_t *combo = &key_combos[i];
+        if (combo->timer && 
+            combo->timer != COMBO_TIMER_ELAPSED && 
+            timer_elapsed(combo->timer) > COMBO_TERM) {
+            
+            /* This disables the combo, meaning key events for this
+             * combo will be handled by the next processors in the chain 
+             */
+            combo->timer = COMBO_TIMER_ELAPSED;
+
+#ifdef COMBO_ALLOW_ACTION_KEYS
+            process_action(&combo->prev_record, 
+                store_or_get_action(combo->prev_record.event.pressed, 
+                                    combo->prev_record.event.key));
+#else
+            unregister_code16(combo->prev_key);
+            register_code16(combo->prev_key);
+#endif
+        }
+    }
+}
diff --git a/quantum/process_keycode/process_combo.h b/quantum/process_keycode/process_combo.h
new file mode 100644
index 0000000000..847f2b7376
--- /dev/null
+++ b/quantum/process_keycode/process_combo.h
@@ -0,0 +1,43 @@
+#ifndef PROCESS_COMBO_H
+#define PROCESS_COMBO_H
+
+#include <stdint.h>
+#include "progmem.h"
+#include "quantum.h"
+
+typedef struct
+{
+    const uint16_t *keys;
+    uint16_t keycode;        
+#ifdef EXTRA_EXTRA_LONG_COMBOS
+    uint32_t state;
+#elif EXTRA_LONG_COMBOS
+    uint16_t state;
+#else
+    uint8_t state;
+#endif
+    uint16_t timer;
+#ifdef COMBO_ALLOW_ACTION_KEYS
+    keyrecord_t prev_record;
+#else
+    uint16_t prev_key;
+#endif
+} combo_t;
+
+
+#define COMBO(ck, ca)       {.keys = &(ck)[0], .keycode = (ca)}
+#define COMBO_ACTION(ck)    {.keys = &(ck)[0]}
+
+#define COMBO_END 0
+#ifndef COMBO_COUNT
+#define COMBO_COUNT 0
+#endif
+#ifndef COMBO_TERM
+#define COMBO_TERM TAPPING_TERM
+#endif
+
+bool process_combo(uint16_t keycode, keyrecord_t *record);
+void matrix_scan_combo(void);
+void process_combo_event(uint8_t combo_index, bool pressed);
+
+#endif
diff --git a/quantum/process_keycode/process_midi.c b/quantum/process_keycode/process_midi.c
index d6ab9c6264..577dad43ac 100644
--- a/quantum/process_keycode/process_midi.c
+++ b/quantum/process_keycode/process_midi.c
@@ -1,13 +1,15 @@
 #include "process_midi.h"
 
 bool midi_activated = false;
-uint8_t starting_note = 0x0C;
-int offset = 7;
+uint8_t midi_starting_note = 0x0C;
+int midi_offset = 7;
 
 bool process_midi(uint16_t keycode, keyrecord_t *record) {
     if (keycode == MI_ON && record->event.pressed) {
       midi_activated = true;
+#ifdef AUDIO_ENABLE
       music_scale_user();
+#endif
       return false;
     }
 
@@ -20,42 +22,42 @@ bool process_midi(uint16_t keycode, keyrecord_t *record) {
     if (midi_activated) {
       if (record->event.key.col == (MATRIX_COLS - 1) && record->event.key.row == (MATRIX_ROWS - 1)) {
           if (record->event.pressed) {
-              starting_note++; // Change key
+              midi_starting_note++; // Change key
               midi_send_cc(&midi_device, 0, 0x7B, 0);
           }
           return false;
       }
       if (record->event.key.col == (MATRIX_COLS - 2) && record->event.key.row == (MATRIX_ROWS - 1)) {
           if (record->event.pressed) {
-              starting_note--; // Change key
+              midi_starting_note--; // Change key
               midi_send_cc(&midi_device, 0, 0x7B, 0);
           }
           return false;
       }
       if (record->event.key.col == (MATRIX_COLS - 3) && record->event.key.row == (MATRIX_ROWS - 1) && record->event.pressed) {
-          offset++; // Change scale
+          midi_offset++; // Change scale
           midi_send_cc(&midi_device, 0, 0x7B, 0);
           return false;
       }
       if (record->event.key.col == (MATRIX_COLS - 4) && record->event.key.row == (MATRIX_ROWS - 1) && record->event.pressed) {
-          offset--; // Change scale
+          midi_offset--; // Change scale
           midi_send_cc(&midi_device, 0, 0x7B, 0);
           return false;
       }
       // basic
-      // uint8_t note = (starting_note + SCALE[record->event.key.col + offset])+12*(MATRIX_ROWS - record->event.key.row);
+      // uint8_t note = (midi_starting_note + SCALE[record->event.key.col + midi_offset])+12*(MATRIX_ROWS - record->event.key.row);
       // advanced
-      // uint8_t note = (starting_note + record->event.key.col + offset)+12*(MATRIX_ROWS - record->event.key.row);
+      // uint8_t note = (midi_starting_note + record->event.key.col + midi_offset)+12*(MATRIX_ROWS - record->event.key.row);
       // guitar
-      uint8_t note = (starting_note + record->event.key.col + offset)+5*(MATRIX_ROWS - record->event.key.row);
+      uint8_t note = (midi_starting_note + record->event.key.col + midi_offset)+5*(MATRIX_ROWS - record->event.key.row);
       // violin
-      // uint8_t note = (starting_note + record->event.key.col + offset)+7*(MATRIX_ROWS - record->event.key.row);
+      // uint8_t note = (midi_starting_note + record->event.key.col + midi_offset)+7*(MATRIX_ROWS - record->event.key.row);
 
       if (record->event.pressed) {
-        // midi_send_noteon(&midi_device, record->event.key.row, starting_note + SCALE[record->event.key.col], 127);
+        // midi_send_noteon(&midi_device, record->event.key.row, midi_starting_note + SCALE[record->event.key.col], 127);
         midi_send_noteon(&midi_device, 0, note, 127);
       } else {
-        // midi_send_noteoff(&midi_device, record->event.key.row, starting_note + SCALE[record->event.key.col], 127);
+        // midi_send_noteoff(&midi_device, record->event.key.row, midi_starting_note + SCALE[record->event.key.col], 127);
         midi_send_noteoff(&midi_device, 0, note, 127);
       }
 
@@ -63,4 +65,4 @@ bool process_midi(uint16_t keycode, keyrecord_t *record) {
         return false;
     }
   return true;
-}
\ No newline at end of file
+}
diff --git a/quantum/process_keycode/process_music.c b/quantum/process_keycode/process_music.c
index c8f3ddb900..1e2648bff5 100644
--- a/quantum/process_keycode/process_music.c
+++ b/quantum/process_keycode/process_music.c
@@ -1,11 +1,12 @@
 #include "process_music.h"
 
 bool music_activated = false;
-uint8_t starting_note = 0x0C;
-int offset = 7;
+uint8_t music_starting_note = 0x0C;
+int music_offset = 7;
 
 // music sequencer
 static bool music_sequence_recording = false;
+static bool music_sequence_recorded = false;
 static bool music_sequence_playing = false;
 static float music_sequence[16] = {0};
 static uint8_t music_sequence_count = 0;
@@ -77,6 +78,7 @@ bool process_music(uint16_t keycode, keyrecord_t *record) {
       if (keycode == KC_LCTL && record->event.pressed) { // Start recording
         stop_all_notes();
         music_sequence_recording = true;
+        music_sequence_recorded = false;
         music_sequence_playing = false;
         music_sequence_count = 0;
         return false;
@@ -84,12 +86,15 @@ bool process_music(uint16_t keycode, keyrecord_t *record) {
 
       if (keycode == KC_LALT && record->event.pressed) { // Stop recording/playing
         stop_all_notes();
+        if (music_sequence_recording) { // was recording
+          music_sequence_recorded = true;
+        }
         music_sequence_recording = false;
         music_sequence_playing = false;
         return false;
       }
 
-      if (keycode == KC_LGUI && record->event.pressed) { // Start playing
+      if (keycode == KC_LGUI && record->event.pressed && music_sequence_recorded) { // Start playing
         stop_all_notes();
         music_sequence_recording = false;
         music_sequence_playing = true;
@@ -109,8 +114,18 @@ bool process_music(uint16_t keycode, keyrecord_t *record) {
             music_sequence_interval+=10;
         return false;
       }
+      #define MUSIC_MODE_GUITAR
+
+      #ifdef MUSIC_MODE_CHROMATIC
+      float freq = ((float)220.0)*pow(2.0, -5.0)*pow(2.0,(music_starting_note + record->event.key.col + music_offset)/12.0+(MATRIX_ROWS - record->event.key.row));
+      #elif defined(MUSIC_MODE_GUITAR)
+      float freq = ((float)220.0)*pow(2.0, -5.0)*pow(2.0,(music_starting_note + record->event.key.col + music_offset)/12.0+(float)(MATRIX_ROWS - record->event.key.row + 7)*5.0/12);
+      #elif defined(MUSIC_MODE_VIOLIN)
+      float freq = ((float)220.0)*pow(2.0, -5.0)*pow(2.0,(music_starting_note + record->event.key.col + music_offset)/12.0+(float)(MATRIX_ROWS - record->event.key.row + 5)*7.0/12);
+      #else
+      float freq = ((float)220.0)*pow(2.0, -5.0)*pow(2.0,(music_starting_note + SCALE[record->event.key.col + music_offset])/12.0+(MATRIX_ROWS - record->event.key.row));
+      #endif
 
-      float freq = ((float)220.0)*pow(2.0, -5.0)*pow(2.0,(starting_note + SCALE[record->event.key.col + offset])/12.0+(MATRIX_ROWS - record->event.key.row));
       if (record->event.pressed) {
         play_note(freq, 0xF);
         if (music_sequence_recording) {
diff --git a/quantum/process_keycode/process_printer.c b/quantum/process_keycode/process_printer.c
new file mode 100644
index 0000000000..2e11dd366c
--- /dev/null
+++ b/quantum/process_keycode/process_printer.c
@@ -0,0 +1,254 @@
+#include "process_printer.h"
+#include "action_util.h"
+
+bool printing_enabled = false;
+uint8_t character_shift = 0;
+
+void enabled_printing() {
+	printing_enabled = true;
+	serial_init();
+}
+
+void disable_printing() {
+	printing_enabled = false;
+}
+
+uint8_t shifted_numbers[10] = {0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29};
+
+// uint8_t keycode_to_ascii[0xFF][2];
+
+// keycode_to_ascii[KC_MINS] = {0x2D, 0x5F};
+
+void print_char(char c) {
+	USB_Disable();
+	serial_send(c);
+	USB_Init();
+}
+
+void print_box_string(uint8_t text[]) {
+	uint8_t len = strlen(text);
+	uint8_t out[len * 3 + 8];
+	out[0] = 0xDA;
+	for (uint8_t i = 0; i < len; i++) {
+		out[i+1] = 0xC4;
+	}
+	out[len + 1] = 0xBF;
+	out[len + 2] = '\n';
+
+	out[len + 3] = 0xB3;
+	for (uint8_t i = 0; i < len; i++) {
+		out[len + 4 + i] = text[i];
+	}
+	out[len * 2 + 4] = 0xB3;
+	out[len * 2 + 5] = '\n';
+
+
+	out[len * 2 + 6] = 0xC0;
+	for (uint8_t i = 0; i < len; i++) {
+		out[len * 2 + 7 + i] = 0xC4;
+	}
+	out[len * 3 + 7] = 0xD9;
+	out[len * 3 + 8] = '\n';
+
+	print_string(out); 
+}
+
+void print_string(char c[]) {
+	for(uint8_t i = 0; i < strlen(c); i++)
+		print_char(c[i]);
+}
+
+bool process_printer(uint16_t keycode, keyrecord_t *record) {
+	if (keycode == PRINT_ON) {
+		enabled_printing();
+		return false;
+	}
+	if (keycode == PRINT_OFF) {
+		disable_printing();
+		return false;
+	}
+
+	if (printing_enabled) {
+		switch(keycode) {
+			case KC_EXLM ... KC_RPRN:
+			case KC_UNDS:
+			case KC_PLUS:
+			case KC_LCBR:
+			case KC_RCBR:
+			case KC_PIPE:
+			case KC_TILD:
+				keycode &= 0xFF;
+			case KC_LSFT:
+			case KC_RSFT:
+				if (record->event.pressed) {
+					character_shift++;
+				} else {
+					character_shift--;
+				}
+				return false;
+			break;
+		}
+
+		switch(keycode) {
+			case KC_F1:
+				if (record->event.pressed) {
+					print_box_string("This is a line of text!");
+				}
+				return false;
+			case KC_ESC:
+				if (record->event.pressed) {
+					print_char(0x1B);
+				}
+				return false;
+			break;
+			case KC_SPC:
+				if (record->event.pressed) {
+					print_char(0x20);
+				}
+				return false;
+			break;
+			case KC_A ... KC_Z:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x41 + (keycode - KC_A));
+					} else {
+						print_char(0x61 + (keycode - KC_A));
+					}
+				}
+				return false;
+			break;
+			case KC_1 ... KC_0:
+				if (record->event.pressed) {
+					if (character_shift) {
+							print_char(shifted_numbers[keycode - KC_1]);
+					} else {
+							print_char(0x30 + ((keycode - KC_1 + 1) % 10));
+					}
+				}
+				return false;
+			break;
+			case KC_ENT:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x0C);
+					} else {
+						print_char(0x0A);
+					}
+				}
+				return false;
+			break;
+			case KC_BSPC:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x18);
+					} else {
+						print_char(0x1A);
+					}
+				}
+				return false;
+			break;
+			case KC_DOT:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x3E);
+					} else {
+						print_char(0x2E);
+					}
+				}
+				return false;
+			break;
+			case KC_COMM:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x3C);
+					} else {
+						print_char(0x2C);
+					}
+				}
+				return false;
+			break;
+			case KC_SLSH:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x3F);
+					} else {
+						print_char(0x2F);
+					}
+				}
+				return false;
+			break;
+			case KC_QUOT:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x22);
+					} else {
+						print_char(0x27);
+					}
+				}
+				return false;
+			break;
+			case KC_GRV:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x7E);
+					} else {
+						print_char(0x60);
+					}
+				}
+				return false;
+			break;
+			case KC_MINS:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x5F);
+					} else {
+						print_char(0x2D);
+					}
+				}
+				return false;
+			break;
+			case KC_EQL:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x2B);
+					} else {
+						print_char(0x3D);
+					}
+				}
+				return false;
+			break;
+			case KC_LBRC:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x7B);
+					} else {
+						print_char(0x5B);
+					}
+				}
+				return false;
+			break;
+			case KC_RBRC:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x7D);
+					} else {
+						print_char(0x5D);
+					}
+				}
+				return false;
+			break;
+			case KC_BSLS:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x7C);
+					} else {
+						print_char(0x5C);
+					}
+				}
+				return false;
+			break;
+		}
+	}
+	return true;
+
+}
\ No newline at end of file
diff --git a/quantum/process_keycode/process_printer.h b/quantum/process_keycode/process_printer.h
new file mode 100644
index 0000000000..fdd36d75a8
--- /dev/null
+++ b/quantum/process_keycode/process_printer.h
@@ -0,0 +1,8 @@
+#ifndef PROCESS_PRINTER_H
+#define PROCESS_PRINTER_H
+
+#include "quantum.h"
+
+#include "protocol/serial.h"
+
+#endif
\ No newline at end of file
diff --git a/quantum/process_keycode/process_printer_bb.c b/quantum/process_keycode/process_printer_bb.c
new file mode 100644
index 0000000000..1924d03774
--- /dev/null
+++ b/quantum/process_keycode/process_printer_bb.c
@@ -0,0 +1,260 @@
+#include "process_printer.h"
+#include "action_util.h"
+
+bool printing_enabled = false;
+uint8_t character_shift = 0;
+
+#define SERIAL_PIN_DDR DDRD
+#define SERIAL_PIN_PORT PORTD
+#define SERIAL_PIN_MASK _BV(PD3)
+#define SERIAL_DELAY 52
+
+inline static
+void serial_delay(void) {
+  _delay_us(SERIAL_DELAY);
+}
+
+inline static
+void serial_high(void) {
+  SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
+}
+
+inline static
+void serial_low(void) {
+  SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK;
+}
+
+inline static
+void serial_output(void) {
+  SERIAL_PIN_DDR |= SERIAL_PIN_MASK;
+}
+
+
+void enabled_printing() {
+	printing_enabled = true;
+	serial_output();
+	serial_high();
+}
+
+void disable_printing() {
+	printing_enabled = false;
+}
+
+uint8_t shifted_numbers[10] = {0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29};
+
+// uint8_t keycode_to_ascii[0xFF][2];
+
+// keycode_to_ascii[KC_MINS] = {0x2D, 0x5F};
+
+void print_char(char c) {
+  uint8_t b = 8;
+  serial_output();
+  while( b-- ) {
+    if(c & (1 << b)) {
+      serial_high();
+    } else {
+      serial_low();
+    }
+    serial_delay();
+  }
+}
+
+void print_string(char c[]) {
+	for(uint8_t i = 0; i < strlen(c); i++)
+		print_char(c[i]);
+}
+
+bool process_printer(uint16_t keycode, keyrecord_t *record) {
+	if (keycode == PRINT_ON) {
+		enabled_printing();
+		return false;
+	}
+	if (keycode == PRINT_OFF) {
+		disable_printing();
+		return false;
+	}
+
+	if (printing_enabled) {
+		switch(keycode) {
+			case KC_EXLM ... KC_RPRN:
+			case KC_UNDS:
+			case KC_PLUS:
+			case KC_LCBR:
+			case KC_RCBR:
+			case KC_PIPE:
+			case KC_TILD:
+				keycode &= 0xFF;
+			case KC_LSFT:
+			case KC_RSFT:
+				if (record->event.pressed) {
+					character_shift++;
+				} else {
+					character_shift--;
+				}
+				return false;
+			break;
+		}
+
+		switch(keycode) {
+			case KC_F1:
+				if (record->event.pressed) {
+					print_string("This is a line of text!\n\n\n");
+				}
+				return false;
+			case KC_ESC:
+				if (record->event.pressed) {
+					print_char(0x1B);
+				}
+				return false;
+			break;
+			case KC_SPC:
+				if (record->event.pressed) {
+					print_char(0x20);
+				}
+				return false;
+			break;
+			case KC_A ... KC_Z:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x41 + (keycode - KC_A));
+					} else {
+						print_char(0x61 + (keycode - KC_A));
+					}
+				}
+				return false;
+			break;
+			case KC_1 ... KC_0:
+				if (record->event.pressed) {
+					if (character_shift) {
+							print_char(shifted_numbers[keycode - KC_1]);
+					} else {
+							print_char(0x30 + ((keycode - KC_1 + 1) % 10));
+					}
+				}
+				return false;
+			break;
+			case KC_ENT:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x0C);
+					} else {
+						print_char(0x0A);
+					}
+				}
+				return false;
+			break;
+			case KC_BSPC:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x18);
+					} else {
+						print_char(0x1A);
+					}
+				}
+				return false;
+			break;
+			case KC_DOT:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x3E);
+					} else {
+						print_char(0x2E);
+					}
+				}
+				return false;
+			break;
+			case KC_COMM:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x3C);
+					} else {
+						print_char(0x2C);
+					}
+				}
+				return false;
+			break;
+			case KC_SLSH:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x3F);
+					} else {
+						print_char(0x2F);
+					}
+				}
+				return false;
+			break;
+			case KC_QUOT:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x22);
+					} else {
+						print_char(0x27);
+					}
+				}
+				return false;
+			break;
+			case KC_GRV:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x7E);
+					} else {
+						print_char(0x60);
+					}
+				}
+				return false;
+			break;
+			case KC_MINS:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x5F);
+					} else {
+						print_char(0x2D);
+					}
+				}
+				return false;
+			break;
+			case KC_EQL:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x2B);
+					} else {
+						print_char(0x3D);
+					}
+				}
+				return false;
+			break;
+			case KC_LBRC:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x7B);
+					} else {
+						print_char(0x5B);
+					}
+				}
+				return false;
+			break;
+			case KC_RBRC:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x7D);
+					} else {
+						print_char(0x5D);
+					}
+				}
+				return false;
+			break;
+			case KC_BSLS:
+				if (record->event.pressed) {
+					if (character_shift) {
+						print_char(0x7C);
+					} else {
+						print_char(0x5C);
+					}
+				}
+				return false;
+			break;
+		}
+	}
+	return true;
+
+}
\ No newline at end of file
diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c
index 9b172e1b6c..403dca5380 100644
--- a/quantum/process_keycode/process_tap_dance.c
+++ b/quantum/process_keycode/process_tap_dance.c
@@ -1,90 +1,141 @@
 #include "quantum.h"
+#include "action_tapping.h"
 
-static qk_tap_dance_state_t qk_tap_dance_state;
+static uint16_t last_td;
+static int8_t highest_td = -1;
 
-static void _process_tap_dance_action_pair (qk_tap_dance_state_t *state,
-                                            uint16_t kc1, uint16_t kc2) {
-  uint16_t kc;
+void qk_tap_dance_pair_finished (qk_tap_dance_state_t *state, void *user_data) {
+  qk_tap_dance_pair_t *pair = (qk_tap_dance_pair_t *)user_data;
 
-  if (state->count == 0)
-    return;
-
-  kc = (state->count == 1) ? kc1 : kc2;
+  if (state->count == 1) {
+    register_code16 (pair->kc1);
+  } else if (state->count == 2) {
+    register_code16 (pair->kc2);
+  }
+}
 
-  register_code (kc);
-  unregister_code (kc);
+void qk_tap_dance_pair_reset (qk_tap_dance_state_t *state, void *user_data) {
+  qk_tap_dance_pair_t *pair = (qk_tap_dance_pair_t *)user_data;
 
-  if (state->count >= 2) {
-    reset_tap_dance (state);
+  if (state->count == 1) {
+    unregister_code16 (pair->kc1);
+  } else if (state->count == 2) {
+    unregister_code16 (pair->kc2);
   }
 }
 
-static void _process_tap_dance_action_fn (qk_tap_dance_state_t *state,
-                                          qk_tap_dance_user_fn_t fn)
+static inline void _process_tap_dance_action_fn (qk_tap_dance_state_t *state,
+                                                 void *user_data,
+                                                 qk_tap_dance_user_fn_t fn)
 {
-  fn(state);
+  if (fn) {
+    fn(state, user_data);
+  }
 }
 
-void process_tap_dance_action (uint16_t keycode)
+static inline void process_tap_dance_action_on_each_tap (qk_tap_dance_action_t *action)
 {
-  uint16_t idx = keycode - QK_TAP_DANCE;
-  qk_tap_dance_action_t action;
-
-  action = tap_dance_actions[idx];
+  _process_tap_dance_action_fn (&action->state, action->user_data, action->fn.on_each_tap);
+}
 
-  switch (action.type) {
-  case QK_TAP_DANCE_TYPE_PAIR:
-    _process_tap_dance_action_pair (&qk_tap_dance_state,
-                                    action.pair.kc1, action.pair.kc2);
-    break;
-  case QK_TAP_DANCE_TYPE_FN:
-    _process_tap_dance_action_fn (&qk_tap_dance_state, action.fn);
-    break;
+static inline void process_tap_dance_action_on_dance_finished (qk_tap_dance_action_t *action)
+{
+  if (action->state.finished)
+    return;
+  action->state.finished = true;
+  add_mods(action->state.oneshot_mods);
+  send_keyboard_report();
+  _process_tap_dance_action_fn (&action->state, action->user_data, action->fn.on_dance_finished);
+}
 
-  default:
-    break;
-  }
+static inline void process_tap_dance_action_on_reset (qk_tap_dance_action_t *action)
+{
+  _process_tap_dance_action_fn (&action->state, action->user_data, action->fn.on_reset);
+  del_mods(action->state.oneshot_mods);
+  send_keyboard_report();
 }
 
 bool process_tap_dance(uint16_t keycode, keyrecord_t *record) {
-  bool r = true;
+  uint16_t idx = keycode - QK_TAP_DANCE;
+  qk_tap_dance_action_t *action;
+
+  if (last_td && last_td != keycode) {
+    (&tap_dance_actions[last_td - QK_TAP_DANCE])->state.interrupted = true;
+  }
 
   switch(keycode) {
   case QK_TAP_DANCE ... QK_TAP_DANCE_MAX:
-    if (qk_tap_dance_state.keycode && qk_tap_dance_state.keycode != keycode) {
-      process_tap_dance_action (qk_tap_dance_state.keycode);
-    } else {
-      r = false;
-    }
+    if ((int16_t)idx > highest_td)
+      highest_td = idx;
+    action = &tap_dance_actions[idx];
 
+    action->state.pressed = record->event.pressed;
     if (record->event.pressed) {
-      qk_tap_dance_state.keycode = keycode;
-      qk_tap_dance_state.timer = timer_read ();
-      qk_tap_dance_state.count++;
+      action->state.keycode = keycode;
+      action->state.count++;
+      action->state.timer = timer_read();
+      action->state.oneshot_mods = get_oneshot_mods();
+      process_tap_dance_action_on_each_tap (action);
+
+      if (last_td && last_td != keycode) {
+        qk_tap_dance_action_t *paction = &tap_dance_actions[last_td - QK_TAP_DANCE];
+        paction->state.interrupted = true;
+        process_tap_dance_action_on_dance_finished (paction);
+        reset_tap_dance (&paction->state);
+      }
+
+      last_td = keycode;
     }
+
     break;
 
   default:
-    if (qk_tap_dance_state.keycode) {
-      process_tap_dance_action (qk_tap_dance_state.keycode);
-
-      reset_tap_dance (&qk_tap_dance_state);
+    if (!record->event.pressed)
+      return true;
+
+    if (highest_td == -1)
+      return true;
+
+    for (int i = 0; i <= highest_td; i++) {
+      action = &tap_dance_actions[i];
+      if (action->state.count == 0)
+        continue;
+      action->state.interrupted = true;
+      process_tap_dance_action_on_dance_finished (action);
+      reset_tap_dance (&action->state);
     }
     break;
   }
 
-  return r;
+  return true;
 }
 
 void matrix_scan_tap_dance () {
-  if (qk_tap_dance_state.keycode && timer_elapsed (qk_tap_dance_state.timer) > TAPPING_TERM) {
-    process_tap_dance_action (qk_tap_dance_state.keycode);
+  if (highest_td == -1)
+    return;
 
-    reset_tap_dance (&qk_tap_dance_state);
+for (int i = 0; i <= highest_td; i++) {
+    qk_tap_dance_action_t *action = &tap_dance_actions[i];
+
+    if (action->state.count && timer_elapsed (action->state.timer) > TAPPING_TERM) {
+      process_tap_dance_action_on_dance_finished (action);
+      reset_tap_dance (&action->state);
+    }
   }
 }
 
 void reset_tap_dance (qk_tap_dance_state_t *state) {
-  state->keycode = 0;
+  qk_tap_dance_action_t *action;
+
+  if (state->pressed)
+    return;
+
+  action = &tap_dance_actions[state->keycode - QK_TAP_DANCE];
+
+  process_tap_dance_action_on_reset (action);
+
   state->count = 0;
+  state->interrupted = false;
+  state->finished = false;
+  last_td = 0;
 }
diff --git a/quantum/process_keycode/process_tap_dance.h b/quantum/process_keycode/process_tap_dance.h
index b9d7c7fcf4..726752ecc7 100644
--- a/quantum/process_keycode/process_tap_dance.h
+++ b/quantum/process_keycode/process_tap_dance.h
@@ -9,43 +9,51 @@
 typedef struct
 {
   uint8_t count;
+  uint8_t oneshot_mods;
   uint16_t keycode;
   uint16_t timer;
+  bool interrupted;
+  bool pressed;
+  bool finished;
 } qk_tap_dance_state_t;
 
 #define TD(n) (QK_TAP_DANCE + n)
 
-typedef enum
-{
-  QK_TAP_DANCE_TYPE_PAIR,
-  QK_TAP_DANCE_TYPE_FN,
-} qk_tap_dance_type_t;
-
-typedef void (*qk_tap_dance_user_fn_t) (qk_tap_dance_state_t *state);
+typedef void (*qk_tap_dance_user_fn_t) (qk_tap_dance_state_t *state, void *user_data);
 
 typedef struct
 {
-  qk_tap_dance_type_t type;
-  union {
-    struct {
-      uint16_t kc1;
-      uint16_t kc2;
-    } pair;
-    qk_tap_dance_user_fn_t fn;
-  };
+  struct {
+    qk_tap_dance_user_fn_t on_each_tap;
+    qk_tap_dance_user_fn_t on_dance_finished;
+    qk_tap_dance_user_fn_t on_reset;
+  } fn;
+  qk_tap_dance_state_t state;
+  void *user_data;
 } qk_tap_dance_action_t;
 
+typedef struct
+{
+  uint16_t kc1;
+  uint16_t kc2;
+} qk_tap_dance_pair_t;
+
 #define ACTION_TAP_DANCE_DOUBLE(kc1, kc2) { \
-    .type = QK_TAP_DANCE_TYPE_PAIR,         \
-    .pair = { kc1, kc2 }                    \
+    .fn = { NULL, qk_tap_dance_pair_finished, qk_tap_dance_pair_reset }, \
+    .user_data = (void *)&((qk_tap_dance_pair_t) { kc1, kc2 }),  \
+  }
+
+#define ACTION_TAP_DANCE_FN(user_fn) {  \
+    .fn = { NULL, user_fn, NULL }, \
+    .user_data = NULL, \
   }
 
-#define ACTION_TAP_DANCE_FN(user_fn) { \
-    .type = QK_TAP_DANCE_TYPE_FN, \
-    .fn = user_fn                 \
+#define ACTION_TAP_DANCE_FN_ADVANCED(user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset) { \
+    .fn = { user_fn_on_each_tap, user_fn_on_dance_finished, user_fn_on_dance_reset }, \
+    .user_data = NULL, \
   }
 
-extern const qk_tap_dance_action_t tap_dance_actions[];
+extern qk_tap_dance_action_t tap_dance_actions[];
 
 /* To be used internally */
 
@@ -53,6 +61,9 @@ bool process_tap_dance(uint16_t keycode, keyrecord_t *record);
 void matrix_scan_tap_dance (void);
 void reset_tap_dance (qk_tap_dance_state_t *state);
 
+void qk_tap_dance_pair_finished (qk_tap_dance_state_t *state, void *user_data);
+void qk_tap_dance_pair_reset (qk_tap_dance_state_t *state, void *user_data);
+
 #else
 
 #define TD(n) KC_NO
diff --git a/quantum/process_keycode/process_ucis.c b/quantum/process_keycode/process_ucis.c
new file mode 100644
index 0000000000..4ad2533b08
--- /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 0000000000..4332f57b35
--- /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 ad5d7f86b7..ccae6fdcad 100644
--- a/quantum/process_keycode/process_unicode.c
+++ b/quantum/process_keycode/process_unicode.c
@@ -1,57 +1,13 @@
 #include "process_unicode.h"
-
-static uint8_t input_mode;
-
-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_mode(uint8_t os_target)
-{
-  input_mode = os_target;
-}
+#include "action_util.h"
 
 bool process_unicode(uint16_t keycode, keyrecord_t *record) {
   if (keycode > QK_UNICODE && record->event.pressed) {
     uint16_t unicode = keycode & 0x7FFF;
-    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);
-        break;
-      case UC_WIN:
-        register_code(KC_LALT);
-        register_code(KC_PPLS);
-        unregister_code(KC_PPLS);
-        break;
-    }
-    for(int i = 3; i >= 0; i--) {
-        uint8_t digit = ((unicode >> (i*4)) & 0xF);
-        register_code(hex_to_keycode(digit));
-        unregister_code(hex_to_keycode(digit));
-    }
-    switch(input_mode) {
-      case UC_OSX:
-      case UC_WIN:
-        unregister_code(KC_LALT);
-        break;
-      case UC_LNX:
-        unregister_code(KC_LCTL);
-        unregister_code(KC_LSFT);
-        break;
-    }
+    unicode_input_start();
+    register_hex(unicode);
+    unicode_input_finish();
   }
   return true;
-}
\ No newline at end of file
+}
+
diff --git a/quantum/process_keycode/process_unicode.h b/quantum/process_keycode/process_unicode.h
index ca17f8f669..4c21f11eb9 100644
--- a/quantum/process_keycode/process_unicode.h
+++ b/quantum/process_keycode/process_unicode.h
@@ -2,121 +2,8 @@
 #define PROCESS_UNICODE_H
 
 #include "quantum.h"
-
-#define UC_OSX 0
-#define UC_LNX 1
-#define UC_WIN 2
-#define UC_BSD 3
-
-void set_unicode_input_mode(uint8_t os_target);
+#include "process_unicode_common.h"
 
 bool process_unicode(uint16_t keycode, keyrecord_t *record);
 
-#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
+#endif
diff --git a/quantum/process_keycode/process_unicode_common.c b/quantum/process_keycode/process_unicode_common.c
new file mode 100644
index 0000000000..31bc3b7ab3
--- /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 0000000000..864693cdd0
--- /dev/null
+++ b/quantum/process_keycode/process_unicode_common.h
@@ -0,0 +1,132 @@
+#ifndef PROCESS_UNICODE_COMMON_H
+#define PROCESS_UNICODE_COMMON_H
+
+#include "quantum.h"
+
+#ifndef UNICODE_TYPE_DELAY
+#define UNICODE_TYPE_DELAY 10
+#endif
+
+__attribute__ ((unused))
+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 0000000000..37f10df866
--- /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 0000000000..64a7a01090
--- /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