aboutsummaryrefslogtreecommitdiff
path: root/quantum/visualizer/visualizer.c
diff options
context:
space:
mode:
Diffstat (limited to 'quantum/visualizer/visualizer.c')
-rw-r--r--quantum/visualizer/visualizer.c226
1 files changed, 47 insertions, 179 deletions
diff --git a/quantum/visualizer/visualizer.c b/quantum/visualizer/visualizer.c
index 5826d909e..6f134097f 100644
--- a/quantum/visualizer/visualizer.c
+++ b/quantum/visualizer/visualizer.c
@@ -48,20 +48,22 @@ SOFTWARE.
48#include "serial_link/system/serial_link.h" 48#include "serial_link/system/serial_link.h"
49#endif 49#endif
50 50
51#include "action_util.h"
52
51// Define this in config.h 53// Define this in config.h
52#ifndef VISUALIZER_THREAD_PRIORITY 54#ifndef VISUALIZER_THREAD_PRIORITY
53#define "Visualizer thread priority not defined" 55#define "Visualizer thread priority not defined"
54#endif 56#endif
55 57
56// mods status
57#include "action_util.h"
58
59static visualizer_keyboard_status_t current_status = { 58static visualizer_keyboard_status_t current_status = {
60 .layer = 0xFFFFFFFF, 59 .layer = 0xFFFFFFFF,
61 .default_layer = 0xFFFFFFFF, 60 .default_layer = 0xFFFFFFFF,
62 .mods = 0xFF, 61 .mods = 0xFF,
63 .leds = 0xFFFFFFFF, 62 .leds = 0xFFFFFFFF,
64 .suspended = false, 63 .suspended = false,
64#ifdef VISUALIZER_USER_DATA_SIZE
65 .user_data = {0}
66#endif
65}; 67};
66 68
67static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboard_status_t* status2) { 69static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboard_status_t* status2) {
@@ -69,11 +71,19 @@ static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboa
69 status1->default_layer == status2->default_layer && 71 status1->default_layer == status2->default_layer &&
70 status1->mods == status2->mods && 72 status1->mods == status2->mods &&
71 status1->leds == status2->leds && 73 status1->leds == status2->leds &&
72 status1->suspended == status2->suspended; 74 status1->suspended == status2->suspended
75#ifdef VISUALIZER_USER_DATA_SIZE
76 && memcmp(status1->user_data, status2->user_data, VISUALIZER_USER_DATA_SIZE) == 0
77#endif
78 ;
73} 79}
74 80
75static bool visualizer_enabled = false; 81static bool visualizer_enabled = false;
76 82
83#ifdef VISUALIZER_USER_DATA_SIZE
84static uint8_t user_data[VISUALIZER_USER_DATA_SIZE];
85#endif
86
77#define MAX_SIMULTANEOUS_ANIMATIONS 4 87#define MAX_SIMULTANEOUS_ANIMATIONS 4
78static keyframe_animation_t* animations[MAX_SIMULTANEOUS_ANIMATIONS] = {}; 88static keyframe_animation_t* animations[MAX_SIMULTANEOUS_ANIMATIONS] = {};
79 89
@@ -144,6 +154,14 @@ void stop_all_keyframe_animations(void) {
144 } 154 }
145} 155}
146 156
157static uint8_t get_num_running_animations(void) {
158 uint8_t count = 0;
159 for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
160 count += animations[i] ? 1 : 0;
161 }
162 return count;
163}
164
147static bool update_keyframe_animation(keyframe_animation_t* animation, visualizer_state_t* state, systemticks_t delta, systemticks_t* sleep_time) { 165static bool update_keyframe_animation(keyframe_animation_t* animation, visualizer_state_t* state, systemticks_t delta, systemticks_t* sleep_time) {
148 // TODO: Clean up this messy code 166 // TODO: Clean up this messy code
149 dprintf("Animation frame%d, left %d, delta %d\n", animation->current_frame, 167 dprintf("Animation frame%d, left %d, delta %d\n", animation->current_frame,
@@ -212,175 +230,6 @@ void run_next_keyframe(keyframe_animation_t* animation, visualizer_state_t* stat
212 (*temp_animation.frame_functions[next_frame])(&temp_animation, &temp_state); 230 (*temp_animation.frame_functions[next_frame])(&temp_animation, &temp_state);
213} 231}
214 232
215bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* state) {
216 (void)animation;
217 (void)state;
218 return false;
219}
220
221#ifdef LCD_BACKLIGHT_ENABLE
222bool keyframe_animate_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state) {
223 int frame_length = animation->frame_lengths[animation->current_frame];
224 int current_pos = frame_length - animation->time_left_in_frame;
225 uint8_t t_h = LCD_HUE(state->target_lcd_color);
226 uint8_t t_s = LCD_SAT(state->target_lcd_color);
227 uint8_t t_i = LCD_INT(state->target_lcd_color);
228 uint8_t p_h = LCD_HUE(state->prev_lcd_color);
229 uint8_t p_s = LCD_SAT(state->prev_lcd_color);
230 uint8_t p_i = LCD_INT(state->prev_lcd_color);
231
232 uint8_t d_h1 = t_h - p_h; //Modulo arithmetic since we want to wrap around
233 int d_h2 = t_h - p_h;
234 // Chose the shortest way around
235 int d_h = abs(d_h2) < d_h1 ? d_h2 : d_h1;
236 int d_s = t_s - p_s;
237 int d_i = t_i - p_i;
238
239 int hue = (d_h * current_pos) / frame_length;
240 int sat = (d_s * current_pos) / frame_length;
241 int intensity = (d_i * current_pos) / frame_length;
242 //dprintf("%X -> %X = %X\n", p_h, t_h, hue);
243 hue += p_h;
244 sat += p_s;
245 intensity += p_i;
246 state->current_lcd_color = LCD_COLOR(hue, sat, intensity);
247 lcd_backlight_color(
248 LCD_HUE(state->current_lcd_color),
249 LCD_SAT(state->current_lcd_color),
250 LCD_INT(state->current_lcd_color));
251
252 return true;
253}
254
255bool keyframe_set_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state) {
256 (void)animation;
257 state->prev_lcd_color = state->target_lcd_color;
258 state->current_lcd_color = state->target_lcd_color;
259 lcd_backlight_color(
260 LCD_HUE(state->current_lcd_color),
261 LCD_SAT(state->current_lcd_color),
262 LCD_INT(state->current_lcd_color));
263 return false;
264}
265#endif // LCD_BACKLIGHT_ENABLE
266
267#ifdef LCD_ENABLE
268bool keyframe_display_layer_text(keyframe_animation_t* animation, visualizer_state_t* state) {
269 (void)animation;
270 gdispClear(White);
271 gdispDrawString(0, 10, state->layer_text, state->font_dejavusansbold12, Black);
272 gdispFlush();
273 return false;
274}
275
276static void format_layer_bitmap_string(uint16_t default_layer, uint16_t layer, char* buffer) {
277 for (int i=0; i<16;i++)
278 {
279 uint32_t mask = (1u << i);
280 if (default_layer & mask) {
281 if (layer & mask) {
282 *buffer = 'B';
283 } else {
284 *buffer = 'D';
285 }
286 } else if (layer & mask) {
287 *buffer = '1';
288 } else {
289 *buffer = '0';
290 }
291 ++buffer;
292
293 if (i==3 || i==7 || i==11) {
294 *buffer = ' ';
295 ++buffer;
296 }
297 }
298 *buffer = 0;
299}
300
301bool keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_state_t* state) {
302 (void)animation;
303 const char* layer_help = "1=On D=Default B=Both";
304 char layer_buffer[16 + 4]; // 3 spaces and one null terminator
305 gdispClear(White);
306 gdispDrawString(0, 0, layer_help, state->font_fixed5x8, Black);
307 format_layer_bitmap_string(state->status.default_layer, state->status.layer, layer_buffer);
308 gdispDrawString(0, 10, layer_buffer, state->font_fixed5x8, Black);
309 format_layer_bitmap_string(state->status.default_layer >> 16, state->status.layer >> 16, layer_buffer);
310 gdispDrawString(0, 20, layer_buffer, state->font_fixed5x8, Black);
311 gdispFlush();
312 return false;
313}
314
315static void format_mods_bitmap_string(uint8_t mods, char* buffer) {
316 *buffer = ' ';
317 ++buffer;
318
319 for (int i = 0; i<8; i++)
320 {
321 uint32_t mask = (1u << i);
322 if (mods & mask) {
323 *buffer = '1';
324 } else {
325 *buffer = '0';
326 }
327 ++buffer;
328
329 if (i==3) {
330 *buffer = ' ';
331 ++buffer;
332 }
333 }
334 *buffer = 0;
335}
336
337bool keyframe_display_mods_bitmap(keyframe_animation_t* animation, visualizer_state_t* state) {
338 (void)animation;
339
340 const char* title = "Modifier states";
341 const char* mods_header = " CSAG CSAG ";
342 char status_buffer[12];
343
344 gdispClear(White);
345 gdispDrawString(0, 0, title, state->font_fixed5x8, Black);
346 gdispDrawString(0, 10, mods_header, state->font_fixed5x8, Black);
347 format_mods_bitmap_string(state->status.mods, status_buffer);
348 gdispDrawString(0, 20, status_buffer, state->font_fixed5x8, Black);
349
350 gdispFlush();
351 return false;
352}
353#endif // LCD_ENABLE
354
355bool keyframe_disable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) {
356 (void)animation;
357 (void)state;
358#ifdef LCD_ENABLE
359 gdispSetPowerMode(powerOff);
360#endif
361#ifdef LCD_BACKLIGHT_ENABLE
362 lcd_backlight_hal_color(0, 0, 0);
363#endif
364 return false;
365}
366
367bool keyframe_enable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) {
368 (void)animation;
369 (void)state;
370#ifdef LCD_ENABLE
371 gdispSetPowerMode(powerOn);
372#endif
373 return false;
374}
375
376bool enable_visualization(keyframe_animation_t* animation, visualizer_state_t* state) {
377 (void)animation;
378 (void)state;
379 dprint("User visualizer inited\n");
380 visualizer_enabled = true;
381 return false;
382}
383
384// TODO: Optimize the stack size, this is probably way too big 233// TODO: Optimize the stack size, this is probably way too big
385static DECLARE_THREAD_STACK(visualizerThreadStack, 1024); 234static DECLARE_THREAD_STACK(visualizerThreadStack, 1024);
386static DECLARE_THREAD_FUNCTION(visualizerThread, arg) { 235static DECLARE_THREAD_FUNCTION(visualizerThread, arg) {
@@ -396,6 +245,9 @@ static DECLARE_THREAD_FUNCTION(visualizerThread, arg) {
396 .mods = 0xFF, 245 .mods = 0xFF,
397 .leds = 0xFFFFFFFF, 246 .leds = 0xFFFFFFFF,
398 .suspended = false, 247 .suspended = false,
248#ifdef VISUALIZER_USER_DATA_SIZE
249 .user_data = {0},
250#endif
399 }; 251 };
400 252
401 visualizer_state_t state = { 253 visualizer_state_t state = {
@@ -418,13 +270,15 @@ static DECLARE_THREAD_FUNCTION(visualizerThread, arg) {
418 270
419 systemticks_t sleep_time = TIME_INFINITE; 271 systemticks_t sleep_time = TIME_INFINITE;
420 systemticks_t current_time = gfxSystemTicks(); 272 systemticks_t current_time = gfxSystemTicks();
273 bool force_update = true;
421 274
422 while(true) { 275 while(true) {
423 systemticks_t new_time = gfxSystemTicks(); 276 systemticks_t new_time = gfxSystemTicks();
424 systemticks_t delta = new_time - current_time; 277 systemticks_t delta = new_time - current_time;
425 current_time = new_time; 278 current_time = new_time;
426 bool enabled = visualizer_enabled; 279 bool enabled = visualizer_enabled;
427 if (!same_status(&state.status, &current_status)) { 280 if (force_update || !same_status(&state.status, &current_status)) {
281 force_update = false;
428 if (visualizer_enabled) { 282 if (visualizer_enabled) {
429 if (current_status.suspended) { 283 if (current_status.suspended) {
430 stop_all_keyframe_animations(); 284 stop_all_keyframe_animations();
@@ -433,8 +287,9 @@ static DECLARE_THREAD_FUNCTION(visualizerThread, arg) {
433 user_visualizer_suspend(&state); 287 user_visualizer_suspend(&state);
434 } 288 }
435 else { 289 else {
290 visualizer_keyboard_status_t prev_status = state.status;
436 state.status = current_status; 291 state.status = current_status;
437 update_user_visualizer_state(&state); 292 update_user_visualizer_state(&state, &prev_status);
438 } 293 }
439 state.prev_lcd_color = state.current_lcd_color; 294 state.prev_lcd_color = state.current_lcd_color;
440 } 295 }
@@ -458,13 +313,17 @@ static DECLARE_THREAD_FUNCTION(visualizerThread, arg) {
458 gdispGFlush(LED_DISPLAY); 313 gdispGFlush(LED_DISPLAY);
459#endif 314#endif
460 315
316#ifdef LCD_ENABLE
317 gdispGFlush(LCD_DISPLAY);
318#endif
319
461#ifdef EMULATOR 320#ifdef EMULATOR
462 draw_emulator(); 321 draw_emulator();
463#endif 322#endif
464 // The animation can enable the visualizer 323 // Enable the visualizer when the startup or the suspend animation has finished
465 // And we might need to update the state when that happens 324 if (!visualizer_enabled && state.status.suspended == false && get_num_running_animations() == 0) {
466 // so don't sleep 325 visualizer_enabled = true;
467 if (enabled != visualizer_enabled) { 326 force_update = true;
468 sleep_time = 0; 327 sleep_time = 0;
469 } 328 }
470 329
@@ -554,6 +413,12 @@ uint8_t visualizer_get_mods() {
554 return mods; 413 return mods;
555} 414}
556 415
416#ifdef VISUALIZER_USER_DATA_SIZE
417void visualizer_set_user_data(void* u) {
418 memcpy(user_data, u, VISUALIZER_USER_DATA_SIZE);
419}
420#endif
421
557void visualizer_update(uint32_t default_state, uint32_t state, uint8_t mods, uint32_t leds) { 422void visualizer_update(uint32_t default_state, uint32_t state, uint8_t mods, uint32_t leds) {
558 // Note that there's a small race condition here, the thread could read 423 // Note that there's a small race condition here, the thread could read
559 // a state where one of these are set but not the other. But this should 424 // a state where one of these are set but not the other. But this should
@@ -582,6 +447,9 @@ void visualizer_update(uint32_t default_state, uint32_t state, uint8_t mods, uin
582 .leds = leds, 447 .leds = leds,
583 .suspended = current_status.suspended, 448 .suspended = current_status.suspended,
584 }; 449 };
450#ifdef VISUALIZER_USER_DATA_SIZE
451 memcpy(new_status.user_data, user_data, VISUALIZER_USER_DATA_SIZE);
452#endif
585 if (!same_status(&current_status, &new_status)) { 453 if (!same_status(&current_status, &new_status)) {
586 changed = true; 454 changed = true;
587 current_status = new_status; 455 current_status = new_status;