~ruther/qmk_firmware

f7eb030e917a8fa360ad7cc7bb26d804cf4c5f6c — Jason Laqua 5 years ago aae1814
Standardize how unicode is processed (fixes #8768) (#8770)

Co-authored-by: Konstantin Đorđević <vomindoraan@gmail.com>
M docs/feature_unicode.md => docs/feature_unicode.md +8 -5
@@ 66,13 66,16 @@ Then define a table like this in your keymap file:

```c
const qk_ucis_symbol_t ucis_symbol_table[] = UCIS_TABLE(
    UCIS_SYM("poop", 0x1F4A9), // 💩
    UCIS_SYM("rofl", 0x1F923), // 🤣
    UCIS_SYM("kiss", 0x1F619)  // 😙
    UCIS_SYM("poop", 0x1F4A9),                // 💩
    UCIS_SYM("rofl", 0x1F923),                // 🤣
    UCIS_SYM("cuba", 0x1F1E8, 0x1F1FA),       // 🇨🇺
    UCIS_SYM("look", 0x0CA0, 0x005F, 0x0CA0), // ಠ_ಠ
);
```

To use it, call `qk_ucis_start()`. Then, type the mnemonic for the character (such as "rofl"), and hit Space or Enter. QMK should erase the "rofl" text and insert the laughing emoji.
By default, each table entry may be up to 3 code points long. This number can be changed by adding `#define UCIS_MAX_CODE_POINTS n` to your `config.h` file.

To use UCIS input, call `qk_ucis_start()`. Then, type the mnemonic for the character (such as "rofl") and hit Space, Enter or Esc. QMK should erase the "rofl" text and insert the laughing emoji.

### Customization



@@ 90,7 93,7 @@ Unicode input in QMK works by inputting a sequence of characters to the OS, sort

The following input modes are available:

* **`UC_MAC`**: macOS built-in Unicode hex input. Supports code points up to `0xFFFF` (`0x10FFFF` with Unicode Map).
* **`UC_MAC`**: macOS built-in Unicode hex input. Supports code points up to `0x10FFFF` (all possible code points).

  To enable, go to _System Preferences > Keyboard > Input Sources_, add _Unicode Hex Input_ to the list (it's under _Other_), then activate it from the input dropdown in the Menu Bar.
  By default, this mode uses the left Option key (`KC_LALT`) for Unicode input, but this can be changed by defining [`UNICODE_KEY_MAC`](#input-key-configuration) with another keycode.

M quantum/process_keycode/process_ucis.c => quantum/process_keycode/process_ucis.c +38 -59
@@ 27,7 27,7 @@ void qk_ucis_start(void) {

__attribute__((weak)) void qk_ucis_start_user(void) {
    unicode_input_start();
    register_hex(0x2328);
    register_hex(0x2328);  // ⌨
    unicode_input_finish();
}



@@ 35,74 35,54 @@ __attribute__((weak)) void qk_ucis_success(uint8_t symbol_index) {}

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;
        uint16_t keycode;
        if ('1' <= seq[i] && seq[i] <= '0') {
            keycode = seq[i] - '1' + KC_1;
        } else {
            keycode = seq[i] - 'a' + KC_A;
        }
        if (i > qk_ucis_state.count || qk_ucis_state.codes[i] != keycode) {
            return false;
        }
    }

    return (qk_ucis_state.codes[i] == KC_ENT || qk_ucis_state.codes[i] == KC_SPC);
    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);
        uint8_t keycode = qk_ucis_state.codes[i];
        register_code(keycode);
        unregister_code(keycode);
        wait_ms(UNICODE_TYPE_DELAY);
    }
}

__attribute__((weak)) void qk_ucis_cancel(void) {}

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);
        }
