4 Copyright (c) 2016 Fred Sundvik
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:
13 The above copyright notice and this permission notice shall be included in all
14 copies or substantial portions of the Software.
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
26 #include "visualizer.h"
28 #ifdef PROTOCOL_CHIBIOS
34 #ifdef LCD_BACKLIGHT_ENABLE
35 #include "lcd_backlight.h"
38 //#define DEBUG_VISUALIZER
40 #ifdef DEBUG_VISUALIZER
46 #ifdef SERIAL_LINK_ENABLE
47 #include "serial_link/protocol/transport.h"
48 #include "serial_link/system/serial_link.h"
51 #include "action_util.h"
53 // Define this in config.h
54 #ifndef VISUALIZER_THREAD_PRIORITY
55 #define "Visualizer thread priority not defined"
58 static visualizer_keyboard_status_t current_status = {
60 .default_layer = 0xFFFFFFFF,
62 #ifdef BACKLIGHT_ENABLE
67 #ifdef VISUALIZER_USER_DATA_SIZE
72 static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboard_status_t* status2) {
73 return status1->layer == status2->layer &&
74 status1->default_layer == status2->default_layer &&
75 status1->mods == status2->mods &&
76 status1->leds == status2->leds &&
77 status1->suspended == status2->suspended
78 #ifdef BACKLIGHT_ENABLE
79 && status1->backlight_level == status2->backlight_level
81 #ifdef VISUALIZER_USER_DATA_SIZE
82 && memcmp(status1->user_data, status2->user_data, VISUALIZER_USER_DATA_SIZE) == 0
87 static bool visualizer_enabled = false;
89 #ifdef VISUALIZER_USER_DATA_SIZE
90 static uint8_t user_data[VISUALIZER_USER_DATA_SIZE];
93 #define MAX_SIMULTANEOUS_ANIMATIONS 4
94 static keyframe_animation_t* animations[MAX_SIMULTANEOUS_ANIMATIONS] = {};
96 #ifdef SERIAL_LINK_ENABLE
97 MASTER_TO_ALL_SLAVES_OBJECT(current_status, visualizer_keyboard_status_t);
99 static remote_object_t* remote_objects[] = {
100 REMOTE_OBJECT(current_status),
105 GDisplay* LCD_DISPLAY = 0;
106 GDisplay* LED_DISPLAY = 0;
108 #ifdef LCD_DISPLAY_NUMBER
109 __attribute__((weak))
110 GDisplay* get_lcd_display(void) {
111 return gdispGetDisplay(LCD_DISPLAY_NUMBER);
115 #ifdef LED_DISPLAY_NUMBER
116 __attribute__((weak))
117 GDisplay* get_led_display(void) {
118 return gdispGetDisplay(LED_DISPLAY_NUMBER);
122 void start_keyframe_animation(keyframe_animation_t* animation) {
123 animation->current_frame = -1;
124 animation->time_left_in_frame = 0;
125 animation->need_update = true;
127 for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
128 if (animations[i] == animation) {
131 if (free_index == -1 && animations[i] == NULL) {
135 if (free_index!=-1) {
136 animations[free_index] = animation;
140 void stop_keyframe_animation(keyframe_animation_t* animation) {
141 animation->current_frame = animation->num_frames;
142 animation->time_left_in_frame = 0;
143 animation->need_update = true;
144 animation->first_update_of_frame = false;
145 animation->last_update_of_frame = false;
146 for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
147 if (animations[i] == animation) {
148 animations[i] = NULL;
154 void stop_all_keyframe_animations(void) {
155 for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
157 animations[i]->current_frame = animations[i]->num_frames;
158 animations[i]->time_left_in_frame = 0;
159 animations[i]->need_update = true;
160 animations[i]->first_update_of_frame = false;
161 animations[i]->last_update_of_frame = false;
162 animations[i] = NULL;
167 static uint8_t get_num_running_animations(void) {
169 for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
170 count += animations[i] ? 1 : 0;
175 static bool update_keyframe_animation(keyframe_animation_t* animation, visualizer_state_t* state, systemticks_t delta, systemticks_t* sleep_time) {
176 // TODO: Clean up this messy code
177 dprintf("Animation frame%d, left %d, delta %d\n", animation->current_frame,
178 animation->time_left_in_frame, delta);
179 if (animation->current_frame == animation->num_frames) {
180 animation->need_update = false;
183 if (animation->current_frame == -1) {
184 animation->current_frame = 0;
185 animation->time_left_in_frame = animation->frame_lengths[0];
186 animation->need_update = true;
187 animation->first_update_of_frame = true;
189 animation->time_left_in_frame -= delta;
190 while (animation->time_left_in_frame <= 0) {
191 int left = animation->time_left_in_frame;
192 if (animation->need_update) {
193 animation->time_left_in_frame = 0;
194 animation->last_update_of_frame = true;
195 (*animation->frame_functions[animation->current_frame])(animation, state);
196 animation->last_update_of_frame = false;
198 animation->current_frame++;
199 animation->need_update = true;
200 animation->first_update_of_frame = true;
201 if (animation->current_frame == animation->num_frames) {
202 if (animation->loop) {
203 animation->current_frame = 0;
206 stop_keyframe_animation(animation);
211 animation->time_left_in_frame = animation->frame_lengths[animation->current_frame];
212 animation->time_left_in_frame -= delta;
215 if (animation->need_update) {
216 animation->need_update = (*animation->frame_functions[animation->current_frame])(animation, state);
217 animation->first_update_of_frame = false;
220 systemticks_t wanted_sleep = animation->need_update ? gfxMillisecondsToTicks(10) : (unsigned)animation->time_left_in_frame;
221 if (wanted_sleep < *sleep_time) {
222 *sleep_time = wanted_sleep;
228 void run_next_keyframe(keyframe_animation_t* animation, visualizer_state_t* state) {
229 int next_frame = animation->current_frame + 1;
230 if (next_frame == animation->num_frames) {
233 keyframe_animation_t temp_animation = *animation;
234 temp_animation.current_frame = next_frame;
235 temp_animation.time_left_in_frame = animation->frame_lengths[next_frame];
236 temp_animation.first_update_of_frame = true;
237 temp_animation.last_update_of_frame = false;
238 temp_animation.need_update = false;
239 visualizer_state_t temp_state = *state;
240 (*temp_animation.frame_functions[next_frame])(&temp_animation, &temp_state);
243 // TODO: Optimize the stack size, this is probably way too big
244 static DECLARE_THREAD_STACK(visualizerThreadStack, 1024);
245 static DECLARE_THREAD_FUNCTION(visualizerThread, arg) {
248 GListener event_listener;
249 geventListenerInit(&event_listener);
250 geventAttachSource(&event_listener, (GSourceHandle)¤t_status, 0);
252 visualizer_keyboard_status_t initial_status = {
253 .default_layer = 0xFFFFFFFF,
258 #ifdef VISUALIZER_USER_DATA_SIZE
263 visualizer_state_t state = {
264 .status = initial_status,
265 .current_lcd_color = 0,
267 .font_fixed5x8 = gdispOpenFont("fixed_5x8"),
268 .font_dejavusansbold12 = gdispOpenFont("DejaVuSansBold12")
271 initialize_user_visualizer(&state);
272 state.prev_lcd_color = state.current_lcd_color;
274 #ifdef LCD_BACKLIGHT_ENABLE
276 LCD_HUE(state.current_lcd_color),
277 LCD_SAT(state.current_lcd_color),
278 LCD_INT(state.current_lcd_color));
281 systemticks_t sleep_time = TIME_INFINITE;
282 systemticks_t current_time = gfxSystemTicks();
283 bool force_update = true;
286 systemticks_t new_time = gfxSystemTicks();
287 systemticks_t delta = new_time - current_time;
288 current_time = new_time;
289 bool enabled = visualizer_enabled;
290 if (force_update || !same_status(&state.status, ¤t_status)) {
291 force_update = false;
293 if(current_status.backlight_level != state.status.backlight_level) {
294 if (current_status.backlight_level != 0) {
295 gdispGSetPowerMode(LED_DISPLAY, powerOn);
296 uint16_t percent = (uint16_t)current_status.backlight_level * 100 / BACKLIGHT_LEVELS;
297 gdispGSetBacklight(LED_DISPLAY, percent);
300 gdispGSetPowerMode(LED_DISPLAY, powerOff);
304 if (visualizer_enabled) {
305 if (current_status.suspended) {
306 stop_all_keyframe_animations();
307 visualizer_enabled = false;
308 state.status = current_status;
309 user_visualizer_suspend(&state);
312 visualizer_keyboard_status_t prev_status = state.status;
313 state.status = current_status;
314 update_user_visualizer_state(&state, &prev_status);
316 state.prev_lcd_color = state.current_lcd_color;
319 if (!enabled && state.status.suspended && current_status.suspended == false) {
320 // Setting the status to the initial status will force an update
321 // when the visualizer is enabled again
322 state.status = initial_status;
323 state.status.suspended = false;
324 stop_all_keyframe_animations();
325 user_visualizer_resume(&state);
326 state.prev_lcd_color = state.current_lcd_color;
328 sleep_time = TIME_INFINITE;
329 for (int i=0;i<MAX_SIMULTANEOUS_ANIMATIONS;i++) {
331 update_keyframe_animation(animations[i], &state, delta, &sleep_time);
334 #ifdef BACKLIGHT_ENABLE
335 gdispGFlush(LED_DISPLAY);
339 gdispGFlush(LCD_DISPLAY);
345 // Enable the visualizer when the startup or the suspend animation has finished
346 if (!visualizer_enabled && state.status.suspended == false && get_num_running_animations() == 0) {
347 visualizer_enabled = true;
352 systemticks_t after_update = gfxSystemTicks();
353 unsigned update_delta = after_update - current_time;
354 if (sleep_time != TIME_INFINITE) {
355 if (sleep_time > update_delta) {
356 sleep_time -= update_delta;
362 dprintf("Update took %d, last delta %d, sleep_time %d\n", update_delta, delta, sleep_time);
363 #ifdef PROTOCOL_CHIBIOS
364 // The gEventWait function really takes milliseconds, even if the documentation says ticks.
365 // Unfortunately there's no generic ugfx conversion from system time to milliseconds,
366 // so let's do it in a platform dependent way.
368 // On windows the system ticks is the same as milliseconds anyway
369 if (sleep_time != TIME_INFINITE) {
370 sleep_time = ST2MS(sleep_time);
373 geventEventWait(&event_listener, sleep_time);
376 gdispCloseFont(state.font_fixed5x8);
377 gdispCloseFont(state.font_dejavusansbold12);
383 void visualizer_init(void) {
386 #ifdef LCD_BACKLIGHT_ENABLE
387 lcd_backlight_init();
390 #ifdef SERIAL_LINK_ENABLE
391 add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*) );
395 LCD_DISPLAY = get_lcd_display();
398 #ifdef BACKLIGHT_ENABLE
399 LED_DISPLAY = get_led_display();
402 // We are using a low priority thread, the idea is to have it run only
403 // when the main thread is sleeping during the matrix scanning
404 gfxThreadCreate(visualizerThreadStack, sizeof(visualizerThreadStack),
405 VISUALIZER_THREAD_PRIORITY, visualizerThread, NULL);
408 void update_status(bool changed) {
410 GSourceListener* listener = geventGetSourceListener((GSourceHandle)¤t_status, NULL);
412 geventSendEvent(listener);
415 #ifdef SERIAL_LINK_ENABLE
416 static systime_t last_update = 0;
417 systime_t current_update = chVTGetSystemTimeX();
418 systime_t delta = current_update - last_update;
419 if (changed || delta > MS2ST(10)) {
420 last_update = current_update;
421 visualizer_keyboard_status_t* r = begin_write_current_status();
423 end_write_current_status();
428 uint8_t visualizer_get_mods() {
429 uint8_t mods = get_mods();
431 #ifndef NO_ACTION_ONESHOT
432 if (!has_oneshot_mods_timed_out()) {
433 mods |= get_oneshot_mods();
439 #ifdef VISUALIZER_USER_DATA_SIZE
440 void visualizer_set_user_data(void* u) {
441 memcpy(user_data, u, VISUALIZER_USER_DATA_SIZE);
445 void visualizer_update(uint32_t default_state, uint32_t state, uint8_t mods, uint32_t leds) {
446 // Note that there's a small race condition here, the thread could read
447 // a state where one of these are set but not the other. But this should
448 // not really matter as it will be fixed during the next loop step.
449 // Alternatively a mutex could be used instead of the volatile variables
451 bool changed = false;
452 #ifdef SERIAL_LINK_ENABLE
453 if (is_serial_link_connected ()) {
454 visualizer_keyboard_status_t* new_status = read_current_status();
456 if (!same_status(¤t_status, new_status)) {
458 current_status = *new_status;
466 visualizer_keyboard_status_t new_status = {
468 .default_layer = default_state,
471 #ifdef BACKLIGHT_ENABLE
472 .backlight_level = current_status.backlight_level,
474 .suspended = current_status.suspended,
476 #ifdef VISUALIZER_USER_DATA_SIZE
477 memcpy(new_status.user_data, user_data, VISUALIZER_USER_DATA_SIZE);
479 if (!same_status(¤t_status, &new_status)) {
481 current_status = new_status;
484 update_status(changed);
487 void visualizer_suspend(void) {
488 current_status.suspended = true;
492 void visualizer_resume(void) {
493 current_status.suspended = false;
497 #ifdef BACKLIGHT_ENABLE
498 void backlight_set(uint8_t level) {
499 current_status.backlight_level = level;