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