diff options
Diffstat (limited to 'quantum/visualizer/visualizer.c')
-rw-r--r-- | quantum/visualizer/visualizer.c | 483 |
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 | /* | ||
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 "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 | |||
59 | static 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 | |||
72 | static 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 | |||
83 | static bool visualizer_enabled = false; | ||
84 | |||
85 | #ifdef VISUALIZER_USER_DATA_SIZE | ||
86 | static uint8_t user_data[VISUALIZER_USER_DATA_SIZE]; | ||
87 | #endif | ||
88 | |||
89 | #define MAX_SIMULTANEOUS_ANIMATIONS 4 | ||
90 | static keyframe_animation_t* animations[MAX_SIMULTANEOUS_ANIMATIONS] = {}; | ||
91 | |||
92 | #ifdef SERIAL_LINK_ENABLE | ||
93 | MASTER_TO_ALL_SLAVES_OBJECT(current_status, visualizer_keyboard_status_t); | ||
94 | |||
95 | static remote_object_t* remote_objects[] = { | ||
96 | REMOTE_OBJECT(current_status), | ||
97 | }; | ||
98 | |||
99 | #endif | ||
100 | |||
101 | GDisplay* LCD_DISPLAY = 0; | ||
102 | GDisplay* 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 | |||
112 | void 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 | |||
130 | void 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 | |||
144 | void 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 | |||
157 | static 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 | |||
165 | static 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 | |||
216 | void 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 | ||
232 | static DECLARE_THREAD_STACK(visualizerThreadStack, 1024); | ||
233 | static DECLARE_THREAD_FUNCTION(visualizerThread, arg) { | ||
234 | (void)arg; | ||
235 | |||
236 | GListener event_listener; | ||
237 | geventListenerInit(&event_listener); | ||
238 | geventAttachSource(&event_listener, (GSourceHandle)¤t_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, ¤t_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 | |||
368 | void 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 | |||
392 | void update_status(bool changed) { | ||
393 | if (changed) { | ||
394 | GSourceListener* listener = geventGetSourceListener((GSourceHandle)¤t_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 | |||
412 | uint8_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 | ||
424 | void visualizer_set_user_data(void* u) { memcpy(user_data, u, VISUALIZER_USER_DATA_SIZE); } | ||
425 | #endif | ||
426 | |||
427 | void 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(¤t_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(¤t_status, &new_status)) { | ||
461 | changed = true; | ||
462 | current_status = new_status; | ||
463 | } | ||
464 | } | ||
465 | update_status(changed); | ||
466 | } | ||
467 | |||
468 | void visualizer_suspend(void) { | ||
469 | current_status.suspended = true; | ||
470 | update_status(true); | ||
471 | } | ||
472 | |||
473 | void visualizer_resume(void) { | ||
474 | current_status.suspended = false; | ||
475 | update_status(true); | ||
476 | } | ||
477 | |||
478 | #ifdef BACKLIGHT_ENABLE | ||
479 | void backlight_set(uint8_t level) { | ||
480 | current_status.backlight_level = level; | ||
481 | update_status(true); | ||
482 | } | ||
483 | #endif | ||