logo

qmk_firmware

custom branch of QMK firmware git clone https://anongit.hacktivis.me/git/qmk_firmware.git

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