]> git.donarmstrong.com Git - qmk_firmware.git/blob - quantum/visualizer/visualizer.c
3b3f51b1ff86470aa4bd3d86dd9aa971c36bd277
[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 #include "gfx.h"
33
34 #ifdef LCD_BACKLIGHT_ENABLE
35 #include "lcd_backlight.h"
36 #endif
37
38 //#define DEBUG_VISUALIZER
39
40 #ifdef DEBUG_VISUALIZER
41 #include "debug.h"
42 #else
43 #include "nodebug.h"
44 #endif
45
46 #ifdef SERIAL_LINK_ENABLE
47 #include "serial_link/protocol/transport.h"
48 #include "serial_link/system/serial_link.h"
49 #endif
50
51 // Define this in config.h
52 #ifndef VISUALIZER_THREAD_PRIORITY
53 #define "Visualizer thread priority not defined"
54 #endif
55
56 // mods status
57 #include "action_util.h"
58
59 #include "led.h"
60
61 static visualizer_keyboard_status_t current_status = {
62     .layer = 0xFFFFFFFF,
63     .default_layer = 0xFFFFFFFF,
64     .mods = 0xFF,
65     .leds = 0xFFFFFFFF,
66     .suspended = false,
67 #ifdef VISUALIZER_USER_DATA_SIZE
68     .user_data = {0}
69 #endif
70 };
71
72 static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboard_status_t* status2) {
73     return status1->layer == status2->layer &&
74         status1->default_layer == status2->default_layer &&
75         status1->mods == status2->mods &&
76         status1->leds == status2->leds &&
77         status1->suspended == status2->suspended
78 #ifdef VISUALIZER_USER_DATA_SIZE
79         && memcmp(status1->user_data, status2->user_data, VISUALIZER_USER_DATA_SIZE) == 0
80 #endif
81     ;
82 }
83
84 static bool visualizer_enabled = false;
85
86 #ifdef VISUALIZER_USER_DATA_SIZE
87 static uint8_t user_data[VISUALIZER_USER_DATA_SIZE];
88 #endif
89
90 #define MAX_SIMULTANEOUS_ANIMATIONS 4
91 static keyframe_animation_t* animations[MAX_SIMULTANEOUS_ANIMATIONS] = {};
92
93 #ifdef SERIAL_LINK_ENABLE
94 MASTER_TO_ALL_SLAVES_OBJECT(current_status, visualizer_keyboard_status_t);
95
96 static remote_object_t* remote_objects[] = {
97     REMOTE_OBJECT(current_status),
98 };
99
100 #endif
101
102 GDisplay* LCD_DISPLAY = 0;
103 GDisplay* LED_DISPLAY = 0;
104
105 __attribute__((weak))
106 GDisplay* get_lcd_display(void) {
107     return gdispGetDisplay(0);
108 }
109
110 __attribute__((weak))
111 GDisplay* get_led_display(void) {
112     return gdispGetDisplay(1);
113 }
114
115 void start_keyframe_animation(keyframe_animation_t* animation) {
116     animation->current_frame = -1;
117     animation->time_left_in_frame = 0;
118     animation->need_update = true;
119     int free_index = -1;
120     for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
121         if (animations[i] == animation) {
122             return;
123         }
124         if (free_index == -1 && animations[i] == NULL) {
125            free_index=i;
126         }
127     }
128     if (free_index!=-1) {
129         animations[free_index] = animation;
130     }
131 }
132
133 void stop_keyframe_animation(keyframe_animation_t* animation) {
134     animation->current_frame = animation->num_frames;
135     animation->time_left_in_frame = 0;
136     animation->need_update = true;
137     animation->first_update_of_frame = false;
138     animation->last_update_of_frame = false;
139     for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
140         if (animations[i] == animation) {
141             animations[i] = NULL;
142             return;
143         }
144     }
145 }
146
147 void stop_all_keyframe_animations(void) {
148     for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
149         if (animations[i]) {
150             animations[i]->current_frame = animations[i]->num_frames;
151             animations[i]->time_left_in_frame = 0;
152             animations[i]->need_update = true;
153             animations[i]->first_update_of_frame = false;
154             animations[i]->last_update_of_frame = false;
155             animations[i] = NULL;
156         }
157     }
158 }
159
160 static bool update_keyframe_animation(keyframe_animation_t* animation, visualizer_state_t* state, systemticks_t delta, systemticks_t* sleep_time) {
161     // TODO: Clean up this messy code
162     dprintf("Animation frame%d, left %d, delta %d\n", animation->current_frame,
163             animation->time_left_in_frame, delta);
164     if (animation->current_frame == animation->num_frames) {
165         animation->need_update = false;
166         return false;
167     }
168     if (animation->current_frame == -1) {
169        animation->current_frame = 0;
170        animation->time_left_in_frame = animation->frame_lengths[0];
171        animation->need_update = true;
172        animation->first_update_of_frame = true;
173     } else {
174         animation->time_left_in_frame -= delta;
175         while (animation->time_left_in_frame <= 0) {
176             int left = animation->time_left_in_frame;
177             if (animation->need_update) {
178                 animation->time_left_in_frame = 0;
179                 animation->last_update_of_frame = true;
180                 (*animation->frame_functions[animation->current_frame])(animation, state);
181                 animation->last_update_of_frame = false;
182             }
183             animation->current_frame++;
184             animation->need_update = true;
185             animation->first_update_of_frame = true;
186             if (animation->current_frame == animation->num_frames) {
187                 if (animation->loop) {
188                     animation->current_frame = 0;
189                 }
190                 else {
191                     stop_keyframe_animation(animation);
192                     return false;
193                 }
194             }
195             delta = -left;
196             animation->time_left_in_frame = animation->frame_lengths[animation->current_frame];
197             animation->time_left_in_frame -= delta;
198         }
199     }
200     if (animation->need_update) {
201         animation->need_update = (*animation->frame_functions[animation->current_frame])(animation, state);
202         animation->first_update_of_frame = false;
203     }
204
205     systemticks_t wanted_sleep = animation->need_update ? gfxMillisecondsToTicks(10) : (unsigned)animation->time_left_in_frame;
206     if (wanted_sleep < *sleep_time) {
207         *sleep_time = wanted_sleep;
208     }
209
210     return true;
211 }
212
213 void run_next_keyframe(keyframe_animation_t* animation, visualizer_state_t* state) {
214     int next_frame = animation->current_frame + 1;
215     if (next_frame == animation->num_frames) {
216         next_frame = 0;
217     }
218     keyframe_animation_t temp_animation = *animation;
219     temp_animation.current_frame = next_frame;
220     temp_animation.time_left_in_frame = animation->frame_lengths[next_frame];
221     temp_animation.first_update_of_frame = true;
222     temp_animation.last_update_of_frame = false;
223     temp_animation.need_update  = false;
224     visualizer_state_t temp_state = *state;
225     (*temp_animation.frame_functions[next_frame])(&temp_animation, &temp_state);
226 }
227
228 bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* state) {
229     (void)animation;
230     (void)state;
231     return false;
232 }
233
234 #ifdef LCD_BACKLIGHT_ENABLE
235 bool keyframe_animate_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state) {
236     int frame_length = animation->frame_lengths[animation->current_frame];
237     int current_pos = frame_length - animation->time_left_in_frame;
238     uint8_t t_h = LCD_HUE(state->target_lcd_color);
239     uint8_t t_s = LCD_SAT(state->target_lcd_color);
240     uint8_t t_i = LCD_INT(state->target_lcd_color);
241     uint8_t p_h = LCD_HUE(state->prev_lcd_color);
242     uint8_t p_s = LCD_SAT(state->prev_lcd_color);
243     uint8_t p_i = LCD_INT(state->prev_lcd_color);
244
245     uint8_t d_h1 = t_h - p_h; //Modulo arithmetic since we want to wrap around
246     int d_h2 = t_h - p_h;
247     // Chose the shortest way around
248     int d_h = abs(d_h2) < d_h1 ? d_h2 : d_h1;
249     int d_s = t_s - p_s;
250     int d_i = t_i - p_i;
251
252     int hue = (d_h * current_pos) / frame_length;
253     int sat = (d_s * current_pos) / frame_length;
254     int intensity = (d_i * current_pos) / frame_length;
255     //dprintf("%X -> %X = %X\n", p_h, t_h, hue);
256     hue += p_h;
257     sat += p_s;
258     intensity += p_i;
259     state->current_lcd_color = LCD_COLOR(hue, sat, intensity);
260     lcd_backlight_color(
261             LCD_HUE(state->current_lcd_color),
262             LCD_SAT(state->current_lcd_color),
263             LCD_INT(state->current_lcd_color));
264
265     return true;
266 }
267
268 bool keyframe_set_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state) {
269     (void)animation;
270     state->prev_lcd_color = state->target_lcd_color;
271     state->current_lcd_color = state->target_lcd_color;
272     lcd_backlight_color(
273             LCD_HUE(state->current_lcd_color),
274             LCD_SAT(state->current_lcd_color),
275             LCD_INT(state->current_lcd_color));
276     return false;
277 }
278 #endif // LCD_BACKLIGHT_ENABLE
279
280 #ifdef LCD_ENABLE
281 bool keyframe_display_layer_text(keyframe_animation_t* animation, visualizer_state_t* state) {
282     (void)animation;
283     gdispClear(White);
284     gdispDrawString(0, 10, state->layer_text, state->font_dejavusansbold12, Black);
285     gdispFlush();
286     return false;
287 }
288
289 static void format_layer_bitmap_string(uint16_t default_layer, uint16_t layer, char* buffer) {
290     for (int i=0; i<16;i++)
291     {
292         uint32_t mask = (1u << i);
293         if (default_layer & mask) {
294             if (layer & mask) {
295                 *buffer = 'B';
296             } else {
297                 *buffer = 'D';
298             }
299         } else if (layer & mask) {
300             *buffer = '1';
301         } else {
302             *buffer = '0';
303         }
304         ++buffer;
305
306         if (i==3 || i==7 || i==11) {
307             *buffer = ' ';
308             ++buffer;
309         }
310     }
311     *buffer = 0;
312 }
313
314 bool keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_state_t* state) {
315     (void)animation;
316     const char* layer_help = "1=On D=Default B=Both";
317     char layer_buffer[16 + 4]; // 3 spaces and one null terminator
318     gdispClear(White);
319     gdispDrawString(0, 0, layer_help, state->font_fixed5x8, Black);
320     format_layer_bitmap_string(state->status.default_layer, state->status.layer, layer_buffer);
321     gdispDrawString(0, 10, layer_buffer, state->font_fixed5x8, Black);
322     format_layer_bitmap_string(state->status.default_layer >> 16, state->status.layer >> 16, layer_buffer);
323     gdispDrawString(0, 20, layer_buffer, state->font_fixed5x8, Black);
324     gdispFlush();
325     return false;
326 }
327
328 static void format_mods_bitmap_string(uint8_t mods, char* buffer) {
329     *buffer = ' ';
330     ++buffer;
331
332     for (int i = 0; i<8; i++)
333     {
334         uint32_t mask = (1u << i);
335         if (mods & mask) {
336             *buffer = '1';
337         } else {
338             *buffer = '0';
339         }
340         ++buffer;
341
342         if (i==3) {
343             *buffer = ' ';
344             ++buffer;
345         }
346     }
347     *buffer = 0;
348 }
349
350 bool keyframe_display_mods_bitmap(keyframe_animation_t* animation, visualizer_state_t* state) {
351     (void)animation;
352
353     const char* title = "Modifier states";
354     const char* mods_header = " CSAG CSAG ";
355     char status_buffer[12]; 
356     
357     gdispClear(White);
358     gdispDrawString(0, 0, title, state->font_fixed5x8, Black);
359     gdispDrawString(0, 10, mods_header, state->font_fixed5x8, Black);
360     format_mods_bitmap_string(state->status.mods, status_buffer);
361     gdispDrawString(0, 20, status_buffer, state->font_fixed5x8, Black);
362
363     gdispFlush();
364     return false;
365 }
366
367 bool keyframe_display_led_states(keyframe_animation_t* animation, visualizer_state_t* state)
368 {
369     char output[sizeof("NUM CAPS SCRL COMP KANA")];
370     uint8_t pos = 0;
371
372     if (state->status.leds & (1u << USB_LED_NUM_LOCK)) {
373        memcpy(output + pos, "NUM ", 4);
374        pos += 4;
375     }
376     if (state->status.leds & (1u << USB_LED_CAPS_LOCK)) {
377        memcpy(output + pos, "CAPS ", 5);
378        pos += 5;
379     }
380     if (state->status.leds & (1u << USB_LED_SCROLL_LOCK)) {
381        memcpy(output + pos, "SCRL ", 5);
382        pos += 5;
383     }
384     if (state->status.leds & (1u << USB_LED_COMPOSE)) {
385        memcpy(output + pos, "COMP ", 5);
386        pos += 5;
387     }
388     if (state->status.leds & (1u << USB_LED_KANA)) {
389        memcpy(output + pos, "KANA ", 5);
390        pos += 5;
391     }
392     output[pos] = 0;
393     gdispClear(White);
394     gdispDrawString(0, 10, output, state->font_dejavusansbold12, Black);
395     gdispFlush();
396     return false;
397 }
398
399 #endif // LCD_ENABLE
400
401 bool keyframe_disable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) {
402     (void)animation;
403     (void)state;
404 #ifdef LCD_ENABLE
405     gdispSetPowerMode(powerOff);
406 #endif
407 #ifdef LCD_BACKLIGHT_ENABLE
408     lcd_backlight_hal_color(0, 0, 0);
409 #endif
410     return false;
411 }
412
413 bool keyframe_enable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) {
414     (void)animation;
415     (void)state;
416 #ifdef LCD_ENABLE
417     gdispSetPowerMode(powerOn);
418 #endif
419     return false;
420 }
421
422 bool enable_visualization(keyframe_animation_t* animation, visualizer_state_t* state) {
423     (void)animation;
424     (void)state;
425     dprint("User visualizer inited\n");
426     visualizer_enabled = true;
427     return false;
428 }
429
430 // TODO: Optimize the stack size, this is probably way too big
431 static DECLARE_THREAD_STACK(visualizerThreadStack, 1024);
432 static DECLARE_THREAD_FUNCTION(visualizerThread, arg) {
433     (void)arg;
434
435     GListener event_listener;
436     geventListenerInit(&event_listener);
437     geventAttachSource(&event_listener, (GSourceHandle)&current_status, 0);
438
439     visualizer_keyboard_status_t initial_status = {
440         .default_layer = 0xFFFFFFFF,
441         .layer = 0xFFFFFFFF,
442         .mods = 0xFF,
443         .leds = 0xFFFFFFFF,
444         .suspended = false,
445 #ifdef VISUALIZER_USER_DATA_SIZE
446         .user_data = {0},
447 #endif
448     };
449
450     visualizer_state_t state = {
451         .status = initial_status,
452         .current_lcd_color = 0,
453 #ifdef LCD_ENABLE
454         .font_fixed5x8 = gdispOpenFont("fixed_5x8"),
455         .font_dejavusansbold12 = gdispOpenFont("DejaVuSansBold12")
456 #endif
457     };
458     initialize_user_visualizer(&state);
459     state.prev_lcd_color = state.current_lcd_color;
460
461 #ifdef LCD_BACKLIGHT_ENABLE
462     lcd_backlight_color(
463             LCD_HUE(state.current_lcd_color),
464             LCD_SAT(state.current_lcd_color),
465             LCD_INT(state.current_lcd_color));
466 #endif
467
468     systemticks_t sleep_time = TIME_INFINITE;
469     systemticks_t current_time = gfxSystemTicks();
470
471     while(true) {
472         systemticks_t new_time = gfxSystemTicks();
473         systemticks_t delta = new_time - current_time;
474         current_time = new_time;
475         bool enabled = visualizer_enabled;
476         if (!same_status(&state.status, &current_status)) {
477             if (visualizer_enabled) {
478                 if (current_status.suspended) {
479                     stop_all_keyframe_animations();
480                     visualizer_enabled = false;
481                     state.status = current_status;
482                     user_visualizer_suspend(&state);
483                 }
484                 else {
485                     visualizer_keyboard_status_t prev_status = state.status;
486                     state.status = current_status;
487                     update_user_visualizer_state(&state, &prev_status);
488                 }
489                 state.prev_lcd_color = state.current_lcd_color;
490             }
491         }
492         if (!enabled && state.status.suspended && current_status.suspended == false) {
493             // Setting the status to the initial status will force an update
494             // when the visualizer is enabled again
495             state.status = initial_status;
496             state.status.suspended = false;
497             stop_all_keyframe_animations();
498             user_visualizer_resume(&state);
499             state.prev_lcd_color = state.current_lcd_color;
500         }
501         sleep_time = TIME_INFINITE;
502         for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
503             if (animations[i]) {
504                 update_keyframe_animation(animations[i], &state, delta, &sleep_time);
505             }
506         }
507 #ifdef LED_ENABLE
508         gdispGFlush(LED_DISPLAY);
509 #endif
510
511 #ifdef EMULATOR
512         draw_emulator();
513 #endif
514         // The animation can enable the visualizer
515         // And we might need to update the state when that happens
516         // so don't sleep
517         if (enabled != visualizer_enabled) {
518             sleep_time = 0;
519         }
520
521         systemticks_t after_update = gfxSystemTicks();
522         unsigned update_delta = after_update - current_time;
523         if (sleep_time != TIME_INFINITE) {
524             if (sleep_time > update_delta) {
525                 sleep_time -= update_delta;
526             }
527             else {
528                 sleep_time = 0;
529             }
530         }
531         dprintf("Update took %d, last delta %d, sleep_time %d\n", update_delta, delta, sleep_time);
532 #ifdef PROTOCOL_CHIBIOS
533         // The gEventWait function really takes milliseconds, even if the documentation says ticks.
534         // Unfortunately there's no generic ugfx conversion from system time to milliseconds,
535         // so let's do it in a platform dependent way.
536
537         // On windows the system ticks is the same as milliseconds anyway
538         if (sleep_time != TIME_INFINITE) {
539             sleep_time = ST2MS(sleep_time);
540         }
541 #endif
542         geventEventWait(&event_listener, sleep_time);
543     }
544 #ifdef LCD_ENABLE
545     gdispCloseFont(state.font_fixed5x8);
546     gdispCloseFont(state.font_dejavusansbold12);
547 #endif
548
549     return 0;
550 }
551
552 void visualizer_init(void) {
553     gfxInit();
554
555 #ifdef LCD_BACKLIGHT_ENABLE
556     lcd_backlight_init();
557 #endif
558
559 #ifdef SERIAL_LINK_ENABLE
560     add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*) );
561 #endif
562
563 #ifdef LCD_ENABLE
564     LCD_DISPLAY = get_lcd_display();
565 #endif
566 #ifdef LED_ENABLE
567     LED_DISPLAY = get_led_display();
568 #endif
569
570     // We are using a low priority thread, the idea is to have it run only
571     // when the main thread is sleeping during the matrix scanning
572     gfxThreadCreate(visualizerThreadStack, sizeof(visualizerThreadStack),
573                               VISUALIZER_THREAD_PRIORITY, visualizerThread, NULL);
574 }
575
576 void update_status(bool changed) {
577     if (changed) {
578         GSourceListener* listener = geventGetSourceListener((GSourceHandle)&current_status, NULL);
579         if (listener) {
580             geventSendEvent(listener);
581         }
582     }
583 #ifdef SERIAL_LINK_ENABLE
584     static systime_t last_update = 0;
585     systime_t current_update = chVTGetSystemTimeX();
586     systime_t delta = current_update - last_update;
587     if (changed || delta > MS2ST(10)) {
588         last_update = current_update;
589         visualizer_keyboard_status_t* r = begin_write_current_status();
590         *r = current_status;
591         end_write_current_status();
592     }
593 #endif
594 }
595
596 uint8_t visualizer_get_mods() {
597   uint8_t mods = get_mods();
598
599 #ifndef NO_ACTION_ONESHOT
600   if (!has_oneshot_mods_timed_out()) {
601     mods |= get_oneshot_mods();
602   }
603 #endif  
604   return mods;
605 }
606
607 #ifdef VISUALIZER_USER_DATA_SIZE
608 void visualizer_set_user_data(void* u) {
609     memcpy(user_data, u, VISUALIZER_USER_DATA_SIZE);
610 }
611 #endif
612
613 void visualizer_update(uint32_t default_state, uint32_t state, uint8_t mods, uint32_t leds) {
614     // Note that there's a small race condition here, the thread could read
615     // a state where one of these are set but not the other. But this should
616     // not really matter as it will be fixed during the next loop step.
617     // Alternatively a mutex could be used instead of the volatile variables
618
619     bool changed = false;
620 #ifdef SERIAL_LINK_ENABLE
621     if (is_serial_link_connected ()) {
622         visualizer_keyboard_status_t* new_status = read_current_status();
623         if (new_status) {
624             if (!same_status(&current_status, new_status)) {
625                 changed = true;
626                 current_status = *new_status;
627             }
628         }
629     }
630     else {
631 #else
632    {
633 #endif
634         visualizer_keyboard_status_t new_status = {
635             .layer = state,
636             .default_layer = default_state,
637             .mods = mods,
638             .leds = leds,
639             .suspended = current_status.suspended,
640         };
641 #ifdef VISUALIZER_USER_DATA_SIZE
642        memcpy(new_status.user_data, user_data, VISUALIZER_USER_DATA_SIZE);
643 #endif
644         if (!same_status(&current_status, &new_status)) {
645             changed = true;
646             current_status = new_status;
647         }
648     }
649     update_status(changed);
650 }
651
652 void visualizer_suspend(void) {
653     current_status.suspended = true;
654     update_status(true);
655 }
656
657 void visualizer_resume(void) {
658     current_status.suspended = false;
659     update_status(true);
660 }