aboutsummaryrefslogtreecommitdiff
path: root/quantum/visualizer/visualizer.c
diff options
context:
space:
mode:
Diffstat (limited to 'quantum/visualizer/visualizer.c')
-rw-r--r--quantum/visualizer/visualizer.c483
1 files changed, 0 insertions, 483 deletions
diff --git a/quantum/visualizer/visualizer.c b/quantum/visualizer/visualizer.c
deleted file mode 100644
index 709affbb7..000000000
--- a/quantum/visualizer/visualizer.c
+++ /dev/null
@@ -1,483 +0,0 @@
1/*
2The MIT License (MIT)
3
4Copyright (c) 2016 Fred Sundvik
5
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
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// The visualizer needs gfx thread priorities
56# define VISUALIZER_THREAD_PRIORITY (NORMAL_PRIORITY - 2)
57#endif
58
59static visualizer_keyboard_status_t current_status = {.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
72static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboard_status_t* status2) {
73 return status1->layer == status2->layer && status1->default_layer == status2->default_layer && status1->mods == status2->mods && status1->leds == status2->leds && status1->suspended == status2->suspended
74#ifdef BACKLIGHT_ENABLE
75 && status1->backlight_level == status2->backlight_level
76#endif
77#ifdef VISUALIZER_USER_DATA_SIZE
78 && memcmp(status1->user_data, status2->user_data, VISUALIZER_USER_DATA_SIZE) == 0
79#endif
80 ;
81}
82
83static bool visualizer_enabled = false;
84
85#ifdef VISUALIZER_USER_DATA_SIZE
86static uint8_t user_data[VISUALIZER_USER_DATA_SIZE];
87#endif
88
89#define MAX_SIMULTANEOUS_ANIMATIONS 4
90static keyframe_animation_t* animations[MAX_SIMULTANEOUS_ANIMATIONS] = {};
91
92#ifdef SERIAL_LINK_ENABLE
93MASTER_TO_ALL_SLAVES_OBJECT(current_status, visualizer_keyboard_status_t);
94
95static remote_object_t* remote_objects[] = {
96 REMOTE_OBJECT(current_status),
97};
98
99#endif
100
101GDisplay* LCD_DISPLAY = 0;
102GDisplay* LED_DISPLAY = 0;
103
104#ifdef LCD_DISPLAY_NUMBER
105__attribute__((weak)) GDisplay* get_lcd_display(void) { return gdispGetDisplay(LCD_DISPLAY_NUMBER); }
106#endif
107
108#ifdef LED_DISPLAY_NUMBER
109__attribute__((weak)) GDisplay* get_led_display(void) { return gdispGetDisplay(LED_DISPLAY_NUMBER); }
110#endif
111
112void start_keyframe_animation(keyframe_animation_t* animation) {
113 animation->current_frame = -1;
114 animation->time_left_in_frame = 0;
115 animation->need_update = true;
116 int free_index = -1;
117 for (int i = 0; i < MAX_SIMULTANEOUS_ANIMATIONS; i++) {
118 if (animations[i] == animation) {
119 return;
120 }
121 if (free_index == -1 && animations[i] == NULL) {
122 free_index = i;
123 }
124 }
125 if (free_index != -1) {
126 animations[free_index] = animation;
127 }
128}
129
130void stop_keyframe_animation(keyframe_animation_t* animation) {
131 animation->current_frame = animation->num_frames;
132 animation->time_left_in_frame = 0;
133 animation->need_update = true;
134 animation->first_update_of_frame = false;
135 animation->last_update_of_frame = false;
136 for (int i = 0; i < MAX_SIMULTANEOUS_ANIMATIONS; i++) {
137 if (animations[i] == animation) {
138 animations[i] = NULL;
139 return;
140 }
141 }
142}
143
144void stop_all_keyframe_animations(void) {
145 for (int i = 0; i < MAX_SIMULTANEOUS_ANIMATIONS; i++) {
146 if (animations[i]) {
147 animations[i]->current_frame = animations[i]->num_frames;
148 animations[i]->time_left_in_frame = 0;
149 animations[i]->need_update = true;
150 animations[i]->first_update_of_frame = false;
151 animations[i]->last_update_of_frame = false;
152 animations[i] = NULL;
153 }
154 }
155}
156
157static uint8_t get_num_running_animations(void) {
158 uint8_t count = 0;
159 for (int i = 0; i < MAX_SIMULTANEOUS_ANIMATIONS; i++) {
160 count += animations[i] ? 1 : 0;
161 }
162 return count;
163}
164
165static bool update_keyframe_animation(keyframe_animation_t* animation, visualizer_state_t* state, systemticks_t delta, systemticks_t* sleep_time) {
166 // TODO: Clean up this messy code
167 dprintf("Animation frame%d, left %d, delta %d\n", animation->current_frame, animation->time_left_in_frame, delta);
168 if (animation->current_frame == animation->num_frames) {
169 animation->need_update = false;
170 return false;
171 }
172 if (animation->current_frame == -1) {
173 animation->current_frame = 0;
174 animation->time_left_in_frame = animation->frame_lengths[0];
175 animation->need_update = true;
176 animation->first_update_of_frame = true;
177 } else {
178 animation->time_left_in_frame -= delta;
179 while (animation->time_left_in_frame <= 0) {
180 int left = animation->time_left_in_frame;
181 if (animation->need_update) {
182 animation->time_left_in_frame = 0;
183 animation->last_update_of_frame = true;
184 (*animation->frame_functions[animation->current_frame])(animation, state);
185 animation->last_update_of_frame = false;
186 }
187 animation->current_frame++;
188 animation->need_update = true;
189 animation->first_update_of_frame = true;
190 if (animation->current_frame == animation->num_frames) {
191 if (animation->loop) {
192 animation->current_frame = 0;
193 } else {
194 stop_keyframe_animation(animation);
195 return false;
196 }
197 }
198 delta = -left;
199 animation->time_left_in_frame = animation->frame_lengths[animation->current_frame];
200 animation->time_left_in_frame -= delta;
201 }
202 }
203 if (animation->need_update) {
204 animation->need_update = (*animation->frame_functions[animation->current_frame])(animation, state);
205 animation->first_update_of_frame = false;
206 }
207
208 systemticks_t wanted_sleep = animation->need_update ? gfxMillisecondsToTicks(10) : (unsigned)animation->time_left_in_frame;
209 if (wanted_sleep < *sleep_time) {
210 *sleep_time = wanted_sleep;
211 }
212
213 return true;
214}
215
216void run_next_keyframe(keyframe_animation_t* animation, visualizer_state_t* state) {
217 int next_frame = animation->current_frame + 1;
218 if (next_frame == animation->num_frames) {
219 next_frame = 0;
220 }
221 keyframe_animation_t temp_animation = *animation;
222 temp_animation.current_frame = next_frame;
223 temp_animation.time_left_in_frame = animation->frame_lengths[next_frame];
224 temp_animation.first_update_of_frame = true;
225 temp_animation.last_update_of_frame = false;
226 temp_animation.need_update = false;
227 visualizer_state_t temp_state = *state;
228 (*temp_animation.frame_functions[next_frame])(&temp_animation, &temp_state);
229}
230
231// TODO: Optimize the stack size, this is probably way too big
232static DECLARE_THREAD_STACK(visualizerThreadStack, 1024);
233static DECLARE_THREAD_FUNCTION(visualizerThread, arg) {
234 (void)arg;
235
236 GListener event_listener;
237 geventListenerInit(&event_listener);
238 geventAttachSource(&event_listener, (GSourceHandle)&current_status, 0);
239
240 visualizer_keyboard_status_t initial_status = {
241 .default_layer = 0xFFFFFFFF,
242 .layer = 0xFFFFFFFF,
243 .mods = 0xFF,
244 .leds = 0xFFFFFFFF,
245 .suspended = false,
246#ifdef BACKLIGHT_ENABLE
247 .backlight_level = 0,
248#endif
249#ifdef VISUALIZER_USER_DATA_SIZE
250 .user_data = {0},
251#endif
252 };
253
254 visualizer_state_t state = {.status = initial_status,
255 .current_lcd_color = 0,
256#ifdef LCD_ENABLE
257 .font_fixed5x8 = gdispOpenFont("fixed_5x8"),
258 .font_dejavusansbold12 = gdispOpenFont("DejaVuSansBold12")
259#endif
260 };
261 initialize_user_visualizer(&state);
262 state.prev_lcd_color = state.current_lcd_color;
263
264#ifdef LCD_BACKLIGHT_ENABLE
265 lcd_backlight_color(LCD_HUE(state.current_lcd_color), LCD_SAT(state.current_lcd_color), LCD_INT(state.current_lcd_color));
266#endif
267
268 systemticks_t sleep_time = TIME_INFINITE;
269 systemticks_t current_time = gfxSystemTicks();
270 bool force_update = true;
271
272 while (true) {
273 systemticks_t new_time = gfxSystemTicks();
274 systemticks_t delta = new_time - current_time;
275 current_time = new_time;
276 bool enabled = visualizer_enabled;
277 if (force_update || !same_status(&state.status, &current_status)) {
278 force_update = false;
279#if BACKLIGHT_ENABLE
280 if (current_status.backlight_level != state.status.backlight_level) {
281 if (current_status.backlight_level != 0) {
282 gdispGSetPowerMode(LED_DISPLAY, powerOn);
283 uint16_t percent = (uint16_t)current_status.backlight_level * 100 / BACKLIGHT_LEVELS;
284 gdispGSetBacklight(LED_DISPLAY, percent);
285 } else {
286 gdispGSetPowerMode(LED_DISPLAY, powerOff);
287 }
288 state.status.backlight_level = current_status.backlight_level;
289 }
290#endif
291 if (visualizer_enabled) {
292 if (current_status.suspended) {
293 stop_all_keyframe_animations();
294 visualizer_enabled = false;
295 state.status = current_status;
296 user_visualizer_suspend(&state);
297 } else {
298 visualizer_keyboard_status_t prev_status = state.status;
299 state.status = current_status;
300 update_user_visualizer_state(&state, &prev_status);
301 }
302 state.prev_lcd_color = state.current_lcd_color;
303 }
304 }
305 if (!enabled && state.status.suspended && current_status.suspended == false) {
306 // Setting the status to the initial status will force an update
307 // when the visualizer is enabled again
308 state.status = initial_status;
309 state.status.suspended = false;
310 stop_all_keyframe_animations();
311 user_visualizer_resume(&state);
312 state.prev_lcd_color = state.current_lcd_color;
313 }
314 sleep_time = TIME_INFINITE;
315 for (int i = 0; i < MAX_SIMULTANEOUS_ANIMATIONS; i++) {
316 if (animations[i]) {
317 update_keyframe_animation(animations[i], &state, delta, &sleep_time);
318 }
319 }
320#ifdef BACKLIGHT_ENABLE
321 gdispGFlush(LED_DISPLAY);
322#endif
323
324#ifdef LCD_ENABLE
325 gdispGFlush(LCD_DISPLAY);
326#endif
327
328#ifdef EMULATOR
329 draw_emulator();
330#endif
331 // Enable the visualizer when the startup or the suspend animation has finished
332 if (!visualizer_enabled && state.status.suspended == false && get_num_running_animations() == 0) {
333 visualizer_enabled = true;
334 force_update = true;
335 sleep_time = 0;
336 }
337
338 systemticks_t after_update = gfxSystemTicks();
339 unsigned update_delta = after_update - current_time;
340 if (sleep_time != TIME_INFINITE) {
341 if (sleep_time > update_delta) {
342 sleep_time -= update_delta;
343 } else {
344 sleep_time = 0;
345 }
346 }
347 dprintf("Update took %d, last delta %d, sleep_time %d\n", update_delta, delta, sleep_time);
348#ifdef PROTOCOL_CHIBIOS
349 // The gEventWait function really takes milliseconds, even if the documentation says ticks.
350 // Unfortunately there's no generic ugfx conversion from system time to milliseconds,
351 // so let's do it in a platform dependent way.
352
353 // On windows the system ticks is the same as milliseconds anyway
354 if (sleep_time != TIME_INFINITE) {
355 sleep_time = TIME_I2MS(sleep_time);
356 }
357#endif
358 geventEventWait(&event_listener, sleep_time);
359 }
360#ifdef LCD_ENABLE
361 gdispCloseFont(state.font_fixed5x8);
362 gdispCloseFont(state.font_dejavusansbold12);
363#endif
364
365 return 0;
366}
367
368void visualizer_init(void) {
369 gfxInit();
370
371#ifdef LCD_BACKLIGHT_ENABLE
372 lcd_backlight_init();
373#endif
374
375#ifdef SERIAL_LINK_ENABLE
376 add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*));
377#endif
378
379#ifdef LCD_ENABLE
380 LCD_DISPLAY = get_lcd_display();
381#endif
382
383#ifdef BACKLIGHT_ENABLE
384 LED_DISPLAY = get_led_display();
385#endif
386
387 // We are using a low priority thread, the idea is to have it run only
388 // when the main thread is sleeping during the matrix scanning
389 gfxThreadCreate(visualizerThreadStack, sizeof(visualizerThreadStack), VISUALIZER_THREAD_PRIORITY, visualizerThread, NULL);
390}
391
392void update_status(bool changed) {
393 if (changed) {
394 GSourceListener* listener = geventGetSourceListener((GSourceHandle)&current_status, NULL);
395 if (listener) {
396 geventSendEvent(listener);
397 }
398 }
399#ifdef SERIAL_LINK_ENABLE
400 static systime_t last_update = 0;
401 systime_t current_update = chVTGetSystemTimeX();
402 systime_t delta = current_update - last_update;
403 if (changed || delta > TIME_MS2I(10)) {
404 last_update = current_update;
405 visualizer_keyboard_status_t* r = begin_write_current_status();
406 *r = current_status;
407 end_write_current_status();
408 }
409#endif
410}
411
412uint8_t visualizer_get_mods() {
413 uint8_t mods = get_mods();
414
415#ifndef NO_ACTION_ONESHOT
416 if (!has_oneshot_mods_timed_out()) {
417 mods |= get_oneshot_mods();
418 }
419#endif
420 return mods;
421}
422
423#ifdef VISUALIZER_USER_DATA_SIZE
424void visualizer_set_user_data(void* u) { memcpy(user_data, u, VISUALIZER_USER_DATA_SIZE); }
425#endif
426
427void visualizer_update(layer_state_t default_state, layer_state_t state, uint8_t mods, uint32_t leds) {
428 // Note that there's a small race condition here, the thread could read
429 // a state where one of these are set but not the other. But this should
430 // not really matter as it will be fixed during the next loop step.
431 // Alternatively a mutex could be used instead of the volatile variables
432
433 bool changed = false;
434#ifdef SERIAL_LINK_ENABLE
435 if (is_serial_link_connected()) {
436 visualizer_keyboard_status_t* new_status = read_current_status();
437 if (new_status) {
438 if (!same_status(&current_status, new_status)) {
439 changed = true;
440 current_status = *new_status;
441 }
442 }
443 } else {
444#else
445 {
446#endif
447 visualizer_keyboard_status_t new_status = {
448 .layer = state,
449 .default_layer = default_state,
450 .mods = mods,
451 .leds = leds,
452#ifdef BACKLIGHT_ENABLE
453 .backlight_level = current_status.backlight_level,
454#endif
455 .suspended = current_status.suspended,
456 };
457#ifdef VISUALIZER_USER_DATA_SIZE
458 memcpy(new_status.user_data, user_data, VISUALIZER_USER_DATA_SIZE);
459#endif
460 if (!same_status(&current_status, &new_status)) {
461 changed = true;
462 current_status = new_status;
463 }
464 }
465 update_status(changed);
466}
467
468void visualizer_suspend(void) {
469 current_status.suspended = true;
470 update_status(true);
471}
472
473void visualizer_resume(void) {
474 current_status.suspended = false;
475 update_status(true);
476}
477
478#ifdef BACKLIGHT_ENABLE
479void backlight_set(uint8_t level) {
480 current_status.backlight_level = level;
481 update_status(true);
482}
483#endif