keymap.c 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. /* Copyright 2019 Yonatan Zunger
  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 QMK_KEYBOARD_H
  17. #include <assert.h>
  18. // This keymap is designed to make it easy to type in a wide variety of languages, as well as
  19. // generate mathematical symbols (à la Space Cadet).
  20. //
  21. // LAYER MAGIC (aka, typing in many alphabets)
  22. // This keyboard has three "base" layers: QWERTY, GREEK, and CADET. The GREEK and CADET layers
  23. // are actually full of Unicode points, and so which point they generate depends on things like
  24. // whether the shift key is down. To handle this, each of those layers is actually *two* layers, one
  25. // with and one without shift. In our main loop, we manage modifier state detection, as well as
  26. // layer switch detection, and pick the right layer on the fly.
  27. // Layers are selected with a combination of three keys. The "Greek" and "Cadet" keys act like
  28. // modifiers: When held down, they transiently select the indicated base layer. The "Layer Lock" key
  29. // locks the value of the base layer at whatever is currently held; so e.g., if you hold Greek +
  30. // Layer Lock, you'll stay in Greek mode until you hit Layer Lock again without any of the mods
  31. // held.
  32. // TODO: This system of layer selection is nice for math, but it's not very nice for actually
  33. // typing in multiple languages. It seems like a better plan will be to reserve one key for each
  34. // base layer -- maybe fn + F(n) -- which can either be held as a modifier or tapped to switch
  35. // layers. That will open up adding some more languages, like Yiddish, but to do this effectively
  36. // we'll need to find a good UI with which to show the currently selected layer. Need to check what
  37. // the melody96 has in the way of outputs (LEDs, sound, etc).
  38. //
  39. // ACCENT MAGIC (aka, typing conveniently in Romance languages)
  40. // We want to support easy typing of diacritical marks. We can't rely on the host OS for this,
  41. // because (e.g.) on MacOS, to make any of the other stuff work, we need to be using the Unicode
  42. // input method at the OS level, which breaks all the normal accent stuff on that end. So we do it
  43. // ourselves. Accents can actually be invoked in two different ways: one fast and very compatible,
  44. // one very versatile but with occasional compatibility problems.
  45. //
  46. // THE MAIN WAY: You can hit one of the "accent request" key patterns immediately *before* typing
  47. // a letter to be accented. It will emit the corresponding accented Unicode. For example, you can
  48. // hit fn-e to request an acute accent, followed by i, and it will output í, U+00ED LATIN SMALL
  49. // LETTER I WITH ACUTE. These "combined characters" are in Unicode normal form C (NFKC), which is
  50. // important because many European websites and apps, in particular, tend to behave very badly
  51. // (misunderstanding and/or crashing) when presented with characters in other forms! The catch is
  52. // that this only works for the various combinations of letters and accents found in the Latin-1
  53. // supplement block of Unicode -- basically, things you need for Western European languages.
  54. //
  55. // (NB: If you make an accent request followed by a letter which can't take the corresponding
  56. // accent, it will output the uncombined form of the accent followed by whatever you typed; so
  57. // e.g., if you hit fn-e followed by f, it will output ´f, U+00B4 ACUTE ACCENT followed by an
  58. // ordinary f. This is very similar to the default behavior of MacOS.)
  59. //
  60. // THE FLEXIBLE WAY: If you hit the accent request with a shift -- e.g., fn-shift-e -- it will
  61. // instead immediately output the corresponding *combining* Unicode accent mark, which will modify
  62. // the *previous* character you typed. For example, if you type i followed by fn-shift-e, it will
  63. // generate í. But don't be fooled by visual similarity: unlike the previous example, this one is
  64. // an ordinary i followed by U+0301 COMBINING ACUTE ACCENT. It's actually *two symbols*, and this
  65. // is Unicode normal form D (NFKD). Unlike NFKC, there are NFKD representations of far more
  66. // combinations of letters and accents, and it's easy to add more of these if you need. (The NFKC
  67. // representation of such combinations is identical to their NFKD representation)
  68. //
  69. // Programs that try to compare Unicode strings *should* first normalize them by converting them
  70. // all into one normal form or another, and there are functions in every programming language to
  71. // do this -- e.g., JavaScript's string.normalize() -- but lots of programmers fail to understand
  72. // this, and so write code that massively freaks out when it encounters the wrong form.
  73. //
  74. // The current accent request codes are modeled on the ones in MacOS.
  75. //
  76. // fn+` Grave accent (`)
  77. // fn+e Acute accent (´)
  78. // fn+i Circumflex (^)
  79. // fn+u Diaresis / umlaut / trema (¨)
  80. // fn+c Cedilla (¸)
  81. // fn+n Tilde (˜)
  82. //
  83. // Together, these functions make for a nice "polyglot" keyboard: one that can easily type in a wide
  84. // variety of languages, which is very useful for people who, well, need to type in a bunch of
  85. // languages.
  86. //
  87. // The major TODOs are:
  88. // - Update the layer selection logic (and add visible layer cues);
  89. // - Factor the code below so that the data layers are more clearly separated from the code logic,
  90. // so that other users of this keymap can easily add whichever alphabets they need without
  91. // having to deeply understand the implementation.
  92. enum custom_keycodes {
  93. // We provide special layer management keys:
  94. // GREEK triggers the Greek (aka "Front") layer, or the SHIFTGREEK layer when shift is held.
  95. // (Because we use Unicode, we need to implement shift-handling at the firmware level,
  96. // rather than the OS level like we do in the QWERTY layer)
  97. // CADET or GREEK+ALT triggers the Cadet (aka "Top") layer, or the SHIFTCADET layer when
  98. // shift is held.
  99. // LAYER_LOCK locks the "base" layer (i.e., QWERTY, GREEK, or CADET) to the value which is
  100. // pressed at the moment that it is being released. When a layer lock is set, the
  101. // analogous layer modifier key is reversed; e.g., if you lock the GREEK layer, then the
  102. // GREEK button bounces you back to QWERTY.
  103. //
  104. // We also parse the shift, alt, and caps lock keys to provide management of those which is
  105. // compatible with these various layers.
  106. KC_GREEK = SAFE_RANGE,
  107. KC_CADET,
  108. KC_LAYER_LOCK,
  109. // These are the keycodes generated by the various "accent request" keystrokes.
  110. KC_ACCENT_START,
  111. KC_CGRV = KC_ACCENT_START, // Grave accent
  112. KC_CAGU, // Acute accent
  113. KC_CDIA, // Diaresis / umlaut / trema
  114. KC_CCIR, // Circumflex
  115. KC_CCED, // Cedilla
  116. KC_CTIL, // Tilde
  117. KC_ACCENT_END,
  118. };
  119. enum layers_keymap {
  120. _QWERTY = 0,
  121. _FUNCTION,
  122. _GREEK,
  123. _SHIFTGREEK,
  124. _CADET,
  125. _SHIFTCADET,
  126. };
  127. // This is so that H(xxxx) has the same width as _______, which makes the grids more legible.
  128. #define H(x) UC(0x##x)
  129. #define MO_FN MO(_FUNCTION)
  130. #define KC_LLCK KC_LAYER_LOCK
  131. const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  132. // NB: Using GESC for escape in the QWERTY layer as a temporary hack because I messed up the
  133. // switch on the KC_GRV key; change back to KC_ESC once this is fixed.
  134. [_QWERTY] = LAYOUT_hotswap(
  135. KC_GESC, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_HOME, KC_END, KC_PGUP, KC_PGDN, KC_MPLY, KC_BRK,
  136. KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_LLCK, KC_PSLS, KC_PAST, KC_PMNS,
  137. KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_P7, KC_P8, KC_P9,
  138. KC_LCTL, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_P4, KC_P5, KC_P6, KC_PPLS,
  139. KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP, KC_P1, KC_P2, KC_P3,
  140. KC_LCTL, KC_LALT, KC_LGUI, KC_SPC, MO_FN, KC_GREEK,KC_CADET,KC_LEFT, KC_DOWN, KC_RGHT, KC_P0, KC_PDOT, KC_PENT),
  141. /* The Greek layers. Shown here are the QWERTY layer (for visual reference) and the two Greek
  142. * layers.
  143. * ,----------------------------------------------------------------------------
  144. * | ` |F1 |F2 |F3 |F4 |F5 |F6 |F7 |F8 |F9 |F10|F11|F12|HOM|END|PGU|PGD|MUT|BRK| QWERTY
  145. * | ` | ₁ | ₂ | ₃ | ₄ | ₅ | ₆ | ₇ | ₈ | ₉ | ₀ | ₋ | ₊ | ₍ | ₎ | | | | | SHIFTGREEK
  146. * | ` | ¹ | ² | ³ | ⁴ | ⁵ | ⁶ | ⁷ | ⁸ | ⁹ | ⁰ | ⁻ | ⁺ | ⁽ | ⁾ | | | | | GREEK
  147. * |---------------------------------------------------------------------------|
  148. * | ` | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | - | + | BKSPC |LCK| / | * | - |
  149. * | ` | ¿ | | € | | | | | | | | | ≁ | BKSPC |LCK| | ⊗ | |
  150. * | | | | | | | | | | | | ∝ | ∼ | BKSPC |LCK| ⊘ | ⊙ | ⊖ |
  151. * |---------------------------------------------------------------------------|
  152. * | TAB | Q | W | E | R | T | Y | U | I | O | P | [ | ] | \ | 7 | 8 | 9 | |
  153. * | | Θ | Ω | Ε | Ρ | Τ | Ψ | Υ | Ι | Ο | Π | | | | | | | |
  154. * | | θ | ω | ε | ρ | τ | ψ | υ | ι | ο | π | | | | | | | |
  155. * |-----------------------------------------------------------------------| + |
  156. * | CTRL | A | S | D | F | G | H | J | K | L | ; | ' | RET | 4 | 5 | 6 | ⊕ |
  157. * | CTRL | Α | Σ | Δ | Φ | Γ | Η | | Κ | Λ | … | ∴ | RET | | | | |
  158. * | CTRL | α | σ | δ | φ | γ | η | ϑ | κ | λ | ⋯ | ⋅ | RET | | | | |
  159. * |-----------------------------------------------------------------------|---|
  160. * | SHIFT | Z | X | C | V | B | N | M | , | . | / |SHFT | ↑ | 1 | 2 | 3 | |
  161. * | SHIFT | Ζ | Ξ | Χ | ✔ | Β | Ν | Μ | ≲ | ≳ | |SHFT | | | | | |
  162. * | SHIFT | ζ | ξ | χ | ς | β | ν | μ | ≪ | ≫ | ∫ |SHFT | | | | | |
  163. * |-----------------------------------------------------------------------|ENT|
  164. * | CTL | ALT| CMD| SPACE | α | β | γ | ← | ↓ | → | 0 | . | |
  165. * | CTL | ALT| CMD| SPACE | α | β | γ | | | | | | |
  166. * | CTL | ALT| CMD| SPACE | α | β | γ | | | | | | |
  167. * `---------------------------------------------------------------------------'
  168. */
  169. [_GREEK] = LAYOUT_hotswap(
  170. KC_GRV, H(00b9), H(00b2), H(00b3), H(2074), H(2075), H(2076), H(2077), H(2078), H(2079), H(2070), H(207b), H(207a), H(207d), H(207e), XXXXXXX, XXXXXXX, XXXXXXX, _______,
  171. KC_GRV, _______, _______, _______, _______, _______, _______, _______, H(00b0), _______, _______, H(221d), H(223c), _______, _______, H(2298), H(2299), H(2296),
  172. _______, H(03b8), H(03c9), H(03b5), H(03c1), H(03c4), H(03c8), H(03c5), H(03b9), H(03bf), H(03c0), KC_LBRC, KC_RBRC, KC_BSLS, KC_P7, KC_P8, KC_P9,
  173. _______, H(03b1), H(03c3), H(03b4), H(03c6), H(03b3), H(03b7), H(03d1), H(03ba), H(03bb), H(22ef), H(22c5), _______, KC_P4, KC_P5, KC_P6, H(2295),
  174. _______, H(03b6), H(03be), H(03c7), H(03c2), H(03b2), H(03bd), H(03bc), H(226a), H(226b), H(222b), _______, _______, KC_P1, KC_P2, KC_P3,
  175. _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_P0, KC_PDOT, KC_PENT),
  176. [_SHIFTGREEK] = LAYOUT_hotswap(
  177. KC_GRV, H(2081), H(2082), H(2083), H(2084), H(2085), H(2086), H(2087), H(2088), H(2089), H(2080), H(208b), H(208a), H(208d), H(208e), XXXXXXX, XXXXXXX, XXXXXXX, _______,
  178. KC_GRV, H(00bf), _______, H(20ac), _______, _______, _______, _______, _______, _______, _______, XXXXXXX, H(2241), _______, _______, XXXXXXX, H(2297), XXXXXXX,
  179. _______, H(0398), H(03a9), H(0395), H(03a1), H(03a4), H(03a8), H(03a5), H(0399), H(039f), H(03a0), KC_LBRC, KC_RBRC, KC_BSLS, KC_P7, KC_P8, KC_P9,
  180. _______, H(0391), H(03a3), H(0394), H(03a6), H(0393), H(0397), XXXXXXX, H(039a), H(039b), H(2026), H(2234), _______, KC_P4, KC_P5, KC_P6, H(2295),
  181. _______, H(0396), H(039e), H(03a7), H(2714), H(0392), H(039d), H(039c), H(2272), H(2273), XXXXXXX, _______, _______, KC_P1, KC_P2, KC_P3,
  182. _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_P0, KC_PDOT, KC_PENT),
  183. /* The Cadet layers. Again, we show the QWERTY layer and the two cadet layers.
  184. * ,----------------------------------------------------------------------------
  185. * | ` |F1 |F2 |F3 |F4 |F5 |F6 |F7 |F8 |F9 |F10|F11|F12|HOM|END|PGU|PGD|MUT|BRK| QWERTY
  186. * | ∅ | | | | | | | | | | | | | | | | | | | SHIFTCADET
  187. * | ¬ | | | | | | | | | | | | | | | | | | | CADET
  188. * |---------------------------------------------------------------------------|
  189. * | ` | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | - | + | BKSPC |LCK| / | * | - |
  190. * | ` | ¡ | | £ | | | | | ° | | | * | ∓ | BKSPC |LCK| | | |
  191. * | | | | | | | | | | | | | ± | BKSPC |LCK| | × | |
  192. * |---------------------------------------------------------------------------|
  193. * | TAB | Q | W | E | R | T | Y | U | I | O | P | [ | ] | \ | 7 | 8 | 9 | |
  194. * | | ℚ | | | ℝ | ⊆ | ⊇ | | ℵ | ∄ | | ∉ | | | * | * | * | |
  195. * | | ∧ | ∨ | ∩ | ∪ | ⊂ | ⊃ | ∀ | ∞ | ∃ | ∂ | ∈ | | | * | * | * | | [1]
  196. * |-----------------------------------------------------------------------| + |
  197. * | CTRL | A | S | D | F | G | H | J | K | L | ; | ' | RET | 4 | 5 | 6 | ⊕ |
  198. * | CTRL | Å | | ∇ | | ⇑ | ⇓ | ⇐ | ⇒ | ⇔ | | | RET | * | * | * | |
  199. * | CTRL | ⟘ | ⊤ | ⊢ | ⊣ | ↑ | ↓ | ← | → | ↔ | | | RET | * | * | * | | [1]
  200. * |-----------------------------------------------------------------------|---|
  201. * | SHIFT | Z | X | C | V | B | N | M | , | . | / |SHFT | ↑ | 1 | 2 | 3 | |
  202. * | SHIFT | ℤ | ℂ | | ≉ | ≢ | ℕ | | | | |SHFT | | * | * | * | |
  203. * | SHIFT | | ≠ | | ≈ | ≡ | ≤ | ≥ | | | ÷ |SHFT | | * | * | * | | [1]
  204. * |-----------------------------------------------------------------------|ENT|
  205. * | CTL | ALT| CMD| SPACE | α | β | γ | ← | ↓ | → | 0 | . | |
  206. * | CTL | ALT| CMD| SPACE | α | β | γ | | | | | | |
  207. * | CTL | ALT| CMD| SPACE | α | β | γ | | | | | | |
  208. * `---------------------------------------------------------------------------'
  209. * [1] CADET + numpad moves the mouse. SHIFT+CADET+NUMPAD moves it more quickly. CADET+5
  210. * clicks the mouse, and SHIFT+CADET+FIVE right-clicks.
  211. */
  212. [_CADET] = LAYOUT_hotswap(
  213. H(00AC), KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, _______,
  214. KC_GRV, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, H(00b1), _______, _______, XXXXXXX, H(00d7), XXXXXXX,
  215. _______, H(2227), H(2228), H(2229), H(222a), H(2282), H(2283), H(2200), H(221e), H(2203), H(2202), H(2208), XXXXXXX, XXXXXXX, KC_P7, KC_P8, KC_P9,
  216. _______, H(22a5), H(22a4), H(22a2), H(22a3), H(2191), H(2193), H(2190), H(2192), H(2194), XXXXXXX, XXXXXXX, _______, KC_P4, KC_P5, KC_P6, XXXXXXX,
  217. _______, XXXXXXX, H(2260), XXXXXXX, H(2248), H(2261), H(2264), H(2265), XXXXXXX, XXXXXXX, H(00f7), _______, _______, KC_P1, KC_P2, KC_P3,
  218. _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_P0, KC_PDOT, KC_PENT),
  219. [_SHIFTCADET] = LAYOUT_hotswap(
  220. H(2205), KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, _______,
  221. KC_GRV, H(00a1), XXXXXXX, H(00a3), XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, XXXXXXX, H(2213), _______, _______, XXXXXXX, XXXXXXX, XXXXXXX,
  222. _______, H(211a), XXXXXXX, XXXXXXX, H(211d), H(2286), H(2287), XXXXXXX, H(2135), H(2204), XXXXXXX, H(2209), XXXXXXX, XXXXXXX, KC_P7, KC_P8, KC_P9,
  223. _______, H(212b), XXXXXXX, H(2207), XXXXXXX, H(21d1), H(21d3), H(21d0), H(21d2), H(21d4), XXXXXXX, XXXXXXX, _______, KC_P4, KC_P5, KC_P6, XXXXXXX,
  224. _______, H(2124), H(2102), XXXXXXX, H(2249), H(2262), H(2115), XXXXXXX, XXXXXXX, XXXXXXX, H(00f7), _______, _______, KC_P1, KC_P2, KC_P3,
  225. _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_P0, KC_PDOT, KC_PENT),
  226. // Function layer is mostly for keyboard meta-control operations, but also contains the combining
  227. // accent marks. These are deliberately placed to match where the analogous controls go on Mac OS.
  228. [_FUNCTION] = LAYOUT_hotswap(
  229. KC_CGRV, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, KC_MUTE, KC_VOLD, KC_VOLU, _______, _______, RESET,
  230. KC_CGRV, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
  231. _______, _______, _______, KC_CAGU, _______, _______, _______, KC_CDIA, KC_CCIR, _______, _______, _______, _______, _______, _______, _______, _______,
  232. _______, _______, _______, UC_M_OS, UC_M_LN, UC_M_WI, UC_M_BS, UC_M_WC, _______, _______, _______, _______, _______, _______, _______, _______, _______,
  233. _______, _______, _______, KC_CCED, _______, _______, KC_CTIL, _______, _______, _______, _______, _______, _______, _______, _______, _______,
  234. _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______),
  235. };
  236. ////////////////////////////////////////////////////////////////////////////////////////////////////
  237. // Accent implementation
  238. //
  239. // In the body of process_record_user, we store an "accent_request", which is the accent keycode if
  240. // one was just selected, or zero otherwise. When the *next* key is hit, we look up whether the
  241. // accent request plus that next keycode (plus the state of the shift key) together amount to an
  242. // interesting combined (NFKC) character, and if so, emit it; otherwise, we emit the accent as a
  243. // separate character and then process the next key normally. The resulting UI behavior is similar
  244. // to that of the combining accent keys in MacOS.
  245. //
  246. // We store two arrays, depending on whether shift is or isn't held. Each is two-dimensional, with
  247. // its outer key by the next keycode struck, and the inner key by the accent requested. The outer
  248. // array has KC_Z + 1 as its upper bound, so that we can save memory by only coding alphabetic keys.
  249. // The contents are either Unicode code points, or zero to indicate that we don't have a point for
  250. // this combination.
  251. #define KC_NUM_ACCENTS (KC_ACCENT_END - KC_ACCENT_START)
  252. #define KC_NUM_SLOTS (KC_Z + 1)
  253. const uint16_t PROGMEM unshifted_accents[KC_NUM_SLOTS][KC_NUM_ACCENTS] = {
  254. // KC_CGRV, KC_CAGU, KC_CDIA, KC_CCIR, KC_CCED, KC_CTIL
  255. [KC_A] = { 0x00e0, 0x00e1, 0x00e4, 0x00e2, 0, 0x00e3 },
  256. [KC_E] = { 0x00e8, 0x00e9, 0x00eb, 0x00ea, 0, 0 },
  257. [KC_I] = { 0x00ec, 0x00ed, 0x00ef, 0x00ee, 0, 0 },
  258. [KC_O] = { 0x00f2, 0x00f3, 0x00f6, 0x00f4, 0, 0x00f5 },
  259. [KC_U] = { 0x00f9, 0x00fa, 0x00fc, 0x00fb, 0, 0 },
  260. [KC_Y] = { 0, 0, 0x00ff, 0, 0, 0 },
  261. [KC_N] = { 0, 0, 0, 0, 0, 0x00f1 },
  262. [KC_C] = { 0, 0, 0, 0, 0x00e7, 0 },
  263. };
  264. const uint16_t PROGMEM shifted_accents[KC_NUM_SLOTS][KC_NUM_ACCENTS] = {
  265. // KC_CGRV, KC_CAGU, KC_CDIA, KC_CCIR, KC_CCED, KC_CTIL
  266. [KC_A] = { 0x00c0, 0x00c1, 0x00c4, 0x00c2, 0, 0x00c3 },
  267. [KC_E] = { 0x00c8, 0x00c9, 0x00cb, 0x00ca, 0, 0 },
  268. [KC_I] = { 0x00cc, 0x00cd, 0x00cf, 0x00ce, 0, 0 },
  269. [KC_O] = { 0x00d2, 0x00d3, 0x00d6, 0x00d4, 0, 0x00d5 },
  270. [KC_U] = { 0x00d9, 0x00da, 0x00dc, 0x00db, 0, 0 },
  271. [KC_Y] = { 0, 0, 0x00df, 0, 0, 0 },
  272. [KC_N] = { 0, 0, 0, 0, 0, 0x00d1 },
  273. [KC_C] = { 0, 0, 0, 0, 0x00c7, 0 },
  274. };
  275. // The uncombined and combined forms of the accents, for when we want to emit them as single
  276. // characters.
  277. const uint16_t PROGMEM uncombined_accents[KC_NUM_ACCENTS] = {
  278. [KC_CGRV - KC_ACCENT_START] = 0x0060,
  279. [KC_CAGU - KC_ACCENT_START] = 0x00b4,
  280. [KC_CDIA - KC_ACCENT_START] = 0x00a8,
  281. [KC_CCIR - KC_ACCENT_START] = 0x005e,
  282. [KC_CCED - KC_ACCENT_START] = 0x00b8,
  283. [KC_CTIL - KC_ACCENT_START] = 0x02dc,
  284. };
  285. const uint16_t PROGMEM combined_accents[KC_NUM_ACCENTS] = {
  286. [KC_CGRV - KC_ACCENT_START] = 0x0300,
  287. [KC_CAGU - KC_ACCENT_START] = 0x0301,
  288. [KC_CDIA - KC_ACCENT_START] = 0x0308,
  289. [KC_CCIR - KC_ACCENT_START] = 0x0302,
  290. [KC_CCED - KC_ACCENT_START] = 0x0327,
  291. [KC_CTIL - KC_ACCENT_START] = 0x0303,
  292. };
  293. // This function manages keypresses that happen after an accent has been selected by an earlier
  294. // keypress.
  295. // Args:
  296. // accent_key: The accent key which was earlier selected. This must be in the range
  297. // [KC_ACCENT_START, KC_ACCENT_END).
  298. // keycode: The keycode which was just pressed.
  299. // is_shifted: The current shift state (as set by a combination of shift and caps lock)
  300. // force_no_accent: If true, we're in a situation where we want to force there to be no
  301. // accent combination -- if e.g. we're in a non-QWERTY layer, or if other modifier keys
  302. // are held.
  303. //
  304. // Returns true if the keycode has been completely handled by this function (and so should not be
  305. // processed further by process_record_user) or false otherwise.
  306. bool process_key_after_accent(
  307. uint16_t accent_key,
  308. uint16_t keycode,
  309. bool is_shifted,
  310. bool force_no_accent
  311. ) {
  312. assert(accent_key >= KC_ACCENT_START);
  313. assert(accent_key < KC_ACCENT_END);
  314. const int accent_index = accent_key - KC_ACCENT_START;
  315. // If the keycode is outside A..Z, or force_no_accent is set, we know we shouldn't even bother
  316. // with a table lookup.
  317. if (keycode <= KC_Z && !force_no_accent) {
  318. // Pick the correct array. Because this is progmem, we're going to need to do the
  319. // two-dimensional array indexing by hand, and so we just cast it to a single-dimensional array.
  320. const uint16_t *points = (const uint16_t*)(is_shifted ? shifted_accents : unshifted_accents);
  321. const uint16_t code_point = pgm_read_word_near(points + KC_NUM_ACCENTS * keycode + accent_index);
  322. if (code_point) {
  323. register_unicode(code_point);
  324. return true;
  325. }
  326. }
  327. // If we get here, there was no accent match. Emit the accent as its own character, and then let
  328. // the caller figure out what to do next.
  329. register_unicode(pgm_read_word_near(uncombined_accents + accent_index));
  330. return false;
  331. }
  332. // Layer bitfields.
  333. #define GREEK_LAYER (1UL << _GREEK)
  334. #define SHIFTGREEK_LAYER (1UL << _SHIFTGREEK)
  335. #define CADET_LAYER (1UL << _CADET)
  336. #define SHIFTCADET_LAYER (1UL << _SHIFTCADET)
  337. // The layers we don't touch.
  338. #define LAYER_MASK ~(GREEK_LAYER|SHIFTGREEK_LAYER|CADET_LAYER|SHIFTCADET_LAYER)
  339. bool process_record_user(uint16_t keycode, keyrecord_t *record) {
  340. // We track these persistent globals and manage them on our own, rather than trying to rely on
  341. // get_mods or the like, because this function is called *before* that's updated!
  342. static bool shift_held = false;
  343. static bool alt_held = false;
  344. static bool ctrl_held = false;
  345. static bool super_held = false;
  346. static bool greek_held = false;
  347. static bool cadet_held = false;
  348. // These are where we remember the values of lock states.
  349. static bool shift_lock = false;
  350. static int layer_lock = _QWERTY;
  351. // The accent request, or zero if there isn't one.
  352. static uint16_t accent_request = 0;
  353. // If this is set to true, don't trigger any handling of pending accent requests. That's what we
  354. // want to do if e.g. the user just hit the shift key or something.
  355. bool ignore_accent_change = !record->event.pressed;
  356. // Step 1: Process any modifier key state changes, so we can maintain that state.
  357. if (keycode == KC_LSHIFT || keycode == KC_RSHIFT) {
  358. shift_held = record->event.pressed;
  359. ignore_accent_change = true;
  360. } else if (keycode == KC_LALT || keycode == KC_RALT) {
  361. alt_held = record->event.pressed;
  362. ignore_accent_change = true;
  363. } else if (keycode == KC_LCTRL || keycode == KC_RCTRL) {
  364. ctrl_held = record->event.pressed;
  365. ignore_accent_change = true;
  366. } else if (keycode == KC_LGUI || keycode == KC_RGUI) {
  367. super_held = record->event.pressed;
  368. ignore_accent_change = true;
  369. } else if (keycode == KC_GREEK) {
  370. greek_held = record->event.pressed;
  371. ignore_accent_change = true;
  372. } else if (keycode == KC_CADET) {
  373. cadet_held = record->event.pressed;
  374. ignore_accent_change = true;
  375. }
  376. // Step 2: Figure out which layer we're supposed to be in, by transforming all the prior stuff
  377. // into layer requests.
  378. const bool greek_request = (greek_held && !alt_held);
  379. const bool cadet_request = (cadet_held || (greek_held && alt_held));
  380. // Now, handle the lock keys. We store next_layer_lock in a local variable so that we can
  381. // determine the layer to pick right now before we update layer_lock.
  382. int next_layer_lock = layer_lock;
  383. if (keycode == KC_CAPS) {
  384. // If we're in QWERTY mode, caps lock is already going to be managed by the host OS, but by
  385. // tracking it ourselves we can also usefully apply it to the GREEK and CADET layers.
  386. if (record->event.pressed) {
  387. shift_lock = !shift_lock;
  388. }
  389. } else if (keycode == KC_LAYER_LOCK) {
  390. if (record->event.pressed) {
  391. if (cadet_request) {
  392. next_layer_lock = _CADET;
  393. } else if (greek_request) {
  394. next_layer_lock = _GREEK;
  395. } else {
  396. next_layer_lock = _QWERTY;
  397. }
  398. }
  399. }
  400. // OK! Now we know which buttons are being held, and the current and upcoming states of the locks.
  401. // We can compute our new base layer. Remember that the CADET and GREEK keys act as their own
  402. // antonyms if they match the layer lock -- e.g., if you have CADET locked, then CADET+X generates
  403. // QWERTY-X.
  404. int base_layer;
  405. if (cadet_request) {
  406. base_layer = (layer_lock == _CADET ? _QWERTY : _CADET);
  407. } else if (greek_request) {
  408. base_layer = (layer_lock == _GREEK ? _QWERTY : _GREEK);
  409. } else {
  410. base_layer = layer_lock;
  411. }
  412. const bool shifted = (shift_held != shift_lock);
  413. int actual_layer;
  414. if (base_layer == _CADET) {
  415. actual_layer = (shifted ? _SHIFTCADET : _CADET);
  416. } else if (base_layer == _GREEK) {
  417. actual_layer = (shifted ? _SHIFTGREEK : _GREEK);
  418. } else {
  419. // We don't do shifting for the QWERTY layer, since for that we emit USB HID codes and shifting
  420. // is managed by the host OS.
  421. actual_layer = _QWERTY;
  422. }
  423. // And now we can update the layer lock and the actual firmware layer selector.
  424. layer_lock = next_layer_lock;
  425. layer_state_t new_layer_state = (layer_state & LAYER_MASK) | (1UL << actual_layer);
  426. if (new_layer_state != layer_state) {
  427. layer_state_set(new_layer_state);
  428. }
  429. // Step 3: Handle accents. If there's a pending accent request, process it. If what the user just
  430. // hit creates a new accent request, update the pending state for the next keypress.
  431. if (!ignore_accent_change && accent_request && record->event.pressed) {
  432. // Only do the accent stuff if we're in the QWERTY layer and we aren't modifying something.
  433. const bool force_no_accent = (
  434. actual_layer != _QWERTY ||
  435. ctrl_held ||
  436. super_held ||
  437. alt_held
  438. );
  439. const uint16_t old_accent = accent_request;
  440. accent_request = 0;
  441. if (process_key_after_accent(old_accent, keycode, shifted, force_no_accent)) {
  442. return false;
  443. }
  444. }
  445. // And if a new accent request just arrived, update accent_request.
  446. if (keycode >= KC_ACCENT_START && keycode < KC_ACCENT_END && record->event.pressed) {
  447. if (shifted) {
  448. // Shift + accent request generates the combining accent key, and leaves accent_request alone.
  449. register_unicode(pgm_read_word_near(combined_accents + keycode - KC_ACCENT_START));
  450. return false;
  451. } else {
  452. accent_request = keycode;
  453. }
  454. }
  455. return true;
  456. }