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 }