theme_djinn_default.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. // Copyright 2018-2022 Nick Brassel (@tzarc)
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <hal.h>
  4. #include <string.h>
  5. #include <ctype.h>
  6. #include <printf.h>
  7. #include "qp.h"
  8. #include "backlight.h"
  9. #include "transactions.h"
  10. #include "split_util.h"
  11. #include "djinn.h"
  12. #include "theme_djinn_default.h"
  13. #include "djinn.qgf.h"
  14. #include "lock-caps-ON.qgf.h"
  15. #include "lock-scrl-ON.qgf.h"
  16. #include "lock-num-ON.qgf.h"
  17. #include "lock-caps-OFF.qgf.h"
  18. #include "lock-scrl-OFF.qgf.h"
  19. #include "lock-num-OFF.qgf.h"
  20. #include "thintel15.qff.h"
  21. static painter_image_handle_t djinn_logo;
  22. static painter_image_handle_t lock_caps_on;
  23. static painter_image_handle_t lock_caps_off;
  24. static painter_image_handle_t lock_num_on;
  25. static painter_image_handle_t lock_num_off;
  26. static painter_image_handle_t lock_scrl_on;
  27. static painter_image_handle_t lock_scrl_off;
  28. static painter_font_handle_t thintel;
  29. //----------------------------------------------------------
  30. // UI Initialisation
  31. void keyboard_post_init_display(void) {
  32. djinn_logo = qp_load_image_mem(gfx_djinn);
  33. lock_caps_on = qp_load_image_mem(gfx_lock_caps_ON);
  34. lock_caps_off = qp_load_image_mem(gfx_lock_caps_OFF);
  35. lock_num_on = qp_load_image_mem(gfx_lock_num_ON);
  36. lock_num_off = qp_load_image_mem(gfx_lock_num_OFF);
  37. lock_scrl_on = qp_load_image_mem(gfx_lock_scrl_ON);
  38. lock_scrl_off = qp_load_image_mem(gfx_lock_scrl_OFF);
  39. thintel = qp_load_font_mem(font_thintel15);
  40. }
  41. //----------------------------------------------------------
  42. // UI Drawing
  43. void draw_ui_user(bool force_redraw) {
  44. bool hue_redraw = force_redraw;
  45. static uint16_t last_hue = 0xFFFF;
  46. #if defined(RGB_MATRIX_ENABLE)
  47. uint16_t curr_hue = rgb_matrix_get_hue();
  48. #else // defined(RGB_MATRIX_ENABLE)
  49. uint16_t curr_hue = 0;
  50. #endif // defined(RGB_MATRIX_ENABLE)
  51. if (last_hue != curr_hue) {
  52. last_hue = curr_hue;
  53. hue_redraw = true;
  54. }
  55. bool layer_state_redraw = false;
  56. static uint32_t last_layer_state = 0;
  57. if (last_layer_state != layer_state) {
  58. last_layer_state = layer_state;
  59. layer_state_redraw = true;
  60. }
  61. bool power_state_redraw = false;
  62. static usbpd_allowance_t last_current_state = (usbpd_allowance_t)(~0);
  63. if (last_current_state != kb_state.current_setting) {
  64. last_current_state = kb_state.current_setting;
  65. power_state_redraw = true;
  66. }
  67. bool scan_redraw = false;
  68. static uint32_t last_scan_update = 0;
  69. if (timer_elapsed32(last_scan_update) > 125) {
  70. last_scan_update = timer_read32();
  71. scan_redraw = true;
  72. }
  73. bool wpm_redraw = false;
  74. static uint32_t last_wpm_update = 0;
  75. if (timer_elapsed32(last_wpm_update) > 125) {
  76. last_wpm_update = timer_read32();
  77. wpm_redraw = true;
  78. }
  79. #if defined(RGB_MATRIX_ENABLE)
  80. bool rgb_effect_redraw = false;
  81. static uint16_t last_effect = 0xFFFF;
  82. uint8_t curr_effect = rgb_matrix_config.mode;
  83. if (last_effect != curr_effect) {
  84. last_effect = curr_effect;
  85. rgb_effect_redraw = true;
  86. }
  87. #endif // defined(RGB_MATRIX_ENABLE)
  88. // Show the Djinn logo and two vertical bars on both sides
  89. if (hue_redraw) {
  90. qp_drawimage_recolor(lcd, 120 - djinn_logo->width / 2, 32, djinn_logo, curr_hue, 255, 255, curr_hue, 255, 0);
  91. qp_rect(lcd, 0, 0, 8, 319, curr_hue, 255, 255, true);
  92. qp_rect(lcd, 231, 0, 239, 319, curr_hue, 255, 255, true);
  93. }
  94. int ypos = 4;
  95. // Show layer info on the left side
  96. if (is_keyboard_left()) {
  97. char buf[64] = {0};
  98. int xpos = 16;
  99. #if defined(RGB_MATRIX_ENABLE)
  100. if (hue_redraw || rgb_effect_redraw) {
  101. static int max_rgb_xpos = 0;
  102. xpos = 16;
  103. snprintf(buf, sizeof(buf), "rgb: %s", rgb_matrix_get_mode_name(curr_effect));
  104. for (int i = 5; i < sizeof(buf); ++i) {
  105. if (buf[i] == 0)
  106. break;
  107. else if (buf[i] == '_')
  108. buf[i] = ' ';
  109. else if (buf[i - 1] == ' ')
  110. buf[i] = toupper(buf[i]);
  111. else if (buf[i - 1] != ' ')
  112. buf[i] = tolower(buf[i]);
  113. }
  114. xpos += qp_drawtext_recolor(lcd, xpos, ypos, thintel, buf, curr_hue, 255, 255, curr_hue, 255, 0);
  115. if (max_rgb_xpos < xpos) {
  116. max_rgb_xpos = xpos;
  117. }
  118. qp_rect(lcd, xpos, ypos, max_rgb_xpos, ypos + thintel->line_height, 0, 0, 0, true);
  119. }
  120. ypos += thintel->line_height + 4;
  121. #endif // defined(RGB_MATRIX_ENABLE)
  122. if (hue_redraw || layer_state_redraw) {
  123. extern const char *current_layer_name(void);
  124. const char *layer_name = current_layer_name();
  125. static int max_layer_xpos = 0;
  126. xpos = 16;
  127. snprintf(buf, sizeof(buf), "layer: %s", layer_name);
  128. xpos += qp_drawtext_recolor(lcd, xpos, ypos, thintel, buf, curr_hue, 255, 255, curr_hue, 255, 0);
  129. if (max_layer_xpos < xpos) {
  130. max_layer_xpos = xpos;
  131. }
  132. qp_rect(lcd, xpos, ypos, max_layer_xpos, ypos + thintel->line_height, 0, 0, 0, true);
  133. }
  134. ypos += thintel->line_height + 4;
  135. if (hue_redraw || power_state_redraw) {
  136. static int max_power_xpos = 0;
  137. xpos = 16;
  138. snprintf(buf, sizeof(buf), "power: %s", usbpd_str(kb_state.current_setting));
  139. xpos += qp_drawtext_recolor(lcd, xpos, ypos, thintel, buf, curr_hue, 255, 255, curr_hue, 255, 0);
  140. if (max_power_xpos < xpos) {
  141. max_power_xpos = xpos;
  142. }
  143. qp_rect(lcd, xpos, ypos, max_power_xpos, ypos + thintel->line_height, 0, 0, 0, true);
  144. }
  145. ypos += thintel->line_height + 4;
  146. if (hue_redraw || scan_redraw) {
  147. static int max_scans_xpos = 0;
  148. xpos = 16;
  149. snprintf(buf, sizeof(buf), "scans: %d", (int)theme_state.scan_rate);
  150. xpos += qp_drawtext_recolor(lcd, xpos, ypos, thintel, buf, curr_hue, 255, 255, curr_hue, 255, 0);
  151. if (max_scans_xpos < xpos) {
  152. max_scans_xpos = xpos;
  153. }
  154. qp_rect(lcd, xpos, ypos, max_scans_xpos, ypos + thintel->line_height, 0, 0, 0, true);
  155. }
  156. ypos += thintel->line_height + 4;
  157. if (hue_redraw || wpm_redraw) {
  158. static int max_wpm_xpos = 0;
  159. xpos = 16;
  160. snprintf(buf, sizeof(buf), "wpm: %d", (int)get_current_wpm());
  161. xpos += qp_drawtext_recolor(lcd, xpos, ypos, thintel, buf, curr_hue, 255, 255, curr_hue, 255, 0);
  162. if (max_wpm_xpos < xpos) {
  163. max_wpm_xpos = xpos;
  164. }
  165. qp_rect(lcd, xpos, ypos, max_wpm_xpos, ypos + thintel->line_height, 0, 0, 0, true);
  166. }
  167. ypos += thintel->line_height + 4;
  168. }
  169. // Show LED lock indicators on the right side
  170. if (!is_keyboard_left()) {
  171. static led_t last_led_state = {0};
  172. if (hue_redraw || last_led_state.raw != host_keyboard_led_state().raw) {
  173. last_led_state.raw = host_keyboard_led_state().raw;
  174. qp_drawimage_recolor(lcd, 239 - 12 - (32 * 3), 0, last_led_state.caps_lock ? lock_caps_on : lock_caps_off, curr_hue, 255, last_led_state.caps_lock ? 255 : 32, curr_hue, 255, 0);
  175. qp_drawimage_recolor(lcd, 239 - 12 - (32 * 2), 0, last_led_state.num_lock ? lock_num_on : lock_num_off, curr_hue, 255, last_led_state.num_lock ? 255 : 32, curr_hue, 255, 0);
  176. qp_drawimage_recolor(lcd, 239 - 12 - (32 * 1), 0, last_led_state.scroll_lock ? lock_scrl_on : lock_scrl_off, curr_hue, 255, last_led_state.scroll_lock ? 255 : 32, curr_hue, 255, 0);
  177. }
  178. }
  179. }
  180. //----------------------------------------------------------
  181. // Sync
  182. theme_runtime_config theme_state;
  183. void rpc_theme_sync_callback(uint8_t m2s_size, const void *m2s_buffer, uint8_t s2m_size, void *s2m_buffer) {
  184. if (m2s_size == sizeof(theme_state)) {
  185. memcpy(&theme_state, m2s_buffer, m2s_size);
  186. }
  187. }
  188. void theme_init(void) {
  189. // Register keyboard state sync split transaction
  190. transaction_register_rpc(THEME_DATA_SYNC, rpc_theme_sync_callback);
  191. // Reset the initial shared data value between master and slave
  192. memset(&theme_state, 0, sizeof(theme_state));
  193. }
  194. void theme_state_update(void) {
  195. if (is_keyboard_master()) {
  196. // Keep the scan rate in sync
  197. theme_state.scan_rate = get_matrix_scan_rate();
  198. }
  199. }
  200. void theme_state_sync(void) {
  201. if (!is_transport_connected()) return;
  202. if (is_keyboard_master()) {
  203. // Keep track of the last state, so that we can tell if we need to propagate to slave
  204. static theme_runtime_config last_theme_state;
  205. static uint32_t last_sync;
  206. bool needs_sync = false;
  207. // Check if the state values are different
  208. if (memcmp(&theme_state, &last_theme_state, sizeof(theme_runtime_config))) {
  209. needs_sync = true;
  210. memcpy(&last_theme_state, &theme_state, sizeof(theme_runtime_config));
  211. }
  212. // Send to slave every 125ms regardless of state change
  213. if (timer_elapsed32(last_sync) > 125) {
  214. needs_sync = true;
  215. }
  216. // Perform the sync if requested
  217. if (needs_sync) {
  218. if (transaction_rpc_send(THEME_DATA_SYNC, sizeof(theme_runtime_config), &theme_state)) {
  219. last_sync = timer_read32();
  220. } else {
  221. dprint("Failed to perform rpc call\n");
  222. }
  223. }
  224. }
  225. }