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