summary refs log tree commit diff
path: root/quantum/send_string
diff options
context:
space:
mode:
authorRyan <fauxpark@gmail.com>2022-07-02 22:10:08 +1000
committerGitHub <noreply@github.com>2022-07-02 22:10:08 +1000
commit3ecb0a80af9e4ce4194a34032642933641730706 (patch)
tree3d4b0e24336762dc9b45a746334ebc09a5c59db1 /quantum/send_string
parent7e41eb02773207dd4a014575fe750f8d511bfb5b (diff)
Feature-ify Send String (#17275)
Diffstat (limited to 'quantum/send_string')
-rw-r--r--quantum/send_string/send_string.c324
-rw-r--r--quantum/send_string/send_string.h152
-rw-r--r--quantum/send_string/send_string_keycodes.h434
3 files changed, 910 insertions, 0 deletions
diff --git a/quantum/send_string/send_string.c b/quantum/send_string/send_string.c
new file mode 100644
index 0000000000..818a52f6dc
--- /dev/null
+++ b/quantum/send_string/send_string.c
@@ -0,0 +1,324 @@
+/* Copyright 2021
+ *
+ * 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 <ctype.h>
+
+#include "quantum.h"
+
+#include "send_string.h"
+
+#if defined(AUDIO_ENABLE) && defined(SENDSTRING_BELL)
+#    include "audio.h"
+#    ifndef BELL_SOUND
+#        define BELL_SOUND TERMINAL_SOUND
+#    endif
+float bell_song[][2] = SONG(BELL_SOUND);
+#endif
+
+// clang-format off
+
+/* Bit-Packed look-up table to convert an ASCII character to whether
+ * [Shift] needs to be sent with the keycode.
+ */
+__attribute__((weak)) const uint8_t ascii_to_shift_lut[16] PROGMEM = {
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+
+    KCLUT_ENTRY(0, 1, 1, 1, 1, 1, 1, 0),
+    KCLUT_ENTRY(1, 1, 1, 1, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 1, 0, 1, 0, 1, 1),
+    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),
+    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),
+    KCLUT_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),
+    KCLUT_ENTRY(1, 1, 1, 0, 0, 0, 1, 1),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 1, 1, 1, 1, 0)
+};
+
+/* Bit-Packed look-up table to convert an ASCII character to whether
+ * [AltGr] needs to be sent with the keycode.
+ */
+__attribute__((weak)) const uint8_t ascii_to_altgr_lut[16] PROGMEM = {
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)
+};
+
+/* Bit-Packed look-up table to convert an ASCII character to whether
+ * [Space] needs to be sent after the keycode
+ */
+__attribute__((weak)) const uint8_t ascii_to_dead_lut[16] PROGMEM = {
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
+    KCLUT_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)
+};
+
+/* Look-up table to convert an ASCII character to a keycode.
+ */
+__attribute__((weak)) const uint8_t ascii_to_keycode_lut[128] PROGMEM = {
+    // NUL   SOH      STX      ETX      EOT      ENQ      ACK      BEL
+    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
+    // BS    TAB      LF       VT       FF       CR       SO       SI
+    KC_BSPC, KC_TAB,  KC_ENT,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
+    // DLE   DC1      DC2      DC3      DC4      NAK      SYN      ETB
+    XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
+    // CAN   EM       SUB      ESC      FS       GS       RS       US
+    XXXXXXX, XXXXXXX, XXXXXXX, KC_ESC,  XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX,
+
+    //       !        "        #        $        %        &        '
+    KC_SPC,  KC_1,    KC_QUOT, KC_3,    KC_4,    KC_5,    KC_7,    KC_QUOT,
+    // (     )        *        +        ,        -        .        /
+    KC_9,    KC_0,    KC_8,    KC_EQL,  KC_COMM, KC_MINS, KC_DOT,  KC_SLSH,
+    // 0     1        2        3        4        5        6        7
+    KC_0,    KC_1,    KC_2,    KC_3,    KC_4,    KC_5,    KC_6,    KC_7,
+    // 8     9        :        ;        <        =        >        ?
+    KC_8,    KC_9,    KC_SCLN, KC_SCLN, KC_COMM, KC_EQL,  KC_DOT,  KC_SLSH,
+    // @     A        B        C        D        E        F        G
+    KC_2,    KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,
+    // H     I        J        K        L        M        N        O
+    KC_H,    KC_I,    KC_J,    KC_K,    KC_L,    KC_M,    KC_N,    KC_O,
+    // P     Q        R        S        T        U        V        W
+    KC_P,    KC_Q,    KC_R,    KC_S,    KC_T,    KC_U,    KC_V,    KC_W,
+    // X     Y        Z        [        \        ]        ^        _
+    KC_X,    KC_Y,    KC_Z,    KC_LBRC, KC_BSLS, KC_RBRC, KC_6,    KC_MINS,
+    // `     a        b        c        d        e        f        g
+    KC_GRV,  KC_A,    KC_B,    KC_C,    KC_D,    KC_E,    KC_F,    KC_G,
+    // h     i        j        k        l        m        n        o
+    KC_H,    KC_I,    KC_J,    KC_K,    KC_L,    KC_M,    KC_N,    KC_O,
+    // p     q        r        s        t        u        v        w
+    KC_P,    KC_Q,    KC_R,    KC_S,    KC_T,    KC_U,    KC_V,    KC_W,
+    // x     y        z        {        |        }        ~        DEL
+    KC_X,    KC_Y,    KC_Z,    KC_LBRC, KC_BSLS, KC_RBRC, KC_GRV,  KC_DEL
+};
+
+// clang-format on
+
+// Note: we bit-pack in "reverse" order to optimize loading
+#define PGM_LOADBIT(mem, pos) ((pgm_read_byte(&((mem)[(pos) / 8])) >> ((pos) % 8)) & 0x01)
+
+void send_string(const char *string) {
+    send_string_with_delay(string, 0);
+}
+
+void send_string_with_delay(const char *string, uint8_t interval) {
+    while (1) {
+        char ascii_code = *string;
+        if (!ascii_code) break;
+        if (ascii_code == SS_QMK_PREFIX) {
+            ascii_code = *(++string);
+            if (ascii_code == SS_TAP_CODE) {
+                // tap
+                uint8_t keycode = *(++string);
+                tap_code(keycode);
+            } else if (ascii_code == SS_DOWN_CODE) {
+                // down
+                uint8_t keycode = *(++string);
+                register_code(keycode);
+            } else if (ascii_code == SS_UP_CODE) {
+                // up
+                uint8_t keycode = *(++string);
+                unregister_code(keycode);
+            } else if (ascii_code == SS_DELAY_CODE) {
+                // delay
+                int     ms      = 0;
+                uint8_t keycode = *(++string);
+                while (isdigit(keycode)) {
+                    ms *= 10;
+                    ms += keycode - '0';
+                    keycode = *(++string);
+                }
+                while (ms--)
+                    wait_ms(1);
+            }
+        } else {
+            send_char(ascii_code);
+        }
+        ++string;
+        // interval
+        {
+            uint8_t ms = interval;
+            while (ms--)
+                wait_ms(1);
+        }
+    }
+}
+
+void send_char(char ascii_code) {
+#if defined(AUDIO_ENABLE) && defined(SENDSTRING_BELL)
+    if (ascii_code == '\a') { // BEL
+        PLAY_SONG(bell_song);
+        return;
+    }
+#endif
+
+    uint8_t keycode    = pgm_read_byte(&ascii_to_keycode_lut[(uint8_t)ascii_code]);
+    bool    is_shifted = PGM_LOADBIT(ascii_to_shift_lut, (uint8_t)ascii_code);
+    bool    is_altgred = PGM_LOADBIT(ascii_to_altgr_lut, (uint8_t)ascii_code);
+    bool    is_dead    = PGM_LOADBIT(ascii_to_dead_lut, (uint8_t)ascii_code);
+
+    if (is_shifted) {
+        register_code(KC_LEFT_SHIFT);
+    }
+    if (is_altgred) {
+        register_code(KC_RIGHT_ALT);
+    }
+    tap_code(keycode);
+    if (is_altgred) {
+        unregister_code(KC_RIGHT_ALT);
+    }
+    if (is_shifted) {
+        unregister_code(KC_LEFT_SHIFT);
+    }
+    if (is_dead) {
+        tap_code(KC_SPACE);
+    }
+}
+
+void send_dword(uint32_t number) {
+    send_word(number >> 16);
+    send_word(number & 0xFFFFUL);
+}
+
+void send_word(uint16_t number) {
+    send_byte(number >> 8);
+    send_byte(number & 0xFF);
+}
+
+void send_byte(uint8_t number) {
+    send_nibble(number >> 4);
+    send_nibble(number & 0xF);
+}
+
+void send_nibble(uint8_t number) {
+    switch (number & 0xF) {
+        case 0 ... 9:
+            send_char(number + '0');
+            break;
+        case 10 ... 15:
+            send_char(number - 10 + 'a');
+            break;
+    }
+}
+
+void tap_random_base64(void) {
+#if defined(__AVR_ATmega32U4__)
+    uint8_t key = (TCNT0 + TCNT1 + TCNT3 + TCNT4) % 64;
+#else
+    uint8_t key = rand() % 64;
+#endif
+    switch (key) {
+        case 0 ... 25:
+            send_char(key + 'A');
+            break;
+        case 26 ... 51:
+            send_char(key - 26 + 'a');
+            break;
+        case 52:
+            send_char('0');
+            break;
+        case 53 ... 61:
+            send_char(key - 53 + '1');
+            break;
+        case 62:
+            send_char('+');
+            break;
+        case 63:
+            send_char('/');
+            break;
+    }
+}
+
+#if defined(__AVR__)
+void send_string_P(const char *string) {
+    send_string_with_delay_P(string, 0);
+}
+
+void send_string_with_delay_P(const char *string, uint8_t interval) {
+    while (1) {
+        char ascii_code = pgm_read_byte(string);
+        if (!ascii_code) break;
+        if (ascii_code == SS_QMK_PREFIX) {
+            ascii_code = pgm_read_byte(++string);
+            if (ascii_code == SS_TAP_CODE) {
+                // tap
+                uint8_t keycode = pgm_read_byte(++string);
+                tap_code(keycode);
+            } else if (ascii_code == SS_DOWN_CODE) {
+                // down
+                uint8_t keycode = pgm_read_byte(++string);
+                register_code(keycode);
+            } else if (ascii_code == SS_UP_CODE) {
+                // up
+                uint8_t keycode = pgm_read_byte(++string);
+                unregister_code(keycode);
+            } else if (ascii_code == SS_DELAY_CODE) {
+                // delay
+                int     ms      = 0;
+                uint8_t keycode = pgm_read_byte(++string);
+                while (isdigit(keycode)) {
+                    ms *= 10;
+                    ms += keycode - '0';
+                    keycode = pgm_read_byte(++string);
+                }
+                while (ms--)
+                    wait_ms(1);
+            }
+        } else {
+            send_char(ascii_code);
+        }
+        ++string;
+        // interval
+        {
+            uint8_t ms = interval;
+            while (ms--)
+                wait_ms(1);
+        }
+    }
+}
+#endif
diff --git a/quantum/send_string/send_string.h b/quantum/send_string/send_string.h
new file mode 100644
index 0000000000..4eb55b88dc
--- /dev/null
+++ b/quantum/send_string/send_string.h
@@ -0,0 +1,152 @@
+/* Copyright 2021
+ *
+ * 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/>.
+ */
+
+/**
+ * \defgroup send_string
+ *
+ * Send String API. These functions allow you to create macros by typing out sequences of keystrokes.
+ * \{
+ */
+
+#include <stdint.h>
+
+#include "progmem.h"
+#include "send_string_keycodes.h"
+
+// Look-Up Tables (LUTs) to convert ASCII character to keycode sequence.
+extern const uint8_t ascii_to_shift_lut[16];
+extern const uint8_t ascii_to_altgr_lut[16];
+extern const uint8_t ascii_to_dead_lut[16];
+extern const uint8_t ascii_to_keycode_lut[128];
+
+// clang-format off
+#define KCLUT_ENTRY(a, b, c, d, e, f, g, h) \
+    ( ((a) ? 1 : 0) << 0 \
+    | ((b) ? 1 : 0) << 1 \
+    | ((c) ? 1 : 0) << 2 \
+    | ((d) ? 1 : 0) << 3 \
+    | ((e) ? 1 : 0) << 4 \
+    | ((f) ? 1 : 0) << 5 \
+    | ((g) ? 1 : 0) << 6 \
+    | ((h) ? 1 : 0) << 7 )
+// clang-format on
+
+/**
+ * \brief Type out a string of ASCII characters.
+ *
+ * This function simply calls `send_string_with_delay(string, 0)`.
+ *
+ * Most keycodes from the basic keycode range are also supported by way of a special sequence - see `send_string_keycodes.h`.
+ *
+ * \param string The string to type out.
+ */
+void send_string(const char *string);
+
+/**
+ * \brief Type out a string of ASCII characters, with a delay between each character.
+ *
+ * \param string The string to type out.
+ * \param interval The amount of time, in milliseconds, to wait before typing the next character.
+ */
+void send_string_with_delay(const char *string, uint8_t interval);
+
+/**
+ * \brief Type out an ASCII character.
+ *
+ * \param ascii_code The character to type.
+ */
+void send_char(char ascii_code);
+
+/**
+ * \brief Type out an eight digit (unsigned 32-bit) hexadecimal value.
+ *
+ * The format is `[0-9a-f]{8}`, eg. `00000000` through `ffffffff`.
+ *
+ * \param number The value to type, from 0 to 4,294,967,295.
+ */
+void send_dword(uint32_t number);
+
+/**
+ * \brief Type out a four digit (unsigned 16-bit) hexadecimal value.
+ *
+ * The format is `[0-9a-f]{4}`, eg. `0000` through `ffff`.
+ *
+ * \param number The value to type, from 0 to 65,535.
+ */
+void send_word(uint16_t number);
+
+/**
+ * \brief Type out a two digit (8-bit) hexadecimal value.
+ *
+ * The format is `[0-9a-f]{2}`, eg. `00` through `ff`.
+ *
+ * \param number The value to type, from 0 to 255.
+ */
+void send_byte(uint8_t number);
+
+/**
+ * \brief Type out a single hexadecimal digit.
+ *
+ * The format is `[0-9a-f]{1}`, eg. `0` through `f`.
+ *
+ * \param number The value to type, from 0 to 15.
+ */
+void send_nibble(uint8_t number);
+
+/**
+ * \brief Type a pseudorandom character from the set `A-Z`, `a-z`, `0-9`, `+` and `/`.
+ */
+void tap_random_base64(void);
+
+#if defined(__AVR__) || defined(__DOXYGEN__)
+/**
+ * \brief Type out a PROGMEM string of ASCII characters.
+ *
+ * On ARM devices, this function is simply an alias for send_string_with_delay(string, 0).
+ *
+ * \param string The string to type out.
+ */
+void send_string_P(const char *string);
+
+/**
+ * \brief Type out a PROGMEM string of ASCII characters, with a delay between each character.
+ *
+ * On ARM devices, this function is simply an alias for send_string_with_delay(string, interval).
+ *
+ * \param string The string to type out.
+ * \param interval The amount of time, in milliseconds, to wait before typing the next character.
+ */
+void send_string_with_delay_P(const char *string, uint8_t interval);
+#else
+#    define send_string_P(string) send_string_with_delay(string, 0)
+#    define send_string_with_delay_P(string, interval) send_string_with_delay(string, interval)
+#endif
+
+/**
+ * \brief Shortcut macro for send_string_with_delay_P(PSTR(string), 0).
+ *
+ * On ARM devices, this define evaluates to send_string_with_delay(string, 0).
+ */
+#define SEND_STRING(string) send_string_with_delay_P(PSTR(string), 0)
+
+/**
+ * \brief Shortcut macro for send_string_with_delay_P(PSTR(string), interval).
+ *
+ * On ARM devices, this define evaluates to send_string_with_delay(string, interval).
+ */
+#define SEND_STRING_DELAY(string, interval) send_string_with_delay_P(PSTR(string), interval)
+
+/** \} */
diff --git a/quantum/send_string/send_string_keycodes.h b/quantum/send_string/send_string_keycodes.h
new file mode 100644
index 0000000000..7017e03d5a
--- /dev/null
+++ b/quantum/send_string/send_string_keycodes.h
@@ -0,0 +1,434 @@
+/* Copyright 2019
+ *
+ * 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
+
+// clang-format off
+
+/* Punctuation */
+#define X_ENT  X_ENTER
+#define X_ESC  X_ESCAPE
+#define X_BSPC X_BSPACE
+#define X_SPC  X_SPACE
+#define X_MINS X_MINUS
+#define X_EQL  X_EQUAL
+#define X_LBRC X_LBRACKET
+#define X_RBRC X_RBRACKET
+#define X_BSLS X_BSLASH
+#define X_NUHS X_NONUS_HASH
+#define X_SCLN X_SCOLON
+#define X_QUOT X_QUOTE
+#define X_GRV  X_GRAVE
+#define X_COMM X_COMMA
+#define X_SLSH X_SLASH
+#define X_NUBS X_NONUS_BSLASH
+
+/* Lock Keys */
+#define X_CLCK X_CAPSLOCK
+#define X_CAPS X_CAPSLOCK
+#define X_SLCK X_SCROLLLOCK
+#define X_NLCK X_NUMLOCK
+#define X_LCAP X_LOCKING_CAPS
+#define X_LNUM X_LOCKING_NUM
+#define X_LSCR X_LOCKING_SCROLL
+
+/* Commands */
+#define X_PSCR X_PSCREEN
+#define X_PAUS X_PAUSE
+#define X_BRK  X_PAUSE
+#define X_INS  X_INSERT
+#define X_DEL  X_DELETE
+#define X_PGDN X_PGDOWN
+#define X_RGHT X_RIGHT
+#define X_APP  X_APPLICATION
+#define X_EXEC X_EXECUTE
+#define X_SLCT X_SELECT
+#define X_AGIN X_AGAIN
+#define X_PSTE X_PASTE
+#define X_ERAS X_ALT_ERASE
+#define X_CLR  X_CLEAR
+
+/* Keypad */
+#define X_PSLS X_KP_SLASH
+#define X_PAST X_KP_ASTERISK
+#define X_PMNS X_KP_MINUS
+#define X_PPLS X_KP_PLUS
+#define X_PENT X_KP_ENTER
+#define X_P1   X_KP_1
+#define X_P2   X_KP_2
+#define X_P3   X_KP_3
+#define X_P4   X_KP_4
+#define X_P5   X_KP_5
+#define X_P6   X_KP_6
+#define X_P7   X_KP_7
+#define X_P8   X_KP_8
+#define X_P9   X_KP_9
+#define X_P0   X_KP_0
+#define X_PDOT X_KP_DOT
+#define X_PEQL X_KP_EQUAL
+#define X_PCMM X_KP_COMMA
+
+/* Japanese specific */
+#define X_ZKHK X_GRAVE
+#define X_RO   X_INT1
+#define X_KANA X_INT2
+#define X_JYEN X_INT3
+#define X_HENK X_INT4
+#define X_MHEN X_INT5
+
+/* Korean specific */
+#define X_HAEN X_LANG1
+#define X_HANJ X_LANG2
+
+/* Modifiers */
+#define X_LCTL X_LCTRL
+#define X_LSFT X_LSHIFT
+#define X_LOPT X_LALT
+#define X_LCMD X_LGUI
+#define X_LWIN X_LGUI
+#define X_RCTL X_RCTRL
+#define X_RSFT X_RSHIFT
+#define X_ALGR X_RALT
+#define X_ROPT X_RALT
+#define X_RCMD X_RGUI
+#define X_RWIN X_RGUI
+
+/* Generic Desktop Page (0x01) */
+#define X_PWR  X_SYSTEM_POWER
+#define X_SLEP X_SYSTEM_SLEEP
+#define X_WAKE X_SYSTEM_WAKE
+
+/* Consumer Page (0x0C) */
+#define X_MUTE X_AUDIO_MUTE
+#define X_VOLU X_AUDIO_VOL_UP
+#define X_VOLD X_AUDIO_VOL_DOWN
+#define X_MNXT X_MEDIA_NEXT_TRACK
+#define X_MPRV X_MEDIA_PREV_TRACK
+#define X_MSTP X_MEDIA_STOP
+#define X_MPLY X_MEDIA_PLAY_PAUSE
+#define X_MSEL X_MEDIA_SELECT
+#define X_EJCT X_MEDIA_EJECT
+#define X_CALC X_CALCULATOR
+#define X_MYCM X_MY_COMPUTER
+#define X_WSCH X_WWW_SEARCH
+#define X_WHOM X_WWW_HOME
+#define X_WBAK X_WWW_BACK
+#define X_WFWD X_WWW_FORWARD
+#define X_WSTP X_WWW_STOP
+#define X_WREF X_WWW_REFRESH
+#define X_WFAV X_WWW_FAVORITES
+#define X_MFFD X_MEDIA_FAST_FORWARD
+#define X_MRWD X_MEDIA_REWIND
+#define X_BRIU X_BRIGHTNESS_UP
+#define X_BRID X_BRIGHTNESS_DOWN
+
+/* System Specific */
+#define X_BRMU X_PAUSE
+#define X_BRMD X_SCROLLLOCK
+
+/* Mouse Keys */
+#define X_MS_U X_MS_UP
+#define X_MS_D X_MS_DOWN
+#define X_MS_L X_MS_LEFT
+#define X_MS_R X_MS_RIGHT
+#define X_BTN1 X_MS_BTN1
+#define X_BTN2 X_MS_BTN2
+#define X_BTN3 X_MS_BTN3
+#define X_BTN4 X_MS_BTN4
+#define X_BTN5 X_MS_BTN5
+#define X_WH_U X_MS_WH_UP
+#define X_WH_D X_MS_WH_DOWN
+#define X_WH_L X_MS_WH_LEFT
+#define X_WH_R X_MS_WH_RIGHT
+#define X_ACL0 X_MS_ACCEL0
+#define X_ACL1 X_MS_ACCEL1
+#define X_ACL2 X_MS_ACCEL2
+
+/* Keyboard/Keypad Page (0x07) */
+#define X_A                  04
+#define X_B                  05
+#define X_C                  06
+#define X_D                  07
+#define X_E                  08
+#define X_F                  09
+#define X_G                  0a
+#define X_H                  0b
+#define X_I                  0c
+#define X_J                  0d
+#define X_K                  0e
+#define X_L                  0f
+#define X_M                  10
+#define X_N                  11
+#define X_O                  12
+#define X_P                  13
+#define X_Q                  14
+#define X_R                  15
+#define X_S                  16
+#define X_T                  17
+#define X_U                  18
+#define X_V                  19
+#define X_W                  1a
+#define X_X                  1b
+#define X_Y                  1c
+#define X_Z                  1d
+#define X_1                  1e
+#define X_2                  1f
+#define X_3                  20
+#define X_4                  21
+#define X_5                  22
+#define X_6                  23
+#define X_7                  24
+#define X_8                  25
+#define X_9                  26
+#define X_0                  27
+#define X_ENTER              28
+#define X_ESCAPE             29
+#define X_BSPACE             2a
+#define X_TAB                2b
+#define X_SPACE              2c
+#define X_MINUS              2d
+#define X_EQUAL              2e
+#define X_LBRACKET           2f
+#define X_RBRACKET           30
+#define X_BSLASH             31
+#define X_NONUS_HASH         32
+#define X_SCOLON             33
+#define X_QUOTE              34
+#define X_GRAVE              35
+#define X_COMMA              36
+#define X_DOT                37
+#define X_SLASH              38
+#define X_CAPSLOCK           39
+#define X_F1                 3a
+#define X_F2                 3b
+#define X_F3                 3c
+#define X_F4                 3d
+#define X_F5                 3e
+#define X_F6                 3f
+#define X_F7                 40
+#define X_F8                 41
+#define X_F9                 42
+#define X_F10                43
+#define X_F11                44
+#define X_F12                45
+#define X_PSCREEN            46
+#define X_SCROLLLOCK         47
+#define X_PAUSE              48
+#define X_INSERT             49
+#define X_HOME               4a
+#define X_PGUP               4b
+#define X_DELETE             4c
+#define X_END                4d
+#define X_PGDOWN             4e
+#define X_RIGHT              4f
+#define X_LEFT               50
+#define X_DOWN               51
+#define X_UP                 52
+#define X_NUMLOCK            53
+#define X_KP_SLASH           54
+#define X_KP_ASTERISK        55
+#define X_KP_MINUS           56
+#define X_KP_PLUS            57
+#define X_KP_ENTER           58
+#define X_KP_1               59
+#define X_KP_2               5a
+#define X_KP_3               5b
+#define X_KP_4               5c
+#define X_KP_5               5d
+#define X_KP_6               5e
+#define X_KP_7               5f
+#define X_KP_8               60
+#define X_KP_9               61
+#define X_KP_0               62
+#define X_KP_DOT             63
+#define X_NONUS_BSLASH       64
+#define X_APPLICATION        65
+#define X_POWER              66
+#define X_KP_EQUAL           67
+#define X_F13                68
+#define X_F14                69
+#define X_F15                6a
+#define X_F16                6b
+#define X_F17                6c
+#define X_F18                6d
+#define X_F19                6e
+#define X_F20                6f
+#define X_F21                70
+#define X_F22                71
+#define X_F23                72
+#define X_F24                73
+#define X_EXECUTE            74
+#define X_HELP               75
+#define X_MENU               76
+#define X_SELECT             77
+#define X_STOP               78
+#define X_AGAIN              79
+#define X_UNDO               7a
+#define X_CUT                7b
+#define X_COPY               7c
+#define X_PASTE              7d
+#define X_FIND               7e
+#define X__MUTE              7f
+#define X__VOLUP             80
+#define X__VOLDOWN           81
+#define X_LOCKING_CAPS       82
+#define X_LOCKING_NUM        83
+#define X_LOCKING_SCROLL     84
+#define X_KP_COMMA           85
+#define X_KP_EQUAL_AS400     86
+#define X_INT1               87
+#define X_INT2               88
+#define X_INT3               89
+#define X_INT4               8a
+#define X_INT5               8b
+#define X_INT6               8c
+#define X_INT7               8d
+#define X_INT8               8e
+#define X_INT9               8f
+#define X_LANG1              90
+#define X_LANG2              91
+#define X_LANG3              92
+#define X_LANG4              93
+#define X_LANG5              94
+#define X_LANG6              95
+#define X_LANG7              96
+#define X_LANG8              97
+#define X_LANG9              98
+#define X_ALT_ERASE          99
+#define X_SYSREQ             9a
+#define X_CANCEL             9b
+#define X_CLEAR              9c
+#define X_PRIOR              9d
+#define X_RETURN             9e
+#define X_SEPARATOR          9f
+#define X_OUT                a0
+#define X_OPER               a1
+#define X_CLEAR_AGAIN        a2
+#define X_CRSEL              a3
+#define X_EXSEL              a4
+
+/* Modifiers */
+#define X_LCTRL              e0
+#define X_LSHIFT             e1
+#define X_LALT               e2
+#define X_LGUI               e3
+#define X_RCTRL              e4
+#define X_RSHIFT             e5
+#define X_RALT               e6
+#define X_RGUI               e7
+
+/* Media and Function keys */
+/* Generic Desktop Page (0x01) */
+#define X_SYSTEM_POWER       a5
+#define X_SYSTEM_SLEEP       a6
+#define X_SYSTEM_WAKE        a7
+
+/* Consumer Page (0x0C) */
+#define X_AUDIO_MUTE         a8
+#define X_AUDIO_VOL_UP       a9
+#define X_AUDIO_VOL_DOWN     aa
+#define X_MEDIA_NEXT_TRACK   ab
+#define X_MEDIA_PREV_TRACK   ac
+#define X_MEDIA_STOP         ad
+#define X_MEDIA_PLAY_PAUSE   ae
+#define X_MEDIA_SELECT       af
+#define X_MEDIA_EJECT        b0
+#define X_MAIL               b1
+#define X_CALCULATOR         b2
+#define X_MY_COMPUTER        b3
+#define X_WWW_SEARCH         b4
+#define X_WWW_HOME           b5
+#define X_WWW_BACK           b6
+#define X_WWW_FORWARD        b7
+#define X_WWW_STOP           b8
+#define X_WWW_REFRESH        b9
+#define X_WWW_FAVORITES      ba
+#define X_MEDIA_FAST_FORWARD bb
+#define X_MEDIA_REWIND       bc
+#define X_BRIGHTNESS_UP      bd
+#define X_BRIGHTNESS_DOWN    be
+
+/* Mouse Buttons (unallocated range in HID spec) */
+#ifdef VIA_ENABLE
+#define X_MS_UP              f0
+#define X_MS_DOWN            f1
+#define X_MS_LEFT            f2
+#define X_MS_RIGHT           f3
+#define X_MS_BTN1            f4
+#define X_MS_BTN2            f5
+#define X_MS_BTN3            f6
+#define X_MS_BTN4            f7
+#define X_MS_BTN5            f8
+#define X_MS_BTN6            f8
+#define X_MS_BTN7            f8
+#define X_MS_BTN8            f8
+#else
+#define X_MS_UP              ed
+#define X_MS_DOWN            ee
+#define X_MS_LEFT            ef
+#define X_MS_RIGHT           f0
+#define X_MS_BTN1            f1
+#define X_MS_BTN2            f2
+#define X_MS_BTN3            f3
+#define X_MS_BTN4            f4
+#define X_MS_BTN5            f5
+#define X_MS_BTN6            f6
+#define X_MS_BTN7            f7
+#define X_MS_BTN8            f8
+#endif
+#define X_MS_WH_UP           f9
+#define X_MS_WH_DOWN         fa
+#define X_MS_WH_LEFT         fb
+#define X_MS_WH_RIGHT        fc
+#define X_MS_ACCEL0          fd
+#define X_MS_ACCEL1          fe
+#define X_MS_ACCEL2          ff
+
+// Send string macros
+#define STRINGIZE(z) #z
+#define ADD_SLASH_X(y) STRINGIZE(\x##y)
+#define SYMBOL_STR(x) ADD_SLASH_X(x)
+
+#define SS_QMK_PREFIX 1
+
+#define SS_TAP_CODE 1
+#define SS_DOWN_CODE 2
+#define SS_UP_CODE 3
+#define SS_DELAY_CODE 4
+
+#define SS_TAP(keycode) "\1\1" SYMBOL_STR(keycode)
+#define SS_DOWN(keycode) "\1\2" SYMBOL_STR(keycode)
+#define SS_UP(keycode) "\1\3" SYMBOL_STR(keycode)
+#define SS_DELAY(msecs) "\1\4" STRINGIZE(msecs) "|"
+
+// `string` arguments must not be parenthesized
+#define SS_LCTL(string) SS_DOWN(X_LCTL) string SS_UP(X_LCTL)
+#define SS_LSFT(string) SS_DOWN(X_LSFT) string SS_UP(X_LSFT)
+#define SS_LALT(string) SS_DOWN(X_LALT) string SS_UP(X_LALT)
+#define SS_LGUI(string) SS_DOWN(X_LGUI) string SS_UP(X_LGUI)
+#define SS_LCMD(string) SS_LGUI(string)
+#define SS_LWIN(string) SS_LGUI(string)
+
+#define SS_RCTL(string) SS_DOWN(X_RCTL) string SS_UP(X_RCTL)
+#define SS_RSFT(string) SS_DOWN(X_RSFT) string SS_UP(X_RSFT)
+#define SS_RALT(string) SS_DOWN(X_RALT) string SS_UP(X_RALT)
+#define SS_RGUI(string) SS_DOWN(X_RGUI) string SS_UP(X_RGUI)
+#define SS_ALGR(string) SS_RALT(string)
+#define SS_RCMD(string) SS_RGUI(string)
+#define SS_RWIN(string) SS_RGUI(string)
+
+// DEPRECATED
+#define SS_LCTRL(string) SS_LCTL(string)