keyboard stuff
at master 391 lines 12 kB view raw
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 65unicode_config_t unicode_config; 66uint8_t unicode_saved_mods; 67led_t unicode_saved_led_state; 68 69#if UNICODE_SELECTED_MODES != -1 70static uint8_t selected[] = {UNICODE_SELECTED_MODES}; 71static int8_t selected_count = ARRAY_SIZE(selected); 72static 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 83static float song_mac[][2] = UNICODE_SONG_MAC; 84# endif 85# ifdef UNICODE_SONG_LNX 86static float song_lnx[][2] = UNICODE_SONG_LNX; 87# endif 88# ifdef UNICODE_SONG_WIN 89static float song_win[][2] = UNICODE_SONG_WIN; 90# endif 91# ifdef UNICODE_SONG_BSD 92static float song_bsd[][2] = UNICODE_SONG_BSD; 93# endif 94# ifdef UNICODE_SONG_WINC 95static float song_winc[][2] = UNICODE_SONG_WINC; 96# endif 97# ifdef UNICODE_SONG_EMACS 98static float song_emacs[][2] = UNICODE_SONG_EMACS; 99# endif 100 101static 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 137void 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 162uint8_t get_unicode_input_mode(void) { 163 return unicode_config.input_mode; 164} 165 166static void persist_unicode_input_mode(void) { 167 eeconfig_update_unicode_mode(&unicode_config); 168} 169 170void 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 180static 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 202void unicode_input_mode_step(void) { 203 cycle_unicode_input_mode(1); 204} 205 206void 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 314static 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 327void 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 334void 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 359void 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 378void 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}