]> git.donarmstrong.com Git - qmk_firmware.git/blob - quantum/visualizer/visualizer.c
Move common visualizer keyframes into visualizer_keyframes.h
[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 #include "action_util.h"
52
53 // Define this in config.h
54 #ifndef VISUALIZER_THREAD_PRIORITY
55 #define "Visualizer thread priority not defined"
56 #endif
57
58 static visualizer_keyboard_status_t current_status = {
59     .layer = 0xFFFFFFFF,
60     .default_layer = 0xFFFFFFFF,
61     .mods = 0xFF,
62     .leds = 0xFFFFFFFF,
63     .suspended = false,
64 #ifdef VISUALIZER_USER_DATA_SIZE
65     .user_data = {0}
66 #endif
67 };
68
69 static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboard_status_t* status2) {
70     return status1->layer == status2->layer &&
71         status1->default_layer == status2->default_layer &&
72         status1->mods == status2->mods &&
73         status1->leds == status2->leds &&
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     ;
79 }
80
81 static bool visualizer_enabled = false;
82
83 #ifdef VISUALIZER_USER_DATA_SIZE
84 static uint8_t user_data[VISUALIZER_USER_DATA_SIZE];
85 #endif
86
87 #define MAX_SIMULTANEOUS_ANIMATIONS 4
88 static keyframe_animation_t* animations[MAX_SIMULTANEOUS_ANIMATIONS] = {};
89
90 #ifdef SERIAL_LINK_ENABLE
91 MASTER_TO_ALL_SLAVES_OBJECT(current_status, visualizer_keyboard_status_t);
92
93 static remote_object_t* remote_objects[] = {
94     REMOTE_OBJECT(current_status),
95 };
96
97 #endif
98
99 GDisplay* LCD_DISPLAY = 0;
100 GDisplay* LED_DISPLAY = 0;
101
102 __attribute__((weak))
103 GDisplay* get_lcd_display(void) {
104     return gdispGetDisplay(0);
105 }
106
107 __attribute__((weak))
108 GDisplay* get_led_display(void) {
109     return gdispGetDisplay(1);
110 }
111
112 void start_keyframe_animation(keyframe_animation_t* animation) {
113     animation->current_frame = -1;
114     animation->time_left_in_frame = 0;
115     animation->need_update = true;
116     int free_index = -1;
117     for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
118         if (animations[i] == animation) {
119             return;
120         }
121         if (free_index == -1 && animations[i] == NULL) {
122            free_index=i;
123         }
124     }
125     if (free_index!=-1) {
126         animations[free_index] = animation;
127     }
128 }
129
130 void stop_keyframe_animation(keyframe_animation_t* animation) {
131     animation->current_frame = animation->num_frames;
132     animation->time_left_in_frame = 0;
133     animation->need_update = true;
134     animation->first_update_of_frame = false;
135     animation->last_update_of_frame = false;
136     for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
137         if (animations[i] == animation) {
138             animations[i] = NULL;
139             return;
140         }
141     }
142 }
143
144 void stop_all_keyframe_animations(void) {
145     for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
146         if (animations[i]) {
147             animations[i]->current_frame = animations[i]->num_frames;
148             animations[i]->time_left_in_frame = 0;
149             animations[i]->need_update = true;
150             animations[i]->first_update_of_frame = false;
151             animations[i]->last_update_of_frame = false;
152             animations[i] = NULL;
153         }
154     }
155 }
156
157 static 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
165 static bool update_keyframe_animation(keyframe_animation_t* animation, visualizer_state_t* state, systemticks_t delta, systemticks_t* sleep_time) {
166     // TODO: Clean up this messy code
167     dprintf("Animation frame%d, left %d, delta %d\n", animation->current_frame,
168             animation->time_left_in_frame, delta);
169     if (animation->current_frame == animation->num_frames) {
170         animation->need_update = false;
171         return false;
172     }
173     if (animation->current_frame == -1) {
174        animation->current_frame = 0;
175        animation->time_left_in_frame = animation->frame_lengths[0];
176        animation->need_update = true;
177        animation->first_update_of_frame = true;
178     } else {
179         animation->time_left_in_frame -= delta;
180         while (animation->time_left_in_frame <= 0) {
181             int left = animation->time_left_in_frame;
182             if (animation->need_update) {
183                 animation->time_left_in_frame = 0;
184                 animation->last_update_of_frame = true;
185                 (*animation->frame_functions[animation->current_frame])(animation, state);
186                 animation->last_update_of_frame = false;
187             }
188             animation->current_frame++;
189             animation->need_update = true;
190             animation->first_update_of_frame = true;
191             if (animation->current_frame == animation->num_frames) {
192                 if (animation->loop) {
193                     animation->current_frame = 0;
194                 }
195                 else {
196                     stop_keyframe_animation(animation);
197                     return false;
198                 }
199             }
200             delta = -left;
201             animation->time_left_in_frame = animation->frame_lengths[animation->current_frame];
202             animation->time_left_in_frame -= delta;
203         }
204     }
205     if (animation->need_update) {
206         animation->need_update = (*animation->frame_functions[animation->current_frame])(animation, state);
207         animation->first_update_of_frame = false;
208     }
209
210     systemticks_t wanted_sleep = animation->need_update ? gfxMillisecondsToTicks(10) : (unsigned)animation->time_left_in_frame;
211     if (wanted_sleep < *sleep_time) {
212         *sleep_time = wanted_sleep;
213     }
214
215     return true;
216 }
217
218 void run_next_keyframe(keyframe_animation_t* animation, visualizer_state_t* state) {
219     int next_frame = animation->current_frame + 1;
220     if (next_frame == animation->num_frames) {
221         next_frame = 0;
222     }
223     keyframe_animation_t temp_animation = *animation;
224     temp_animation.current_frame = next_frame;
225     temp_animation.time_left_in_frame = animation->frame_lengths[next_frame];
226     temp_animation.first_update_of_frame = true;
227     temp_animation.last_update_of_frame = false;
228     temp_animation.need_update  = false;
229     visualizer_state_t temp_state = *state;
230     (*temp_animation.frame_functions[next_frame])(&temp_animation, &temp_state);
231 }
232
233 // TODO: Optimize the stack size, this is probably way too big
234 static DECLARE_THREAD_STACK(visualizerThreadStack, 1024);
235 static DECLARE_THREAD_FUNCTION(visualizerThread, arg) {
236     (void)arg;
237
238     GListener event_listener;
239     geventListenerInit(&event_listener);
240     geventAttachSource(&event_listener, (GSourceHandle)&current_status, 0);
241
242     visualizer_keyboard_status_t initial_status = {
243         .default_layer = 0xFFFFFFFF,
244         .layer = 0xFFFFFFFF,
245         .mods = 0xFF,
246         .leds = 0xFFFFFFFF,
247         .suspended = false,
248 #ifdef VISUALIZER_USER_DATA_SIZE
249         .user_data = {0},
250 #endif
251     };
252
253     visualizer_state_t state = {
254         .status = initial_status,
255         .current_lcd_color = 0,
256 #ifdef LCD_ENABLE
257         .font_fixed5x8 = gdispOpenFont("fixed_5x8"),
258         .font_dejavusansbold12 = gdispOpenFont("DejaVuSansBold12")
259 #endif
260     };
261     initialize_user_visualizer(&state);
262     state.prev_lcd_color = state.current_lcd_color;
263
264 #ifdef LCD_BACKLIGHT_ENABLE
265     lcd_backlight_color(
266             LCD_HUE(state.current_lcd_color),
267             LCD_SAT(state.current_lcd_color),
268             LCD_INT(state.current_lcd_color));
269 #endif
270
271     systemticks_t sleep_time = TIME_INFINITE;
272     systemticks_t current_time = gfxSystemTicks();
273     bool force_update = true;
274
275     while(true) {
276         systemticks_t new_time = gfxSystemTicks();
277         systemticks_t delta = new_time - current_time;
278         current_time = new_time;
279         bool enabled = visualizer_enabled;
280         if (force_update || !same_status(&state.status, &current_status)) {
281             force_update = false;
282             if (visualizer_enabled) {
283                 if (current_status.suspended) {
284                     stop_all_keyframe_animations();
285                     visualizer_enabled = false;
286                     state.status = current_status;
287                     user_visualizer_suspend(&state);
288                 }
289                 else {
290                     visualizer_keyboard_status_t prev_status = state.status;
291                     state.status = current_status;
292                     update_user_visualizer_state(&state, &prev_status);
293                 }
294                 state.prev_lcd_color = state.current_lcd_color;
295             }
296         }
297         if (!enabled && state.status.suspended && current_status.suspended == false) {
298             // Setting the status to the initial status will force an update
299             // when the visualizer is enabled again
300             state.status = initial_status;
301             state.status.suspended = false;
302             stop_all_keyframe_animations();
303             user_visualizer_resume(&state);
304             state.prev_lcd_color = state.current_lcd_color;
305         }
306         sleep_time = TIME_INFINITE;
307         for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
308             if (animations[i]) {
309                 update_keyframe_animation(animations[i], &state, delta, &sleep_time);
310             }
311         }
312 #ifdef LED_ENABLE
313         gdispGFlush(LED_DISPLAY);
314 #endif
315
316 #ifdef EMULATOR
317         draw_emulator();
318 #endif
319         // Enable the visualizer when the startup or the suspend animation has finished
320         if (!visualizer_enabled && state.status.suspended == false && get_num_running_animations() == 0) {
321             visualizer_enabled = true;
322             force_update = true;
323             sleep_time = 0;
324         }
325
326         systemticks_t after_update = gfxSystemTicks();
327         unsigned update_delta = after_update - current_time;
328         if (sleep_time != TIME_INFINITE) {
329             if (sleep_time > update_delta) {
330                 sleep_time -= update_delta;
331             }
332             else {
333                 sleep_time = 0;
334             }
335         }
336         dprintf("Update took %d, last delta %d, sleep_time %d\n", update_delta, delta, sleep_time);
337 #ifdef PROTOCOL_CHIBIOS
338         // The gEventWait function really takes milliseconds, even if the documentation says ticks.
339         // Unfortunately there's no generic ugfx conversion from system time to milliseconds,
340         // so let's do it in a platform dependent way.
341
342         // On windows the system ticks is the same as milliseconds anyway
343         if (sleep_time != TIME_INFINITE) {
344             sleep_time = ST2MS(sleep_time);
345         }
346 #endif
347         geventEventWait(&event_listener, sleep_time);
348     }
349 #ifdef LCD_ENABLE
350     gdispCloseFont(state.font_fixed5x8);
351     gdispCloseFont(state.font_dejavusansbold12);
352 #endif
353
354     return 0;
355 }
356
357 void visualizer_init(void) {
358     gfxInit();
359
360 #ifdef LCD_BACKLIGHT_ENABLE
361     lcd_backlight_init();
362 #endif
363
364 #ifdef SERIAL_LINK_ENABLE
365     add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*) );
366 #endif
367
368 #ifdef LCD_ENABLE
369     LCD_DISPLAY = get_lcd_display();
370 #endif
371 #ifdef LED_ENABLE
372     LED_DISPLAY = get_led_display();
373 #endif
374
375     // We are using a low priority thread, the idea is to have it run only
376     // when the main thread is sleeping during the matrix scanning
377     gfxThreadCreate(visualizerThreadStack, sizeof(visualizerThreadStack),
378                               VISUALIZER_THREAD_PRIORITY, visualizerThread, NULL);
379 }
380
381 void update_status(bool changed) {
382     if (changed) {
383         GSourceListener* listener = geventGetSourceListener((GSourceHandle)&current_status, NULL);
384         if (listener) {
385             geventSendEvent(listener);
386         }
387     }
388 #ifdef SERIAL_LINK_ENABLE
389     static systime_t last_update = 0;
390     systime_t current_update = chVTGetSystemTimeX();
391     systime_t delta = current_update - last_update;
392     if (changed || delta > MS2ST(10)) {
393         last_update = current_update;
394         visualizer_keyboard_status_t* r = begin_write_current_status();
395         *r = current_status;
396         end_write_current_status();
397     }
398 #endif
399 }
400
401 uint8_t visualizer_get_mods() {
402   uint8_t mods = get_mods();
403
404 #ifndef NO_ACTION_ONESHOT
405   if (!has_oneshot_mods_timed_out()) {
406     mods |= get_oneshot_mods();
407   }
408 #endif  
409   return mods;
410 }
411
412 #ifdef VISUALIZER_USER_DATA_SIZE
413 void visualizer_set_user_data(void* u) {
414     memcpy(user_data, u, VISUALIZER_USER_DATA_SIZE);
415 }
416 #endif
417
418 void visualizer_update(uint32_t default_state, uint32_t state, uint8_t mods, uint32_t leds) {
419     // Note that there's a small race condition here, the thread could read
420     // a state where one of these are set but not the other. But this should
421     // not really matter as it will be fixed during the next loop step.
422     // Alternatively a mutex could be used instead of the volatile variables
423
424     bool changed = false;
425 #ifdef SERIAL_LINK_ENABLE
426     if (is_serial_link_connected ()) {
427         visualizer_keyboard_status_t* new_status = read_current_status();
428         if (new_status) {
429             if (!same_status(&current_status, new_status)) {
430                 changed = true;
431                 current_status = *new_status;
432             }
433         }
434     }
435     else {
436 #else
437    {
438 #endif
439         visualizer_keyboard_status_t new_status = {
440             .layer = state,
441             .default_layer = default_state,
442             .mods = mods,
443             .leds = leds,
444             .suspended = current_status.suspended,
445         };
446 #ifdef VISUALIZER_USER_DATA_SIZE
447        memcpy(new_status.user_data, user_data, VISUALIZER_USER_DATA_SIZE);
448 #endif
449         if (!same_status(&current_status, &new_status)) {
450             changed = true;
451             current_status = new_status;
452         }
453     }
454     update_status(changed);
455 }
456
457 void visualizer_suspend(void) {
458     current_status.suspended = true;
459     update_status(true);
460 }
461
462 void visualizer_resume(void) {
463     current_status.suspended = false;
464     update_status(true);
465 }