keymap.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. /*
  2. Copyright 2020 Seth Bonner <fl3tching101@gmail.com>
  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. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. #include QMK_KEYBOARD_H
  15. #include "keycode_lookup.h"
  16. #include <string.h>
  17. #ifdef CONSOLE_ENABLE
  18. #include "print.h"
  19. #endif
  20. // Each layer gets a name for readability, which is then used in the keymap matrix below.
  21. // The underscores don't mean anything - you can have a layer called STUFF or any other name.
  22. // Layer names don't all need to be of the same length, obviously, and you can also skip them
  23. // entirely and just use numbers.
  24. #define _BASE 0
  25. #define _SPEC 1 // Special layer
  26. // Use the following format to create custom key codes to make macros out of and such
  27. enum custom_keycodes {
  28. KC_EXAM = SAFE_RANGE // "Examine" key code to show the keycode of a key pressed afterwards on the OLED
  29. };
  30. const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  31. [_BASE] = LAYOUT(
  32. RGB_MOD, KC_NLCK,
  33. KC_P7, KC_P8, KC_P9, KC_DEL,
  34. KC_P4, KC_P5, KC_P6, KC_END,
  35. KC_P1, KC_P2, KC_P3, KC_F13,
  36. KC_P0, MO(1), KC_PDOT, KC_PENT
  37. ),
  38. [_SPEC] = LAYOUT(
  39. RGB_RMOD, KC_MUTE,
  40. KC_NO, KC_NO, KC_NO, KC_EXAM,
  41. KC_NO, KC_NO, KC_NO, KC_NO,
  42. QK_BOOT, RGB_TOG, RGB_SPI, RGB_SPD,
  43. KC_NO, _______, KC_NO, KC_NO
  44. )
  45. };
  46. bool encoder_update_user(uint8_t index, bool clockwise){
  47. if(index == 0) { // first encoder
  48. if(clockwise){
  49. tap_code(KC_AUDIO_VOL_UP);
  50. }else{
  51. tap_code(KC_AUDIO_VOL_DOWN);
  52. }
  53. }else if(index == 1){ // second encoder
  54. if(clockwise){
  55. rgblight_increase_val();
  56. }else{
  57. rgblight_decrease_val();
  58. }
  59. }
  60. return true;
  61. }
  62. #ifdef OLED_ENABLE
  63. #define ANIM_FRAMES 3
  64. #define ANIM_FRAME_DURATION 110 // Number of milliseconds per frame (no faster than 110ms, last line struggles)
  65. #define BACKGROUND_FRAMES 21
  66. #define ROCKET_CENTER_POS 3
  67. #define SPLASH_DUR 100 // Measured in frames, see above for frame length (note, 231 is used as a key value later on, CTRL+F for uses of this to make sure everything is good)
  68. uint32_t anim_timer = 0;
  69. uint8_t current_frame = 0;
  70. uint8_t rocket_y_position = 3;
  71. uint8_t rocket_pos_change = 0;
  72. uint8_t background_frame = 0;
  73. uint8_t splash_dur_counter = 0;
  74. bool examine_engaged = false;
  75. uint16_t examined_keycode = KC_NO;
  76. char lastKeycodeString[32] = { 0 };
  77. const char star_background [8] [21] =
  78. {
  79. {0x88, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x00, 0x93},
  80. {0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00},
  81. {0x00, 0x8F, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
  82. {0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00},
  83. {0x8D, 0x00, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x8B, 0x00, 0x00, 0x00, 0x00},
  84. {0x00, 0x8A, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00},
  85. {0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x8F, 0x00, 0x89, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x8F},
  86. {0x00, 0x8B, 0x00, 0x00, 0x91, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x90, 0x00, 0x00, 0x8C, 0x00, 0x00},
  87. };
  88. static void oled_write_ln_centered(const char * data, bool inverted)
  89. {
  90. if(strlen(data) >= 21) // If more than 1 line of text is passed in, return without doing anything
  91. {
  92. return;
  93. }
  94. // Character buffer to build up the string in
  95. char line_buf[21];
  96. // Amount to offset string from left side
  97. uint8_t offset = (22 - strlen(data)) / 2;
  98. // Formatted string centering... look, it works, don't ask how...
  99. snprintf(line_buf, 21, "%*s%s%*s", offset, "", data, offset, ""); // Centers data within 21 character buffer
  100. oled_write_ln(line_buf, inverted);
  101. }
  102. // Prints the exhaust characters in an order determined by the phase for animation purposes
  103. // startX - The x axis starting point in characters for the exhaust (3 behind the rocket)
  104. // startY - The y axis starting point in characters for the exhaust (middle of the rocket)
  105. // phase - The "phase" of the animation, no real rhyme or reason to the exact number, but each frame move +1 to make the animation work
  106. static void render_exhaust(uint8_t startX, uint8_t startY, uint8_t phase)
  107. {
  108. oled_set_cursor(startX, startY);
  109. oled_write_char(0x85 + (phase % 3), false);
  110. phase++;
  111. oled_write_char(0x85 + (phase % 3), false);
  112. phase++;
  113. oled_write_char(0x85 + (phase % 3), false);
  114. }
  115. // Renders the "stars" behind the rocket
  116. // startY - The starting Y location (in characters) of the rocket so that stars aren't rendered on top of the rocket
  117. static void render_stars(uint8_t startY, uint8_t phase)
  118. {
  119. // Line 0
  120. oled_set_cursor(0, 0);
  121. for(int i = 0; i < 21; i++)
  122. {
  123. oled_write_char(star_background[0][(i + phase) % 21], false);
  124. }
  125. // Line 1
  126. oled_set_cursor(0, 1);
  127. for(int i = 0; i < 21; i++)
  128. {
  129. oled_write_char(star_background[1][(i + phase) % 21], false);
  130. }
  131. // Line 2
  132. oled_set_cursor(0, 2);
  133. for(int i = 0; i < 21; i++)
  134. {
  135. oled_write_char(star_background[2][(i + phase) % 21], false);
  136. }
  137. // Line 3
  138. oled_set_cursor(0, 3);
  139. for(int i = 0; i < 21; i++)
  140. {
  141. oled_write_char(star_background[3][(i + phase) % 21], false);
  142. }
  143. // Line 4
  144. oled_set_cursor(0, 4);
  145. for(int i = 0; i < 21; i++)
  146. {
  147. oled_write_char(star_background[4][(i + phase) % 21], false);
  148. }
  149. // Line 5
  150. oled_set_cursor(0, 5);
  151. for(int i = 0; i < 21; i++)
  152. {
  153. oled_write_char(star_background[5][(i + phase) % 21], false);
  154. }
  155. // Line 6
  156. oled_set_cursor(0, 6);
  157. for(int i = 0; i < 21; i++)
  158. {
  159. oled_write_char(star_background[6][(i + phase) % 21], false);
  160. }
  161. // Line 7
  162. oled_set_cursor(0, 7);
  163. for(int i = 0; i < 21; i++)
  164. {
  165. oled_write_char(star_background[7][(i + phase) % 21], false);
  166. }
  167. }
  168. static void render_logo(uint8_t startX, uint8_t startY)
  169. {
  170. oled_set_cursor(startX, startY);
  171. oled_write_char(0x80, false);
  172. oled_write_char(0x81, false);
  173. oled_write_char(0x82, false);
  174. oled_write_char(0x83, false);
  175. oled_write_char(0x84, false);
  176. oled_set_cursor(startX, startY + 1);
  177. oled_write_char(0xA0, false);
  178. oled_write_char(0xA1, false);
  179. oled_write_char(0xA2, false);
  180. oled_write_char(0xA3, false);
  181. oled_write_char(0xA4, false);
  182. oled_write_char(0xA5, false);
  183. oled_set_cursor(startX, startY + 2);
  184. oled_write_char(0xC0, false);
  185. oled_write_char(0xC1, false);
  186. oled_write_char(0xC2, false);
  187. oled_write_char(0xC3, false);
  188. oled_write_char(0xC4, false);
  189. }
  190. oled_rotation_t oled_init_user(oled_rotation_t rotation) { return OLED_ROTATION_180; }
  191. bool oled_task_user(void)
  192. {
  193. // Playing the animation
  194. if((timer_elapsed32(anim_timer) > ANIM_FRAME_DURATION) && (splash_dur_counter < SPLASH_DUR))
  195. {
  196. anim_timer = timer_read32(); // read the current timer value
  197. current_frame = (current_frame + 1) % ANIM_FRAMES; // Frame in the exhaust animation
  198. background_frame = (background_frame + 1) % BACKGROUND_FRAMES; // Frame in the star animation
  199. // Move the rocket up and down
  200. if((rocket_pos_change / 9) == 0)
  201. {
  202. rocket_y_position = ROCKET_CENTER_POS;
  203. }
  204. else if((rocket_pos_change / 9) == 1)
  205. {
  206. rocket_y_position = ROCKET_CENTER_POS + 1;
  207. }
  208. else if((rocket_pos_change / 9) == 2)
  209. {
  210. rocket_y_position = ROCKET_CENTER_POS;
  211. }
  212. if((rocket_pos_change / 9) == 3)
  213. {
  214. rocket_y_position = ROCKET_CENTER_POS - 1;
  215. }
  216. // Renders the scene piece by piece
  217. render_stars(8, background_frame); // Render star background
  218. render_exhaust(6, rocket_y_position + 1, current_frame); // Render exhaust
  219. render_logo(9, rocket_y_position); // Render the rocket
  220. // Timing for rocket position change
  221. if(rocket_pos_change < 36)
  222. {
  223. rocket_pos_change++;
  224. }
  225. else
  226. {
  227. rocket_pos_change = 0;
  228. }
  229. splash_dur_counter++;
  230. }
  231. else if((splash_dur_counter >= SPLASH_DUR) && (splash_dur_counter != 231)) // Should only run once at end of splash screen duration
  232. {
  233. splash_dur_counter = 231; // Nice known value
  234. oled_clear(); // Clear the screen
  235. }
  236. // After the splash screen
  237. if(splash_dur_counter == 231)
  238. {
  239. uint8_t light_level = rgblight_get_val();
  240. light_level = (uint8_t)(100.0 * ((float)light_level/(float)RGBLIGHT_LIMIT_VAL)); // Convert to %
  241. char c_light_level[3];
  242. itoa(light_level, c_light_level, 10);
  243. // Display lock LED statuses
  244. led_t led_state = host_keyboard_led_state();
  245. if(led_state.num_lock)
  246. {
  247. oled_write(PSTR(" |"), false);
  248. oled_write(PSTR("NUM"), true);
  249. oled_write(PSTR("|"), false);
  250. }
  251. else
  252. {
  253. oled_write(PSTR(" |NUM|"), false);
  254. }
  255. if(led_state.caps_lock)
  256. {
  257. oled_write(PSTR("|"), false);
  258. oled_write(PSTR("CAP"), true);
  259. oled_write(PSTR("|"), false);
  260. }
  261. else
  262. {
  263. oled_write(PSTR("|CAP|"), false);
  264. }
  265. if(led_state.scroll_lock)
  266. {
  267. oled_write(PSTR("|"), false);
  268. oled_write(PSTR("SCR"), true);
  269. oled_write(PSTR("| "), false);
  270. }
  271. else
  272. {
  273. oled_write(PSTR("|SCR| "), false);
  274. }
  275. // Print the examine info
  276. if(examine_engaged == true)
  277. {
  278. oled_set_cursor(0, 2);
  279. oled_write_ln(PSTR(" Keycode: "), false);
  280. oled_write_ln_centered(lastKeycodeString, false);
  281. }
  282. else
  283. {
  284. oled_set_cursor(0, 2);
  285. oled_write_ln(PSTR(" "), false);
  286. oled_write_ln(PSTR(" "), false);
  287. }
  288. // Print the backlight % bottom right
  289. oled_set_cursor(11, 7);
  290. oled_write(PSTR("BKLT: "), false);
  291. oled_write(c_light_level, false);
  292. oled_write(PSTR("%"), false);
  293. // Print the layer number in bottom left
  294. oled_set_cursor(0, 7);
  295. oled_write(PSTR("L: "), false);
  296. switch (get_highest_layer(layer_state))
  297. {
  298. case 0:
  299. oled_write(PSTR("0"), false);
  300. break;
  301. case 1:
  302. oled_write(PSTR("1"), false);
  303. break;
  304. case 2:
  305. oled_write(PSTR("2"), false);
  306. break;
  307. case 3:
  308. oled_write(PSTR("3"), false);
  309. break;
  310. default:
  311. oled_write(PSTR("Und"), false);
  312. break;
  313. }
  314. }
  315. return false;
  316. }
  317. // Process the extra/extended keycode functionality
  318. bool process_record_user(uint16_t keycode, keyrecord_t *record)
  319. {
  320. bool ret = true; // True will allow QMK to process the key as usual after the function runs, false skips QMK processing after this function runs
  321. switch (keycode)
  322. {
  323. case KC_EXAM:
  324. if(record->event.pressed) // On pressed, flip bool examine_engaged
  325. {
  326. if(examine_engaged == false)
  327. {
  328. examine_engaged = true;
  329. }
  330. else
  331. {
  332. examine_engaged = false;
  333. }
  334. ret = false;
  335. }
  336. else // On release do nothing
  337. {
  338. ret = false;
  339. }
  340. break;
  341. default: // For any key other than EX, simply let QMK process after saving away what it was
  342. memset(lastKeycodeString, 0, sizeof(lastKeycodeString));
  343. memcpy(lastKeycodeString, translate_keycode_to_string(keycode), sizeof(((lookup_table_t *)0)->key_string));
  344. ret = true;
  345. break;
  346. }
  347. return ret;
  348. }
  349. #endif