unicode.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  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. #include "unicode.h"
  17. #include "eeprom.h"
  18. #include "eeconfig.h"
  19. #include "action.h"
  20. #include "action_util.h"
  21. #include "host.h"
  22. #include "keycode.h"
  23. #include "wait.h"
  24. #include "audio.h"
  25. #include "send_string.h"
  26. #include "utf8.h"
  27. #if defined(UNICODE_ENABLE) + defined(UNICODEMAP_ENABLE) + defined(UCIS_ENABLE) > 1
  28. # error "Cannot enable more than one Unicode method (UNICODE, UNICODEMAP, UCIS) at the same time"
  29. #endif
  30. // Keycodes used for starting Unicode input on different platforms
  31. #ifndef UNICODE_KEY_MAC
  32. # define UNICODE_KEY_MAC KC_LEFT_ALT
  33. #endif
  34. #ifndef UNICODE_KEY_LNX
  35. # define UNICODE_KEY_LNX LCTL(LSFT(KC_U))
  36. #endif
  37. #ifndef UNICODE_KEY_WINC
  38. # define UNICODE_KEY_WINC KC_RIGHT_ALT
  39. #endif
  40. // Comma-delimited, ordered list of input modes selected for use (e.g. in cycle)
  41. // Example: #define UNICODE_SELECTED_MODES UC_WINC, UC_LNX
  42. #ifndef UNICODE_SELECTED_MODES
  43. # define UNICODE_SELECTED_MODES -1
  44. #endif
  45. // Whether input mode changes in cycle should be written to EEPROM
  46. #ifndef UNICODE_CYCLE_PERSIST
  47. # define UNICODE_CYCLE_PERSIST true
  48. #endif
  49. // Delay between starting Unicode input and sending a sequence, in ms
  50. #ifndef UNICODE_TYPE_DELAY
  51. # define UNICODE_TYPE_DELAY 10
  52. #endif
  53. unicode_config_t unicode_config;
  54. uint8_t unicode_saved_mods;
  55. led_t unicode_saved_led_state;
  56. #if UNICODE_SELECTED_MODES != -1
  57. static uint8_t selected[] = {UNICODE_SELECTED_MODES};
  58. static int8_t selected_count = ARRAY_SIZE(selected);
  59. static int8_t selected_index;
  60. #endif
  61. /** \brief unicode input mode set at user level
  62. *
  63. * Run user code on unicode input mode change
  64. */
  65. __attribute__((weak)) void unicode_input_mode_set_user(uint8_t input_mode) {}
  66. /** \brief unicode input mode set at keyboard level
  67. *
  68. * Run keyboard code on unicode input mode change
  69. */
  70. __attribute__((weak)) void unicode_input_mode_set_kb(uint8_t input_mode) {
  71. unicode_input_mode_set_user(input_mode);
  72. }
  73. #ifdef AUDIO_ENABLE
  74. # ifdef UNICODE_SONG_MAC
  75. static float song_mac[][2] = UNICODE_SONG_MAC;
  76. # endif
  77. # ifdef UNICODE_SONG_LNX
  78. static float song_lnx[][2] = UNICODE_SONG_LNX;
  79. # endif
  80. # ifdef UNICODE_SONG_WIN
  81. static float song_win[][2] = UNICODE_SONG_WIN;
  82. # endif
  83. # ifdef UNICODE_SONG_BSD
  84. static float song_bsd[][2] = UNICODE_SONG_BSD;
  85. # endif
  86. # ifdef UNICODE_SONG_WINC
  87. static float song_winc[][2] = UNICODE_SONG_WINC;
  88. # endif
  89. # ifdef UNICODE_SONG_EMACS
  90. static float song_emacs[][2] = UNICODE_SONG_EMACS;
  91. # endif
  92. static void unicode_play_song(uint8_t mode) {
  93. switch (mode) {
  94. # ifdef UNICODE_SONG_MAC
  95. case UC_MAC:
  96. PLAY_SONG(song_mac);
  97. break;
  98. # endif
  99. # ifdef UNICODE_SONG_LNX
  100. case UC_LNX:
  101. PLAY_SONG(song_lnx);
  102. break;
  103. # endif
  104. # ifdef UNICODE_SONG_WIN
  105. case UC_WIN:
  106. PLAY_SONG(song_win);
  107. break;
  108. # endif
  109. # ifdef UNICODE_SONG_BSD
  110. case UC_BSD:
  111. PLAY_SONG(song_bsd);
  112. break;
  113. # endif
  114. # ifdef UNICODE_SONG_WINC
  115. case UC_WINC:
  116. PLAY_SONG(song_winc);
  117. break;
  118. # endif
  119. # ifdef UNICODE_SONG_EMACS
  120. case UC_EMACS:
  121. PLAY_SONG(song_emacs);
  122. break;
  123. # endif
  124. }
  125. }
  126. #endif
  127. void unicode_input_mode_init(void) {
  128. unicode_config.raw = eeprom_read_byte(EECONFIG_UNICODEMODE);
  129. #if UNICODE_SELECTED_MODES != -1
  130. # if UNICODE_CYCLE_PERSIST
  131. // Find input_mode in selected modes
  132. int8_t i;
  133. for (i = 0; i < selected_count; i++) {
  134. if (selected[i] == unicode_config.input_mode) {
  135. selected_index = i;
  136. break;
  137. }
  138. }
  139. if (i == selected_count) {
  140. // Not found: input_mode isn't selected, change to one that is
  141. unicode_config.input_mode = selected[selected_index = 0];
  142. }
  143. # else
  144. // Always change to the first selected input mode
  145. unicode_config.input_mode = selected[selected_index = 0];
  146. # endif
  147. #endif
  148. unicode_input_mode_set_kb(unicode_config.input_mode);
  149. dprintf("Unicode input mode init to: %u\n", unicode_config.input_mode);
  150. }
  151. uint8_t get_unicode_input_mode(void) {
  152. return unicode_config.input_mode;
  153. }
  154. void set_unicode_input_mode(uint8_t mode) {
  155. unicode_config.input_mode = mode;
  156. persist_unicode_input_mode();
  157. #ifdef AUDIO_ENABLE
  158. unicode_play_song(mode);
  159. #endif
  160. unicode_input_mode_set_kb(mode);
  161. dprintf("Unicode input mode set to: %u\n", unicode_config.input_mode);
  162. }
  163. void cycle_unicode_input_mode(int8_t offset) {
  164. #if UNICODE_SELECTED_MODES != -1
  165. selected_index = (selected_index + offset) % selected_count;
  166. if (selected_index < 0) {
  167. selected_index += selected_count;
  168. }
  169. unicode_config.input_mode = selected[selected_index];
  170. # if UNICODE_CYCLE_PERSIST
  171. persist_unicode_input_mode();
  172. # endif
  173. # ifdef AUDIO_ENABLE
  174. unicode_play_song(unicode_config.input_mode);
  175. # endif
  176. unicode_input_mode_set_kb(unicode_config.input_mode);
  177. dprintf("Unicode input mode cycle to: %u\n", unicode_config.input_mode);
  178. #endif
  179. }
  180. void persist_unicode_input_mode(void) {
  181. eeprom_update_byte(EECONFIG_UNICODEMODE, unicode_config.input_mode);
  182. }
  183. __attribute__((weak)) void unicode_input_start(void) {
  184. unicode_saved_led_state = host_keyboard_led_state();
  185. // Note the order matters here!
  186. // Need to do this before we mess around with the mods, or else
  187. // UNICODE_KEY_LNX (which is usually Ctrl-Shift-U) might not work
  188. // correctly in the shifted case.
  189. if (unicode_config.input_mode == UC_LNX && unicode_saved_led_state.caps_lock) {
  190. tap_code(KC_CAPS_LOCK);
  191. }
  192. unicode_saved_mods = get_mods(); // Save current mods
  193. clear_mods(); // Unregister mods to start from a clean state
  194. clear_weak_mods();
  195. switch (unicode_config.input_mode) {
  196. case UC_MAC:
  197. register_code(UNICODE_KEY_MAC);
  198. break;
  199. case UC_LNX:
  200. tap_code16(UNICODE_KEY_LNX);
  201. break;
  202. case UC_WIN:
  203. // For increased reliability, use numpad keys for inputting digits
  204. if (!unicode_saved_led_state.num_lock) {
  205. tap_code(KC_NUM_LOCK);
  206. }
  207. register_code(KC_LEFT_ALT);
  208. wait_ms(UNICODE_TYPE_DELAY);
  209. tap_code(KC_KP_PLUS);
  210. break;
  211. case UC_WINC:
  212. tap_code(UNICODE_KEY_WINC);
  213. tap_code(KC_U);
  214. break;
  215. case UC_EMACS:
  216. // The usual way to type unicode in emacs is C-x-8 <RET> then the unicode number in hex
  217. tap_code16(LCTL(KC_X));
  218. tap_code16(KC_8);
  219. tap_code16(KC_ENTER);
  220. break;
  221. }
  222. wait_ms(UNICODE_TYPE_DELAY);
  223. }
  224. __attribute__((weak)) void unicode_input_finish(void) {
  225. switch (unicode_config.input_mode) {
  226. case UC_MAC:
  227. unregister_code(UNICODE_KEY_MAC);
  228. break;
  229. case UC_LNX:
  230. tap_code(KC_SPACE);
  231. if (unicode_saved_led_state.caps_lock) {
  232. tap_code(KC_CAPS_LOCK);
  233. }
  234. break;
  235. case UC_WIN:
  236. unregister_code(KC_LEFT_ALT);
  237. if (!unicode_saved_led_state.num_lock) {
  238. tap_code(KC_NUM_LOCK);
  239. }
  240. break;
  241. case UC_WINC:
  242. tap_code(KC_ENTER);
  243. break;
  244. case UC_EMACS:
  245. tap_code16(KC_ENTER);
  246. break;
  247. }
  248. set_mods(unicode_saved_mods); // Reregister previously set mods
  249. }
  250. __attribute__((weak)) void unicode_input_cancel(void) {
  251. switch (unicode_config.input_mode) {
  252. case UC_MAC:
  253. unregister_code(UNICODE_KEY_MAC);
  254. break;
  255. case UC_LNX:
  256. tap_code(KC_ESCAPE);
  257. if (unicode_saved_led_state.caps_lock) {
  258. tap_code(KC_CAPS_LOCK);
  259. }
  260. break;
  261. case UC_WINC:
  262. tap_code(KC_ESCAPE);
  263. break;
  264. case UC_WIN:
  265. unregister_code(KC_LEFT_ALT);
  266. if (!unicode_saved_led_state.num_lock) {
  267. tap_code(KC_NUM_LOCK);
  268. }
  269. break;
  270. case UC_EMACS:
  271. tap_code16(LCTL(KC_G)); // C-g cancels
  272. break;
  273. }
  274. set_mods(unicode_saved_mods); // Reregister previously set mods
  275. }
  276. // clang-format off
  277. static void send_nibble_wrapper(uint8_t digit) {
  278. if (unicode_config.input_mode == UC_WIN) {
  279. uint8_t kc = digit < 10
  280. ? KC_KP_1 + (10 + digit - 1) % 10
  281. : KC_A + (digit - 10);
  282. tap_code(kc);
  283. return;
  284. }
  285. send_nibble(digit);
  286. }
  287. // clang-format on
  288. void register_hex(uint16_t hex) {
  289. for (int i = 3; i >= 0; i--) {
  290. uint8_t digit = ((hex >> (i * 4)) & 0xF);
  291. send_nibble_wrapper(digit);
  292. }
  293. }
  294. void register_hex32(uint32_t hex) {
  295. bool onzerostart = true;
  296. for (int i = 7; i >= 0; i--) {
  297. if (i <= 3) {
  298. onzerostart = false;
  299. }
  300. uint8_t digit = ((hex >> (i * 4)) & 0xF);
  301. if (digit == 0) {
  302. if (!onzerostart) {
  303. send_nibble_wrapper(digit);
  304. }
  305. } else {
  306. send_nibble_wrapper(digit);
  307. onzerostart = false;
  308. }
  309. }
  310. }
  311. void register_unicode(uint32_t code_point) {
  312. if (code_point > 0x10FFFF || (code_point > 0xFFFF && unicode_config.input_mode == UC_WIN)) {
  313. // Code point out of range, do nothing
  314. return;
  315. }
  316. unicode_input_start();
  317. if (code_point > 0xFFFF && unicode_config.input_mode == UC_MAC) {
  318. // Convert code point to UTF-16 surrogate pair on macOS
  319. code_point -= 0x10000;
  320. uint32_t lo = code_point & 0x3FF, hi = (code_point & 0xFFC00) >> 10;
  321. register_hex32(hi + 0xD800);
  322. register_hex32(lo + 0xDC00);
  323. } else {
  324. register_hex32(code_point);
  325. }
  326. unicode_input_finish();
  327. }
  328. void send_unicode_string(const char *str) {
  329. if (!str) {
  330. return;
  331. }
  332. while (*str) {
  333. int32_t code_point = 0;
  334. str = decode_utf8(str, &code_point);
  335. if (code_point >= 0) {
  336. register_unicode(code_point);
  337. }
  338. }
  339. }