]> git.donarmstrong.com Git - qmk_firmware.git/blob - quantum/visualizer/visualizer.c
Merge commit '73d890a2c9c34b905cd5e74e7146fdd4578dcb96' into add_visualizer
[qmk_firmware.git] / quantum / visualizer / visualizer.c
1 /*
2 The MIT License (MIT)
3
4 Copyright (c) 2016 Fred Sundvik
5
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12
13 The above copyright notice and this permission notice shall be included in all
14 copies or substantial portions of the Software.
15
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 SOFTWARE.
23 */
24
25 #include "visualizer.h"
26 #include "config.h"
27 #include <string.h>
28 #ifdef PROTOCOL_CHIBIOS
29 #include "ch.h"
30 #endif
31
32 #ifdef LCD_ENABLE
33 #include "gfx.h"
34 #endif
35
36 #ifdef LCD_BACKLIGHT_ENABLE
37 #include "lcd_backlight.h"
38 #endif
39
40 //#define DEBUG_VISUALIZER
41
42 #ifdef DEBUG_VISUALIZER
43 #include "debug.h"
44 #else
45 #include "nodebug.h"
46 #endif
47
48 #ifdef USE_SERIAL_LINK
49 #include "serial_link/protocol/transport.h"
50 #include "serial_link/system/serial_link.h"
51 #endif
52
53 // Define this in config.h
54 #ifndef VISUALIZER_THREAD_PRIORITY
55 #define "Visualizer thread priority not defined"
56 #endif
57
58
59 static visualizer_keyboard_status_t current_status = {
60     .layer = 0xFFFFFFFF,
61     .default_layer = 0xFFFFFFFF,
62     .leds = 0xFFFFFFFF,
63     .suspended = false,
64 };
65
66 static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboard_status_t* status2) {
67     return status1->layer == status2->layer &&
68         status1->default_layer == status2->default_layer &&
69         status1->leds == status2->leds &&
70         status1->suspended == status2->suspended;
71 }
72
73 static bool visualizer_enabled = false;
74
75 #define MAX_SIMULTANEOUS_ANIMATIONS 4
76 static keyframe_animation_t* animations[MAX_SIMULTANEOUS_ANIMATIONS] = {};
77
78 #ifdef USE_SERIAL_LINK
79 MASTER_TO_ALL_SLAVES_OBJECT(current_status, visualizer_keyboard_status_t);
80
81 static remote_object_t* remote_objects[] = {
82     REMOTE_OBJECT(current_status),
83 };
84
85 #endif
86
87 GDisplay* LCD_DISPLAY = 0;
88 GDisplay* LED_DISPLAY = 0;
89
90 __attribute__((weak))
91 GDisplay* get_lcd_display(void) {
92     return gdispGetDisplay(0);
93 }
94
95 __attribute__((weak))
96 GDisplay* get_led_display(void) {
97     return gdispGetDisplay(1);
98 }
99
100 void start_keyframe_animation(keyframe_animation_t* animation) {
101     animation->current_frame = -1;
102     animation->time_left_in_frame = 0;
103     animation->need_update = true;
104     int free_index = -1;
105     for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
106         if (animations[i] == animation) {
107             return;
108         }
109         if (free_index == -1 && animations[i] == NULL) {
110            free_index=i;
111         }
112     }
113     if (free_index!=-1) {
114         animations[free_index] = animation;
115     }
116 }
117
118 void stop_keyframe_animation(keyframe_animation_t* animation) {
119     animation->current_frame = animation->num_frames;
120     animation->time_left_in_frame = 0;
121     animation->need_update = true;
122     animation->first_update_of_frame = false;
123     animation->last_update_of_frame = false;
124     for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
125         if (animations[i] == animation) {
126             animations[i] = NULL;
127             return;
128         }
129     }
130 }
131
132 void stop_all_keyframe_animations(void) {
133     for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
134         if (animations[i]) {
135             animations[i]->current_frame = animations[i]->num_frames;
136             animations[i]->time_left_in_frame = 0;
137             animations[i]->need_update = true;
138             animations[i]->first_update_of_frame = false;
139             animations[i]->last_update_of_frame = false;
140             animations[i] = NULL;
141         }
142     }
143 }
144
145 static bool update_keyframe_animation(keyframe_animation_t* animation, visualizer_state_t* state, systemticks_t delta, systemticks_t* sleep_time) {
146     // TODO: Clean up this messy code
147     dprintf("Animation frame%d, left %d, delta %d\n", animation->current_frame,
148             animation->time_left_in_frame, delta);
149     if (animation->current_frame == animation->num_frames) {
150         animation->need_update = false;
151         return false;
152     }
153     if (animation->current_frame == -1) {
154        animation->current_frame = 0;
155        animation->time_left_in_frame = animation->frame_lengths[0];
156        animation->need_update = true;
157        animation->first_update_of_frame = true;
158     } else {
159         animation->time_left_in_frame -= delta;
160         while (animation->time_left_in_frame <= 0) {
161             int left = animation->time_left_in_frame;
162             if (animation->need_update) {
163                 animation->time_left_in_frame = 0;
164                 animation->last_update_of_frame = true;
165                 (*animation->frame_functions[animation->current_frame])(animation, state);
166                 animation->last_update_of_frame = false;
167             }
168             animation->current_frame++;
169             animation->need_update = true;
170             animation->first_update_of_frame = true;
171             if (animation->current_frame == animation->num_frames) {
172                 if (animation->loop) {
173                     animation->current_frame = 0;
174                 }
175                 else {
176                     stop_keyframe_animation(animation);
177                     return false;
178                 }
179             }
180             delta = -left;
181             animation->time_left_in_frame = animation->frame_lengths[animation->current_frame];
182             animation->time_left_in_frame -= delta;
183         }
184     }
185     if (animation->need_update) {
186         animation->need_update = (*animation->frame_functions[animation->current_frame])(animation, state);
187         animation->first_update_of_frame = false;
188     }
189
190     systemticks_t wanted_sleep = animation->need_update ? gfxMillisecondsToTicks(10) : (unsigned)animation->time_left_in_frame;
191     if (wanted_sleep < *sleep_time) {
192         *sleep_time = wanted_sleep;
193     }
194
195     return true;
196 }
197
198 void run_next_keyframe(keyframe_animation_t* animation, visualizer_state_t* state) {
199     int next_frame = animation->current_frame + 1;
200     if (next_frame == animation->num_frames) {
201         next_frame = 0;
202     }
203     keyframe_animation_t temp_animation = *animation;
204     temp_animation.current_frame = next_frame;
205     temp_animation.time_left_in_frame = animation->frame_lengths[next_frame];
206     temp_animation.first_update_of_frame = true;
207     temp_animation.last_update_of_frame = false;
208     temp_animation.need_update  = false;
209     visualizer_state_t temp_state = *state;
210     (*temp_animation.frame_functions[next_frame])(&temp_animation, &temp_state);
211 }
212
213 bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* state) {
214     (void)animation;
215     (void)state;
216     return false;
217 }
218
219 #ifdef LCD_BACKLIGHT_ENABLE
220 bool keyframe_animate_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state) {
221     int frame_length = animation->frame_lengths[animation->current_frame];
222     int current_pos = frame_length - animation->time_left_in_frame;
223     uint8_t t_h = LCD_HUE(state->target_lcd_color);
224     uint8_t t_s = LCD_SAT(state->target_lcd_color);
225     uint8_t t_i = LCD_INT(state->target_lcd_color);
226     uint8_t p_h = LCD_HUE(state->prev_lcd_color);
227     uint8_t p_s = LCD_SAT(state->prev_lcd_color);
228     uint8_t p_i = LCD_INT(state->prev_lcd_color);
229
230     uint8_t d_h1 = t_h - p_h; //Modulo arithmetic since we want to wrap around
231     int d_h2 = t_h - p_h;
232     // Chose the shortest way around
233     int d_h = abs(d_h2) < d_h1 ? d_h2 : d_h1;
234     int d_s = t_s - p_s;
235     int d_i = t_i - p_i;
236
237     int hue = (d_h * current_pos) / frame_length;
238     int sat = (d_s * current_pos) / frame_length;
239     int intensity = (d_i * current_pos) / frame_length;
240     //dprintf("%X -> %X = %X\n", p_h, t_h, hue);
241     hue += p_h;
242     sat += p_s;
243     intensity += p_i;
244     state->current_lcd_color = LCD_COLOR(hue, sat, intensity);
245     lcd_backlight_color(
246             LCD_HUE(state->current_lcd_color),
247             LCD_SAT(state->current_lcd_color),
248             LCD_INT(state->current_lcd_color));
249
250     return true;
251 }
252
253 bool keyframe_set_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state) {
254     (void)animation;
255     state->prev_lcd_color = state->target_lcd_color;
256     state->current_lcd_color = state->target_lcd_color;
257     lcd_backlight_color(
258             LCD_HUE(state->current_lcd_color),
259             LCD_SAT(state->current_lcd_color),
260             LCD_INT(state->current_lcd_color));
261     return false;
262 }
263 #endif // LCD_BACKLIGHT_ENABLE
264
265 #ifdef LCD_ENABLE
266 bool keyframe_display_layer_text(keyframe_animation_t* animation, visualizer_state_t* state) {
267     (void)animation;
268     gdispClear(White);
269     gdispDrawString(0, 10, state->layer_text, state->font_dejavusansbold12, Black);
270     gdispFlush();
271     return false;
272 }
273
274 static void format_layer_bitmap_string(uint16_t default_layer, uint16_t layer, char* buffer) {
275     for (int i=0; i<16;i++)
276     {
277         uint32_t mask = (1u << i);
278         if (default_layer & mask) {
279             if (layer & mask) {
280                 *buffer = 'B';
281             } else {
282                 *buffer = 'D';
283             }
284         } else if (layer & mask) {
285             *buffer = '1';
286         } else {
287             *buffer = '0';
288         }
289         ++buffer;
290
291         if (i==3 || i==7 || i==11) {
292             *buffer = ' ';
293             ++buffer;
294         }
295     }
296     *buffer = 0;
297 }
298
299 bool keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_state_t* state) {
300     (void)animation;
301     const char* layer_help = "1=On D=Default B=Both";
302     char layer_buffer[16 + 4]; // 3 spaces and one null terminator
303     gdispClear(White);
304     gdispDrawString(0, 0, layer_help, state->font_fixed5x8, Black);
305     format_layer_bitmap_string(state->status.default_layer, state->status.layer, layer_buffer);
306     gdispDrawString(0, 10, layer_buffer, state->font_fixed5x8, Black);
307     format_layer_bitmap_string(state->status.default_layer >> 16, state->status.layer >> 16, layer_buffer);
308     gdispDrawString(0, 20, layer_buffer, state->font_fixed5x8, Black);
309     gdispFlush();
310     return false;
311 }
312 #endif // LCD_ENABLE
313
314 bool keyframe_disable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) {
315     (void)animation;
316     (void)state;
317 #ifdef LCD_ENABLE
318     gdispSetPowerMode(powerOff);
319 #endif
320 #ifdef LCD_BACKLIGHT_ENABLE
321     lcd_backlight_hal_color(0, 0, 0);
322 #endif
323     return false;
324 }
325
326 bool keyframe_enable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) {
327     (void)animation;
328     (void)state;
329 #ifdef LCD_ENABLE
330     gdispSetPowerMode(powerOn);
331 #endif
332     return false;
333 }
334
335 bool enable_visualization(keyframe_animation_t* animation, visualizer_state_t* state) {
336     (void)animation;
337     (void)state;
338     dprint("User visualizer inited\n");
339     visualizer_enabled = true;
340     return false;
341 }
342
343 // TODO: Optimize the stack size, this is probably way too big
344 static DECLARE_THREAD_STACK(visualizerThreadStack, 1024);
345 static DECLARE_THREAD_FUNCTION(visualizerThread, arg) {
346     (void)arg;
347
348     GListener event_listener;
349     geventListenerInit(&event_listener);
350     geventAttachSource(&event_listener, (GSourceHandle)&current_status, 0);
351
352     visualizer_keyboard_status_t initial_status = {
353         .default_layer = 0xFFFFFFFF,
354         .layer = 0xFFFFFFFF,
355         .leds = 0xFFFFFFFF,
356         .suspended = false,
357     };
358
359     visualizer_state_t state = {
360         .status = initial_status,
361         .current_lcd_color = 0,
362 #ifdef LCD_ENABLE
363         .font_fixed5x8 = gdispOpenFont("fixed_5x8"),
364         .font_dejavusansbold12 = gdispOpenFont("DejaVuSansBold12")
365 #endif
366     };
367     initialize_user_visualizer(&state);
368     state.prev_lcd_color = state.current_lcd_color;
369
370 #ifdef LCD_BACKLIGHT_ENABLE
371     lcd_backlight_color(
372             LCD_HUE(state.current_lcd_color),
373             LCD_SAT(state.current_lcd_color),
374             LCD_INT(state.current_lcd_color));
375 #endif
376
377     systemticks_t sleep_time = TIME_INFINITE;
378     systemticks_t current_time = gfxSystemTicks();
379
380     while(true) {
381         systemticks_t new_time = gfxSystemTicks();
382         systemticks_t delta = new_time - current_time;
383         current_time = new_time;
384         bool enabled = visualizer_enabled;
385         if (!same_status(&state.status, &current_status)) {
386             if (visualizer_enabled) {
387                 if (current_status.suspended) {
388                     stop_all_keyframe_animations();
389                     visualizer_enabled = false;
390                     state.status = current_status;
391                     user_visualizer_suspend(&state);
392                 }
393                 else {
394                     state.status = current_status;
395                     update_user_visualizer_state(&state);
396                 }
397                 state.prev_lcd_color = state.current_lcd_color;
398             }
399         }
400         if (!enabled && state.status.suspended && current_status.suspended == false) {
401             // Setting the status to the initial status will force an update
402             // when the visualizer is enabled again
403             state.status = initial_status;
404             state.status.suspended = false;
405             stop_all_keyframe_animations();
406             user_visualizer_resume(&state);
407             state.prev_lcd_color = state.current_lcd_color;
408         }
409         sleep_time = TIME_INFINITE;
410         for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
411             if (animations[i]) {
412                 update_keyframe_animation(animations[i], &state, delta, &sleep_time);
413             }
414         }
415 #ifdef LED_ENABLE
416         gdispGFlush(LED_DISPLAY);
417 #endif
418
419 #ifdef EMULATOR
420         draw_emulator();
421 #endif
422         // The animation can enable the visualizer
423         // And we might need to update the state when that happens
424         // so don't sleep
425         if (enabled != visualizer_enabled) {
426             sleep_time = 0;
427         }
428
429         systemticks_t after_update = gfxSystemTicks();
430         unsigned update_delta = after_update - current_time;
431         if (sleep_time != TIME_INFINITE) {
432             if (sleep_time > update_delta) {
433                 sleep_time -= update_delta;
434             }
435             else {
436                 sleep_time = 0;
437             }
438         }
439         dprintf("Update took %d, last delta %d, sleep_time %d\n", update_delta, delta, sleep_time);
440 #ifdef PROTOCOL_CHIBIOS
441         // The gEventWait function really takes milliseconds, even if the documentation says ticks.
442         // Unfortunately there's no generic ugfx conversion from system time to milliseconds,
443         // so let's do it in a platform dependent way.
444
445         // On windows the system ticks is the same as milliseconds anyway
446         if (sleep_time != TIME_INFINITE) {
447             sleep_time = ST2MS(sleep_time);
448         }
449 #endif
450         geventEventWait(&event_listener, sleep_time);
451     }
452 #ifdef LCD_ENABLE
453     gdispCloseFont(state.font_fixed5x8);
454     gdispCloseFont(state.font_dejavusansbold12);
455 #endif
456
457     return 0;
458 }
459
460 void visualizer_init(void) {
461 #ifdef LCD_ENABLE
462     gfxInit();
463 #endif
464
465 #ifdef LCD_BACKLIGHT_ENABLE
466     lcd_backlight_init();
467 #endif
468
469 #ifdef USE_SERIAL_LINK
470     add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*) );
471 #endif
472
473 #ifdef LCD_ENABLE
474     LCD_DISPLAY = get_lcd_display();
475 #endif
476 #ifdef LED_ENABLE
477     LED_DISPLAY = get_led_display();
478 #endif
479
480     // We are using a low priority thread, the idea is to have it run only
481     // when the main thread is sleeping during the matrix scanning
482     gfxThreadCreate(visualizerThreadStack, sizeof(visualizerThreadStack),
483                               VISUALIZER_THREAD_PRIORITY, visualizerThread, NULL);
484 }
485
486 void update_status(bool changed) {
487     if (changed) {
488         GSourceListener* listener = geventGetSourceListener((GSourceHandle)&current_status, NULL);
489         if (listener) {
490             geventSendEvent(listener);
491         }
492     }
493 #ifdef USE_SERIAL_LINK
494     static systime_t last_update = 0;
495     systime_t current_update = chVTGetSystemTimeX();
496     systime_t delta = current_update - last_update;
497     if (changed || delta > MS2ST(10)) {
498         last_update = current_update;
499         visualizer_keyboard_status_t* r = begin_write_current_status();
500         *r = current_status;
501         end_write_current_status();
502     }
503 #endif
504 }
505
506 void visualizer_update(uint32_t default_state, uint32_t state, uint32_t leds) {
507     // Note that there's a small race condition here, the thread could read
508     // a state where one of these are set but not the other. But this should
509     // not really matter as it will be fixed during the next loop step.
510     // Alternatively a mutex could be used instead of the volatile variables
511
512     bool changed = false;
513 #ifdef USE_SERIAL_LINK
514     if (is_serial_link_connected ()) {
515         visualizer_keyboard_status_t* new_status = read_current_status();
516         if (new_status) {
517             if (!same_status(&current_status, new_status)) {
518                 changed = true;
519                 current_status = *new_status;
520             }
521         }
522     }
523     else {
524 #else
525    {
526 #endif
527         visualizer_keyboard_status_t new_status = {
528             .layer = state,
529             .default_layer = default_state,
530             .leds = leds,
531             .suspended = current_status.suspended,
532         };
533         if (!same_status(&current_status, &new_status)) {
534             changed = true;
535             current_status = new_status;
536         }
537     }
538     update_status(changed);
539 }
540
541 void visualizer_suspend(void) {
542     current_status.suspended = true;
543     update_status(true);
544 }
545
546 void visualizer_resume(void) {
547     current_status.suspended = false;
548     update_status(true);
549 }