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