]> git.donarmstrong.com Git - qmk_firmware.git/blob - quantum/visualizer/visualizer.c
Add readme file
[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 static visualizer_keyboard_status_t current_status = {
60     .layer = 0xFFFFFFFF,
61     .default_layer = 0xFFFFFFFF,
62     .mods = 0xFF,
63     .leds = 0xFFFFFFFF,
64     .suspended = false,
65 };
66
67 static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboard_status_t* status2) {
68     return status1->layer == status2->layer &&
69         status1->default_layer == status2->default_layer &&
70         status1->mods == status2->mods &&
71         status1->leds == status2->leds &&
72         status1->suspended == status2->suspended;
73 }
74
75 static bool visualizer_enabled = false;
76
77 #define MAX_SIMULTANEOUS_ANIMATIONS 4
78 static keyframe_animation_t* animations[MAX_SIMULTANEOUS_ANIMATIONS] = {};
79
80 #ifdef SERIAL_LINK_ENABLE
81 MASTER_TO_ALL_SLAVES_OBJECT(current_status, visualizer_keyboard_status_t);
82
83 static remote_object_t* remote_objects[] = {
84     REMOTE_OBJECT(current_status),
85 };
86
87 #endif
88
89 GDisplay* LCD_DISPLAY = 0;
90 GDisplay* LED_DISPLAY = 0;
91
92 __attribute__((weak))
93 GDisplay* get_lcd_display(void) {
94     return gdispGetDisplay(0);
95 }
96
97 __attribute__((weak))
98 GDisplay* get_led_display(void) {
99     return gdispGetDisplay(1);
100 }
101
102 void start_keyframe_animation(keyframe_animation_t* animation) {
103     animation->current_frame = -1;
104     animation->time_left_in_frame = 0;
105     animation->need_update = true;
106     int free_index = -1;
107     for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
108         if (animations[i] == animation) {
109             return;
110         }
111         if (free_index == -1 && animations[i] == NULL) {
112            free_index=i;
113         }
114     }
115     if (free_index!=-1) {
116         animations[free_index] = animation;
117     }
118 }
119
120 void stop_keyframe_animation(keyframe_animation_t* animation) {
121     animation->current_frame = animation->num_frames;
122     animation->time_left_in_frame = 0;
123     animation->need_update = true;
124     animation->first_update_of_frame = false;
125     animation->last_update_of_frame = false;
126     for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
127         if (animations[i] == animation) {
128             animations[i] = NULL;
129             return;
130         }
131     }
132 }
133
134 void stop_all_keyframe_animations(void) {
135     for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
136         if (animations[i]) {
137             animations[i]->current_frame = animations[i]->num_frames;
138             animations[i]->time_left_in_frame = 0;
139             animations[i]->need_update = true;
140             animations[i]->first_update_of_frame = false;
141             animations[i]->last_update_of_frame = false;
142             animations[i] = NULL;
143         }
144     }
145 }
146
147 static 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
149     dprintf("Animation frame%d, left %d, delta %d\n", animation->current_frame,
150             animation->time_left_in_frame, delta);
151     if (animation->current_frame == animation->num_frames) {
152         animation->need_update = false;
153         return false;
154     }
155     if (animation->current_frame == -1) {
156        animation->current_frame = 0;
157        animation->time_left_in_frame = animation->frame_lengths[0];
158        animation->need_update = true;
159        animation->first_update_of_frame = true;
160     } else {
161         animation->time_left_in_frame -= delta;
162         while (animation->time_left_in_frame <= 0) {
163             int left = animation->time_left_in_frame;
164             if (animation->need_update) {
165                 animation->time_left_in_frame = 0;
166                 animation->last_update_of_frame = true;
167                 (*animation->frame_functions[animation->current_frame])(animation, state);
168                 animation->last_update_of_frame = false;
169             }
170             animation->current_frame++;
171             animation->need_update = true;
172             animation->first_update_of_frame = true;
173             if (animation->current_frame == animation->num_frames) {
174                 if (animation->loop) {
175                     animation->current_frame = 0;
176                 }
177                 else {
178                     stop_keyframe_animation(animation);
179                     return false;
180                 }
181             }
182             delta = -left;
183             animation->time_left_in_frame = animation->frame_lengths[animation->current_frame];
184             animation->time_left_in_frame -= delta;
185         }
186     }
187     if (animation->need_update) {
188         animation->need_update = (*animation->frame_functions[animation->current_frame])(animation, state);
189         animation->first_update_of_frame = false;
190     }
191
192     systemticks_t wanted_sleep = animation->need_update ? gfxMillisecondsToTicks(10) : (unsigned)animation->time_left_in_frame;
193     if (wanted_sleep < *sleep_time) {
194         *sleep_time = wanted_sleep;
195     }
196
197     return true;
198 }
199
200 void run_next_keyframe(keyframe_animation_t* animation, visualizer_state_t* state) {
201     int next_frame = animation->current_frame + 1;
202     if (next_frame == animation->num_frames) {
203         next_frame = 0;
204     }
205     keyframe_animation_t temp_animation = *animation;
206     temp_animation.current_frame = next_frame;
207     temp_animation.time_left_in_frame = animation->frame_lengths[next_frame];
208     temp_animation.first_update_of_frame = true;
209     temp_animation.last_update_of_frame = false;
210     temp_animation.need_update  = false;
211     visualizer_state_t temp_state = *state;
212     (*temp_animation.frame_functions[next_frame])(&temp_animation, &temp_state);
213 }
214
215 bool 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
222 bool 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
255 bool 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
268 bool 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
276 static 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
301 bool 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
315 static 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
337 bool 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
355 bool 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
367 bool 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
376 bool 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
385 static DECLARE_THREAD_STACK(visualizerThreadStack, 1024);
386 static DECLARE_THREAD_FUNCTION(visualizerThread, arg) {
387     (void)arg;
388
389     GListener event_listener;
390     geventListenerInit(&event_listener);
391     geventAttachSource(&event_listener, (GSourceHandle)&current_status, 0);
392
393     visualizer_keyboard_status_t initial_status = {
394         .default_layer = 0xFFFFFFFF,
395         .layer = 0xFFFFFFFF,
396         .mods = 0xFF,
397         .leds = 0xFFFFFFFF,
398         .suspended = false,
399     };
400
401     visualizer_state_t state = {
402         .status = initial_status,
403         .current_lcd_color = 0,
404 #ifdef LCD_ENABLE
405         .font_fixed5x8 = gdispOpenFont("fixed_5x8"),
406         .font_dejavusansbold12 = gdispOpenFont("DejaVuSansBold12")
407 #endif
408     };
409     initialize_user_visualizer(&state);
410     state.prev_lcd_color = state.current_lcd_color;
411
412 #ifdef LCD_BACKLIGHT_ENABLE
413     lcd_backlight_color(
414             LCD_HUE(state.current_lcd_color),
415             LCD_SAT(state.current_lcd_color),
416             LCD_INT(state.current_lcd_color));
417 #endif
418
419     systemticks_t sleep_time = TIME_INFINITE;
420     systemticks_t current_time = gfxSystemTicks();
421
422     while(true) {
423         systemticks_t new_time = gfxSystemTicks();
424         systemticks_t delta = new_time - current_time;
425         current_time = new_time;
426         bool enabled = visualizer_enabled;
427         if (!same_status(&state.status, &current_status)) {
428             if (visualizer_enabled) {
429                 if (current_status.suspended) {
430                     stop_all_keyframe_animations();
431                     visualizer_enabled = false;
432                     state.status = current_status;
433                     user_visualizer_suspend(&state);
434                 }
435                 else {
436                     state.status = current_status;
437                     update_user_visualizer_state(&state);
438                 }
439                 state.prev_lcd_color = state.current_lcd_color;
440             }
441         }
442         if (!enabled && state.status.suspended && current_status.suspended == false) {
443             // Setting the status to the initial status will force an update
444             // when the visualizer is enabled again
445             state.status = initial_status;
446             state.status.suspended = false;
447             stop_all_keyframe_animations();
448             user_visualizer_resume(&state);
449             state.prev_lcd_color = state.current_lcd_color;
450         }
451         sleep_time = TIME_INFINITE;
452         for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
453             if (animations[i]) {
454                 update_keyframe_animation(animations[i], &state, delta, &sleep_time);
455             }
456         }
457 #ifdef LED_ENABLE
458         gdispGFlush(LED_DISPLAY);
459 #endif
460
461 #ifdef EMULATOR
462         draw_emulator();
463 #endif
464         // The animation can enable the visualizer
465         // And we might need to update the state when that happens
466         // so don't sleep
467         if (enabled != visualizer_enabled) {
468             sleep_time = 0;
469         }
470
471         systemticks_t after_update = gfxSystemTicks();
472         unsigned update_delta = after_update - current_time;
473         if (sleep_time != TIME_INFINITE) {
474             if (sleep_time > update_delta) {
475                 sleep_time -= update_delta;
476             }
477             else {
478                 sleep_time = 0;
479             }
480         }
481         dprintf("Update took %d, last delta %d, sleep_time %d\n", update_delta, delta, sleep_time);
482 #ifdef PROTOCOL_CHIBIOS
483         // The gEventWait function really takes milliseconds, even if the documentation says ticks.
484         // Unfortunately there's no generic ugfx conversion from system time to milliseconds,
485         // so let's do it in a platform dependent way.
486
487         // On windows the system ticks is the same as milliseconds anyway
488         if (sleep_time != TIME_INFINITE) {
489             sleep_time = ST2MS(sleep_time);
490         }
491 #endif
492         geventEventWait(&event_listener, sleep_time);
493     }
494 #ifdef LCD_ENABLE
495     gdispCloseFont(state.font_fixed5x8);
496     gdispCloseFont(state.font_dejavusansbold12);
497 #endif
498
499     return 0;
500 }
501
502 void visualizer_init(void) {
503     gfxInit();
504
505 #ifdef LCD_BACKLIGHT_ENABLE
506     lcd_backlight_init();
507 #endif
508
509 #ifdef SERIAL_LINK_ENABLE
510     add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*) );
511 #endif
512
513 #ifdef LCD_ENABLE
514     LCD_DISPLAY = get_lcd_display();
515 #endif
516 #ifdef LED_ENABLE
517     LED_DISPLAY = get_led_display();
518 #endif
519
520     // We are using a low priority thread, the idea is to have it run only
521     // when the main thread is sleeping during the matrix scanning
522     gfxThreadCreate(visualizerThreadStack, sizeof(visualizerThreadStack),
523                               VISUALIZER_THREAD_PRIORITY, visualizerThread, NULL);
524 }
525
526 void update_status(bool changed) {
527     if (changed) {
528         GSourceListener* listener = geventGetSourceListener((GSourceHandle)&current_status, NULL);
529         if (listener) {
530             geventSendEvent(listener);
531         }
532     }
533 #ifdef SERIAL_LINK_ENABLE
534     static systime_t last_update = 0;
535     systime_t current_update = chVTGetSystemTimeX();
536     systime_t delta = current_update - last_update;
537     if (changed || delta > MS2ST(10)) {
538         last_update = current_update;
539         visualizer_keyboard_status_t* r = begin_write_current_status();
540         *r = current_status;
541         end_write_current_status();
542     }
543 #endif
544 }
545
546 uint8_t visualizer_get_mods() {
547   uint8_t mods = get_mods();
548
549 #ifndef NO_ACTION_ONESHOT
550   if (!has_oneshot_mods_timed_out()) {
551     mods |= get_oneshot_mods();
552   }
553 #endif  
554   return mods;
555 }
556
557 void 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
559     // a state where one of these are set but not the other. But this should
560     // not really matter as it will be fixed during the next loop step.
561     // Alternatively a mutex could be used instead of the volatile variables
562
563     bool changed = false;
564 #ifdef SERIAL_LINK_ENABLE
565     if (is_serial_link_connected ()) {
566         visualizer_keyboard_status_t* new_status = read_current_status();
567         if (new_status) {
568             if (!same_status(&current_status, new_status)) {
569                 changed = true;
570                 current_status = *new_status;
571             }
572         }
573     }
574     else {
575 #else
576    {
577 #endif
578         visualizer_keyboard_status_t new_status = {
579             .layer = state,
580             .default_layer = default_state,
581             .mods = mods,
582             .leds = leds,
583             .suspended = current_status.suspended,
584         };
585         if (!same_status(&current_status, &new_status)) {
586             changed = true;
587             current_status = new_status;
588         }
589     }
590     update_status(changed);
591 }
592
593 void visualizer_suspend(void) {
594     current_status.suspended = true;
595     update_status(true);
596 }
597
598 void visualizer_resume(void) {
599     current_status.suspended = false;
600     update_status(true);
601 }