keymap.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. /* Copyright 2021 dogspace <https://github.com/dogspace>
  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. enum custom_keycodes {
  18. KC_CUST = SAFE_RANGE,
  19. };
  20. enum layer_names {
  21. _MA,
  22. _L1,
  23. _L2,
  24. _L3
  25. };
  26. // NOTE: Default keymap layers were designed for ANSI split-space layout http://www.keyboard-layout-editor.com/#/gists/f28bd5ff4e62f69e89896df3a59671c6
  27. const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  28. [_MA] = LAYOUT_ansi(
  29. KC_ESC, 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_DEL,
  30. KC_MUTE, 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, LCTL(KC_F),
  31. KC_CAPS, MO(_L2), 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_CALC,
  32. TG(_L2), 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_WHOM,
  33. MO(_L3), KC_LCTL, KC_LGUI, KC_LALT, KC_SPC, MO(_L1), LCTL(KC_C), LCTL(KC_V), KC_LEFT, KC_DOWN, KC_RGHT
  34. ),
  35. [_L1] = LAYOUT_ansi(
  36. KC_GRAVE, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
  37. RGB_TOG, _______, _______, _______, _______, _______, _______, _______, KC_PGUP, KC_UP, _______, _______, _______, _______, _______, _______,
  38. _______, LCTL(KC_Z), KC_LCTL, KC_LSFT, _______, _______, _______, KC_HOME, KC_LEFT, KC_DOWN, KC_RIGHT, KC_END, _______, _______, _______,
  39. _______, _______, _______, _______, _______, _______, _______, _______, KC_PGDN, _______, _______, LCTL(KC_SLSH), _______, _______, _______,
  40. _______, _______, _______, _______, _______, _______, LCTL(KC_X), _______, _______, _______, _______
  41. ),
  42. [_L2] = LAYOUT_ansi(
  43. KC_GRAVE, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
  44. RGB_TOG, _______, _______, _______, _______, _______, _______, KC_PAST, KC_7, KC_8, KC_9, _______, _______, _______, _______, _______,
  45. _______, _______, _______, _______, _______, _______, _______, KC_PPLS, KC_4, KC_5, KC_6, _______, _______, _______, _______,
  46. _______, _______, _______, _______, _______, _______, _______, KC_PMNS, KC_1, KC_2, KC_3, _______, _______, _______, _______,
  47. _______, _______, _______, _______, KC_0, KC_PSLS, _______, _______, _______, _______, _______
  48. ),
  49. [_L3] = LAYOUT_ansi(
  50. _______, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, _______, _______,
  51. RGB_TOG, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
  52. _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
  53. KC_SYSREQ, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
  54. _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______
  55. )
  56. };
  57. #ifdef OLED_ENABLE
  58. /*=========================================== OLED CONFIGURATION ===========================================*/
  59. bool oled_horizontal = true; // OLED rotation (true = horizontal, false = vertical)
  60. bool ansi_layout = true; // ANSI or ISO layout (true = ANSI, false = ISO)
  61. bool split_space = true; // Split spacebar (true = split spacebar, false = 6.25u or 7u spacebar)
  62. bool three_mods_left = true; // Left mods layout (true = 3x 1.25u keys, false = 2x 1.5u keys)
  63. bool three_mods_right = false; // Right mods layout (true = 3x 1u keys, false = 2x 1.5u keys)
  64. bool graph_direction = true; // Graph movement (true = right to left, false = left to right)
  65. float graph_top_wpm = 100.0; // Minimum WPM required to reach the top of the graph
  66. int graph_refresh = 1000; // In milliseconds, determines the graph-line frequency
  67. int icon_med_wpm = 50; // WPM required to display the medium snail
  68. int icon_fast_wpm = 72; // WPM required to display the fast snail
  69. // Layer names: Should be exactly 5 characters in length if vertical display, or 6 characters if horizontal
  70. #define MA_LAYER_NAME "QWERTY" // Layer _MA name
  71. #define L1_LAYER_NAME "ARROWS" // Layer _L1 name
  72. #define L2_LAYER_NAME "NUMPAD" // Layer _L2 name
  73. #define L3_LAYER_NAME "FUNCTN" // Layer _L3 name
  74. /*================================================================================================================*/
  75. bool first_loop = true;
  76. int timer = 0;
  77. int wpm_limit = 20;
  78. int max_wpm = -1;
  79. int wpm_icon = -1;
  80. int graph_lines[64];
  81. // Set OLED rotation
  82. oled_rotation_t oled_init_user(oled_rotation_t rotation) {
  83. if (oled_horizontal) {
  84. return OLED_ROTATION_180;
  85. } else {
  86. return OLED_ROTATION_90;
  87. }
  88. }
  89. // Toggles pixel on/off, converts horizontal coordinates to vertical equivalent if necessary
  90. static void write_pixel(int x, int y, bool onoff) {
  91. if (oled_horizontal) {
  92. oled_write_pixel(x, y, onoff);
  93. } else {
  94. oled_write_pixel(y, 127 - x, onoff);
  95. }
  96. }
  97. // Draw static background image to OLED (keyboard with no bottom row)
  98. static void render_background(void) {
  99. if (oled_horizontal) {
  100. static const char PROGMEM oled_keymap_horizontal[] = {
  101. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  102. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  103. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
  104. 0x84, 0x80, 0x80, 0x80, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x04,
  105. 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00,
  106. 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00,
  107. 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00,
  108. 0x80, 0x04, 0x04, 0x04, 0x04, 0x84, 0x84, 0x84, 0x84, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00,
  109. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  110. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  111. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
  112. 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10,
  113. 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
  114. 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  115. 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
  116. 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
  117. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  118. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  119. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
  120. 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  121. 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
  122. 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02,
  123. 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02,
  124. 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
  125. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  126. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  127. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  128. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  129. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  130. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  131. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  132. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  133. };
  134. oled_write_raw_P(oled_keymap_horizontal, sizeof(oled_keymap_horizontal));
  135. } else {
  136. static const char PROGMEM oled_keymap_vertical[] = {
  137. 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00,
  138. 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  139. 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
  140. 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  141. 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
  142. 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  143. 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
  144. 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  145. 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00,
  146. 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  147. 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
  148. 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  149. 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00,
  150. 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  151. 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
  152. 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  153. 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00,
  154. 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  155. 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00,
  156. 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  157. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
  158. 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  159. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  160. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  161. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  162. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  163. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  164. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  165. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  166. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  167. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  168. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  169. };
  170. oled_write_raw_P(oled_keymap_vertical, sizeof(oled_keymap_vertical));
  171. }
  172. }
  173. // Location of OLED keyboard's top left pixel, relative to the display
  174. static const int keymap_template[2] = {41, 0};
  175. // Location of key highlights top left pixels, relative to keymap_template {X, Y, Key length in px}
  176. static int keymap_coords[MATRIX_ROWS][MATRIX_COLS][3] = {
  177. { {12, 15, 1}, {5, 0, 1}, {10, 0, 1}, {15, 0, 1}, {20, 0, 1}, {25, 0, 1}, {30, 0, 1}, {35, 0, 1}, {40, 0, 1}, {45, 0, 1}, {50, 0, 1}, {55, 0, 1}, {60, 0, 1}, {65, 0, 1}, {70, 0, 8}, {82, 0, 1} },
  178. { {0, 5, 1}, {5, 5, 5}, {14, 5, 1}, {19, 5, 1}, {24, 5, 1}, {29, 5, 1}, {34, 5, 1}, {39, 5, 1}, {44, 5, 1}, {49, 5, 1}, {54, 5, 1}, {59, 5, 1}, {64, 5, 1}, {69, 5, 1}, {74, 5, 4}, {82, 5, 1} },
  179. { {0, 10, 1}, {5, 10, 6}, {15, 10, 1}, {20, 10, 1}, {25, 10, 1}, {30, 10, 1}, {35, 10, 1}, {40, 10, 1}, {45, 10, 1}, {50, 10, 1}, {55, 10, 1}, {60, 10, 1}, {65, 10, 1}, {0, 0, 0}, {70, 10, 8}, {82, 10, 1} },
  180. { {0, 15, 1}, {5, 15, 8}, {17, 15, 1}, {22, 15, 1}, {27, 15, 1}, {32, 15, 1}, {37, 15, 1}, {42, 15, 1}, {47, 15, 1}, {52, 15, 1}, {57, 15, 1}, {62, 15, 1}, {67, 15, 6}, {0, 0, 0}, {77, 15, 1}, {82, 15, 1} },
  181. { {0, 20, 1}, {5, 20, 2}, {11, 20, 2}, {17, 20, 2}, {0, 0, 0}, {0, 0, 0}, {23, 20, 12}, {0, 0, 0}, {0, 0, 0}, {39, 20, 3}, {56, 20, 4}, {64, 20, 4}, {72, 20, 1}, {0, 0, 0}, {77, 20, 1}, {82, 20, 1} }
  182. };
  183. // Draw the bottom row of the keyboard (based on OLED config variables), update coordinates
  184. static void render_fn_row(void) {
  185. // Update locations of spacebar and modifier key highlights
  186. if ((split_space == false) && (three_mods_left == false)) {
  187. keymap_coords[4][1][2] = 3;
  188. keymap_coords[4][2][0] = 12;
  189. keymap_coords[4][2][2] = 3;
  190. keymap_coords[4][3][0] = 0;
  191. keymap_coords[4][3][1] = 0;
  192. keymap_coords[4][3][2] = 0;
  193. keymap_coords[4][6][0] = 19;
  194. keymap_coords[4][6][2] = 34;
  195. } else if ((split_space == false) && (three_mods_left == true)) {
  196. keymap_coords[4][6][2] = 30;
  197. }
  198. if ((split_space == false) && (three_mods_right == true)) {
  199. keymap_coords[4][9][0] = 57;
  200. keymap_coords[4][9][2] = 1;
  201. keymap_coords[4][10][0] = 62;
  202. keymap_coords[4][10][2] = 1;
  203. keymap_coords[4][11][0] = 67;
  204. keymap_coords[4][11][2] = 1;
  205. }
  206. // Draw modifiers
  207. for (int i = 0; i < 16; i++) {
  208. if (keymap_coords[4][i][2] != 0) {
  209. for (int p = 0; p < keymap_coords[4][i][2]; p++) {
  210. int x = keymap_template[0] + keymap_coords[4][i][0] + 2 + p;
  211. write_pixel(x, 22, true);
  212. }
  213. }
  214. }
  215. // Draw second line for split spacebar
  216. if (split_space == true) {
  217. for (int i = 0; i < 6; i++) {
  218. int x = keymap_template[0] + 46 + 2 + i;
  219. write_pixel(x, 22, true);
  220. }
  221. }
  222. }
  223. // Update OLED keyboard with ISO layout, update coordinates
  224. static void render_iso(void) {
  225. for (int i = 0; i < 6; i++) {
  226. // Turn off ANSI enter
  227. write_pixel(keymap_template[0] + 73 + i, keymap_template[1] + 12, false);
  228. if (i < 4) {
  229. // Turn off part of ANSI left shift
  230. write_pixel(keymap_template[0] + 10 + i, keymap_template[1] + 17, false);
  231. // Draw vertical line for ISO enter
  232. write_pixel(keymap_template[0] + 79, keymap_template[1] + 8 + i, true);
  233. }
  234. }
  235. // Update locations of shift and grave key highlights
  236. keymap_coords[3][1][2] = 3;
  237. keymap_coords[1][14][0] = 70;
  238. keymap_coords[1][14][1] = 10;
  239. keymap_coords[1][14][2] = 1;
  240. }
  241. // Toggles pixels surrounding key
  242. static void render_keymap(uint8_t key_row, uint8_t key_col, bool onoff) {
  243. int length = keymap_coords[key_row][key_col][2] + 4;
  244. int left = keymap_coords[key_row][key_col][0] + keymap_template[0];
  245. int top = keymap_coords[key_row][key_col][1] + keymap_template[1];
  246. int right = left + length - 1;
  247. int bottom = top + 4;
  248. // Special case 1 - Draw enter key on ISO layout, return
  249. if ((ansi_layout == false) && (key_row == 2) && (key_col == 14)) {
  250. for (int i = 0; i < 10; i++) {
  251. write_pixel(keymap_template[0] + 81, keymap_template[1] + 5 + i, onoff);
  252. if (i < 5) {
  253. write_pixel(keymap_template[0] + 74, keymap_template[1] + 5 + i, onoff);
  254. }
  255. if (i < 6) {
  256. write_pixel(keymap_template[0] + 75, keymap_template[1] + 9 + i, onoff);
  257. }
  258. if (i < 7) {
  259. write_pixel(keymap_template[0] + 75 + i, keymap_template[1] + 5, onoff);
  260. write_pixel(keymap_template[0] + 75 + i, keymap_template[1] + 14, onoff);
  261. }
  262. }
  263. return;
  264. }
  265. // Draw top and bottom walls (horizontal for <length>px)
  266. for (int x = 0; x < length; x++) {
  267. write_pixel(left + x, top, onoff);
  268. write_pixel(left + x, bottom, onoff);
  269. }
  270. // Draw left and right walls (vertical for 5px)
  271. for (int y = 0; y < 5; y++) {
  272. write_pixel(left, top + y, onoff);
  273. write_pixel(right, top + y, onoff);
  274. }
  275. // Special case 2 - Draw right spacebar on split-space layout
  276. if ((split_space == true) && (key_row == 4) && (key_col == 6)) {
  277. int start = keymap_template[0] + 46;
  278. int stop = keymap_template[0] + 55;
  279. for (int x = start; x < stop; x++) {
  280. write_pixel(x, top, onoff);
  281. write_pixel(x, bottom, onoff);
  282. }
  283. for (int y = 0; y < 5; y++) {
  284. write_pixel(start, top + y, onoff);
  285. write_pixel(stop, top + y, onoff);
  286. }
  287. }
  288. }
  289. // Write active layer name
  290. static void render_layer_state(void) {
  291. if (oled_horizontal) {
  292. oled_set_cursor(0, 0);
  293. } else {
  294. oled_set_cursor(0, 15);
  295. }
  296. switch (get_highest_layer(layer_state)) {
  297. case _MA:
  298. oled_write_P(PSTR(MA_LAYER_NAME), false);
  299. break;
  300. case _L1:
  301. oled_write_P(PSTR(L1_LAYER_NAME), false);
  302. break;
  303. case _L2:
  304. oled_write_P(PSTR(L2_LAYER_NAME), false);
  305. break;
  306. case _L3:
  307. oled_write_P(PSTR(L3_LAYER_NAME), false);
  308. break;
  309. default:
  310. oled_write("ERROR", false);
  311. break;
  312. }
  313. }
  314. // Update WPM counters
  315. static void render_wpm_counters(int current_wpm) {
  316. int cursorposition_cur = 2;
  317. int cursorposition_max = 1;
  318. if (oled_horizontal == false) {
  319. cursorposition_cur = 13;
  320. cursorposition_max = 14;
  321. }
  322. char wpm_counter[4];
  323. wpm_counter[3] = '\0';
  324. wpm_counter[2] = '0' + current_wpm % 10;
  325. wpm_counter[1] = '0' + (current_wpm / 10) % 10;
  326. wpm_counter[0] = '0' + (current_wpm / 100) % 10;
  327. oled_set_cursor(0, cursorposition_cur);
  328. oled_write(wpm_counter, false);
  329. if (current_wpm > max_wpm) {
  330. max_wpm = current_wpm;
  331. wpm_limit = max_wpm + 20;
  332. oled_set_cursor(0, cursorposition_max);
  333. oled_write(wpm_counter, false);
  334. }
  335. }
  336. // Update WPM snail icon
  337. static void render_wpm_icon(int current_wpm) {
  338. // wpm_icon is used to prevent unnecessary redraw
  339. if ((current_wpm < icon_med_wpm) && (wpm_icon != 0)) {
  340. wpm_icon = 0;
  341. } else if ((current_wpm >= icon_med_wpm) && (current_wpm < icon_fast_wpm) && (wpm_icon != 1)) {
  342. wpm_icon = 1;
  343. } else if ((current_wpm >= icon_fast_wpm) && (wpm_icon != 2)) {
  344. wpm_icon = 2;
  345. } else {
  346. return;
  347. }
  348. static const char PROGMEM snails[][2][24] = {
  349. {{0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x20, 0xA0, 0x20, 0x40, 0x40, 0x80, 0x00, 0x00, 0x00, 0x80, 0x40, 0x20, 0x50, 0x88, 0x04, 0x00, 0x00},
  350. {0x40, 0x60, 0x50, 0x4E, 0x51, 0x64, 0x4A, 0x51, 0x54, 0x49, 0x41, 0x62, 0x54, 0x49, 0x46, 0x41, 0x40, 0x30, 0x09, 0x04, 0x02, 0x01, 0x00, 0x00}},
  351. {{0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x40, 0x40, 0x40, 0x40, 0x80, 0x80, 0x00, 0x00, 0x00, 0x04, 0x98, 0x60, 0x80, 0x00, 0x00, 0x00, 0x00},
  352. {0x60, 0x50, 0x54, 0x4A, 0x51, 0x64, 0x4A, 0x51, 0x55, 0x49, 0x41, 0x62, 0x54, 0x49, 0x46, 0x41, 0x21, 0x10, 0x0A, 0x08, 0x05, 0x02, 0x00, 0x00}},
  353. {{0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x80, 0x10, 0x10, 0x10, 0x20, 0x40, 0x40, 0xC0, 0x80, 0x80, 0x00, 0x00, 0x00},
  354. {0x60, 0x58, 0x54, 0x62, 0x49, 0x54, 0x52, 0x51, 0x55, 0x49, 0x62, 0x52, 0x4D, 0x45, 0x46, 0x22, 0x21, 0x11, 0x10, 0x0A, 0x08, 0x05, 0x02, 0x00}}
  355. };
  356. if (oled_horizontal) {
  357. oled_set_cursor(3, 1);
  358. oled_write_raw_P(snails[wpm_icon][0], sizeof(snails[wpm_icon][0]));
  359. oled_set_cursor(3, 2);
  360. oled_write_raw_P(snails[wpm_icon][1], sizeof(snails[wpm_icon][1]));
  361. } else {
  362. oled_set_cursor(0, 11);
  363. oled_write_raw_P(snails[wpm_icon][0], sizeof(snails[wpm_icon][0]));
  364. oled_set_cursor(0, 12);
  365. oled_write_raw_P(snails[wpm_icon][1], sizeof(snails[wpm_icon][1]));
  366. }
  367. }
  368. // Update WPM graph
  369. static void render_wpm_graph(int current_wpm) {
  370. int line_height = ((current_wpm / graph_top_wpm) * 7);
  371. if (line_height > 7) {
  372. line_height = 7;
  373. }
  374. // Count graph line pixels, return if nothing to draw
  375. int pixel_count = line_height;
  376. for (int i = 0; i < 63; i++) {
  377. pixel_count += graph_lines[i];
  378. }
  379. if (pixel_count == 0) {
  380. return;
  381. }
  382. // Shift array elements left or right depending on graph_direction, append new graph line
  383. if (graph_direction) {
  384. for (int i = 0; i < 63; i++) {
  385. graph_lines[i] = graph_lines[i + 1];
  386. }
  387. graph_lines[63] = line_height;
  388. } else {
  389. for (int i = 63; i > 0; i--) {
  390. graph_lines[i] = graph_lines[i - 1];
  391. }
  392. graph_lines[0] = line_height;
  393. }
  394. // Draw all graph lines (left to right, bottom to top)
  395. int draw_count, arrpos;
  396. for (int x = 1; x <= 127; x += 2) {
  397. arrpos = x / 2;
  398. draw_count = graph_lines[arrpos];
  399. for (int y = 31; y >= 25; y--) {
  400. if (draw_count > 0) {
  401. write_pixel(x, y, true);
  402. draw_count--;
  403. } else {
  404. write_pixel(x, y, false);
  405. }
  406. }
  407. }
  408. }
  409. // Call OLED functions
  410. void oled_task_user(void) {
  411. // Draw OLED keyboard, prevent redraw
  412. if (first_loop) {
  413. render_background();
  414. render_fn_row();
  415. if (ansi_layout == false) {
  416. render_iso();
  417. }
  418. first_loop = false;
  419. }
  420. // Get current WPM, subtract 25% for accuracy and prevent large jumps caused by simultaneous keypresses
  421. int current_wpm = get_current_wpm();
  422. // Note: This will most likely be removed once QMK's WPM calculation is updated
  423. current_wpm -= current_wpm >> 2;
  424. if (current_wpm > wpm_limit) {
  425. current_wpm = max_wpm;
  426. set_current_wpm(max_wpm);
  427. }
  428. // Write active layer name to display
  429. render_layer_state();
  430. // Update WPM counters
  431. render_wpm_counters(current_wpm);
  432. // Update WPM snail icon
  433. render_wpm_icon(current_wpm);
  434. // Update WPM graph every graph_refresh milliseconds
  435. if (timer_elapsed(timer) > graph_refresh) {
  436. render_wpm_graph(current_wpm);
  437. timer = timer_read();
  438. }
  439. }
  440. #endif
  441. // Called by QMK during key processing
  442. bool process_record_user(uint16_t keycode, keyrecord_t *record) {
  443. // Forwards keystrokes from an external input device over UART/TRRS
  444. process_record_remote_kb(keycode, record);
  445. #ifdef OLED_ENABLE
  446. // Toggle pixels surrounding key
  447. render_keymap(record->event.key.row, record->event.key.col, record->event.pressed);
  448. #endif
  449. return true;
  450. }
  451. // Rotary encoder - RGB and OLED settings
  452. void change_RGB(bool clockwise) {
  453. // While on any layer except default: // Rotary = RGB Mode
  454. bool shift = get_mods() & MOD_MASK_SHIFT; // Rotary + Shift = OLED Brightness
  455. bool ctrl = get_mods() & MOD_MASK_CTRL; // Rotary + Ctrl = RGB Brightness
  456. bool gui = get_mods() & MOD_MASK_GUI; // Rotary + Gui = RGB Saturation
  457. bool alt = get_mods() & MOD_MASK_ALT; // Rotary + Alt = RGB Hue
  458. if (clockwise) {
  459. if (shift) {
  460. int new_brightness = oled_get_brightness() + 10;
  461. if (new_brightness < 255) {
  462. oled_set_brightness(new_brightness);
  463. } else {
  464. oled_set_brightness(255);
  465. }
  466. } else if (ctrl) {
  467. rgblight_increase_val();
  468. } else if (gui) {
  469. rgblight_increase_sat();
  470. } else if (alt) {
  471. rgblight_increase_hue();
  472. } else {
  473. rgblight_step();
  474. }
  475. } else {
  476. if (shift) {
  477. int new_brightness = oled_get_brightness() - 10;
  478. if (new_brightness > 0) {
  479. oled_set_brightness(new_brightness);
  480. } else {
  481. oled_set_brightness(0);
  482. }
  483. } else if (ctrl) {
  484. rgblight_decrease_val();
  485. } else if (gui) {
  486. rgblight_decrease_sat();
  487. } else if (alt) {
  488. rgblight_decrease_hue();
  489. } else {
  490. rgblight_step_reverse();
  491. }
  492. }
  493. }
  494. // Rotary encoder behavior - Change volume on default layer, RGB/OLED on other layers
  495. bool encoder_update_user(uint8_t index, bool clockwise) {
  496. if (layer_state_is(0)) {
  497. if (clockwise) {
  498. tap_code(KC_VOLU);
  499. } else {
  500. tap_code(KC_VOLD);
  501. }
  502. } else {
  503. change_RGB(clockwise);
  504. }
  505. return true;
  506. }
  507. // Initialize remote keyboard, if connected
  508. void matrix_init_user(void) {
  509. matrix_init_remote_kb();
  510. }
  511. // Scan and parse keystrokes from remote keyboard, if connected
  512. void matrix_scan_user(void) {
  513. matrix_scan_remote_kb();
  514. }