]> git.donarmstrong.com Git - qmk_firmware.git/blob - visualizer.c
Suspend power off, fix backlight animation
[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[animation->current_frame];
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 #ifdef LCD_ENABLE
274     gdispSetPowerMode(powerOff);
275 #endif
276 #ifdef LCD_BACKLIGHT_ENABLE
277     lcd_backlight_hal_color(0, 0, 0);
278 #endif
279     return false;
280 }
281
282 bool keyframe_enable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) {
283     (void)animation;
284     (void)state;
285 #ifdef LCD_ENABLE
286     gdispSetPowerMode(powerOn);
287 #endif
288     return false;
289 }
290
291 bool enable_visualization(keyframe_animation_t* animation, visualizer_state_t* state) {
292     (void)animation;
293     (void)state;
294     dprint("User visualizer inited\n");
295     visualizer_enabled = true;
296     return false;
297 }
298
299 // TODO: Optimize the stack size, this is probably way too big
300 static THD_WORKING_AREA(visualizerThreadStack, 1024);
301 static THD_FUNCTION(visualizerThread, arg) {
302     (void)arg;
303
304     event_listener_t event_listener;
305     chEvtRegister(&layer_changed_event, &event_listener, 0);
306
307     visualizer_keyboard_status_t initial_status = {
308         .default_layer = 0xFFFFFFFF,
309         .layer = 0xFFFFFFFF,
310         .leds = 0xFFFFFFFF,
311         .suspended = false,
312     };
313
314     visualizer_state_t state = {
315         .status = initial_status,
316         .current_lcd_color = 0,
317 #ifdef LCD_ENABLE
318         .font_fixed5x8 = gdispOpenFont("fixed_5x8"),
319         .font_dejavusansbold12 = gdispOpenFont("DejaVuSansBold12")
320 #endif
321     };
322     initialize_user_visualizer(&state);
323     state.prev_lcd_color = state.current_lcd_color;
324
325 #ifdef LCD_BACKLIGHT_ENABLE
326     lcd_backlight_color(
327             LCD_HUE(state.current_lcd_color),
328             LCD_SAT(state.current_lcd_color),
329             LCD_INT(state.current_lcd_color));
330 #endif
331
332     systime_t sleep_time = TIME_INFINITE;
333     systime_t current_time = chVTGetSystemTimeX();
334
335     while(true) {
336         systime_t new_time = chVTGetSystemTimeX();
337         systime_t delta = new_time - current_time;
338         current_time = new_time;
339         bool enabled = visualizer_enabled;
340         if (!same_status(&state.status, &current_status)) {
341             if (visualizer_enabled) {
342                 if (current_status.suspended) {
343                     stop_all_keyframe_animations();
344                     visualizer_enabled = false;
345                     state.status = current_status;
346                     user_visualizer_suspend(&state);
347                 }
348                 else {
349                     state.status = current_status;
350                     update_user_visualizer_state(&state);
351                 }
352                 state.prev_lcd_color = state.current_lcd_color;
353             }
354         }
355         if (!enabled && state.status.suspended && current_status.suspended == false) {
356             // Setting the status to the initial status will force an update
357             // when the visualizer is enabled again
358             state.status = initial_status;
359             state.status.suspended = false;
360             stop_all_keyframe_animations();
361             user_visualizer_resume(&state);
362             state.prev_lcd_color = state.current_lcd_color;
363         }
364         sleep_time = TIME_INFINITE;
365         for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
366             if (animations[i]) {
367                 update_keyframe_animation(animations[i], &state, delta, &sleep_time);
368             }
369         }
370         // The animation can enable the visualizer
371         // And we might need to update the state when that happens
372         // so don't sleep
373         if (enabled != visualizer_enabled) {
374             sleep_time = 0;
375         }
376
377         systime_t after_update = chVTGetSystemTimeX();
378         unsigned update_delta = after_update - current_time;
379         if (sleep_time != TIME_INFINITE) {
380             if (sleep_time > update_delta) {
381                 sleep_time -= update_delta;
382             }
383             else {
384                 sleep_time = 0;
385             }
386         }
387         dprintf("Update took %d, last delta %d, sleep_time %d\n", update_delta, delta, sleep_time);
388         chEvtWaitOneTimeout(EVENT_MASK(0), sleep_time);
389     }
390 #ifdef LCD_ENABLE
391     gdispCloseFont(state.font_fixed5x8);
392     gdispCloseFont(state.font_dejavusansbold12);
393 #endif
394 }
395
396 void visualizer_init(void) {
397 #ifdef LCD_ENABLE
398     gfxInit();
399 #endif
400
401 #ifdef LCD_BACKLIGHT_ENABLE
402     lcd_backlight_init();
403 #endif
404
405 #ifdef USE_SERIAL_LINK
406     add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*) );
407 #endif
408     // We are using a low priority thread, the idea is to have it run only
409     // when the main thread is sleeping during the matrix scanning
410     chEvtObjectInit(&layer_changed_event);
411     (void)chThdCreateStatic(visualizerThreadStack, sizeof(visualizerThreadStack),
412                               LOWPRIO, visualizerThread, NULL);
413 }
414
415 void update_status(bool changed) {
416     if (changed) {
417         chEvtBroadcast(&layer_changed_event);
418     }
419 #ifdef USE_SERIAL_LINK
420     static systime_t last_update = 0;
421     systime_t current_update = chVTGetSystemTimeX();
422     systime_t delta = current_update - last_update;
423     if (changed || delta > MS2ST(10)) {
424         last_update = current_update;
425         visualizer_keyboard_status_t* r = begin_write_current_status();
426         *r = current_status;
427         end_write_current_status();
428     }
429 #endif
430 }
431
432 void visualizer_update(uint32_t default_state, uint32_t state, uint32_t leds) {
433     // Note that there's a small race condition here, the thread could read
434     // a state where one of these are set but not the other. But this should
435     // not really matter as it will be fixed during the next loop step.
436     // Alternatively a mutex could be used instead of the volatile variables
437
438     bool changed = false;
439 #ifdef USE_SERIAL_LINK
440     if (is_serial_link_connected ()) {
441         visualizer_keyboard_status_t* new_status = read_current_status();
442         if (new_status) {
443             if (!same_status(&current_status, new_status)) {
444                 changed = true;
445                 current_status = *new_status;
446             }
447         }
448     }
449     else {
450 #else
451    {
452 #endif
453         visualizer_keyboard_status_t new_status = {
454             .layer = state,
455             .default_layer = default_state,
456             .leds = leds,
457             .suspended = current_status.suspended,
458         };
459         if (!same_status(&current_status, &new_status)) {
460             changed = true;
461             current_status = new_status;
462         }
463     }
464     update_status(changed);
465 }
466
467 void visualizer_suspend(void) {
468     current_status.suspended = true;
469     update_status(true);
470 }
471
472 void visualizer_resume(void) {
473     current_status.suspended = false;
474     update_status(true);
475 }