You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

307 lines
11 KiB

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