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