void register_ucis(const uint32_t *code_points) {
    for (int i = 0; i < UCIS_MAX_CODE_POINTS && code_points[i]; i++) {
        register_unicode(code_points[i]);
        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.in_progress || !record->event.pressed) {
        return true;
    }

    if (qk_ucis_state.count >= UCIS_MAX_SYMBOL_LENGTH && !(keycode == KC_BSPC || keycode == KC_ESC || keycode == KC_SPC || keycode == KC_ENT)) {
    bool special = keycode == KC_SPC || keycode == KC_ENT ||
                   keycode == KC_ESC || keycode == KC_BSPC;
    if (qk_ucis_state.count >= UCIS_MAX_SYMBOL_LENGTH && !special) {
        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) {
    switch (keycode) {
    case KC_BSPC:
        if (qk_ucis_state.count >= 2) {
            qk_ucis_state.count -= 2;
            return true;


@@ 110,12 90,11 @@ bool process_ucis(uint16_t keycode, keyrecord_t *record) {
            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--) {
    case KC_SPC:
    case KC_ENT:
    case KC_ESC:
        for (uint8_t i = 0; i < qk_ucis_state.count; i++) {
            register_code(KC_BSPC);
            unregister_code(KC_BSPC);
            wait_ms(UNICODE_TYPE_DELAY);


@@ 127,25 106,25 @@ bool process_ucis(uint16_t keycode, keyrecord_t *record) {
            return false;
        }

        unicode_input_start();
        uint8_t i;
        bool    symbol_found = false;
        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);
                register_ucis(ucis_symbol_table[i].code_points);
                break;
            }
        }
        if (!symbol_found) {
            qk_ucis_symbol_fallback();
        }
        unicode_input_finish();

        if (symbol_found) {
            qk_ucis_success(i);
        } else {
            qk_ucis_symbol_fallback();
        }

        qk_ucis_state.in_progress = false;
        return false;

    default:
        return true;
    }
    return true;
}

M quantum/process_keycode/process_ucis.h => quantum/process_keycode/process_ucis.h +18 -8
@@ 22,10 22,13 @@
#ifndef UCIS_MAX_SYMBOL_LENGTH
#    define UCIS_MAX_SYMBOL_LENGTH 32
#endif
#ifndef UCIS_MAX_CODE_POINTS
#    define UCIS_MAX_CODE_POINTS 3
#endif

typedef struct {
    char *symbol;
    char *code;
    char *   symbol;
    uint32_t code_points[UCIS_MAX_CODE_POINTS];
} qk_ucis_symbol_t;

typedef struct {


@@ 36,12 39,17 @@ typedef struct {

extern qk_ucis_state_t qk_ucis_state;

#define UCIS_TABLE(...)             \
    {                               \
        __VA_ARGS__, { NULL, NULL } \
// clang-format off

#define UCIS_TABLE(...) \
    {                   \
        __VA_ARGS__,    \
        { NULL, {} }    \
    }
#define UCIS_SYM(name, code) \
    { name, #code }
#define UCIS_SYM(name, ...) \
    { name, {__VA_ARGS__} }

// clang-format on

extern const qk_ucis_symbol_t ucis_symbol_table[];



@@ 49,5 57,7 @@ void qk_ucis_start(void);
void qk_ucis_start_user(void);
void qk_ucis_symbol_fallback(void);
void qk_ucis_success(uint8_t symbol_index);
void register_ucis(const char *hex);

void register_ucis(const uint32_t *code_points);

bool process_ucis(uint16_t keycode, keyrecord_t *record);

M quantum/process_keycode/process_unicode_common.c => quantum/process_keycode/process_unicode_common.c +21 -4
@@ 171,6 171,25 @@ void register_hex32(uint32_t hex) {
    }
}

void register_unicode(uint32_t code_point) {
    if (code_point > 0x10FFFF || (code_point > 0xFFFF && unicode_config.input_mode == UC_WIN)) {
        // Code point out of range, do nothing
        return;
    }

    unicode_input_start();
    if (code_point > 0xFFFF && unicode_config.input_mode == UC_MAC) {
        // Convert code point to UTF-16 surrogate pair on macOS
        code_point -= 0x10000;
        uint32_t lo = code_point & 0x3FF, hi = (code_point & 0xFFC00) >> 10;
        register_hex32(hi + 0xD800);
        register_hex32(lo + 0xDC00);
    } else {
        register_hex32(code_point);
    }
    unicode_input_finish();
}

// clang-format off

void send_unicode_hex_string(const char *str) {


@@ 236,14 255,12 @@ void send_unicode_string(const char *str) {
        return;
    }

    int32_t code_point = 0;
    while (*str) {
        int32_t code_point = 0;
        str = decode_utf8(str, &code_point);

        if (code_point >= 0) {
            unicode_input_start();
            register_hex32(code_point);
            unicode_input_finish();
            register_unicode(code_point);
        }
    }
}

M quantum/process_keycode/process_unicode_common.h => quantum/process_keycode/process_unicode_common.h +2 -0
@@ 89,6 89,8 @@ void unicode_input_cancel(void);

void register_hex(uint16_t hex);
void register_hex32(uint32_t hex);
void register_unicode(uint32_t code_point);

void send_unicode_hex_string(const char *str);
void send_unicode_string(const char *str);


M quantum/process_keycode/process_unicodemap.c => quantum/process_keycode/process_unicodemap.c +2 -19
@@ 36,25 36,8 @@ __attribute__((weak)) uint16_t unicodemap_index(uint16_t keycode) {

bool process_unicodemap(uint16_t keycode, keyrecord_t *record) {
    if (keycode >= QK_UNICODEMAP && keycode <= QK_UNICODEMAP_PAIR_MAX && record->event.pressed) {
        unicode_input_start();

        uint32_t code       = pgm_read_dword(unicode_map + unicodemap_index(keycode));
        uint8_t  input_mode = get_unicode_input_mode();

        if (code > 0x10FFFF || (code > 0xFFFF && input_mode == UC_WIN)) {
            // Character is out of range supported by the platform
            unicode_input_cancel();
        } else if (code > 0xFFFF && input_mode == UC_MAC) {
            // Convert to UTF-16 surrogate pair on Mac
            code -= 0x10000;
            uint32_t lo = code & 0x3FF, hi = (code & 0xFFC00) >> 10;
            register_hex32(hi + 0xD800);
            register_hex32(lo + 0xDC00);
            unicode_input_finish();
        } else {
            register_hex32(code);
            unicode_input_finish();
        }
        uint32_t code_point = pgm_read_dword(unicode_map + unicodemap_index(keycode));
        register_unicode(code_point);
    }
    return true;
}