]> git.donarmstrong.com Git - qmk_firmware.git/blob - visualizer.c
Suspend support for the visualizer
[qmk_firmware.git] / 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 "ch.h"
27 #include <string.h>
28
29 #ifdef LCD_ENABLE
30 #include "gfx.h"
31 #endif
32
33 #ifdef LCD_BACKLIGHT_ENABLE
34 #include "lcd_backlight.h"
35 #endif
36
37 //#define DEBUG_VISUALIZER
38
39 #ifdef DEBUG_VISUALIZER
40 #include "debug.h"
41 #else
42 #include "nodebug.h"
43 #endif
44
45 #ifdef USE_SERIAL_LINK
46 #include "serial_link/protocol/transport.h"
47 #include "serial_link/system/driver.h"
48 #endif
49
50
51 static visualizer_keyboard_status_t current_status = {
52     .layer = 0xFFFFFFFF,
53     .default_layer = 0xFFFFFFFF,
54     .leds = 0xFFFFFFFF,
55     .suspended = false,
56 };
57
58 static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboard_status_t* status2) {
59     return status1->layer == status2->layer &&
60         status1->default_layer == status2->default_layer &&
61         status1->leds == status2->leds &&
62         status1->suspended == status2->suspended;
63 }
64
65 static event_source_t layer_changed_event;
66 static bool visualizer_enabled = false;
67
68 #define MAX_SIMULTANEOUS_ANIMATIONS 4
69 static keyframe_animation_t* animations[MAX_SIMULTANEOUS_ANIMATIONS] = {};
70
71 #ifdef USE_SERIAL_LINK
72 MASTER_TO_ALL_SLAVES_OBJECT(current_status, visualizer_keyboard_status_t);
73
74 static remote_object_t* remote_objects[] = {
75     REMOTE_OBJECT(current_status),
76 };
77
78 #endif
79
80
81 void start_keyframe_animation(keyframe_animation_t* animation) {
82     animation->current_frame = -1;
83     animation->time_left_in_frame = 0;
84     animation->need_update = true;
85     int free_index = -1;
86     for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
87         if (animations[i] == animation) {
88             return;
89         }
90         if (free_index == -1 && animations[i] == NULL) {
91            free_index=i;
92         }
93     }
94     if (free_index!=-1) {
95         animations[free_index] = animation;
96     }
97 }
98
99 void stop_keyframe_animation(keyframe_animation_t* animation) {
100     animation->current_frame = animation->num_frames;
101     animation->time_left_in_frame = 0;
102     animation->need_update = true;
103     for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
104         if (animations[i] == animation) {
105             animations[i] = NULL;
106             return;
107         }
108     }
109 }
110
111 void stop_all_keyframe_animations(void) {
112     for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
113         if (animations[i]) {
114             animations[i]->current_frame = animations[i]->num_frames;
115             animations[i]->time_left_in_frame = 0;
116             animations[i]->need_update = true;
117             animations[i] = NULL;
118         }
119     }
120 }
121
122 static bool update_keyframe_animation(keyframe_animation_t* animation, visualizer_state_t* state, systime_t delta, systime_t* sleep_time) {
123     dprintf("Animation frame%d, left %d, delta %d\n", animation->current_frame,
124             animation->time_left_in_frame, delta);
125     if (animation->current_frame == animation->num_frames) {
126         animation->need_update = false;
127         return false;
128     }
129     if (animation->current_frame == -1) {
130        animation->current_frame = 0;
131        animation->time_left_in_frame = animation->frame_lengths[0];
132        animation->need_update = true;
133     } else {
134         animation->time_left_in_frame -= delta;
135         while (animation->time_left_in_frame <= 0) {
136             int left = animation->time_left_in_frame;
137             if (animation->need_update) {
138                 animation->time_left_in_frame = 0;
139                 (*animation->frame_functions[animation->current_frame])(animation, state);
140             }
141             animation->current_frame++;
142             animation->need_update = true;
143             if (animation->current_frame == animation->num_frames) {
144                 if (animation->loop) {
145                     animation->current_frame = 0;
146                 }
147                 else {
148                     stop_keyframe_animation(animation);
149                     return false;
150                 }
151             }
152             delta = -left;
153             animation->time_left_in_frame = animation->frame_lengths[animation->current_frame];
154             animation->time_left_in_frame -= delta;
155         }
156     }
157     if (animation->need_update) {
158         animation->need_update = (*animation->frame_functions[animation->current_frame])(animation, state);
159     }
160
161     int wanted_sleep = animation->need_update ? 10 : animation->time_left_in_frame;
162     if ((unsigned)wanted_sleep < *sleep_time) {
163         *sleep_time = wanted_sleep;
164     }
165
166     return true;
167 }
168
169 bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* state) {
170     (void)animation;
171     (void)state;
172     return false;
173 }
174
175 #ifdef LCD_BACKLIGHT_ENABLE
176 bool keyframe_animate_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state) {
177     int frame_length = animation->frame_lengths[1];
178     int current_pos = frame_length - animation->time_left_in_frame;
179     uint8_t t_h = LCD_HUE(state->target_lcd_color);
180     uint8_t t_s = LCD_SAT(state->target_lcd_color);
181     uint8_t t_i = LCD_INT(state->target_lcd_color);
182     uint8_t p_h = LCD_HUE(state->prev_lcd_color);
183     uint8_t p_s = LCD_SAT(state->prev_lcd_color);
184     uint8_t p_i = LCD_INT(state->prev_lcd_color);
185
186     uint8_t d_h1 = t_h - p_h; //Modulo arithmetic since we want to wrap around
187     int d_h2 = t_h - p_h;
188     // Chose the shortest way around
189     int d_h = abs(d_h2) < d_h1 ? d_h2 : d_h1;
190     int d_s = t_s - p_s;
191     int d_i = t_i - p_i;
192
193     int hue = (d_h * current_pos) / frame_length;
194     int sat = (d_s * current_pos) / frame_length;
195     int intensity = (d_i * current_pos) / frame_length;
196     //dprintf("%X -> %X = %X\n", p_h, t_h, hue);
197     hue += p_h;
198     sat += p_s;
199     intensity += p_i;
200     state->current_lcd_color = LCD_COLOR(hue, sat, intensity);
201     lcd_backlight_color(
202             LCD_HUE(state->current_lcd_color),
203             LCD_SAT(state->current_lcd_color),
204             LCD_INT(state->current_lcd_color));
205
206     return true;
207 }
208
209 bool keyframe_set_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state) {
210     (void)animation;
211     state->prev_lcd_color = state->target_lcd_color;
212     state->current_lcd_color = state->target_lcd_color;
213     lcd_backlight_color(
214             LCD_HUE(state->current_lcd_color),
215             LCD_SAT(state->current_lcd_color),
216             LCD_INT(state->current_lcd_color));
217     return false;
218 }
219 #endif // LCD_BACKLIGHT_ENABLE
220
221 #ifdef LCD_ENABLE
222 bool keyframe_display_layer_text(keyframe_animation_t* animation, visualizer_state_t* state) {
223     (void)animation;
224     gdispClear(White);
225     gdispDrawString(0, 10, state->layer_text, state->font_dejavusansbold12, Black);
226     gdispFlush();
227     return false;
228 }
229
230 static void format_layer_bitmap_string(uint16_t default_layer, uint16_t layer, char* buffer) {
231     for (int i=0; i<16;i++)
232     {
233         uint32_t mask = (1u << i);
234         if (default_layer & mask) {
235             if (layer & mask) {
236                 *buffer = 'B';
237             } else {
238                 *buffer = 'D';
239             }
240         } else if (layer & mask) {
241             *buffer = '1';
242         } else {
243             *buffer = '0';
244         }
245         ++buffer;
246
247         if (i==3 || i==7 || i==11) {
248             *buffer = ' ';
249             ++buffer;
250         }
251     }
252     *buffer = 0;
253 }
254
255 bool keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_state_t* state) {
256     (void)animation;
257     const char* layer_help = "1=On D=Default B=Both";
258     char layer_buffer[16 + 4]; // 3 spaces and one null terminator
259     gdispClear(White);
260     gdispDrawString(0, 0, layer_help, state->font_fixed5x8, Black);
261     format_layer_bitmap_string(state->status.default_layer, state->status.layer, layer_buffer);
262     gdispDrawString(0, 10, layer_buffer, state->font_fixed5x8, Black);
263     format_layer_bitmap_string(state->status.default_layer >> 16, state->status.layer >> 16, layer_buffer);
264     gdispDrawString(0, 20, layer_buffer, state->font_fixed5x8, Black);
265     gdispFlush();
266     return false;
267 }
268 #endif // LCD_ENABLE
269
270 bool keyframe_disable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) {
271     (void)animation;
272     (void)state;
273     return false;
274 }
275
276 bool keyframe_enable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) {
277     (void)animation;
278     (void)state;
279     return false;
280 }
281
282 bool enable_visualization(keyframe_animation_t* animation, visualizer_state_t* state) {
283     (void)animation;
284     (void)state;
285     dprint("User visualizer inited\n");
286     visualizer_enabled = true;
287     return false;
288 }
289
290 // TODO: Optimize the stack size, this is probably way too big
291 static THD_WORKING_AREA(visualizerThreadStack, 1024);
292 static THD_FUNCTION(visualizerThread, arg) {
293     (void)arg;
294
295     event_listener_t event_listener;
296     chEvtRegister(&layer_changed_event, &event_listener, 0);
297
298     visualizer_keyboard_status_t initial_status = {
299         .default_layer = 0xFFFFFFFF,
300         .layer = 0xFFFFFFFF,
301         .leds = 0xFFFFFFFF,
302         .suspended = false,
303     };
304
305     visualizer_state_t state = {
306         .status = initial_status,
307         .current_lcd_color = 0,
308 #ifdef LCD_ENABLE
309         .font_fixed5x8 = gdispOpenFont("fixed_5x8"),
310         .font_dejavusansbold12 = gdispOpenFont("DejaVuSansBold12")
311 #endif
312     };
313     initialize_user_visualizer(&state);
314     state.prev_lcd_color = state.current_lcd_color;
315
316 #ifdef LCD_BACKLIGHT_ENABLE
317     lcd_backlight_color(
318             LCD_HUE(state.current_lcd_color),
319             LCD_SAT(state.current_lcd_color),
320             LCD_INT(state.current_lcd_color));
321 #endif
322
323     systime_t sleep_time = TIME_INFINITE;
324     systime_t current_time = chVTGetSystemTimeX();
325
326     while(true) {
327         systime_t new_time = chVTGetSystemTimeX();
328         systime_t delta = new_time - current_time;
329         current_time = new_time;
330         bool enabled = visualizer_enabled;
331         if (!same_status(&state.status, &current_status)) {
332             if (visualizer_enabled) {
333                 if (current_status.suspended) {
334                     stop_all_keyframe_animations();
335                     visualizer_enabled = false;
336                     state.status = current_status;
337                     user_visualizer_suspend(&state);
338                 }
339                 else {
340                     state.status = current_status;
341                     update_user_visualizer_state(&state);
342                     state.prev_lcd_color = state.current_lcd_color;
343                 }
344             }
345         }
346         if (!enabled && state.status.suspended && current_status.suspended == false) {
347             // Setting the status to the initial status will force an update
348             // when the visualizer is enabled again
349             state.status = initial_status;
350             state.status.suspended = false;
351             stop_all_keyframe_animations();
352             user_visualizer_resume(&state);
353         }
354         sleep_time = TIME_INFINITE;
355         for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
356             if (animations[i]) {
357                 update_keyframe_animation(animations[i], &state, delta, &sleep_time);
358             }
359         }
360         // The animation can enable the visualizer
361         // And we might need to update the state when that happens
362         // so don't sleep
363         if (enabled != visualizer_enabled) {
364             sleep_time = 0;
365         }
366
367         systime_t after_update = chVTGetSystemTimeX();
368         unsigned update_delta = after_update - current_time;
369         if (sleep_time != TIME_INFINITE) {
370             if (sleep_time > update_delta) {
371                 sleep_time -= update_delta;
372             }
373             else {
374                 sleep_time = 0;
375             }
376         }
377         dprintf("Update took %d, last delta %d, sleep_time %d\n", update_delta, delta, sleep_time);
378         chEvtWaitOneTimeout(EVENT_MASK(0), sleep_time);
379     }
380 #ifdef LCD_ENABLE
381     gdispCloseFont(state.font_fixed5x8);
382     gdispCloseFont(state.font_dejavusansbold12);
383 #endif
384 }
385
386 void visualizer_init(void) {
387 #ifdef LCD_ENABLE
388     gfxInit();
389 #endif
390
391 #ifdef LCD_BACKLIGHT_ENABLE
392     lcd_backlight_init();
393 #endif
394
395 #ifdef USE_SERIAL_LINK
396     add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*) );
397 #endif
398     // We are using a low priority thread, the idea is to have it run only
399     // when the main thread is sleeping during the matrix scanning
400     chEvtObjectInit(&layer_changed_event);
401     (void)chThdCreateStatic(visualizerThreadStack, sizeof(visualizerThreadStack),
402                               LOWPRIO, visualizerThread, NULL);
403 }
404
405 void update_status(bool changed) {
406     if (changed) {
407         chEvtBroadcast(&layer_changed_event);
408     }
409 #ifdef USE_SERIAL_LINK
410     static systime_t last_update = 0;
411     systime_t current_update = chVTGetSystemTimeX();
412     systime_t delta = current_update - last_update;
413     if (changed || delta > MS2ST(10)) {
414         last_update = current_update;
415         visualizer_keyboard_status_t* r = begin_write_current_status();
416         *r = current_status;
417         end_write_current_status();
418     }
419 #endif
420 }
421
422 void visualizer_update(uint32_t default_state, uint32_t state, uint32_t leds) {
423     // Note that there's a small race condition here, the thread could read
424     // a state where one of these are set but not the other. But this should
425     // not really matter as it will be fixed during the next loop step.
426     // Alternatively a mutex could be used instead of the volatile variables
427
428     bool changed = false;
429 #ifdef USE_SERIAL_LINK
430     if (is_serial_link_connected ()) {
431         visualizer_keyboard_status_t* new_status = read_current_status();
432         if (new_status) {
433             if (!same_status(&current_status, new_status)) {
434                 changed = true;
435                 current_status = *new_status;
436             }
437         }
438     }
439     else {
440 #else
441    {
442 #endif
443         visualizer_keyboard_status_t new_status = {
444             .layer = state,
445             .default_layer = default_state,
446             .leds = leds,
447             .suspended = current_status.suspended,
448         };
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 }