qmk_firmware

QMK firmware for my keyboards (Corne, Sweep Ferris) and trackball (Ploopy Adept)
Log | Files | Refs | Submodules | LICENSE

unicode.c (11580B)


      1 /* Copyright 2022
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 2 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
     15  */
     16 
     17 #include "unicode.h"
     18 
     19 #include "eeconfig.h"
     20 #include "action.h"
     21 #include "action_util.h"
     22 #include "host.h"
     23 #include "keycode.h"
     24 #include "wait.h"
     25 #include "send_string.h"
     26 #include "utf8.h"
     27 #include "debug.h"
     28 #include "quantum.h"
     29 
     30 #if defined(AUDIO_ENABLE)
     31 #    include "audio.h"
     32 #endif
     33 
     34 #if defined(UNICODE_ENABLE) + defined(UNICODEMAP_ENABLE) + defined(UCIS_ENABLE) > 1
     35 #    error "Cannot enable more than one Unicode method (UNICODE, UNICODEMAP, UCIS) at the same time"
     36 #endif
     37 
     38 // Keycodes used for starting Unicode input on different platforms
     39 #ifndef UNICODE_KEY_MAC
     40 #    define UNICODE_KEY_MAC KC_LEFT_ALT
     41 #endif
     42 #ifndef UNICODE_KEY_LNX
     43 #    define UNICODE_KEY_LNX LCTL(LSFT(KC_U))
     44 #endif
     45 #ifndef UNICODE_KEY_WINC
     46 #    define UNICODE_KEY_WINC KC_RIGHT_ALT
     47 #endif
     48 
     49 // Comma-delimited, ordered list of input modes selected for use (e.g. in cycle)
     50 // Example: #define UNICODE_SELECTED_MODES UNICODE_MODE_WINCOMPOSE, UNICODE_MODE_LINUX
     51 #ifndef UNICODE_SELECTED_MODES
     52 #    define UNICODE_SELECTED_MODES -1
     53 #endif
     54 
     55 // Whether input mode changes in cycle should be written to EEPROM
     56 #ifndef UNICODE_CYCLE_PERSIST
     57 #    define UNICODE_CYCLE_PERSIST true
     58 #endif
     59 
     60 // Delay between starting Unicode input and sending a sequence, in ms
     61 #ifndef UNICODE_TYPE_DELAY
     62 #    define UNICODE_TYPE_DELAY 10
     63 #endif
     64 
     65 unicode_config_t unicode_config;
     66 uint8_t          unicode_saved_mods;
     67 led_t            unicode_saved_led_state;
     68 
     69 #if UNICODE_SELECTED_MODES != -1
     70 static uint8_t selected[]     = {UNICODE_SELECTED_MODES};
     71 static int8_t  selected_count = ARRAY_SIZE(selected);
     72 static int8_t  selected_index;
     73 #endif
     74 
     75 __attribute__((weak)) void unicode_input_mode_set_user(uint8_t input_mode) {}
     76 
     77 __attribute__((weak)) void unicode_input_mode_set_kb(uint8_t input_mode) {
     78     unicode_input_mode_set_user(input_mode);
     79 }
     80 
     81 #ifdef AUDIO_ENABLE
     82 #    ifdef UNICODE_SONG_MAC
     83 static float song_mac[][2] = UNICODE_SONG_MAC;
     84 #    endif
     85 #    ifdef UNICODE_SONG_LNX
     86 static float song_lnx[][2] = UNICODE_SONG_LNX;
     87 #    endif
     88 #    ifdef UNICODE_SONG_WIN
     89 static float song_win[][2] = UNICODE_SONG_WIN;
     90 #    endif
     91 #    ifdef UNICODE_SONG_BSD
     92 static float song_bsd[][2] = UNICODE_SONG_BSD;
     93 #    endif
     94 #    ifdef UNICODE_SONG_WINC
     95 static float song_winc[][2] = UNICODE_SONG_WINC;
     96 #    endif
     97 #    ifdef UNICODE_SONG_EMACS
     98 static float song_emacs[][2] = UNICODE_SONG_EMACS;
     99 #    endif
    100 
    101 static void unicode_play_song(uint8_t mode) {
    102     switch (mode) {
    103 #    ifdef UNICODE_SONG_MAC
    104         case UNICODE_MODE_MACOS:
    105             PLAY_SONG(song_mac);
    106             break;
    107 #    endif
    108 #    ifdef UNICODE_SONG_LNX
    109         case UNICODE_MODE_LINUX:
    110             PLAY_SONG(song_lnx);
    111             break;
    112 #    endif
    113 #    ifdef UNICODE_SONG_WIN
    114         case UNICODE_MODE_WINDOWS:
    115             PLAY_SONG(song_win);
    116             break;
    117 #    endif
    118 #    ifdef UNICODE_SONG_BSD
    119         case UNICODE_MODE_BSD:
    120             PLAY_SONG(song_bsd);
    121             break;
    122 #    endif
    123 #    ifdef UNICODE_SONG_WINC
    124         case UNICODE_MODE_WINCOMPOSE:
    125             PLAY_SONG(song_winc);
    126             break;
    127 #    endif
    128 #    ifdef UNICODE_SONG_EMACS
    129         case UNICODE_MODE_EMACS:
    130             PLAY_SONG(song_emacs);
    131             break;
    132 #    endif
    133     }
    134 }
    135 #endif
    136 
    137 void unicode_input_mode_init(void) {
    138     eeconfig_read_unicode_mode(&unicode_config);
    139 #if UNICODE_SELECTED_MODES != -1
    140 #    if UNICODE_CYCLE_PERSIST
    141     // Find input_mode in selected modes
    142     int8_t i;
    143     for (i = 0; i < selected_count; i++) {
    144         if (selected[i] == unicode_config.input_mode) {
    145             selected_index = i;
    146             break;
    147         }
    148     }
    149     if (i == selected_count) {
    150         // Not found: input_mode isn't selected, change to one that is
    151         unicode_config.input_mode = selected[selected_index = 0];
    152     }
    153 #    else
    154     // Always change to the first selected input mode
    155     unicode_config.input_mode = selected[selected_index = 0];
    156 #    endif
    157 #endif
    158     unicode_input_mode_set_kb(unicode_config.input_mode);
    159     dprintf("Unicode input mode init to: %u\n", unicode_config.input_mode);
    160 }
    161 
    162 uint8_t get_unicode_input_mode(void) {
    163     return unicode_config.input_mode;
    164 }
    165 
    166 static void persist_unicode_input_mode(void) {
    167     eeconfig_update_unicode_mode(&unicode_config);
    168 }
    169 
    170 void set_unicode_input_mode(uint8_t mode) {
    171     unicode_config.input_mode = mode;
    172     persist_unicode_input_mode();
    173 #ifdef AUDIO_ENABLE
    174     unicode_play_song(mode);
    175 #endif
    176     unicode_input_mode_set_kb(mode);
    177     dprintf("Unicode input mode set to: %u\n", unicode_config.input_mode);
    178 }
    179 
    180 static void cycle_unicode_input_mode(int8_t offset) {
    181 #if UNICODE_SELECTED_MODES != -1
    182     selected_index = (selected_index + offset) % selected_count;
    183     if (selected_index < 0) {
    184         selected_index += selected_count;
    185     }
    186 
    187     unicode_config.input_mode = selected[selected_index];
    188 
    189 #    if UNICODE_CYCLE_PERSIST
    190     persist_unicode_input_mode();
    191 #    endif
    192 
    193 #    ifdef AUDIO_ENABLE
    194     unicode_play_song(unicode_config.input_mode);
    195 #    endif
    196 
    197     unicode_input_mode_set_kb(unicode_config.input_mode);
    198     dprintf("Unicode input mode cycle to: %u\n", unicode_config.input_mode);
    199 #endif
    200 }
    201 
    202 void unicode_input_mode_step(void) {
    203     cycle_unicode_input_mode(1);
    204 }
    205 
    206 void unicode_input_mode_step_reverse(void) {
    207     cycle_unicode_input_mode(-1);
    208 }
    209 
    210 __attribute__((weak)) void unicode_input_start(void) {
    211     unicode_saved_led_state = host_keyboard_led_state();
    212 
    213     // Note the order matters here!
    214     // Need to do this before we mess around with the mods, or else
    215     // UNICODE_KEY_LNX (which is usually Ctrl-Shift-U) might not work
    216     // correctly in the shifted case.
    217     if (unicode_config.input_mode == UNICODE_MODE_LINUX && unicode_saved_led_state.caps_lock) {
    218         tap_code(KC_CAPS_LOCK);
    219     }
    220 
    221     unicode_saved_mods = get_mods(); // Save current mods
    222     clear_mods();                    // Unregister mods to start from a clean state
    223     clear_weak_mods();
    224 
    225     switch (unicode_config.input_mode) {
    226         case UNICODE_MODE_MACOS:
    227             register_code(UNICODE_KEY_MAC);
    228             break;
    229         case UNICODE_MODE_LINUX:
    230             tap_code16(UNICODE_KEY_LNX);
    231             break;
    232         case UNICODE_MODE_WINDOWS:
    233             // For increased reliability, use numpad keys for inputting digits
    234             if (!unicode_saved_led_state.num_lock) {
    235                 tap_code(KC_NUM_LOCK);
    236             }
    237             register_code(KC_LEFT_ALT);
    238             wait_ms(UNICODE_TYPE_DELAY);
    239             tap_code(KC_KP_PLUS);
    240             break;
    241         case UNICODE_MODE_WINCOMPOSE:
    242             tap_code(UNICODE_KEY_WINC);
    243             tap_code(KC_U);
    244             break;
    245         case UNICODE_MODE_EMACS:
    246             // The usual way to type unicode in emacs is C-x-8 <RET> then the unicode number in hex
    247             tap_code16(LCTL(KC_X));
    248             tap_code16(KC_8);
    249             tap_code16(KC_ENTER);
    250             break;
    251     }
    252 
    253     wait_ms(UNICODE_TYPE_DELAY);
    254 }
    255 
    256 __attribute__((weak)) void unicode_input_finish(void) {
    257     switch (unicode_config.input_mode) {
    258         case UNICODE_MODE_MACOS:
    259             unregister_code(UNICODE_KEY_MAC);
    260             break;
    261         case UNICODE_MODE_LINUX:
    262             tap_code(KC_SPACE);
    263             if (unicode_saved_led_state.caps_lock) {
    264                 tap_code(KC_CAPS_LOCK);
    265             }
    266             break;
    267         case UNICODE_MODE_WINDOWS:
    268             unregister_code(KC_LEFT_ALT);
    269             if (!unicode_saved_led_state.num_lock) {
    270                 tap_code(KC_NUM_LOCK);
    271             }
    272             break;
    273         case UNICODE_MODE_WINCOMPOSE:
    274             tap_code(KC_ENTER);
    275             break;
    276         case UNICODE_MODE_EMACS:
    277             tap_code16(KC_ENTER);
    278             break;
    279     }
    280 
    281     set_mods(unicode_saved_mods); // Reregister previously set mods
    282 }
    283 
    284 __attribute__((weak)) void unicode_input_cancel(void) {
    285     switch (unicode_config.input_mode) {
    286         case UNICODE_MODE_MACOS:
    287             unregister_code(UNICODE_KEY_MAC);
    288             break;
    289         case UNICODE_MODE_LINUX:
    290             tap_code(KC_ESCAPE);
    291             if (unicode_saved_led_state.caps_lock) {
    292                 tap_code(KC_CAPS_LOCK);
    293             }
    294             break;
    295         case UNICODE_MODE_WINCOMPOSE:
    296             tap_code(KC_ESCAPE);
    297             break;
    298         case UNICODE_MODE_WINDOWS:
    299             unregister_code(KC_LEFT_ALT);
    300             if (!unicode_saved_led_state.num_lock) {
    301                 tap_code(KC_NUM_LOCK);
    302             }
    303             break;
    304         case UNICODE_MODE_EMACS:
    305             tap_code16(LCTL(KC_G)); // C-g cancels
    306             break;
    307     }
    308 
    309     set_mods(unicode_saved_mods); // Reregister previously set mods
    310 }
    311 
    312 // clang-format off
    313 
    314 static void send_nibble_wrapper(uint8_t digit) {
    315     if (unicode_config.input_mode == UNICODE_MODE_WINDOWS) {
    316         uint8_t kc = digit < 10
    317                    ? KC_KP_1 + (10 + digit - 1) % 10
    318                    : KC_A + (digit - 10);
    319         tap_code(kc);
    320         return;
    321     }
    322     send_nibble(digit);
    323 }
    324 
    325 // clang-format on
    326 
    327 void register_hex(uint16_t hex) {
    328     for (int i = 3; i >= 0; i--) {
    329         uint8_t digit = ((hex >> (i * 4)) & 0xF);
    330         send_nibble_wrapper(digit);
    331     }
    332 }
    333 
    334 void register_hex32(uint32_t hex) {
    335     bool first_digit        = true;
    336     bool needs_leading_zero = (unicode_config.input_mode == UNICODE_MODE_WINCOMPOSE);
    337     for (int i = 7; i >= 0; i--) {
    338         // Work out the digit we're going to transmit
    339         uint8_t digit = ((hex >> (i * 4)) & 0xF);
    340 
    341         // If we're still searching for the first digit, and found one
    342         // that needs a leading zero sent out, send the zero.
    343         if (first_digit && needs_leading_zero && digit > 9) {
    344             send_nibble_wrapper(0);
    345         }
    346 
    347         // Always send digits (including zero) if we're down to the last
    348         // two bytes of nibbles.
    349         bool must_send = i < 4;
    350 
    351         // If we've found a digit worth transmitting, do so.
    352         if (digit != 0 || !first_digit || must_send) {
    353             send_nibble_wrapper(digit);
    354             first_digit = false;
    355         }
    356     }
    357 }
    358 
    359 void register_unicode(uint32_t code_point) {
    360     if (code_point > 0x10FFFF || (code_point > 0xFFFF && unicode_config.input_mode == UNICODE_MODE_WINDOWS)) {
    361         // Code point out of range, do nothing
    362         return;
    363     }
    364 
    365     unicode_input_start();
    366     if (code_point > 0xFFFF && unicode_config.input_mode == UNICODE_MODE_MACOS) {
    367         // Convert code point to UTF-16 surrogate pair on macOS
    368         code_point -= 0x10000;
    369         uint32_t lo = code_point & 0x3FF, hi = (code_point & 0xFFC00) >> 10;
    370         register_hex32(hi + 0xD800);
    371         register_hex32(lo + 0xDC00);
    372     } else {
    373         register_hex32(code_point);
    374     }
    375     unicode_input_finish();
    376 }
    377 
    378 void send_unicode_string(const char *str) {
    379     if (!str) {
    380         return;
    381     }
    382 
    383     while (*str) {
    384         int32_t code_point = 0;
    385         str                = decode_utf8(str, &code_point);
    386 
    387         if (code_point >= 0) {
    388             register_unicode(code_point);
    389         }
    390     }
    391 }