logo

qmk_firmware

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

keymap.c (15198B)


  1. /* Copyright 2020 Sergey Vlasov <sigprof@gmail.com>
  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 tap_dances {
  18. TD_OLED,
  19. };
  20. enum oled_test_modes {
  21. // Modes between TEST_FIRST and TEST_LAST (inclusive) can be switched with a keypress.
  22. TEST_FIRST,
  23. TEST_LOGO = TEST_FIRST,
  24. TEST_CHARACTERS,
  25. TEST_SLOW_UPDATE,
  26. TEST_ALL_ON,
  27. TEST_FRAME,
  28. TEST_ALL_OFF,
  29. TEST_FILL_HORZ_0,
  30. TEST_FILL_HORZ_1,
  31. TEST_FILL_VERT_0,
  32. TEST_FILL_VERT_1,
  33. TEST_FILL_CHECKERBOARD_1,
  34. TEST_FILL_CHECKERBOARD_2,
  35. TEST_FILL_CHECKERBOARD_4,
  36. TEST_LAST = TEST_FILL_CHECKERBOARD_4,
  37. // Special modes which are not reachable normally.
  38. TEST_DRAW_ALWAYS_ON,
  39. TEST_DRAW_ALWAYS_OFF,
  40. };
  41. static enum oled_test_modes test_mode = TEST_FIRST;
  42. static oled_rotation_t rotation = OLED_ROTATION_0;
  43. static bool scrolling;
  44. static uint8_t scrolling_speed;
  45. static bool need_update = true;
  46. static bool draw_always;
  47. static bool update_speed_test;
  48. static uint32_t update_speed_start_timer;
  49. static uint16_t update_speed_count;
  50. static bool restart_test;
  51. static void stop_scrolling(void) {
  52. if (scrolling) {
  53. oled_scroll_off();
  54. scrolling = false;
  55. }
  56. }
  57. static void dance_oled_finished(tap_dance_state_t *state, void *user_data) {
  58. switch (state->count) {
  59. case 1:
  60. if (state->pressed) {
  61. // single hold - step through rotations
  62. switch (rotation) {
  63. case OLED_ROTATION_0:
  64. rotation = OLED_ROTATION_90;
  65. break;
  66. case OLED_ROTATION_90:
  67. rotation = OLED_ROTATION_180;
  68. break;
  69. case OLED_ROTATION_180:
  70. rotation = OLED_ROTATION_270;
  71. break;
  72. default:
  73. rotation = OLED_ROTATION_0;
  74. break;
  75. }
  76. stop_scrolling();
  77. oled_init(rotation);
  78. } else {
  79. // single tap - step through test modes
  80. if (test_mode < TEST_LAST) {
  81. ++test_mode;
  82. } else {
  83. test_mode = TEST_FIRST;
  84. }
  85. stop_scrolling();
  86. oled_clear();
  87. }
  88. restart_test = true;
  89. need_update = true;
  90. break;
  91. case 2:
  92. if (state->pressed) {
  93. // tap + hold - change scrolling speed
  94. scrolling_speed = (scrolling_speed + 1) % 8;
  95. stop_scrolling();
  96. oled_scroll_set_speed(scrolling_speed);
  97. // Cannot reactivate scrolling here, because oled_scroll_off()
  98. // marks the whole display as dirty, and oled_scroll_left()
  99. // silently does nothing if either the display is dirty or
  100. // scrolling is already active.
  101. } else {
  102. // double tap - toggle scrolling
  103. if (!scrolling) {
  104. scrolling = true;
  105. oled_scroll_left();
  106. } else {
  107. scrolling = false;
  108. oled_scroll_off();
  109. }
  110. }
  111. need_update = true;
  112. break;
  113. case 3:
  114. if (state->pressed) {
  115. // double tap + hold - toggle `draw_always`
  116. draw_always = !draw_always;
  117. if (draw_always) {
  118. test_mode = TEST_DRAW_ALWAYS_ON;
  119. } else {
  120. test_mode = TEST_DRAW_ALWAYS_OFF;
  121. }
  122. stop_scrolling();
  123. oled_clear();
  124. restart_test = true;
  125. need_update = true;
  126. } else {
  127. // triple tap - toggle update speed test
  128. update_speed_test = !update_speed_test;
  129. if (update_speed_test) {
  130. stop_scrolling();
  131. update_speed_start_timer = timer_read32();
  132. update_speed_count = 0;
  133. }
  134. }
  135. break;
  136. case 4:
  137. if (!state->pressed) {
  138. // quadruple tap - step through brightness levels
  139. oled_set_brightness(oled_get_brightness() + 0x10);
  140. }
  141. break;
  142. default:
  143. break;
  144. }
  145. }
  146. tap_dance_action_t tap_dance_actions[] = {[TD_OLED] = ACTION_TAP_DANCE_FN(dance_oled_finished)};
  147. const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {LAYOUT_ortho_1x1(TD(TD_OLED))};
  148. // `bool oled_is_dirty(void)` does not exist at the moment
  149. extern OLED_BLOCK_TYPE oled_dirty;
  150. static inline uint8_t pixel_width(void) {
  151. if (!(rotation & OLED_ROTATION_90)) {
  152. return OLED_DISPLAY_WIDTH;
  153. }
  154. return OLED_DISPLAY_HEIGHT;
  155. }
  156. static inline uint8_t pixel_height(void) {
  157. if (!(rotation & OLED_ROTATION_90)) {
  158. return OLED_DISPLAY_HEIGHT;
  159. }
  160. return OLED_DISPLAY_WIDTH;
  161. }
  162. // Draw the QMK logo at the top left corner, clipping if it does not fit.
  163. static void test_logo(void) {
  164. uint8_t lines = oled_max_lines();
  165. if (lines > 3) {
  166. lines = 3;
  167. }
  168. uint8_t chars = oled_max_chars();
  169. if (chars > 21) {
  170. chars = 21;
  171. }
  172. for (uint8_t row = 0; row < lines; ++row) {
  173. oled_set_cursor(0, row);
  174. for (uint8_t col = 0; col < chars; ++col) {
  175. oled_write_char(0x80 + 0x20 * row + col, false);
  176. }
  177. }
  178. }
  179. static const PROGMEM char fill_ff[OLED_MATRIX_SIZE] = {[0 ... OLED_MATRIX_SIZE - 1] = 0xff};
  180. // Fill the whole screen with a pattern made from two bytes alternating after the specified number of repeats.
  181. static void test_fill(uint8_t byte0, uint8_t byte1, uint8_t repeats) {
  182. uint8_t width = pixel_width();
  183. uint8_t lines = oled_max_lines();
  184. uint16_t index = 0;
  185. for (uint8_t row = 0; row < lines; ++row) {
  186. for (uint8_t col = 0; col < width; ++col) {
  187. uint8_t byte = ((col / repeats) % 2) ? byte1 : byte0;
  188. oled_write_raw_byte(byte, index++);
  189. }
  190. }
  191. }
  192. // Draw a frame at the edges of the OLED screen.
  193. static void test_frame(void) {
  194. uint8_t width = pixel_width();
  195. uint8_t height = pixel_height();
  196. for (uint8_t x = 0; x < width; ++x) {
  197. oled_write_pixel(x, 0, true);
  198. oled_write_pixel(x, height - 1, true);
  199. }
  200. for (uint8_t y = 1; y < height - 1; ++y) {
  201. oled_write_pixel(0, y, true);
  202. oled_write_pixel(width - 1, y, true);
  203. }
  204. }
  205. // Use all 94 visible ASCII characters for testing.
  206. #define TEST_CHAR_COUNT ('~' - '!' + 1)
  207. static char get_test_char(uint8_t char_index) { return char_index + '!'; }
  208. // Fill the whole screen with distinct characters (if the display is large enough to show more than 94 characters
  209. // at once, the sequence is repeated the second time with inverted characters).
  210. static void test_characters(void) {
  211. uint8_t cols = oled_max_chars();
  212. uint8_t rows = oled_max_lines();
  213. bool invert = false;
  214. uint8_t char_index = 0;
  215. for (uint8_t row = 0; row < rows; ++row) {
  216. for (uint8_t col = 0; col < cols; ++col) {
  217. oled_write_char(get_test_char(char_index), invert);
  218. if (++char_index >= TEST_CHAR_COUNT) {
  219. char_index = 0;
  220. invert = !invert;
  221. }
  222. }
  223. }
  224. }
  225. // Test screen updating after drawing a single character or pixel.
  226. void test_slow_update(void) {
  227. static uint8_t phase, x, y, char_index, first_char;
  228. static uint16_t timer;
  229. static uint16_t delay = 500;
  230. if (restart_test) {
  231. // Initialize all state variables before starting the test.
  232. restart_test = false;
  233. phase = 0;
  234. x = 0;
  235. y = 0;
  236. char_index = 0;
  237. first_char = 0;
  238. delay = 500;
  239. } else {
  240. // Wait for the specified time between steps.
  241. if (timer_elapsed(timer) < delay) {
  242. return;
  243. }
  244. }
  245. timer = timer_read();
  246. switch (phase) {
  247. case 0:
  248. // Phase 0: fill the whole screen with mostly distinct characters, one character at a time. Here the
  249. // inversion trick is not used, so that the frame which is drawn in subsequent phases would not be
  250. // overlapped by the inverted character background.
  251. oled_set_cursor(x, y);
  252. oled_write_char(get_test_char(char_index), false);
  253. if (++char_index >= TEST_CHAR_COUNT) {
  254. char_index = 0;
  255. }
  256. if (++x >= oled_max_chars()) {
  257. x = 0;
  258. if (++y >= oled_max_lines()) {
  259. // The whole screen was filled - start the next phase.
  260. ++phase;
  261. x = y = 0;
  262. }
  263. }
  264. delay = 250;
  265. break;
  266. case 1:
  267. // Phase 1: draw a line along the left edge of the screen, one pixel at a time.
  268. oled_write_pixel(x, y, true);
  269. if (y < pixel_height() - 1) {
  270. ++y;
  271. } else {
  272. // The bottom left corner is reached - start the next phase.
  273. ++phase;
  274. ++x;
  275. }
  276. delay = 50;
  277. break;
  278. case 2:
  279. // Phase 2: draw a line along the bottom edge of the screen, one pixel at a time.
  280. oled_write_pixel(x, y, true);
  281. if (x < pixel_width() - 1) {
  282. ++x;
  283. } else {
  284. // The bottom right corner was reached - start the next phase.
  285. ++phase;
  286. --y;
  287. }
  288. delay = 50;
  289. break;
  290. case 3:
  291. // Phase 3: draw a line along the right edge of the screen, one pixel at a time.
  292. oled_write_pixel(x, y, true);
  293. if (y > 0) {
  294. --y;
  295. } else {
  296. // The top right corner was reached - start the next phase.
  297. ++phase;
  298. --x;
  299. }
  300. delay = 50;
  301. break;
  302. case 4:
  303. // Phase 4: draw a line along the top edge of the screen, one pixel at a time.
  304. oled_write_pixel(x, y, true);
  305. if (x > 0) {
  306. --x;
  307. } else {
  308. // The top left corner was reached - start the next phase.
  309. ++phase;
  310. }
  311. delay = 50;
  312. break;
  313. default:
  314. // Restart from phase 0, but change the first character of the sequence to make screen updates visible.
  315. if (++first_char >= TEST_CHAR_COUNT) {
  316. first_char = 0;
  317. }
  318. phase = 0;
  319. x = 0;
  320. y = 0;
  321. char_index = first_char;
  322. delay = 500;
  323. break;
  324. }
  325. }
  326. oled_rotation_t oled_init_user(oled_rotation_t rotation) {
  327. oled_scroll_set_area(0, 0);
  328. oled_scroll_set_speed(scrolling_speed);
  329. return rotation;
  330. }
  331. bool oled_task_user(void) {
  332. if (update_speed_test) {
  333. // Speed test mode - wait for screen update completion.
  334. if (!oled_dirty) {
  335. // Update statistics and send the measurement result to the console.
  336. update_speed_count++;
  337. if (update_speed_count % 256 == 0) {
  338. uprintf("OLED: %u updates, %lu ms\n", update_speed_count, timer_elapsed32(update_speed_start_timer));
  339. }
  340. // Toggle between the "all on" and "all off" states and trigger the screen update again.
  341. if (test_mode == TEST_ALL_ON) {
  342. test_mode = TEST_ALL_OFF;
  343. } else {
  344. test_mode = TEST_ALL_ON;
  345. }
  346. need_update = true;
  347. }
  348. }
  349. // The sample implementation of oled_task_user() in the documentation redraws the image after every call, relying on
  350. // the fact that drawing functions check whether the output actually changes anything in the image, and set dirty
  351. // bits only when something has actually changed. However, redrawing the image only when some of the underlying
  352. // data has changed is more efficient. Make it possible to test both modes here.
  353. if (!draw_always || update_speed_test) {
  354. // Draw the image only when the `need_update` flag is set, except for the "slow update" test.
  355. // This mode is also forced when the screen update speed test is performed.
  356. if (!need_update) {
  357. if (test_mode != TEST_SLOW_UPDATE) {
  358. return false;
  359. }
  360. }
  361. need_update = false;
  362. }
  363. switch (test_mode) {
  364. case TEST_LOGO:
  365. test_logo();
  366. break;
  367. case TEST_CHARACTERS:
  368. test_characters();
  369. break;
  370. case TEST_SLOW_UPDATE:
  371. test_slow_update();
  372. break;
  373. case TEST_ALL_ON:
  374. oled_write_raw_P(fill_ff, sizeof(fill_ff));
  375. break;
  376. case TEST_FRAME:
  377. test_frame();
  378. break;
  379. case TEST_ALL_OFF:
  380. // `oled_clear()` is faster, but cannot be used with `draw_always`, because it does not check the previous
  381. // content of the buffer and always marks the whole buffer as dirty.
  382. if (update_speed_test) {
  383. oled_clear();
  384. } else {
  385. test_fill(0x00, 0x00, 1);
  386. }
  387. break;
  388. case TEST_FILL_HORZ_0:
  389. test_fill(0x55, 0x55, 1);
  390. break;
  391. case TEST_FILL_HORZ_1:
  392. test_fill(0xaa, 0xaa, 1);
  393. break;
  394. case TEST_FILL_VERT_0:
  395. test_fill(0xff, 0x00, 1);
  396. break;
  397. case TEST_FILL_VERT_1:
  398. test_fill(0x00, 0xff, 1);
  399. break;
  400. case TEST_FILL_CHECKERBOARD_1:
  401. test_fill(0x55, 0xaa, 1);
  402. break;
  403. case TEST_FILL_CHECKERBOARD_2:
  404. test_fill(0x33, 0xcc, 2);
  405. break;
  406. case TEST_FILL_CHECKERBOARD_4:
  407. test_fill(0x0f, 0xf0, 4);
  408. break;
  409. case TEST_DRAW_ALWAYS_ON:
  410. oled_write_P(PSTR("Draw Always"), false);
  411. break;
  412. case TEST_DRAW_ALWAYS_OFF:
  413. oled_write_P(PSTR("Draw Once"), false);
  414. break;
  415. }
  416. return false;
  417. }
  418. void keyboard_post_init_user(void) {
  419. // Console messages are used for update speed test results
  420. debug_enable = true;
  421. }