From 3568065141279baa3dfff0e6a65fbf6ab8d14676 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Fri, 12 Feb 2016 09:33:38 +0200 Subject: [PATCH 01/41] Add readme --- readme.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 readme.md diff --git a/readme.md b/readme.md new file mode 100644 index 0000000000..82bf8bfdfa --- /dev/null +++ b/readme.md @@ -0,0 +1 @@ +A visualization library for the TMK keyboard firmware From 4452f4965dae25bb3707e64858221784318c4f56 Mon Sep 17 00:00:00 2001 From: fredizzimo Date: Fri, 12 Feb 2016 09:59:39 +0200 Subject: [PATCH 02/41] Add MIT license --- LICENSE.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000000..d7cc3198cb --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From bb75446b0beb4db28b775a2e2db61d91f0f0324a Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 13 Feb 2016 16:44:14 +0200 Subject: [PATCH 03/41] Add uGFX submodule v 2.4 --- .gitmodules | 3 +++ ugfx | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 ugfx diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..2ab25f6883 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "ugfx"] + path = ugfx + url = https://bitbucket.org/Tectu/ugfx.git diff --git a/ugfx b/ugfx new file mode 160000 index 0000000000..2b66ac524b --- /dev/null +++ b/ugfx @@ -0,0 +1 @@ +Subproject commit 2b66ac524bd56853ba97b917683971f3ebc0104c From fb681b5a60ae5b13a0413c815b4564d60fa200c5 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 13 Feb 2016 17:47:39 +0200 Subject: [PATCH 04/41] Add makefile that includes ugfx --- visualizer.mk | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 visualizer.mk diff --git a/visualizer.mk b/visualizer.mk new file mode 100644 index 0000000000..c5ae0dfa95 --- /dev/null +++ b/visualizer.mk @@ -0,0 +1,26 @@ +# The MIT License (MIT) +# +# Copyright (c) 2016 Fred Sundvik +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +GFXLIB = $(VISUALIZER_DIR)/ugfx +include $(GFXLIB)/gfx.mk +SRC += $(GFXSRC) +INC += $(GFXINC) From 01b955aa64766d51191a716d3a9d74d35f221b28 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 13 Feb 2016 19:21:16 +0200 Subject: [PATCH 05/41] Add LCD backlight support Also possibility to disable the LCD support --- lcd_backlight.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++ lcd_backlight.h | 42 ++++++++++++++++++++++++ visualizer.mk | 9 +++++- 3 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 lcd_backlight.c create mode 100644 lcd_backlight.h diff --git a/lcd_backlight.c b/lcd_backlight.c new file mode 100644 index 0000000000..70187d1e00 --- /dev/null +++ b/lcd_backlight.c @@ -0,0 +1,85 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "lcd_backlight.h" +#include + +static uint8_t current_hue = 0x00; +static uint8_t current_saturation = 0x00; +static uint8_t current_intensity = 0xFF; +static uint8_t current_brightness = 0x7F; + +void lcd_backlight_init(void) { + lcd_backlight_hal_init(); + lcd_backlight_color(current_hue, current_saturation, current_intensity); +} + +// This code is based on Brian Neltner's blogpost and example code +// "Why every LED light should be using HSI colorspace". +// http://blog.saikoled.com/post/43693602826/why-every-led-light-should-be-using-hsi +static void hsi_to_rgb(float h, float s, float i, uint16_t* r_out, uint16_t* g_out, uint16_t* b_out) { + unsigned int r, g, b; + h = fmodf(h, 360.0f); // cycle h around to 0-360 degrees + h = 3.14159f * h / 180.0f; // Convert to radians. + s = s > 0.0f ? (s < 1.0f ? s : 1.0f) : 0.0f; // clamp s and i to interval [0,1] + i = i > 0.0f ? (i < 1.0f ? i : 1.0f) : 0.0f; + + // Math! Thanks in part to Kyle Miller. + if(h < 2.09439f) { + r = 65535.0f * i/3.0f *(1.0f + s * cos(h) / cosf(1.047196667f - h)); + g = 65535.0f * i/3.0f *(1.0f + s *(1.0f - cosf(h) / cos(1.047196667f - h))); + b = 65535.0f * i/3.0f *(1.0f - s); + } else if(h < 4.188787) { + h = h - 2.09439; + g = 65535.0f * i/3.0f *(1.0f + s * cosf(h) / cosf(1.047196667f - h)); + b = 65535.0f * i/3.0f *(1.0f + s * (1.0f - cosf(h) / cosf(1.047196667f - h))); + r = 65535.0f * i/3.0f *(1.0f - s); + } else { + h = h - 4.188787; + b = 65535.0f*i/3.0f * (1.0f + s * cosf(h) / cosf(1.047196667f - h)); + r = 65535.0f*i/3.0f * (1.0f + s * (1.0f - cosf(h) / cosf(1.047196667f - h))); + g = 65535.0f*i/3.0f * (1.0f - s); + } + *r_out = r > 65535 ? 65535 : r; + *g_out = g > 65535 ? 65535 : g; + *b_out = b > 65535 ? 65535 : b; +} + +void lcd_backlight_color(uint8_t hue, uint8_t saturation, uint8_t intensity) { + uint16_t r, g, b; + float hue_f = 360.0f * (float)hue / 255.0f; + float saturation_f = (float)saturation / 255.0f; + float intensity_f = (float)intensity / 255.0f; + intensity_f *= (float)current_brightness / 255.0f; + hsi_to_rgb(hue_f, saturation_f, intensity_f, &r, &g, &b); + current_hue = hue; + current_saturation = saturation; + current_intensity = intensity; + lcd_backlight_hal_color(r, g, b); +} + +void lcd_backlight_brightness(uint8_t b) { + current_brightness = b; + lcd_backlight_color(current_hue, current_saturation, current_intensity); +} diff --git a/lcd_backlight.h b/lcd_backlight.h new file mode 100644 index 0000000000..dd3e37a064 --- /dev/null +++ b/lcd_backlight.h @@ -0,0 +1,42 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef LCD_BACKLIGHT_H_ +#define LCD_BACKLIGHT_H_ +#include "stdint.h" + +// Helper macros for storing hue, staturation and intensity as unsigned integers +#define LCD_COLOR(hue, saturation, intensity) (hue << 16 | saturation << 8 | intensity) +#define LCD_HUE(color) ((color >> 16) & 0xFF) +#define LCD_SAT(color) ((color >> 8) & 0xFF) +#define LCD_INT(color) (color & 0xFF) + +void lcd_backlight_init(void); +void lcd_backlight_color(uint8_t hue, uint8_t saturation, uint8_t intensity); +void lcd_backlight_brightness(uint8_t b); + +void lcd_backlight_hal_init(void); +void lcd_backlight_hal_color(uint16_t r, uint16_t g, uint16_t b); + +#endif /* LCD_BACKLIGHT_H_ */ diff --git a/visualizer.mk b/visualizer.mk index c5ae0dfa95..8ffc1e4aca 100644 --- a/visualizer.mk +++ b/visualizer.mk @@ -21,6 +21,13 @@ # SOFTWARE. GFXLIB = $(VISUALIZER_DIR)/ugfx +ifdef LCD_ENABLE include $(GFXLIB)/gfx.mk +endif SRC += $(GFXSRC) -INC += $(GFXINC) +INC += $(GFXINC) $(VISUALIZER_DIR) + +ifdef LCD_BACKLIGHT_ENABLE +SRC += $(VISUALIZER_DIR)/lcd_backlight.c +SRC += lcd_backlight_hal.c +endif From 9e58d022ba4320ce0917defe4b81c1c7b5de77bb Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 13 Feb 2016 19:38:23 +0200 Subject: [PATCH 06/41] Add visualizer A generic visualizer that supports animations. There's a few predefined keyframe types included, and more can be added by the user. --- visualizer.c | 349 ++++++++++++++++++++++++++++++++++++++++++++++++++ visualizer.h | 120 +++++++++++++++++ visualizer.mk | 4 +- 3 files changed, 472 insertions(+), 1 deletion(-) create mode 100644 visualizer.c create mode 100644 visualizer.h diff --git a/visualizer.c b/visualizer.c new file mode 100644 index 0000000000..2a92524e25 --- /dev/null +++ b/visualizer.c @@ -0,0 +1,349 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "visualizer.h" +#include "ch.h" +#include + +#ifdef LCD_ENABLE +#include "gfx.h" +#endif + +#ifdef LCD_BACKLIGHT_ENABLE +#include "lcd_backlight.h" +#endif + +//#define DEBUG_VISUALIZER + +#ifdef DEBUG_VISUALIZER +#include "debug.h" +#else +#include "nodebug.h" +#endif + + +static visualizer_keyboard_status_t current_status = { + .layer = 0xFFFFFFFF, + .default_layer = 0xFFFFFFFF, + .leds = 0xFFFFFFFF, +}; + +static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboard_status_t* status2) { + return memcmp(status1, status2, sizeof(visualizer_keyboard_status_t)) == 0; +} + +static event_source_t layer_changed_event; +static bool visualizer_enabled = false; + +#define MAX_SIMULTANEOUS_ANIMATIONS 4 +static keyframe_animation_t* animations[MAX_SIMULTANEOUS_ANIMATIONS] = {}; + +void start_keyframe_animation(keyframe_animation_t* animation) { + animation->current_frame = -1; + animation->time_left_in_frame = 0; + animation->need_update = true; + int free_index = -1; + for (int i=0;icurrent_frame = animation->num_frames; + animation->time_left_in_frame = 0; + animation->need_update = true; + for (int i=0;icurrent_frame, + animation->time_left_in_frame, delta); + if (animation->current_frame == animation->num_frames) { + animation->need_update = false; + return false; + } + if (animation->current_frame == -1) { + animation->current_frame = 0; + animation->time_left_in_frame = animation->frame_lengths[0]; + animation->need_update = true; + } else { + animation->time_left_in_frame -= delta; + while (animation->time_left_in_frame <= 0) { + int left = animation->time_left_in_frame; + if (animation->need_update) { + animation->time_left_in_frame = 0; + (*animation->frame_functions[animation->current_frame])(animation, state); + } + animation->current_frame++; + animation->need_update = true; + if (animation->current_frame == animation->num_frames) { + if (animation->loop) { + animation->current_frame = 0; + } + else { + stop_keyframe_animation(animation); + return false; + } + } + delta = -left; + animation->time_left_in_frame = animation->frame_lengths[animation->current_frame]; + animation->time_left_in_frame -= delta; + } + } + if (animation->need_update) { + animation->need_update = (*animation->frame_functions[animation->current_frame])(animation, state); + } + + int wanted_sleep = animation->need_update ? 10 : animation->time_left_in_frame; + if ((unsigned)wanted_sleep < *sleep_time) { + *sleep_time = wanted_sleep; + } + + return true; +} + +bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)animation; + (void)state; + return false; +} + +#ifdef LCD_BACKLIGHT_ENABLE +bool keyframe_animate_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state) { + int frame_length = animation->frame_lengths[1]; + int current_pos = frame_length - animation->time_left_in_frame; + uint8_t t_h = LCD_HUE(state->target_lcd_color); + uint8_t t_s = LCD_SAT(state->target_lcd_color); + uint8_t t_i = LCD_INT(state->target_lcd_color); + uint8_t p_h = LCD_HUE(state->prev_lcd_color); + uint8_t p_s = LCD_SAT(state->prev_lcd_color); + uint8_t p_i = LCD_INT(state->prev_lcd_color); + + uint8_t d_h1 = t_h - p_h; //Modulo arithmetic since we want to wrap around + int d_h2 = t_h - p_h; + // Chose the shortest way around + int d_h = abs(d_h2) < d_h1 ? d_h2 : d_h1; + int d_s = t_s - p_s; + int d_i = t_i - p_i; + + int hue = (d_h * current_pos) / frame_length; + int sat = (d_s * current_pos) / frame_length; + int intensity = (d_i * current_pos) / frame_length; + //dprintf("%X -> %X = %X\n", p_h, t_h, hue); + hue += p_h; + sat += p_s; + intensity += p_i; + state->current_lcd_color = LCD_COLOR(hue, sat, intensity); + lcd_backlight_color( + LCD_HUE(state->current_lcd_color), + LCD_SAT(state->current_lcd_color), + LCD_INT(state->current_lcd_color)); + + return true; +} + +bool keyframe_set_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)animation; + state->prev_lcd_color = state->target_lcd_color; + state->current_lcd_color = state->target_lcd_color; + lcd_backlight_color( + LCD_HUE(state->current_lcd_color), + LCD_SAT(state->current_lcd_color), + LCD_INT(state->current_lcd_color)); + return false; +} +#endif // LCD_BACKLIGHT_ENABLE + +#ifdef LCD_ENABLE +bool keyframe_display_layer_text(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)animation; + gdispClear(White); + gdispDrawString(0, 10, state->layer_text, state->font_dejavusansbold12, Black); + gdispFlush(); + return false; +} + +static void format_layer_bitmap_string(uint16_t default_layer, uint16_t layer, char* buffer) { + for (int i=0; i<16;i++) + { + uint32_t mask = (1u << i); + if (default_layer & mask) { + if (layer & mask) { + *buffer = 'B'; + } else { + *buffer = 'D'; + } + } else if (layer & mask) { + *buffer = '1'; + } else { + *buffer = '0'; + } + ++buffer; + + if (i==3 || i==7 || i==11) { + *buffer = ' '; + ++buffer; + } + } + *buffer = 0; +} + +bool keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)animation; + const char* layer_help = "1=On D=Default B=Both"; + char layer_buffer[16 + 4]; // 3 spaces and one null terminator + gdispClear(White); + gdispDrawString(0, 0, layer_help, state->font_fixed5x8, Black); + format_layer_bitmap_string(state->status.default_layer, state->status.layer, layer_buffer); + gdispDrawString(0, 10, layer_buffer, state->font_fixed5x8, Black); + format_layer_bitmap_string(state->status.default_layer >> 16, state->status.layer >> 16, layer_buffer); + gdispDrawString(0, 20, layer_buffer, state->font_fixed5x8, Black); + gdispFlush(); + return false; +} +#endif // LCD_ENABLE + +bool user_visualizer_inited(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)animation; + (void)state; + dprint("User visualizer inited\n"); + visualizer_enabled = true; + return false; +} + +// TODO: Optimize the stack size, this is probably way too big +static THD_WORKING_AREA(visualizerThreadStack, 1024); +static THD_FUNCTION(visualizerThread, arg) { + (void)arg; + + event_listener_t event_listener; + chEvtRegister(&layer_changed_event, &event_listener, 0); + + visualizer_state_t state = { + .status = { + .default_layer = 0xFFFFFFFF, + .layer = 0xFFFFFFFF, + .leds = 0xFFFFFFFF, + }, + + .current_lcd_color = 0, +#ifdef LCD_ENABLE + .font_fixed5x8 = gdispOpenFont("fixed_5x8"), + .font_dejavusansbold12 = gdispOpenFont("DejaVuSansBold12") +#endif + }; + initialize_user_visualizer(&state); + state.prev_lcd_color = state.current_lcd_color; + +#ifdef LCD_BACKLIGHT_ENABLE + lcd_backlight_color( + LCD_HUE(state.current_lcd_color), + LCD_SAT(state.current_lcd_color), + LCD_INT(state.current_lcd_color)); +#endif + + systime_t sleep_time = TIME_INFINITE; + systime_t current_time = chVTGetSystemTimeX(); + + while(true) { + systime_t new_time = chVTGetSystemTimeX(); + systime_t delta = new_time - current_time; + current_time = new_time; + bool enabled = visualizer_enabled; + if (!same_status(&state.status, ¤t_status)) { + if (visualizer_enabled) { + state.status = current_status; + update_user_visualizer_state(&state); + state.prev_lcd_color = state.current_lcd_color; + } + } + sleep_time = TIME_INFINITE; + for (int i=0;i update_delta) { + sleep_time -= update_delta; + } + else { + sleep_time = 0; + } + } + dprintf("Update took %d, last delta %d, sleep_time %d\n", update_delta, delta, sleep_time); + chEvtWaitOneTimeout(EVENT_MASK(0), sleep_time); + } +#ifdef LCD_ENABLE + gdispCloseFont(state.font_fixed5x8); + gdispCloseFont(state.font_dejavusansbold12); +#endif +} + +void visualizer_init(void) { + // We are using a low priority thread, the idea is to have it run only + // when the main thread is sleeping during the matrix scanning + chEvtObjectInit(&layer_changed_event); + (void)chThdCreateStatic(visualizerThreadStack, sizeof(visualizerThreadStack), + LOWPRIO, visualizerThread, NULL); +} + +void visualizer_set_state(uint32_t default_state, uint32_t state, uint32_t leds) { + // Note that there's a small race condition here, the thread could read + // a state where one of these are set but not the other. But this should + // not really matter as it will be fixed during the next loop step. + // Alternatively a mutex could be used instead of the volatile variables + bool changed = false; + visualizer_keyboard_status_t new_status = { + .layer = state, + .default_layer = default_state, + .leds = leds, + }; + if (!same_status(¤t_status, &new_status)) { + changed = true; + } + current_status = new_status; + if (changed) { + chEvtBroadcast(&layer_changed_event); + } +} diff --git a/visualizer.h b/visualizer.h new file mode 100644 index 0000000000..b7b0a3a7d0 --- /dev/null +++ b/visualizer.h @@ -0,0 +1,120 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef VISUALIZER_H +#define VISUALIZER_H +#include +#include +#include + +#ifdef LCD_ENABLE +#include "gfx.h" +#endif + +#ifdef LCD_BACKLIGHT_ENABLE +#include "lcd_backlight.h" +#endif + +// This need to be called once at the start +void visualizer_init(void); +// This should be called before every matrix scan +void visualizer_set_state(uint32_t default_state, uint32_t state, uint32_t leds); + +// If you need support for more than 8 keyframes per animation, you can change this +#define MAX_VISUALIZER_KEY_FRAMES 8 + +struct keyframe_animation_t; + +typedef struct { + uint32_t layer; + uint32_t default_layer; + uint32_t leds; // See led.h for available statuses +} visualizer_keyboard_status_t; + +// The state struct is used by the various keyframe functions +// It's also used for setting the LCD color and layer text +// from the user customized code +typedef struct visualizer_state_t { + // The user code should primarily be modifying these + uint32_t target_lcd_color; + const char* layer_text; + + // The user visualizer(and animation functions) can read these + visualizer_keyboard_status_t status; + + // These are used by the animation functions + uint32_t current_lcd_color; + uint32_t prev_lcd_color; +#ifdef LCD_ENABLE + font_t font_fixed5x8; + font_t font_dejavusansbold12; +#endif +} visualizer_state_t; + +// Any custom keyframe function should have this signature +// return true to get continuous updates, otherwise you will only get one +// update per frame +typedef bool (*frame_func)(struct keyframe_animation_t*, visualizer_state_t*); + +// Represents a keyframe animation, so fields are internal to the system +// while others are meant to be initialized by the user code +typedef struct keyframe_animation_t { + // These should be initialized + int num_frames; + bool loop; + int frame_lengths[MAX_VISUALIZER_KEY_FRAMES]; + frame_func frame_functions[MAX_VISUALIZER_KEY_FRAMES]; + + // Used internally by the system, and can also be read by + // keyframe update functions + int current_frame; + int time_left_in_frame; + bool need_update; + +} keyframe_animation_t; + +void start_keyframe_animation(keyframe_animation_t* animation); +void stop_keyframe_animation(keyframe_animation_t* animation); + +// Some predefined keyframe functions that can be used by the user code +// Does nothing, useful for adding delays +bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* state); +// Animates the LCD backlight color between the current color and the target color (of the state) +bool keyframe_animate_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state); +// Sets the backlight color to the target color +bool keyframe_set_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state); +// Displays the layer text centered vertically on the screen +bool keyframe_display_layer_text(keyframe_animation_t* animation, visualizer_state_t* state); +// Displays a bitmap (0/1) of all the currently active layers +bool keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_state_t* state); +// Call this once, when the initial animation has finished, alternatively you can call it +// directly from the initalize_user_visualizer function (the animation can be null) +bool user_visualizer_inited(keyframe_animation_t* animation, visualizer_state_t* state); + +// These two functions have to be implemented by the user +void initialize_user_visualizer(visualizer_state_t* state); +void update_user_visualizer_state(visualizer_state_t* state); + + +#endif /* VISUALIZER_H */ diff --git a/visualizer.mk b/visualizer.mk index 8ffc1e4aca..ff4b61f089 100644 --- a/visualizer.mk +++ b/visualizer.mk @@ -23,11 +23,13 @@ GFXLIB = $(VISUALIZER_DIR)/ugfx ifdef LCD_ENABLE include $(GFXLIB)/gfx.mk +OPT_DEFS += -DLCD_ENABLE endif -SRC += $(GFXSRC) +SRC += $(GFXSRC) $(VISUALIZER_DIR)/visualizer.c INC += $(GFXINC) $(VISUALIZER_DIR) ifdef LCD_BACKLIGHT_ENABLE SRC += $(VISUALIZER_DIR)/lcd_backlight.c SRC += lcd_backlight_hal.c +OPT_DEFS += -DLCD_BACKLIGHT_ENABLE endif From 209167d4d6a7e65a728fab5d996904cc5fb33a3c Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 13 Feb 2016 19:52:57 +0200 Subject: [PATCH 07/41] Compile the user visualizer --- visualizer.mk | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/visualizer.mk b/visualizer.mk index ff4b61f089..e6e0d63d03 100644 --- a/visualizer.mk +++ b/visualizer.mk @@ -33,3 +33,8 @@ SRC += $(VISUALIZER_DIR)/lcd_backlight.c SRC += lcd_backlight_hal.c OPT_DEFS += -DLCD_BACKLIGHT_ENABLE endif + +ifndef VISUALIZER_USER +VISUALIZER_USER = visualizer_user.c +endif +SRC += $(VISUALIZER_USER) \ No newline at end of file From 8ce60649c85f1ad5371d443675f91c8fc36c3d2e Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 13 Feb 2016 20:29:49 +0200 Subject: [PATCH 08/41] Initialize backlight and LCD during visualizer_init --- visualizer.c | 7 +++++++ visualizer.mk | 1 + 2 files changed, 8 insertions(+) diff --git a/visualizer.c b/visualizer.c index 2a92524e25..402bbd1517 100644 --- a/visualizer.c +++ b/visualizer.c @@ -321,6 +321,13 @@ static THD_FUNCTION(visualizerThread, arg) { } void visualizer_init(void) { +#ifdef LCD_ENABLE + gfxInit(); +#endif + +#ifdef LCD_BACKLIGHT_ENABLE + lcd_backlight_init(); +#endif // We are using a low priority thread, the idea is to have it run only // when the main thread is sleeping during the matrix scanning chEvtObjectInit(&layer_changed_event); diff --git a/visualizer.mk b/visualizer.mk index e6e0d63d03..eef2d5cc8a 100644 --- a/visualizer.mk +++ b/visualizer.mk @@ -24,6 +24,7 @@ GFXLIB = $(VISUALIZER_DIR)/ugfx ifdef LCD_ENABLE include $(GFXLIB)/gfx.mk OPT_DEFS += -DLCD_ENABLE +OPT_LIBS += -lm endif SRC += $(GFXSRC) $(VISUALIZER_DIR)/visualizer.c INC += $(GFXINC) $(VISUALIZER_DIR) From 8479e6aa390bdd7711753d9fa1141bd1073e2b0a Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 13 Feb 2016 22:08:49 +0200 Subject: [PATCH 09/41] Update readme and license Also add integration examples --- LICENSE.md | 8 + example_integration/callbacks.c | 50 ++++ example_integration/gfxconf.h | 325 ++++++++++++++++++++++++ example_integration/lcd_backlight_hal.c | 90 +++++++ example_integration/visualizer_user.c | 135 ++++++++++ readme.md | 19 +- 6 files changed, 626 insertions(+), 1 deletion(-) create mode 100644 example_integration/callbacks.c create mode 100644 example_integration/gfxconf.h create mode 100644 example_integration/lcd_backlight_hal.c create mode 100644 example_integration/visualizer_user.c diff --git a/LICENSE.md b/LICENSE.md index d7cc3198cb..22d4c3f08b 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,3 +1,11 @@ +The files in this project are licensed under the MIT license +It uses the following libraries +uGFX - with it's own license, see the license.html file in the uGFX subfolder for more information +tmk_core - is indirectly used and not included in the repository. It's licensed under the GPLv2 license +Chibios - which is used by tmk_core is licensed under GPLv3. + +Therefore the effective license for any project using the library is GPLv3 + The MIT License (MIT) Copyright (c) 2016 Fred Sundvik diff --git a/example_integration/callbacks.c b/example_integration/callbacks.c new file mode 100644 index 0000000000..21b2d9f748 --- /dev/null +++ b/example_integration/callbacks.c @@ -0,0 +1,50 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "keyboard.h" +#include "action_layer.h" +#include "visualizer.h" +#include "host.h" + +void post_keyboard_init(void) { + visualizer_init(); +} + +void post_keyboard_task() { + visualizer_set_state(default_layer_state, layer_state, host_keyboard_leds()); +} diff --git a/example_integration/gfxconf.h b/example_integration/gfxconf.h new file mode 100644 index 0000000000..304c5d187a --- /dev/null +++ b/example_integration/gfxconf.h @@ -0,0 +1,325 @@ +/** + * This file has a different license to the rest of the uGFX system. + * You can copy, modify and distribute this file as you see fit. + * You do not need to publish your source modifications to this file. + * The only thing you are not permitted to do is to relicense it + * under a different license. + */ + +/** + * Copy this file into your project directory and rename it as gfxconf.h + * Edit your copy to turn on the uGFX features you want to use. + * The values below are the defaults. + * + * Only remove the comments from lines where you want to change the + * default value. This allows definitions to be included from + * driver makefiles when required and provides the best future + * compatibility for your project. + * + * Please use spaces instead of tabs in this file. + */ + +#ifndef _GFXCONF_H +#define _GFXCONF_H + + +/////////////////////////////////////////////////////////////////////////// +// GOS - One of these must be defined, preferably in your Makefile // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_OS_CHIBIOS TRUE +//#define GFX_USE_OS_FREERTOS FALSE +// #define GFX_FREERTOS_USE_TRACE FALSE +//#define GFX_USE_OS_WIN32 FALSE +//#define GFX_USE_OS_LINUX FALSE +//#define GFX_USE_OS_OSX FALSE +//#define GFX_USE_OS_ECOS FALSE +//#define GFX_USE_OS_RAWRTOS FALSE +//#define GFX_USE_OS_ARDUINO FALSE +//#define GFX_USE_OS_KEIL FALSE +//#define GFX_USE_OS_CMSIS FALSE +//#define GFX_USE_OS_RAW32 FALSE +// #define INTERRUPTS_OFF() optional_code +// #define INTERRUPTS_ON() optional_code +// These are not defined by default for some reason +#define GOS_NEED_X_THREADS FALSE +#define GOS_NEED_X_HEAP FALSE + +// Options that (should where relevant) apply to all operating systems + #define GFX_NO_INLINE FALSE +// #define GFX_COMPILER GFX_COMPILER_UNKNOWN +// #define GFX_CPU GFX_CPU_UNKNOWN +// #define GFX_OS_HEAP_SIZE 0 +// #define GFX_OS_NO_INIT FALSE +// #define GFX_OS_INIT_NO_WARNING FALSE +// #define GFX_OS_PRE_INIT_FUNCTION myHardwareInitRoutine +// #define GFX_OS_EXTRA_INIT_FUNCTION myOSInitRoutine +// #define GFX_OS_EXTRA_DEINIT_FUNCTION myOSDeInitRoutine + + +/////////////////////////////////////////////////////////////////////////// +// GDISP // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GDISP TRUE + +//#define GDISP_NEED_AUTOFLUSH FALSE +//#define GDISP_NEED_TIMERFLUSH FALSE +//#define GDISP_NEED_VALIDATION TRUE +//#define GDISP_NEED_CLIP TRUE +//#define GDISP_NEED_CIRCLE FALSE +//#define GDISP_NEED_ELLIPSE FALSE +//#define GDISP_NEED_ARC FALSE +//#define GDISP_NEED_ARCSECTORS FALSE +//#define GDISP_NEED_CONVEX_POLYGON FALSE +//#define GDISP_NEED_SCROLL FALSE +//#define GDISP_NEED_PIXELREAD FALSE +//#define GDISP_NEED_CONTROL FALSE +//#define GDISP_NEED_QUERY FALSE +//#define GDISP_NEED_MULTITHREAD FALSE +//#define GDISP_NEED_STREAMING FALSE +#define GDISP_NEED_TEXT TRUE +// #define GDISP_NEED_TEXT_WORDWRAP FALSE +// #define GDISP_NEED_ANTIALIAS FALSE +// #define GDISP_NEED_UTF8 FALSE + #define GDISP_NEED_TEXT_KERNING TRUE +// #define GDISP_INCLUDE_FONT_UI1 FALSE +// #define GDISP_INCLUDE_FONT_UI2 FALSE // The smallest preferred font. +// #define GDISP_INCLUDE_FONT_LARGENUMBERS FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS10 FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS12 FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS16 FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS20 FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS24 FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS32 FALSE + #define GDISP_INCLUDE_FONT_DEJAVUSANSBOLD12 TRUE +// #define GDISP_INCLUDE_FONT_FIXED_10X20 FALSE +// #define GDISP_INCLUDE_FONT_FIXED_7X14 FALSE + #define GDISP_INCLUDE_FONT_FIXED_5X8 TRUE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS12_AA FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS16_AA FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS20_AA FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS24_AA FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANS32_AA FALSE +// #define GDISP_INCLUDE_FONT_DEJAVUSANSBOLD12_AA FALSE +// #define GDISP_INCLUDE_USER_FONTS FALSE + +//#define GDISP_NEED_IMAGE FALSE +// #define GDISP_NEED_IMAGE_NATIVE FALSE +// #define GDISP_NEED_IMAGE_GIF FALSE +// #define GDISP_NEED_IMAGE_BMP FALSE +// #define GDISP_NEED_IMAGE_BMP_1 FALSE +// #define GDISP_NEED_IMAGE_BMP_4 FALSE +// #define GDISP_NEED_IMAGE_BMP_4_RLE FALSE +// #define GDISP_NEED_IMAGE_BMP_8 FALSE +// #define GDISP_NEED_IMAGE_BMP_8_RLE FALSE +// #define GDISP_NEED_IMAGE_BMP_16 FALSE +// #define GDISP_NEED_IMAGE_BMP_24 FALSE +// #define GDISP_NEED_IMAGE_BMP_32 FALSE +// #define GDISP_NEED_IMAGE_JPG FALSE +// #define GDISP_NEED_IMAGE_PNG FALSE +// #define GDISP_NEED_IMAGE_ACCOUNTING FALSE + +//#define GDISP_NEED_PIXMAP FALSE +// #define GDISP_NEED_PIXMAP_IMAGE FALSE + +//#define GDISP_DEFAULT_ORIENTATION GDISP_ROTATE_LANDSCAPE // If not defined the native hardware orientation is used. +//#define GDISP_LINEBUF_SIZE 128 +//#define GDISP_STARTUP_COLOR Black +#define GDISP_NEED_STARTUP_LOGO FALSE + +//#define GDISP_TOTAL_DISPLAYS 1 + +//#define GDISP_DRIVER_LIST GDISPVMT_Win32, GDISPVMT_Win32 +// #ifdef GDISP_DRIVER_LIST +// // For code and speed optimization define as TRUE or FALSE if all controllers have the same capability +// #define GDISP_HARDWARE_STREAM_WRITE FALSE +// #define GDISP_HARDWARE_STREAM_READ FALSE +// #define GDISP_HARDWARE_STREAM_POS FALSE +// #define GDISP_HARDWARE_DRAWPIXEL FALSE +// #define GDISP_HARDWARE_CLEARS FALSE +// #define GDISP_HARDWARE_FILLS FALSE +// #define GDISP_HARDWARE_BITFILLS FALSE +// #define GDISP_HARDWARE_SCROLL FALSE +// #define GDISP_HARDWARE_PIXELREAD FALSE +// #define GDISP_HARDWARE_CONTROL FALSE +// #define GDISP_HARDWARE_QUERY FALSE +// #define GDISP_HARDWARE_CLIP FALSE + + #define GDISP_PIXELFORMAT GDISP_PIXELFORMAT_RGB888 +// #endif + +// The custom format is not defined for some reason, so define it as error +// so we don't get compiler warnings +#define GDISP_PIXELFORMAT_CUSTOM GDISP_PIXELFORMAT_ERROR + +#define GDISP_USE_GFXNET FALSE +// #define GDISP_GFXNET_PORT 13001 +// #define GDISP_GFXNET_CUSTOM_LWIP_STARTUP FALSE +// #define GDISP_DONT_WAIT_FOR_NET_DISPLAY FALSE +// #define GDISP_GFXNET_UNSAFE_SOCKETS FALSE + + +/////////////////////////////////////////////////////////////////////////// +// GWIN // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GWIN FALSE + +//#define GWIN_NEED_WINDOWMANAGER FALSE +// #define GWIN_REDRAW_IMMEDIATE FALSE +// #define GWIN_REDRAW_SINGLEOP FALSE +// #define GWIN_NEED_FLASHING FALSE +// #define GWIN_FLASHING_PERIOD 250 + +//#define GWIN_NEED_CONSOLE FALSE +// #define GWIN_CONSOLE_USE_HISTORY FALSE +// #define GWIN_CONSOLE_HISTORY_AVERAGING FALSE +// #define GWIN_CONSOLE_HISTORY_ATCREATE FALSE +// #define GWIN_CONSOLE_ESCSEQ FALSE +// #define GWIN_CONSOLE_USE_BASESTREAM FALSE +// #define GWIN_CONSOLE_USE_FLOAT FALSE +//#define GWIN_NEED_GRAPH FALSE +//#define GWIN_NEED_GL3D FALSE + +//#define GWIN_NEED_WIDGET FALSE +//#define GWIN_FOCUS_HIGHLIGHT_WIDTH 1 +// #define GWIN_NEED_LABEL FALSE +// #define GWIN_LABEL_ATTRIBUTE FALSE +// #define GWIN_NEED_BUTTON FALSE +// #define GWIN_BUTTON_LAZY_RELEASE FALSE +// #define GWIN_NEED_SLIDER FALSE +// #define GWIN_SLIDER_NOSNAP FALSE +// #define GWIN_SLIDER_DEAD_BAND 5 +// #define GWIN_SLIDER_TOGGLE_INC 20 +// #define GWIN_NEED_CHECKBOX FALSE +// #define GWIN_NEED_IMAGE FALSE +// #define GWIN_NEED_IMAGE_ANIMATION FALSE +// #define GWIN_NEED_RADIO FALSE +// #define GWIN_NEED_LIST FALSE +// #define GWIN_NEED_LIST_IMAGES FALSE +// #define GWIN_NEED_PROGRESSBAR FALSE +// #define GWIN_PROGRESSBAR_AUTO FALSE +// #define GWIN_NEED_KEYBOARD FALSE +// #define GWIN_KEYBOARD_DEFAULT_LAYOUT VirtualKeyboard_English1 +// #define GWIN_NEED_KEYBOARD_ENGLISH1 TRUE +// #define GWIN_NEED_TEXTEDIT FALSE +// #define GWIN_FLAT_STYLING FALSE +// #define GWIN_WIDGET_TAGS FALSE + +//#define GWIN_NEED_CONTAINERS FALSE +// #define GWIN_NEED_CONTAINER FALSE +// #define GWIN_NEED_FRAME FALSE +// #define GWIN_NEED_TABSET FALSE +// #define GWIN_TABSET_TABHEIGHT 18 + + +/////////////////////////////////////////////////////////////////////////// +// GEVENT // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GEVENT FALSE + +//#define GEVENT_ASSERT_NO_RESOURCE FALSE +//#define GEVENT_MAXIMUM_SIZE 32 +//#define GEVENT_MAX_SOURCE_LISTENERS 32 + + +/////////////////////////////////////////////////////////////////////////// +// GTIMER // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GTIMER FALSE + +//#define GTIMER_THREAD_PRIORITY HIGH_PRIORITY +//#define GTIMER_THREAD_WORKAREA_SIZE 2048 + + +/////////////////////////////////////////////////////////////////////////// +// GQUEUE // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GQUEUE FALSE + +//#define GQUEUE_NEED_ASYNC FALSE +//#define GQUEUE_NEED_GSYNC FALSE +//#define GQUEUE_NEED_FSYNC FALSE +//#define GQUEUE_NEED_BUFFERS FALSE + +/////////////////////////////////////////////////////////////////////////// +// GINPUT // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GINPUT FALSE + +//#define GINPUT_NEED_MOUSE FALSE +// #define GINPUT_TOUCH_STARTRAW FALSE +// #define GINPUT_TOUCH_NOTOUCH FALSE +// #define GINPUT_TOUCH_NOCALIBRATE FALSE +// #define GINPUT_TOUCH_NOCALIBRATE_GUI FALSE +// #define GINPUT_MOUSE_POLL_PERIOD 25 +// #define GINPUT_MOUSE_CLICK_TIME 300 +// #define GINPUT_TOUCH_CXTCLICK_TIME 700 +// #define GINPUT_TOUCH_USER_CALIBRATION_LOAD FALSE +// #define GINPUT_TOUCH_USER_CALIBRATION_SAVE FALSE +// #define GMOUSE_DRIVER_LIST GMOUSEVMT_Win32, GMOUSEVMT_Win32 +//#define GINPUT_NEED_KEYBOARD FALSE +// #define GINPUT_KEYBOARD_POLL_PERIOD 200 +// #define GKEYBOARD_DRIVER_LIST GKEYBOARDVMT_Win32, GKEYBOARDVMT_Win32 +// #define GKEYBOARD_LAYOUT_OFF FALSE +// #define GKEYBOARD_LAYOUT_SCANCODE2_US FALSE +//#define GINPUT_NEED_TOGGLE FALSE +//#define GINPUT_NEED_DIAL FALSE + + +/////////////////////////////////////////////////////////////////////////// +// GFILE // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GFILE FALSE + +//#define GFILE_NEED_PRINTG FALSE +//#define GFILE_NEED_SCANG FALSE +//#define GFILE_NEED_STRINGS FALSE +//#define GFILE_NEED_FILELISTS FALSE +//#define GFILE_NEED_STDIO FALSE +//#define GFILE_NEED_NOAUTOMOUNT FALSE +//#define GFILE_NEED_NOAUTOSYNC FALSE + +//#define GFILE_NEED_MEMFS FALSE +//#define GFILE_NEED_ROMFS FALSE +//#define GFILE_NEED_RAMFS FALSE +//#define GFILE_NEED_FATFS FALSE +//#define GFILE_NEED_NATIVEFS FALSE +//#define GFILE_NEED_CHBIOSFS FALSE + +//#define GFILE_ALLOW_FLOATS FALSE +//#define GFILE_ALLOW_DEVICESPECIFIC FALSE +//#define GFILE_MAX_GFILES 3 + +/////////////////////////////////////////////////////////////////////////// +// GADC // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GADC FALSE + +//#define GADC_MAX_LOWSPEED_DEVICES 4 + + +/////////////////////////////////////////////////////////////////////////// +// GAUDIO // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GAUDIO FALSE +// There seems to be a bug in the ugfx code, the wrong define is used +// So define it in order to avoid warnings +#define GFX_USE_GAUDIN GFX_USE_GAUDIO +// #define GAUDIO_NEED_PLAY FALSE +// #define GAUDIO_NEED_RECORD FALSE + + +/////////////////////////////////////////////////////////////////////////// +// GMISC // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GMISC FALSE + +//#define GMISC_NEED_ARRAYOPS FALSE +//#define GMISC_NEED_FASTTRIG FALSE +//#define GMISC_NEED_FIXEDTRIG FALSE +//#define GMISC_NEED_INVSQRT FALSE +// #define GMISC_INVSQRT_MIXED_ENDIAN FALSE +// #define GMISC_INVSQRT_REAL_SLOW FALSE +//#define GMISC_NEED_MATRIXFLOAT2D FALSE +//#define GMISC_NEED_MATRIXFIXED2D FALSE + +#endif /* _GFXCONF_H */ diff --git a/example_integration/lcd_backlight_hal.c b/example_integration/lcd_backlight_hal.c new file mode 100644 index 0000000000..0f35cb6755 --- /dev/null +++ b/example_integration/lcd_backlight_hal.c @@ -0,0 +1,90 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#include "lcd_backlight.h" +#include "hal.h" + +#define RED_PIN 1 +#define GREEN_PIN 2 +#define BLUE_PIN 3 +#define CHANNEL_RED FTM0->CHANNEL[0] +#define CHANNEL_GREEN FTM0->CHANNEL[1] +#define CHANNEL_BLUE FTM0->CHANNEL[2] + +#define RGB_PORT PORTC +#define RGB_PORT_GPIO GPIOC + +// Base FTM clock selection (72 MHz system clock) +// @ 0xFFFF period, 72 MHz / (0xFFFF * 2) = Actual period +// Higher pre-scalar will use the most power (also look the best) +// Pre-scalar calculations +// 0 - 72 MHz -> 549 Hz +// 1 - 36 MHz -> 275 Hz +// 2 - 18 MHz -> 137 Hz +// 3 - 9 MHz -> 69 Hz (Slightly visible flicker) +// 4 - 4 500 kHz -> 34 Hz (Visible flickering) +// 5 - 2 250 kHz -> 17 Hz +// 6 - 1 125 kHz -> 9 Hz +// 7 - 562 500 Hz -> 4 Hz +// Using a higher pre-scalar without flicker is possible but FTM0_MOD will need to be reduced +// Which will reduce the brightness range +#define PRESCALAR_DEFINE 0 + +void lcd_backlight_hal_init(void) { + // Setup Backlight + SIM->SCGC6 |= SIM_SCGC6_FTM0; + FTM0->CNT = 0; // Reset counter + + // PWM Period + // 16-bit maximum + FTM0->MOD = 0xFFFF; + + // Set FTM to PWM output - Edge Aligned, Low-true pulses +#define CNSC_MODE FTM_SC_CPWMS | FTM_SC_PS(4) | FTM_SC_CLKS(0) + CHANNEL_RED.CnSC = CNSC_MODE; + CHANNEL_GREEN.CnSC = CNSC_MODE; + CHANNEL_BLUE.CnSC = CNSC_MODE; + + // System clock, /w prescalar setting + FTM0->SC = FTM_SC_CLKS(1) | FTM_SC_PS(PRESCALAR_DEFINE); + + CHANNEL_RED.CnV = 0; + CHANNEL_GREEN.CnV = 0; + CHANNEL_BLUE.CnV = 0; + + RGB_PORT_GPIO->PDDR |= (1 << RED_PIN); + RGB_PORT_GPIO->PDDR |= (1 << GREEN_PIN); + RGB_PORT_GPIO->PDDR |= (1 << BLUE_PIN); + +#define RGB_MODE PORTx_PCRn_SRE | PORTx_PCRn_DSE | PORTx_PCRn_MUX(4) + RGB_PORT->PCR[RED_PIN] = RGB_MODE; + RGB_PORT->PCR[GREEN_PIN] = RGB_MODE; + RGB_PORT->PCR[BLUE_PIN] = RGB_MODE; +} + +void lcd_backlight_hal_color(uint16_t r, uint16_t g, uint16_t b) { + CHANNEL_RED.CnV = r; + CHANNEL_GREEN.CnV = g; + CHANNEL_BLUE.CnV = b; +} + diff --git a/example_integration/visualizer_user.c b/example_integration/visualizer_user.c new file mode 100644 index 0000000000..a50ecc60da --- /dev/null +++ b/example_integration/visualizer_user.c @@ -0,0 +1,135 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +// Currently we are assuming that both the backlight and LCD are enabled +// But it's entirely possible to write a custom visualizer that use only +// one of them +#ifndef LCD_BACKLIGHT_ENABLE +#error This visualizer needs that LCD backlight is enabled +#endif + +#ifndef LCD_ENABLE +#error This visualizer needs that LCD is enabled +#endif + +#include "visualizer.h" + +static const char* welcome_text[] = {"TMK", "Infinity Ergodox"}; + +// Just an example how to write custom keyframe functions, we could have moved +// all this into the init function +bool display_welcome(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)animation; + // Read the uGFX documentation for information how to use the displays + // http://wiki.ugfx.org/index.php/Main_Page + gdispClear(White); + // You can use static variables for things that can't be found in the animation + // or state structs + gdispDrawString(0, 3, welcome_text[0], state->font_dejavusansbold12, Black); + gdispDrawString(0, 15, welcome_text[1], state->font_dejavusansbold12, Black); + // Always remember to flush the display + gdispFlush(); + // you could set the backlight color as well, but we won't do it here, since + // it's part of the following animation + // lcd_backlight_color(hue, saturation, intensity); + // We don't need constant updates, just drawing the screen once is enough + return false; +} + +// Feel free to modify the animations below, or even add new ones if needed + +// Don't worry, if the startup animation is long, you can use the keyboard like normal +// during that time +static keyframe_animation_t startup_animation = { + .num_frames = 4, + .loop = false, + .frame_lengths = {0, MS2ST(1000), MS2ST(5000), 0}, + .frame_functions = {display_welcome, keyframe_animate_backlight_color, keyframe_no_operation, user_visualizer_inited}, +}; + +// The color animation animates the LCD color when you change layers +static keyframe_animation_t color_animation = { + .num_frames = 2, + .loop = false, + // Note that there's a 200 ms no-operation frame, + // this prevents the color from changing when activating the layer + // momentarily + .frame_lengths = {MS2ST(200), MS2ST(500)}, + .frame_functions = {keyframe_no_operation, keyframe_animate_backlight_color}, +}; + +// The LCD animation alternates between the layer name display and a +// bitmap that displays all active layers +static keyframe_animation_t lcd_animation = { + .num_frames = 2, + .loop = true, + .frame_lengths = {MS2ST(2000), MS2ST(2000)}, + .frame_functions = {keyframe_display_layer_text, keyframe_display_layer_bitmap}, +}; + +void initialize_user_visualizer(visualizer_state_t* state) { + // The brightness will be dynamically adjustable in the future + // But for now, change it here. + lcd_backlight_brightness(0x50); + state->current_lcd_color = LCD_COLOR(0x00, 0x00, 0xFF); + state->target_lcd_color = LCD_COLOR(0x10, 0xFF, 0xFF); + start_keyframe_animation(&startup_animation); +} + +void update_user_visualizer_state(visualizer_state_t* state) { + // Add more tests, change the colors and layer texts here + // Usually you want to check the high bits (higher layers first) + // because that's the order layers are processed for keypresses + // You can for check for example: + // state->status.layer + // state->status.default_layer + // state->status.leds (see led.h for available statuses) + if (state->status.layer & 0x2) { + state->target_lcd_color = LCD_COLOR(0xA0, 0xB0, 0xFF); + state->layer_text = "Layer 2"; + } + else { + state->target_lcd_color = LCD_COLOR(0x50, 0xB0, 0xFF); + state->layer_text = "Layer 1"; + } + // You can also stop existing animations, and start your custom ones here + // remember that you should normally have only one animation for the LCD + // and one for the background. But you can also combine them if you want. + start_keyframe_animation(&lcd_animation); + start_keyframe_animation(&color_animation); +} diff --git a/readme.md b/readme.md index 82bf8bfdfa..545ba22707 100644 --- a/readme.md +++ b/readme.md @@ -1 +1,18 @@ -A visualization library for the TMK keyboard firmware +# A visualization library for the TMK keyboard firmware + +This library is designed to work together with the [TMK keyboard firmware](https://github.com/tmk/tmk_keyboard). Currently it only works for [Chibios](http://www.chibios.org/) + flavors, but it would be possible to add support for other configurations as well. The LCD display functionality is provided by the [uGFX library](http://www.ugfx.org/). + +## To use this library as a user +You can and should modify the visualizer\_user.c file. Check the comments in the file for more information. + +## To add this library to custom keyboard projects + +1. Add tmk_visualizer as a submodule to your project +1. Set VISUALIZER_DIR in the main keyboard project makefile to point to the submodule +1. Define LCD\_ENABLE and/or LCD\_BACKLIGHT\_ENABLE, to enable support +1. Include the visualizer.mk make file +1. Copy the files in the example\_integration folder to your keyboard project +1. All other files than the callback.c file are included automatically, so you will need to add callback.c to your makefile manually. If you already have a similar file in your project, you can just copy the functions instead of the whole file. +1. Edit the files to match your hardware. You might might want to read the Chibios and UGfx documentation, for more information. +1. If you enable LCD support you might also have to write a custom uGFX display driver, check the uGFX documentation for that. You probably also want to enable SPI support in your Chibios configuration. From c5db02925c6bf73ef1df1f718e09ff304266a535 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 13 Feb 2016 22:19:49 +0200 Subject: [PATCH 10/41] Clean up wrong license information --- example_integration/callbacks.c | 14 -------------- example_integration/lcd_backlight_hal.c | 1 + example_integration/visualizer_user.c | 14 -------------- 3 files changed, 1 insertion(+), 28 deletions(-) diff --git a/example_integration/callbacks.c b/example_integration/callbacks.c index 21b2d9f748..2539615d65 100644 --- a/example_integration/callbacks.c +++ b/example_integration/callbacks.c @@ -22,20 +22,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - #include "keyboard.h" #include "action_layer.h" #include "visualizer.h" diff --git a/example_integration/lcd_backlight_hal.c b/example_integration/lcd_backlight_hal.c index 0f35cb6755..913131b169 100644 --- a/example_integration/lcd_backlight_hal.c +++ b/example_integration/lcd_backlight_hal.c @@ -21,6 +21,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + #include "lcd_backlight.h" #include "hal.h" diff --git a/example_integration/visualizer_user.c b/example_integration/visualizer_user.c index a50ecc60da..6c4619d954 100644 --- a/example_integration/visualizer_user.c +++ b/example_integration/visualizer_user.c @@ -22,20 +22,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ - // Currently we are assuming that both the backlight and LCD are enabled // But it's entirely possible to write a custom visualizer that use only // one of them From 725929ec8bb5d57b6c4f53fa7d15e04df15f2535 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Tue, 23 Feb 2016 09:37:24 +0200 Subject: [PATCH 11/41] Fix include paths for new tmk_core update --- visualizer.mk | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/visualizer.mk b/visualizer.mk index eef2d5cc8a..13c5d31587 100644 --- a/visualizer.mk +++ b/visualizer.mk @@ -23,16 +23,16 @@ GFXLIB = $(VISUALIZER_DIR)/ugfx ifdef LCD_ENABLE include $(GFXLIB)/gfx.mk -OPT_DEFS += -DLCD_ENABLE -OPT_LIBS += -lm +UDEFS += -DLCD_ENABLE +ULIBS += -lm endif SRC += $(GFXSRC) $(VISUALIZER_DIR)/visualizer.c -INC += $(GFXINC) $(VISUALIZER_DIR) +UINCDIR += $(GFXINC) $(VISUALIZER_DIR) ifdef LCD_BACKLIGHT_ENABLE SRC += $(VISUALIZER_DIR)/lcd_backlight.c SRC += lcd_backlight_hal.c -OPT_DEFS += -DLCD_BACKLIGHT_ENABLE +UDEFS += -DLCD_BACKLIGHT_ENABLE endif ifndef VISUALIZER_USER From 315edb48265e6baedd07f34c9e6e323d28814b4e Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 12 Mar 2016 19:42:57 +0200 Subject: [PATCH 12/41] Add serial link support for visualizer --- visualizer.c | 63 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/visualizer.c b/visualizer.c index 402bbd1517..ca7bcb7765 100644 --- a/visualizer.c +++ b/visualizer.c @@ -42,6 +42,11 @@ SOFTWARE. #include "nodebug.h" #endif +#ifdef USE_SERIAL_LINK +#include "serial_link/protocol/transport.h" +#include "serial_link/system/driver.h" +#endif + static visualizer_keyboard_status_t current_status = { .layer = 0xFFFFFFFF, @@ -59,6 +64,16 @@ static bool visualizer_enabled = false; #define MAX_SIMULTANEOUS_ANIMATIONS 4 static keyframe_animation_t* animations[MAX_SIMULTANEOUS_ANIMATIONS] = {}; +#ifdef USE_SERIAL_LINK +MASTER_TO_ALL_SLAVES_OBJECT(current_status, visualizer_keyboard_status_t); + +static remote_object_t* remote_objects[] = { + REMOTE_OBJECT(current_status), +}; + +#endif + + void start_keyframe_animation(keyframe_animation_t* animation) { animation->current_frame = -1; animation->time_left_in_frame = 0; @@ -328,6 +343,10 @@ void visualizer_init(void) { #ifdef LCD_BACKLIGHT_ENABLE lcd_backlight_init(); #endif + +#ifdef USE_SERIAL_LINK + add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*) ); +#endif // We are using a low priority thread, the idea is to have it run only // when the main thread is sleeping during the matrix scanning chEvtObjectInit(&layer_changed_event); @@ -340,17 +359,45 @@ void visualizer_set_state(uint32_t default_state, uint32_t state, uint32_t leds) // a state where one of these are set but not the other. But this should // not really matter as it will be fixed during the next loop step. // Alternatively a mutex could be used instead of the volatile variables + bool changed = false; - visualizer_keyboard_status_t new_status = { - .layer = state, - .default_layer = default_state, - .leds = leds, - }; - if (!same_status(¤t_status, &new_status)) { - changed = true; +#ifdef USE_SERIAL_LINK + if (is_serial_link_connected ()) { + visualizer_keyboard_status_t* new_status = read_current_status(); + if (new_status) { + if (!same_status(¤t_status, new_status)) { + changed = true; + current_status = *new_status; + } + } + } + else { +#else + { +#endif + visualizer_keyboard_status_t new_status = { + .layer = state, + .default_layer = default_state, + .leds = leds, + }; + if (!same_status(¤t_status, &new_status)) { + changed = true; + current_status = new_status; + } } - current_status = new_status; if (changed) { chEvtBroadcast(&layer_changed_event); + } +#ifdef USE_SERIAL_LINK + static systime_t last_update = 0; + systime_t current_update = chVTGetSystemTimeX(); + systime_t delta = current_update - last_update; + if (changed || delta > MS2ST(10)) { + last_update = current_update; + visualizer_keyboard_status_t* r = begin_write_current_status(); + *r = current_status; + end_write_current_status(); + } +#endif } From b93d07198a18063594a59dd193d0961622087868 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 13 Mar 2016 17:54:45 +0200 Subject: [PATCH 13/41] Suspend support for the visualizer --- example_integration/visualizer_user.c | 2 +- visualizer.c | 114 ++++++++++++++++++++------ visualizer.h | 17 +++- 3 files changed, 103 insertions(+), 30 deletions(-) diff --git a/example_integration/visualizer_user.c b/example_integration/visualizer_user.c index 6c4619d954..fc09fe2eaf 100644 --- a/example_integration/visualizer_user.c +++ b/example_integration/visualizer_user.c @@ -65,7 +65,7 @@ static keyframe_animation_t startup_animation = { .num_frames = 4, .loop = false, .frame_lengths = {0, MS2ST(1000), MS2ST(5000), 0}, - .frame_functions = {display_welcome, keyframe_animate_backlight_color, keyframe_no_operation, user_visualizer_inited}, + .frame_functions = {display_welcome, keyframe_animate_backlight_color, keyframe_no_operation, enable_visualization}, }; // The color animation animates the LCD color when you change layers diff --git a/visualizer.c b/visualizer.c index ca7bcb7765..5b0d560ed9 100644 --- a/visualizer.c +++ b/visualizer.c @@ -52,10 +52,14 @@ static visualizer_keyboard_status_t current_status = { .layer = 0xFFFFFFFF, .default_layer = 0xFFFFFFFF, .leds = 0xFFFFFFFF, + .suspended = false, }; static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboard_status_t* status2) { - return memcmp(status1, status2, sizeof(visualizer_keyboard_status_t)) == 0; + return status1->layer == status2->layer && + status1->default_layer == status2->default_layer && + status1->leds == status2->leds && + status1->suspended == status2->suspended; } static event_source_t layer_changed_event; @@ -104,6 +108,17 @@ void stop_keyframe_animation(keyframe_animation_t* animation) { } } +void stop_all_keyframe_animations(void) { + for (int i=0;icurrent_frame = animations[i]->num_frames; + animations[i]->time_left_in_frame = 0; + animations[i]->need_update = true; + animations[i] = NULL; + } + } +} + static bool update_keyframe_animation(keyframe_animation_t* animation, visualizer_state_t* state, systime_t delta, systime_t* sleep_time) { dprintf("Animation frame%d, left %d, delta %d\n", animation->current_frame, animation->time_left_in_frame, delta); @@ -252,7 +267,19 @@ bool keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_s } #endif // LCD_ENABLE -bool user_visualizer_inited(keyframe_animation_t* animation, visualizer_state_t* state) { +bool keyframe_disable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)animation; + (void)state; + return false; +} + +bool keyframe_enable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)animation; + (void)state; + return false; +} + +bool enable_visualization(keyframe_animation_t* animation, visualizer_state_t* state) { (void)animation; (void)state; dprint("User visualizer inited\n"); @@ -268,13 +295,15 @@ static THD_FUNCTION(visualizerThread, arg) { event_listener_t event_listener; chEvtRegister(&layer_changed_event, &event_listener, 0); - visualizer_state_t state = { - .status = { - .default_layer = 0xFFFFFFFF, - .layer = 0xFFFFFFFF, - .leds = 0xFFFFFFFF, - }, + visualizer_keyboard_status_t initial_status = { + .default_layer = 0xFFFFFFFF, + .layer = 0xFFFFFFFF, + .leds = 0xFFFFFFFF, + .suspended = false, + }; + visualizer_state_t state = { + .status = initial_status, .current_lcd_color = 0, #ifdef LCD_ENABLE .font_fixed5x8 = gdispOpenFont("fixed_5x8"), @@ -301,17 +330,36 @@ static THD_FUNCTION(visualizerThread, arg) { bool enabled = visualizer_enabled; if (!same_status(&state.status, ¤t_status)) { if (visualizer_enabled) { - state.status = current_status; - update_user_visualizer_state(&state); - state.prev_lcd_color = state.current_lcd_color; + if (current_status.suspended) { + stop_all_keyframe_animations(); + visualizer_enabled = false; + state.status = current_status; + user_visualizer_suspend(&state); + } + else { + state.status = current_status; + update_user_visualizer_state(&state); + state.prev_lcd_color = state.current_lcd_color; + } } } + if (!enabled && state.status.suspended && current_status.suspended == false) { + // Setting the status to the initial status will force an update + // when the visualizer is enabled again + state.status = initial_status; + state.status.suspended = false; + stop_all_keyframe_animations(); + user_visualizer_resume(&state); + } sleep_time = TIME_INFINITE; for (int i=0;i MS2ST(10)) { + last_update = current_update; + visualizer_keyboard_status_t* r = begin_write_current_status(); + *r = current_status; + end_write_current_status(); + } +#endif +} + +void visualizer_update(uint32_t default_state, uint32_t state, uint32_t leds) { // Note that there's a small race condition here, the thread could read // a state where one of these are set but not the other. But this should // not really matter as it will be fixed during the next loop step. @@ -379,25 +444,22 @@ void visualizer_set_state(uint32_t default_state, uint32_t state, uint32_t leds) .layer = state, .default_layer = default_state, .leds = leds, + .suspended = current_status.suspended, }; if (!same_status(¤t_status, &new_status)) { changed = true; current_status = new_status; } } - if (changed) { - chEvtBroadcast(&layer_changed_event); + update_status(changed); +} - } -#ifdef USE_SERIAL_LINK - static systime_t last_update = 0; - systime_t current_update = chVTGetSystemTimeX(); - systime_t delta = current_update - last_update; - if (changed || delta > MS2ST(10)) { - last_update = current_update; - visualizer_keyboard_status_t* r = begin_write_current_status(); - *r = current_status; - end_write_current_status(); - } -#endif +void visualizer_suspend(void) { + current_status.suspended = true; + update_status(true); +} + +void visualizer_resume(void) { + current_status.suspended = false; + update_status(true); } diff --git a/visualizer.h b/visualizer.h index b7b0a3a7d0..22798cda6b 100644 --- a/visualizer.h +++ b/visualizer.h @@ -38,8 +38,12 @@ SOFTWARE. // This need to be called once at the start void visualizer_init(void); -// This should be called before every matrix scan -void visualizer_set_state(uint32_t default_state, uint32_t state, uint32_t leds); +// This should be called at every matrix scan +void visualizer_update(uint32_t default_state, uint32_t state, uint32_t leds); +// This should be called when the keyboard goes to suspend state +void visualizer_suspend(void); +// This should be called when the keyboard wakes up from suspend state +void visualizer_resume(void); // If you need support for more than 8 keyframes per animation, you can change this #define MAX_VISUALIZER_KEY_FRAMES 8 @@ -50,6 +54,7 @@ typedef struct { uint32_t layer; uint32_t default_layer; uint32_t leds; // See led.h for available statuses + bool suspended; } visualizer_keyboard_status_t; // The state struct is used by the various keyframe functions @@ -108,13 +113,19 @@ bool keyframe_set_backlight_color(keyframe_animation_t* animation, visualizer_st bool keyframe_display_layer_text(keyframe_animation_t* animation, visualizer_state_t* state); // Displays a bitmap (0/1) of all the currently active layers bool keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_state_t* state); + +bool keyframe_disable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state); +bool keyframe_enable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state); + // Call this once, when the initial animation has finished, alternatively you can call it // directly from the initalize_user_visualizer function (the animation can be null) -bool user_visualizer_inited(keyframe_animation_t* animation, visualizer_state_t* state); +bool enable_visualization(keyframe_animation_t* animation, visualizer_state_t* state); // These two functions have to be implemented by the user void initialize_user_visualizer(visualizer_state_t* state); void update_user_visualizer_state(visualizer_state_t* state); +void user_visualizer_suspend(visualizer_state_t* state); +void user_visualizer_resume(visualizer_state_t* state); #endif /* VISUALIZER_H */ From f4c11740f7bac033194ab1d5f5a52721b1d7c6d2 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 13 Mar 2016 21:35:42 +0200 Subject: [PATCH 14/41] Suspend power off, fix backlight animation The backlight color animation was using the wrong frame number, so it didn't work properly. --- visualizer.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/visualizer.c b/visualizer.c index 5b0d560ed9..867a1d3347 100644 --- a/visualizer.c +++ b/visualizer.c @@ -174,7 +174,7 @@ bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* #ifdef LCD_BACKLIGHT_ENABLE bool keyframe_animate_backlight_color(keyframe_animation_t* animation, visualizer_state_t* state) { - int frame_length = animation->frame_lengths[1]; + int frame_length = animation->frame_lengths[animation->current_frame]; int current_pos = frame_length - animation->time_left_in_frame; uint8_t t_h = LCD_HUE(state->target_lcd_color); uint8_t t_s = LCD_SAT(state->target_lcd_color); @@ -270,12 +270,21 @@ bool keyframe_display_layer_bitmap(keyframe_animation_t* animation, visualizer_s bool keyframe_disable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) { (void)animation; (void)state; +#ifdef LCD_ENABLE + gdispSetPowerMode(powerOff); +#endif +#ifdef LCD_BACKLIGHT_ENABLE + lcd_backlight_hal_color(0, 0, 0); +#endif return false; } bool keyframe_enable_lcd_and_backlight(keyframe_animation_t* animation, visualizer_state_t* state) { (void)animation; (void)state; +#ifdef LCD_ENABLE + gdispSetPowerMode(powerOn); +#endif return false; } @@ -339,8 +348,8 @@ static THD_FUNCTION(visualizerThread, arg) { else { state.status = current_status; update_user_visualizer_state(&state); - state.prev_lcd_color = state.current_lcd_color; } + state.prev_lcd_color = state.current_lcd_color; } } if (!enabled && state.status.suspended && current_status.suspended == false) { @@ -350,6 +359,7 @@ static THD_FUNCTION(visualizerThread, arg) { state.status.suspended = false; stop_all_keyframe_animations(); user_visualizer_resume(&state); + state.prev_lcd_color = state.current_lcd_color; } sleep_time = TIME_INFINITE; for (int i=0;i Date: Sat, 9 Apr 2016 19:49:52 +0300 Subject: [PATCH 15/41] Update ugfx with ChibiOS 4 support. Also change the URL to fredizzimo bitbucket --- .gitmodules | 2 +- ugfx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 2ab25f6883..b320458c0e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "ugfx"] path = ugfx - url = https://bitbucket.org/Tectu/ugfx.git + url = https://bitbucket.org/fredizzimo/ugfx.git diff --git a/ugfx b/ugfx index 2b66ac524b..e221a69061 160000 --- a/ugfx +++ b/ugfx @@ -1 +1 @@ -Subproject commit 2b66ac524bd56853ba97b917683971f3ebc0104c +Subproject commit e221a690616e20f87e0b0088baffdbd5427be862 From c95b17b536b4437f001d5f5e8a54753969e36bb2 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 24 Apr 2016 13:46:19 +0300 Subject: [PATCH 16/41] Add simple led support Also the first version of a led testing animation --- led_test.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ led_test.h | 35 +++++++++++++++++++++++++++++++++++ visualizer.c | 8 ++++++++ visualizer.h | 3 +++ visualizer.mk | 10 ++++++++-- 5 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 led_test.c create mode 100644 led_test.h diff --git a/led_test.c b/led_test.c new file mode 100644 index 0000000000..1aadd55414 --- /dev/null +++ b/led_test.c @@ -0,0 +1,45 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ +#include "led_test.h" +#include "gfx.h" + +keyframe_animation_t led_test_animation = { + .num_frames = 1, + .loop = true, + .frame_lengths = {MS2ST(1000)}, + .frame_functions = { + keyframe_fade_in_all_leds, + }, +}; + +bool keyframe_fade_in_all_leds(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)state; + int frame_length = animation->frame_lengths[animation->current_frame]; + int current_pos = frame_length - animation->time_left_in_frame; + uint8_t luma = 0x255 * current_pos / frame_length; + color_t color = LUMA2COLOR(luma); + gdispGClear(LED_DISPLAY, color); + gdispGFlush(LED_DISPLAY); + return true; +} diff --git a/led_test.h b/led_test.h new file mode 100644 index 0000000000..521e052164 --- /dev/null +++ b/led_test.h @@ -0,0 +1,35 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 Fred Sundvik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef TMK_VISUALIZER_LED_TEST_H_ +#define TMK_VISUALIZER_LED_TEST_H_ + +#include "visualizer.h" + +bool keyframe_fade_in_all_leds(keyframe_animation_t* animation, visualizer_state_t* state); + +extern keyframe_animation_t led_test_animation; + + +#endif /* TMK_VISUALIZER_LED_TEST_H_ */ diff --git a/visualizer.c b/visualizer.c index 867a1d3347..ed5c9fa2c9 100644 --- a/visualizer.c +++ b/visualizer.c @@ -77,6 +77,9 @@ static remote_object_t* remote_objects[] = { #endif +GDisplay* LCDDisplay; +GDisplay* LEDDisplay; + void start_keyframe_animation(keyframe_animation_t* animation) { animation->current_frame = -1; @@ -405,6 +408,11 @@ void visualizer_init(void) { #ifdef USE_SERIAL_LINK add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*) ); #endif + // TODO: Make sure these works when either of these are disabled + LCDDisplay = gdispGetDisplay(0); + LEDDisplay = gdispGetDisplay(1); + + // We are using a low priority thread, the idea is to have it run only // when the main thread is sleeping during the matrix scanning chEvtObjectInit(&layer_changed_event); diff --git a/visualizer.h b/visualizer.h index 22798cda6b..6a72fde1fd 100644 --- a/visualizer.h +++ b/visualizer.h @@ -99,6 +99,9 @@ typedef struct keyframe_animation_t { } keyframe_animation_t; +extern GDisplay* LCD_DISPLAY; +extern GDisplay* LED_DISPLAY; + void start_keyframe_animation(keyframe_animation_t* animation); void stop_keyframe_animation(keyframe_animation_t* animation); diff --git a/visualizer.mk b/visualizer.mk index 13c5d31587..96ca468e86 100644 --- a/visualizer.mk +++ b/visualizer.mk @@ -21,13 +21,14 @@ # SOFTWARE. GFXLIB = $(VISUALIZER_DIR)/ugfx +SRC += $(GFXSRC) $(VISUALIZER_DIR)/visualizer.c +UINCDIR += $(GFXINC) $(VISUALIZER_DIR) + ifdef LCD_ENABLE include $(GFXLIB)/gfx.mk UDEFS += -DLCD_ENABLE ULIBS += -lm endif -SRC += $(GFXSRC) $(VISUALIZER_DIR)/visualizer.c -UINCDIR += $(GFXINC) $(VISUALIZER_DIR) ifdef LCD_BACKLIGHT_ENABLE SRC += $(VISUALIZER_DIR)/lcd_backlight.c @@ -35,6 +36,11 @@ SRC += lcd_backlight_hal.c UDEFS += -DLCD_BACKLIGHT_ENABLE endif +ifdef LED_ENABLE +SRC += $(VISUALIZER_DIR)/led_test.c +UDEFS += -DLED_ENABLE +endif + ifndef VISUALIZER_USER VISUALIZER_USER = visualizer_user.c endif From a960a1b0066b84bbf279fbebe2d62dfee6ea3812 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 24 Apr 2016 14:10:01 +0300 Subject: [PATCH 17/41] Add fade out for the led test --- led_test.c | 24 +++++++++++++++++++----- led_test.h | 1 + 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/led_test.c b/led_test.c index 1aadd55414..1fba32fa0d 100644 --- a/led_test.c +++ b/led_test.c @@ -25,21 +25,35 @@ SOFTWARE. #include "gfx.h" keyframe_animation_t led_test_animation = { - .num_frames = 1, + .num_frames = 3, .loop = true, - .frame_lengths = {MS2ST(1000)}, + .frame_lengths = {MS2ST(1000), MS2ST(1000), MS2ST(1000)}, .frame_functions = { keyframe_fade_in_all_leds, + keyframe_no_operation, + keyframe_fade_out_all_leds, }, }; -bool keyframe_fade_in_all_leds(keyframe_animation_t* animation, visualizer_state_t* state) { - (void)state; +static void keyframe_fade_all_leds_from_to(keyframe_animation_t* animation, uint8_t from, uint8_t to) { int frame_length = animation->frame_lengths[animation->current_frame]; int current_pos = frame_length - animation->time_left_in_frame; - uint8_t luma = 0x255 * current_pos / frame_length; + int delta = to - from; + int luma = (delta * current_pos) / frame_length; + luma += from; color_t color = LUMA2COLOR(luma); gdispGClear(LED_DISPLAY, color); gdispGFlush(LED_DISPLAY); +} + +bool keyframe_fade_in_all_leds(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)state; + keyframe_fade_all_leds_from_to(animation, 0, 255); + return true; +} + +bool keyframe_fade_out_all_leds(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)state; + keyframe_fade_all_leds_from_to(animation, 255, 0); return true; } diff --git a/led_test.h b/led_test.h index 521e052164..a722cd9fe3 100644 --- a/led_test.h +++ b/led_test.h @@ -28,6 +28,7 @@ SOFTWARE. #include "visualizer.h" bool keyframe_fade_in_all_leds(keyframe_animation_t* animation, visualizer_state_t* state); +bool keyframe_fade_out_all_leds(keyframe_animation_t* animation, visualizer_state_t* state); extern keyframe_animation_t led_test_animation; From 0e0488623e8d8820a909a48a6c847866a65bf845 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 24 Apr 2016 14:54:40 +0300 Subject: [PATCH 18/41] Add left to right gradient keyframe for leds --- led_test.c | 34 ++++++++++++++++++++++++++++++++-- led_test.h | 1 + 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/led_test.c b/led_test.c index 1fba32fa0d..197550fdd6 100644 --- a/led_test.c +++ b/led_test.c @@ -23,15 +23,21 @@ SOFTWARE. */ #include "led_test.h" #include "gfx.h" +#include "math.h" keyframe_animation_t led_test_animation = { - .num_frames = 3, + .num_frames = 4, .loop = true, - .frame_lengths = {MS2ST(1000), MS2ST(1000), MS2ST(1000)}, + .frame_lengths = { + MS2ST(1000), + MS2ST(1000), + MS2ST(1000), + MS2ST(3000)}, .frame_functions = { keyframe_fade_in_all_leds, keyframe_no_operation, keyframe_fade_out_all_leds, + keyframe_led_left_to_right_gradient, }, }; @@ -46,6 +52,18 @@ static void keyframe_fade_all_leds_from_to(keyframe_animation_t* animation, uint gdispGFlush(LED_DISPLAY); } +// TODO: Should be customizable per keyboard +#define NUM_ROWS 7 +#define NUM_COLS 7 + +static uint8_t compute_gradient_color(float t, float index, float num) { + float d = fabs(index - t); + if (d > num / 2.0f) { + d = num - d; + } + return (uint8_t)(255.0f * d); +} + bool keyframe_fade_in_all_leds(keyframe_animation_t* animation, visualizer_state_t* state) { (void)state; keyframe_fade_all_leds_from_to(animation, 0, 255); @@ -57,3 +75,15 @@ bool keyframe_fade_out_all_leds(keyframe_animation_t* animation, visualizer_stat keyframe_fade_all_leds_from_to(animation, 255, 0); return true; } + +bool keyframe_led_left_to_right_gradient(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)state; + int frame_length = animation->frame_lengths[animation->current_frame]; + int current_pos = frame_length - animation->time_left_in_frame; + float t = current_pos / frame_length; + for (int i=0; i< NUM_COLS; i++) { + uint8_t color = compute_gradient_color(t, i, NUM_COLS); + gdispGDrawLine(LED_DISPLAY, i, 0, i, NUM_ROWS - 1, LUMA2COLOR(color)); + } + return true; +} diff --git a/led_test.h b/led_test.h index a722cd9fe3..03737a717d 100644 --- a/led_test.h +++ b/led_test.h @@ -29,6 +29,7 @@ SOFTWARE. bool keyframe_fade_in_all_leds(keyframe_animation_t* animation, visualizer_state_t* state); bool keyframe_fade_out_all_leds(keyframe_animation_t* animation, visualizer_state_t* state); +bool keyframe_led_left_to_right_gradient(keyframe_animation_t* animation, visualizer_state_t* state); extern keyframe_animation_t led_test_animation; From 444132edd056cd52a60e3551d1f6c1a07909c040 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 24 Apr 2016 15:45:52 +0300 Subject: [PATCH 19/41] Add last and first update of frame for anims --- visualizer.c | 9 +++++++++ visualizer.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/visualizer.c b/visualizer.c index ed5c9fa2c9..219d44cd3c 100644 --- a/visualizer.c +++ b/visualizer.c @@ -103,6 +103,8 @@ void stop_keyframe_animation(keyframe_animation_t* animation) { animation->current_frame = animation->num_frames; animation->time_left_in_frame = 0; animation->need_update = true; + animation->first_update_of_frame = false; + animation->last_update_of_frame = false; for (int i=0;icurrent_frame = animations[i]->num_frames; animations[i]->time_left_in_frame = 0; animations[i]->need_update = true; + animations[i]->first_update_of_frame = false; + animations[i]->last_update_of_frame = false; animations[i] = NULL; } } @@ -133,16 +137,20 @@ static bool update_keyframe_animation(keyframe_animation_t* animation, visualize animation->current_frame = 0; animation->time_left_in_frame = animation->frame_lengths[0]; animation->need_update = true; + animation->first_update_of_frame = true; } else { animation->time_left_in_frame -= delta; while (animation->time_left_in_frame <= 0) { int left = animation->time_left_in_frame; if (animation->need_update) { animation->time_left_in_frame = 0; + animation->last_update_of_frame = true; (*animation->frame_functions[animation->current_frame])(animation, state); + animation->last_update_of_frame = false; } animation->current_frame++; animation->need_update = true; + animation->first_update_of_frame = true; if (animation->current_frame == animation->num_frames) { if (animation->loop) { animation->current_frame = 0; @@ -159,6 +167,7 @@ static bool update_keyframe_animation(keyframe_animation_t* animation, visualize } if (animation->need_update) { animation->need_update = (*animation->frame_functions[animation->current_frame])(animation, state); + animation->first_update_of_frame = false; } int wanted_sleep = animation->need_update ? 10 : animation->time_left_in_frame; diff --git a/visualizer.h b/visualizer.h index 6a72fde1fd..82d7a71d37 100644 --- a/visualizer.h +++ b/visualizer.h @@ -95,6 +95,8 @@ typedef struct keyframe_animation_t { // keyframe update functions int current_frame; int time_left_in_frame; + bool first_update_of_frame; + bool last_update_of_frame; bool need_update; } keyframe_animation_t; From 891edbd533acdffec66416c59f4b6066e5e18aaa Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 24 Apr 2016 16:19:31 +0300 Subject: [PATCH 20/41] Add function for running the next keyframe --- visualizer.c | 15 +++++++++++++++ visualizer.h | 3 +++ 2 files changed, 18 insertions(+) diff --git a/visualizer.c b/visualizer.c index 219d44cd3c..2ec6e34f58 100644 --- a/visualizer.c +++ b/visualizer.c @@ -178,6 +178,21 @@ static bool update_keyframe_animation(keyframe_animation_t* animation, visualize return true; } +void run_next_keyframe(keyframe_animation_t* animation, visualizer_state_t* state) { + int next_frame = animation->current_frame + 1; + if (next_frame == animation->num_frames) { + next_frame = 0; + } + keyframe_animation_t temp_animation = *animation; + temp_animation.current_frame = next_frame; + temp_animation.time_left_in_frame = animation->frame_lengths[next_frame]; + temp_animation.first_update_of_frame = true; + temp_animation.last_update_of_frame = false; + temp_animation.need_update = false; + visualizer_state_t temp_state = *state; + (*temp_animation.frame_functions[next_frame])(&temp_animation, &temp_state); +} + bool keyframe_no_operation(keyframe_animation_t* animation, visualizer_state_t* state) { (void)animation; (void)state; diff --git a/visualizer.h b/visualizer.h index 82d7a71d37..5375e0130f 100644 --- a/visualizer.h +++ b/visualizer.h @@ -106,6 +106,9 @@ extern GDisplay* LED_DISPLAY; void start_keyframe_animation(keyframe_animation_t* animation); void stop_keyframe_animation(keyframe_animation_t* animation); +// This runs the next keyframe, but does not update the animation state +// Useful for crossfades for example +void run_next_keyframe(keyframe_animation_t* animation, visualizer_state_t* state); // Some predefined keyframe functions that can be used by the user code // Does nothing, useful for adding delays From 0530ebb77d6961a7edc14f3a5b943165a8b52497 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 24 Apr 2016 16:20:00 +0300 Subject: [PATCH 21/41] Add led crossfading --- led_test.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++----- led_test.h | 2 ++ 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/led_test.c b/led_test.c index 197550fdd6..8c0de604a2 100644 --- a/led_test.c +++ b/led_test.c @@ -26,27 +26,41 @@ SOFTWARE. #include "math.h" keyframe_animation_t led_test_animation = { - .num_frames = 4, + .num_frames = 8, .loop = true, .frame_lengths = { - MS2ST(1000), - MS2ST(1000), - MS2ST(1000), - MS2ST(3000)}, + MS2ST(1000), // fade in + MS2ST(1000), // no op (leds on) + MS2ST(1000), // fade out + MS2ST(1000), // crossfade + MS2ST(3000), // left to rigt + MS2ST(1000), // crossfade + MS2ST(3000), // top_to_bottom + MS2ST(1000), // crossfade + }, .frame_functions = { keyframe_fade_in_all_leds, keyframe_no_operation, keyframe_fade_out_all_leds, + keyframe_led_crossfade, keyframe_led_left_to_right_gradient, + keyframe_led_crossfade, + keyframe_led_top_to_bottom_gradient, + keyframe_led_crossfade }, }; -static void keyframe_fade_all_leds_from_to(keyframe_animation_t* animation, uint8_t from, uint8_t to) { +static uint8_t fade_led_color(keyframe_animation_t* animation, uint8_t from, uint8_t to) { int frame_length = animation->frame_lengths[animation->current_frame]; int current_pos = frame_length - animation->time_left_in_frame; int delta = to - from; int luma = (delta * current_pos) / frame_length; luma += from; + return luma; +} + +static void keyframe_fade_all_leds_from_to(keyframe_animation_t* animation, uint8_t from, uint8_t to) { + uint8_t luma = fade_led_color(animation, from, to); color_t color = LUMA2COLOR(luma); gdispGClear(LED_DISPLAY, color); gdispGFlush(LED_DISPLAY); @@ -56,6 +70,9 @@ static void keyframe_fade_all_leds_from_to(keyframe_animation_t* animation, uint #define NUM_ROWS 7 #define NUM_COLS 7 +static uint8_t crossfade_start_frame[NUM_ROWS][NUM_COLS]; +static uint8_t crossfade_end_frame[NUM_ROWS][NUM_COLS]; + static uint8_t compute_gradient_color(float t, float index, float num) { float d = fabs(index - t); if (d > num / 2.0f) { @@ -85,5 +102,44 @@ bool keyframe_led_left_to_right_gradient(keyframe_animation_t* animation, visual uint8_t color = compute_gradient_color(t, i, NUM_COLS); gdispGDrawLine(LED_DISPLAY, i, 0, i, NUM_ROWS - 1, LUMA2COLOR(color)); } + gdispGFlush(LED_DISPLAY); + return true; +} + +bool keyframe_led_top_to_bottom_gradient(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)state; + int frame_length = animation->frame_lengths[animation->current_frame]; + int current_pos = frame_length - animation->time_left_in_frame; + float t = current_pos / frame_length; + for (int i=0; i< NUM_ROWS; i++) { + uint8_t color = compute_gradient_color(t, i, NUM_ROWS); + gdispGDrawLine(LED_DISPLAY, 0, i, NUM_COLS - 1, i, LUMA2COLOR(color)); + } + gdispGFlush(LED_DISPLAY); + return true; +} + +static void copy_current_led_state(uint8_t* dest) { + for (int i=0;ifirst_update_of_frame) { + copy_current_led_state(&crossfade_start_frame[0][0]); + run_next_keyframe(animation, state); + copy_current_led_state(&crossfade_end_frame[0][0]); + } + for (int i=0;i Date: Sun, 24 Apr 2016 16:26:53 +0300 Subject: [PATCH 22/41] LEDS are flushed automatically After running the animation, instead of having to do it manually. This avoids duplicate flushing, and better support for cross-fades. --- led_test.c | 4 ---- visualizer.c | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/led_test.c b/led_test.c index 8c0de604a2..c987eca380 100644 --- a/led_test.c +++ b/led_test.c @@ -63,7 +63,6 @@ static void keyframe_fade_all_leds_from_to(keyframe_animation_t* animation, uint uint8_t luma = fade_led_color(animation, from, to); color_t color = LUMA2COLOR(luma); gdispGClear(LED_DISPLAY, color); - gdispGFlush(LED_DISPLAY); } // TODO: Should be customizable per keyboard @@ -102,7 +101,6 @@ bool keyframe_led_left_to_right_gradient(keyframe_animation_t* animation, visual uint8_t color = compute_gradient_color(t, i, NUM_COLS); gdispGDrawLine(LED_DISPLAY, i, 0, i, NUM_ROWS - 1, LUMA2COLOR(color)); } - gdispGFlush(LED_DISPLAY); return true; } @@ -115,7 +113,6 @@ bool keyframe_led_top_to_bottom_gradient(keyframe_animation_t* animation, visual uint8_t color = compute_gradient_color(t, i, NUM_ROWS); gdispGDrawLine(LED_DISPLAY, 0, i, NUM_COLS - 1, i, LUMA2COLOR(color)); } - gdispGFlush(LED_DISPLAY); return true; } @@ -140,6 +137,5 @@ bool keyframe_led_crossfade(keyframe_animation_t* animation, visualizer_state_t* gdispGDrawPixel(LED_DISPLAY, j, i, color); } } - gdispGFlush(LED_DISPLAY); return true; } diff --git a/visualizer.c b/visualizer.c index 2ec6e34f58..81ec8298fd 100644 --- a/visualizer.c +++ b/visualizer.c @@ -127,6 +127,7 @@ void stop_all_keyframe_animations(void) { } static bool update_keyframe_animation(keyframe_animation_t* animation, visualizer_state_t* state, systime_t delta, systime_t* sleep_time) { + // TODO: Clean up this messy code dprintf("Animation frame%d, left %d, delta %d\n", animation->current_frame, animation->time_left_in_frame, delta); if (animation->current_frame == animation->num_frames) { @@ -394,6 +395,9 @@ static THD_FUNCTION(visualizerThread, arg) { update_keyframe_animation(animations[i], &state, delta, &sleep_time); } } +#ifdef LED_ENABLE + gdispGFlush(LED_DISPLAY); +#endif // The animation can enable the visualizer // And we might need to update the state when that happens // so don't sleep From 74baa4895c8efd409eb10eaf1e6cfc0e2677b45c Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 24 Apr 2016 16:40:41 +0300 Subject: [PATCH 23/41] Run mirrored keyframes for led tests as well --- led_test.c | 34 ++++++++++++++++++++++++++++++---- led_test.h | 2 ++ visualizer.h | 4 ++-- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/led_test.c b/led_test.c index c987eca380..ce6c2e68ec 100644 --- a/led_test.c +++ b/led_test.c @@ -26,17 +26,24 @@ SOFTWARE. #include "math.h" keyframe_animation_t led_test_animation = { - .num_frames = 8, + .num_frames = 14, .loop = true, .frame_lengths = { MS2ST(1000), // fade in MS2ST(1000), // no op (leds on) MS2ST(1000), // fade out MS2ST(1000), // crossfade - MS2ST(3000), // left to rigt + MS2ST(3000), // left to rigt (outside in) MS2ST(1000), // crossfade MS2ST(3000), // top_to_bottom + 0, // mirror leds MS2ST(1000), // crossfade + MS2ST(3000), // left_to_right (mirrored, so inside out) + MS2ST(1000), // crossfade + MS2ST(3000), // top_to_bottom + 0, // normal leds + MS2ST(1000), // crossfade + }, .frame_functions = { keyframe_fade_in_all_leds, @@ -46,7 +53,13 @@ keyframe_animation_t led_test_animation = { keyframe_led_left_to_right_gradient, keyframe_led_crossfade, keyframe_led_top_to_bottom_gradient, - keyframe_led_crossfade + keyframe_mirror_led_orientation, + keyframe_led_crossfade, + keyframe_led_left_to_right_gradient, + keyframe_led_crossfade, + keyframe_led_top_to_bottom_gradient, + keyframe_normal_led_orientation, + keyframe_led_crossfade, }, }; @@ -123,7 +136,6 @@ static void copy_current_led_state(uint8_t* dest) { } } } - bool keyframe_led_crossfade(keyframe_animation_t* animation, visualizer_state_t* state) { (void)state; if (animation->first_update_of_frame) { @@ -139,3 +151,17 @@ bool keyframe_led_crossfade(keyframe_animation_t* animation, visualizer_state_t* } return true; } + +bool keyframe_mirror_led_orientation(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)state; + (void)animation; + gdispGSetOrientation(LED_DISPLAY, GDISP_ROTATE_180); + return true; +} + +bool keyframe_normal_led_orientation(keyframe_animation_t* animation, visualizer_state_t* state) { + (void)state; + (void)animation; + gdispGSetOrientation(LED_DISPLAY, GDISP_ROTATE_0); + return true; +} diff --git a/led_test.h b/led_test.h index e14b25e796..5e2325753e 100644 --- a/led_test.h +++ b/led_test.h @@ -32,6 +32,8 @@ bool keyframe_fade_out_all_leds(keyframe_animation_t* animation, visualizer_stat bool keyframe_led_left_to_right_gradient(keyframe_animation_t* animation, visualizer_state_t* state); bool keyframe_led_top_to_bottom_gradient(keyframe_animation_t* animation, visualizer_state_t* state); bool keyframe_led_crossfade(keyframe_animation_t* animation, visualizer_state_t* state); +bool keyframe_mirror_led_orientation(keyframe_animation_t* animation, visualizer_state_t* state); +bool keyframe_normal_led_orientation(keyframe_animation_t* animation, visualizer_state_t* state); extern keyframe_animation_t led_test_animation; diff --git a/visualizer.h b/visualizer.h index 5375e0130f..8a2772c6da 100644 --- a/visualizer.h +++ b/visualizer.h @@ -45,8 +45,8 @@ void visualizer_suspend(void); // This should be called when the keyboard wakes up from suspend state void visualizer_resume(void); -// If you need support for more than 8 keyframes per animation, you can change this -#define MAX_VISUALIZER_KEY_FRAMES 8 +// If you need support for more than 16 keyframes per animation, you can change this +#define MAX_VISUALIZER_KEY_FRAMES 16 struct keyframe_animation_t; From 6313cfd5597ffce0c70a472aa87ed68b6daa4419 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 24 Apr 2016 17:44:11 +0300 Subject: [PATCH 24/41] Fix some makefile issues, and symbol issues --- visualizer.c | 8 ++++---- visualizer.mk | 10 ++++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/visualizer.c b/visualizer.c index 81ec8298fd..0684a26d8f 100644 --- a/visualizer.c +++ b/visualizer.c @@ -77,8 +77,8 @@ static remote_object_t* remote_objects[] = { #endif -GDisplay* LCDDisplay; -GDisplay* LEDDisplay; +GDisplay* LCD_DISPLAY = 0; +GDisplay* LED_DISPLAY = 0; void start_keyframe_animation(keyframe_animation_t* animation) { @@ -437,8 +437,8 @@ void visualizer_init(void) { add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*) ); #endif // TODO: Make sure these works when either of these are disabled - LCDDisplay = gdispGetDisplay(0); - LEDDisplay = gdispGetDisplay(1); + LCD_DISPLAY = gdispGetDisplay(0); + LED_DISPLAY = gdispGetDisplay(1); // We are using a low priority thread, the idea is to have it run only diff --git a/visualizer.mk b/visualizer.mk index 96ca468e86..5cc199cf48 100644 --- a/visualizer.mk +++ b/visualizer.mk @@ -21,13 +21,13 @@ # SOFTWARE. GFXLIB = $(VISUALIZER_DIR)/ugfx -SRC += $(GFXSRC) $(VISUALIZER_DIR)/visualizer.c +SRC += $(VISUALIZER_DIR)/visualizer.c UINCDIR += $(GFXINC) $(VISUALIZER_DIR) ifdef LCD_ENABLE -include $(GFXLIB)/gfx.mk UDEFS += -DLCD_ENABLE ULIBS += -lm +USE_UGFX = yes endif ifdef LCD_BACKLIGHT_ENABLE @@ -39,6 +39,12 @@ endif ifdef LED_ENABLE SRC += $(VISUALIZER_DIR)/led_test.c UDEFS += -DLED_ENABLE +USE_UGFX = yes +endif + +ifdef USE_UGFX +include $(GFXLIB)/gfx.mk +SRC += $(GFXSRC) endif ifndef VISUALIZER_USER From f0c8e7c495af2aad476f4c428b700ed26188a8e5 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 30 Apr 2016 22:45:41 +0300 Subject: [PATCH 25/41] Fix a few led test animation issues --- led_test.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/led_test.c b/led_test.c index ce6c2e68ec..ed65a0baa5 100644 --- a/led_test.c +++ b/led_test.c @@ -63,7 +63,7 @@ keyframe_animation_t led_test_animation = { }, }; -static uint8_t fade_led_color(keyframe_animation_t* animation, uint8_t from, uint8_t to) { +static uint8_t fade_led_color(keyframe_animation_t* animation, int from, int to) { int frame_length = animation->frame_lengths[animation->current_frame]; int current_pos = frame_length - animation->time_left_in_frame; int delta = to - from; @@ -107,8 +107,8 @@ bool keyframe_fade_out_all_leds(keyframe_animation_t* animation, visualizer_stat bool keyframe_led_left_to_right_gradient(keyframe_animation_t* animation, visualizer_state_t* state) { (void)state; - int frame_length = animation->frame_lengths[animation->current_frame]; - int current_pos = frame_length - animation->time_left_in_frame; + float frame_length = animation->frame_lengths[animation->current_frame]; + float current_pos = frame_length - animation->time_left_in_frame; float t = current_pos / frame_length; for (int i=0; i< NUM_COLS; i++) { uint8_t color = compute_gradient_color(t, i, NUM_COLS); @@ -119,8 +119,8 @@ bool keyframe_led_left_to_right_gradient(keyframe_animation_t* animation, visual bool keyframe_led_top_to_bottom_gradient(keyframe_animation_t* animation, visualizer_state_t* state) { (void)state; - int frame_length = animation->frame_lengths[animation->current_frame]; - int current_pos = frame_length - animation->time_left_in_frame; + float frame_length = animation->frame_lengths[animation->current_frame]; + float current_pos = frame_length - animation->time_left_in_frame; float t = current_pos / frame_length; for (int i=0; i< NUM_ROWS; i++) { uint8_t color = compute_gradient_color(t, i, NUM_ROWS); @@ -156,12 +156,12 @@ bool keyframe_mirror_led_orientation(keyframe_animation_t* animation, visualizer (void)state; (void)animation; gdispGSetOrientation(LED_DISPLAY, GDISP_ROTATE_180); - return true; + return false; } bool keyframe_normal_led_orientation(keyframe_animation_t* animation, visualizer_state_t* state) { (void)state; (void)animation; gdispGSetOrientation(LED_DISPLAY, GDISP_ROTATE_0); - return true; + return false; } From 25382cb6f21b6136b0f490a618ce8d494ca5cd38 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Wed, 11 May 2016 22:06:26 +0300 Subject: [PATCH 26/41] Fix compute_gradient_color --- led_test.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/led_test.c b/led_test.c index ed65a0baa5..d358ef81eb 100644 --- a/led_test.c +++ b/led_test.c @@ -86,10 +86,13 @@ static uint8_t crossfade_start_frame[NUM_ROWS][NUM_COLS]; static uint8_t crossfade_end_frame[NUM_ROWS][NUM_COLS]; static uint8_t compute_gradient_color(float t, float index, float num) { - float d = fabs(index - t); - if (d > num / 2.0f) { + const float target = t * (num - 1.0f); + const float half_num = num / 2.0f; + float d = fabs(index - target); + if (d > half_num) { d = num - d; } + d = 1.0f - (d / half_num); return (uint8_t)(255.0f * d); } From 5651be7a2f50857b1ddd120a4b0184a28cdca4aa Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 15 May 2016 13:00:27 +0300 Subject: [PATCH 27/41] Configurable visualizer thread priority --- visualizer.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/visualizer.c b/visualizer.c index 867a1d3347..4c47114990 100644 --- a/visualizer.c +++ b/visualizer.c @@ -24,6 +24,7 @@ SOFTWARE. #include "visualizer.h" #include "ch.h" +#include "config.h" #include #ifdef LCD_ENABLE @@ -47,6 +48,11 @@ SOFTWARE. #include "serial_link/system/driver.h" #endif +// Define this in config.h +#ifndef VISUALIZER_THREAD_PRIORITY +#define "Visualizer thread priority not defined" +#endif + static visualizer_keyboard_status_t current_status = { .layer = 0xFFFFFFFF, @@ -409,7 +415,7 @@ void visualizer_init(void) { // when the main thread is sleeping during the matrix scanning chEvtObjectInit(&layer_changed_event); (void)chThdCreateStatic(visualizerThreadStack, sizeof(visualizerThreadStack), - LOWPRIO, visualizerThread, NULL); + VISUALIZER_THREAD_PRIORITY, visualizerThread, NULL); } void update_status(bool changed) { From 81f89cc6725d4e513f85ed1c841d25d0ac64bfe3 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 15 May 2016 13:39:58 +0300 Subject: [PATCH 28/41] Update include dir for new serial_link include --- visualizer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/visualizer.c b/visualizer.c index 4c47114990..605be30594 100644 --- a/visualizer.c +++ b/visualizer.c @@ -45,7 +45,7 @@ SOFTWARE. #ifdef USE_SERIAL_LINK #include "serial_link/protocol/transport.h" -#include "serial_link/system/driver.h" +#include "serial_link/system/serial_link.h" #endif // Define this in config.h From 15bdef3ee92e6809ec5d0f25901f4a490cd91b58 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Mon, 16 May 2016 09:45:39 +0300 Subject: [PATCH 29/41] Makefile changes to support emulator build --- ugfx | 2 +- visualizer.mk | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ugfx b/ugfx index e221a69061..314a066d11 160000 --- a/ugfx +++ b/ugfx @@ -1 +1 @@ -Subproject commit e221a690616e20f87e0b0088baffdbd5427be862 +Subproject commit 314a066d11f09d295d42054a0b53fa1a95c0ba0a diff --git a/visualizer.mk b/visualizer.mk index 5cc199cf48..c51f8ba5d4 100644 --- a/visualizer.mk +++ b/visualizer.mk @@ -21,7 +21,9 @@ # SOFTWARE. GFXLIB = $(VISUALIZER_DIR)/ugfx +ifndef EMULATOR SRC += $(VISUALIZER_DIR)/visualizer.c +endif UINCDIR += $(GFXINC) $(VISUALIZER_DIR) ifdef LCD_ENABLE @@ -45,9 +47,13 @@ endif ifdef USE_UGFX include $(GFXLIB)/gfx.mk SRC += $(GFXSRC) +UDEFS += $(patsubst %,-D%,$(patsubst -D%,%,$(GFXDEFS))) +ULIBS += $(patsubst %,-l%,$(patsubst -l%,%,$(GFXLIBS))) endif ifndef VISUALIZER_USER +ifndef EMULATOR VISUALIZER_USER = visualizer_user.c endif +endif SRC += $(VISUALIZER_USER) \ No newline at end of file From d79e94adb1182ae867df0cc7621ef3d44d213bbc Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Tue, 17 May 2016 09:35:02 +0300 Subject: [PATCH 30/41] Use ugfx API instead of chibios --- ugfx | 2 +- visualizer.c | 34 ++++++++++++++++++---------------- visualizer.mk | 12 +++++++++--- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/ugfx b/ugfx index 314a066d11..7d7eeef0ad 160000 --- a/ugfx +++ b/ugfx @@ -1 +1 @@ -Subproject commit 314a066d11f09d295d42054a0b53fa1a95c0ba0a +Subproject commit 7d7eeef0ad0f1b28f4fb86ad931cb6774c7b9e81 diff --git a/visualizer.c b/visualizer.c index 579837edcd..ea84546fb6 100644 --- a/visualizer.c +++ b/visualizer.c @@ -23,7 +23,6 @@ SOFTWARE. */ #include "visualizer.h" -#include "ch.h" #include "config.h" #include @@ -68,7 +67,7 @@ static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboa status1->suspended == status2->suspended; } -static event_source_t layer_changed_event; +static GSourceHandle layer_changed_event; static bool visualizer_enabled = false; #define MAX_SIMULTANEOUS_ANIMATIONS 4 @@ -132,7 +131,7 @@ void stop_all_keyframe_animations(void) { } } -static bool update_keyframe_animation(keyframe_animation_t* animation, visualizer_state_t* state, systime_t delta, systime_t* sleep_time) { +static bool update_keyframe_animation(keyframe_animation_t* animation, visualizer_state_t* state, systemticks_t delta, systemticks_t* sleep_time) { // TODO: Clean up this messy code dprintf("Animation frame%d, left %d, delta %d\n", animation->current_frame, animation->time_left_in_frame, delta); @@ -331,12 +330,13 @@ bool enable_visualization(keyframe_animation_t* animation, visualizer_state_t* s } // TODO: Optimize the stack size, this is probably way too big -static THD_WORKING_AREA(visualizerThreadStack, 1024); -static THD_FUNCTION(visualizerThread, arg) { +static DECLARE_THREAD_STACK(visualizerThreadStack, 1024); +static DECLARE_THREAD_FUNCTION(visualizerThread, arg) { (void)arg; - event_listener_t event_listener; - chEvtRegister(&layer_changed_event, &event_listener, 0); + GListener event_listener; + geventListenerInit(&event_listener); + geventAttachSource(&event_listener, layer_changed_event, 0); visualizer_keyboard_status_t initial_status = { .default_layer = 0xFFFFFFFF, @@ -363,12 +363,12 @@ static THD_FUNCTION(visualizerThread, arg) { LCD_INT(state.current_lcd_color)); #endif - systime_t sleep_time = TIME_INFINITE; - systime_t current_time = chVTGetSystemTimeX(); + systemticks_t sleep_time = TIME_INFINITE; + systemticks_t current_time = gfxSystemTicks(); while(true) { - systime_t new_time = chVTGetSystemTimeX(); - systime_t delta = new_time - current_time; + systemticks_t new_time = gfxSystemTicks(); + systemticks_t delta = new_time - current_time; current_time = new_time; bool enabled = visualizer_enabled; if (!same_status(&state.status, ¤t_status)) { @@ -411,7 +411,7 @@ static THD_FUNCTION(visualizerThread, arg) { sleep_time = 0; } - systime_t after_update = chVTGetSystemTimeX(); + systemticks_t after_update = gfxSystemTicks(); unsigned update_delta = after_update - current_time; if (sleep_time != TIME_INFINITE) { if (sleep_time > update_delta) { @@ -422,12 +422,14 @@ static THD_FUNCTION(visualizerThread, arg) { } } dprintf("Update took %d, last delta %d, sleep_time %d\n", update_delta, delta, sleep_time); - chEvtWaitOneTimeout(EVENT_MASK(0), sleep_time); + geventEventWait(&event_listener, sleep_time); } #ifdef LCD_ENABLE gdispCloseFont(state.font_fixed5x8); gdispCloseFont(state.font_dejavusansbold12); #endif + + return 0; } void visualizer_init(void) { @@ -449,14 +451,14 @@ void visualizer_init(void) { // We are using a low priority thread, the idea is to have it run only // when the main thread is sleeping during the matrix scanning - chEvtObjectInit(&layer_changed_event); - (void)chThdCreateStatic(visualizerThreadStack, sizeof(visualizerThreadStack), + gfxThreadCreate(visualizerThreadStack, sizeof(visualizerThreadStack), VISUALIZER_THREAD_PRIORITY, visualizerThread, NULL); } void update_status(bool changed) { if (changed) { - chEvtBroadcast(&layer_changed_event); + GSourceListener* listener = geventGetSourceListener(layer_changed_event, NULL); + geventSendEvent(listener); } #ifdef USE_SERIAL_LINK static systime_t last_update = 0; diff --git a/visualizer.mk b/visualizer.mk index c51f8ba5d4..6788293290 100644 --- a/visualizer.mk +++ b/visualizer.mk @@ -21,9 +21,7 @@ # SOFTWARE. GFXLIB = $(VISUALIZER_DIR)/ugfx -ifndef EMULATOR SRC += $(VISUALIZER_DIR)/visualizer.c -endif UINCDIR += $(GFXINC) $(VISUALIZER_DIR) ifdef LCD_ENABLE @@ -33,13 +31,17 @@ USE_UGFX = yes endif ifdef LCD_BACKLIGHT_ENABLE +ifndef EMULATOR SRC += $(VISUALIZER_DIR)/lcd_backlight.c SRC += lcd_backlight_hal.c +endif UDEFS += -DLCD_BACKLIGHT_ENABLE endif ifdef LED_ENABLE +ifndef EMULATOR SRC += $(VISUALIZER_DIR)/led_test.c +endif UDEFS += -DLED_ENABLE USE_UGFX = yes endif @@ -56,4 +58,8 @@ ifndef EMULATOR VISUALIZER_USER = visualizer_user.c endif endif -SRC += $(VISUALIZER_USER) \ No newline at end of file +SRC += $(VISUALIZER_USER) + +ifdef EMULATOR +UINCDIR += $(TMK_DIR)/common +endif \ No newline at end of file From 4e89732617454a600189242d417f5e2ba0855683 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Tue, 17 May 2016 09:45:05 +0300 Subject: [PATCH 31/41] Enable and fix compilation of more files --- led_test.c | 24 ++++++++++++------------ visualizer.mk | 6 +----- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/led_test.c b/led_test.c index d358ef81eb..6d3f3b2ed7 100644 --- a/led_test.c +++ b/led_test.c @@ -29,20 +29,20 @@ keyframe_animation_t led_test_animation = { .num_frames = 14, .loop = true, .frame_lengths = { - MS2ST(1000), // fade in - MS2ST(1000), // no op (leds on) - MS2ST(1000), // fade out - MS2ST(1000), // crossfade - MS2ST(3000), // left to rigt (outside in) - MS2ST(1000), // crossfade - MS2ST(3000), // top_to_bottom + gfxMillisecondsToTicks(1000), // fade in + gfxMillisecondsToTicks(1000), // no op (leds on) + gfxMillisecondsToTicks(1000), // fade out + gfxMillisecondsToTicks(1000), // crossfade + gfxMillisecondsToTicks(3000), // left to rigt (outside in) + gfxMillisecondsToTicks(1000), // crossfade + gfxMillisecondsToTicks(3000), // top_to_bottom 0, // mirror leds - MS2ST(1000), // crossfade - MS2ST(3000), // left_to_right (mirrored, so inside out) - MS2ST(1000), // crossfade - MS2ST(3000), // top_to_bottom + gfxMillisecondsToTicks(1000), // crossfade + gfxMillisecondsToTicks(3000), // left_to_right (mirrored, so inside out) + gfxMillisecondsToTicks(1000), // crossfade + gfxMillisecondsToTicks(3000), // top_to_bottom 0, // normal leds - MS2ST(1000), // crossfade + gfxMillisecondsToTicks(1000), // crossfade }, .frame_functions = { diff --git a/visualizer.mk b/visualizer.mk index 6788293290..56525ffd93 100644 --- a/visualizer.mk +++ b/visualizer.mk @@ -31,17 +31,15 @@ USE_UGFX = yes endif ifdef LCD_BACKLIGHT_ENABLE -ifndef EMULATOR SRC += $(VISUALIZER_DIR)/lcd_backlight.c +ifndef EMULATOR SRC += lcd_backlight_hal.c endif UDEFS += -DLCD_BACKLIGHT_ENABLE endif ifdef LED_ENABLE -ifndef EMULATOR SRC += $(VISUALIZER_DIR)/led_test.c -endif UDEFS += -DLED_ENABLE USE_UGFX = yes endif @@ -54,10 +52,8 @@ ULIBS += $(patsubst %,-l%,$(patsubst -l%,%,$(GFXLIBS))) endif ifndef VISUALIZER_USER -ifndef EMULATOR VISUALIZER_USER = visualizer_user.c endif -endif SRC += $(VISUALIZER_USER) ifdef EMULATOR From 15300cb681f74672547aa5e53d165ac748d43a17 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Tue, 17 May 2016 09:51:27 +0300 Subject: [PATCH 32/41] Build lcd_backlight_hal_emulator.c When the emulator is set, instead of the lcd_backlight_hal.c file. --- visualizer.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/visualizer.mk b/visualizer.mk index 56525ffd93..3e361491c9 100644 --- a/visualizer.mk +++ b/visualizer.mk @@ -34,6 +34,8 @@ ifdef LCD_BACKLIGHT_ENABLE SRC += $(VISUALIZER_DIR)/lcd_backlight.c ifndef EMULATOR SRC += lcd_backlight_hal.c +else +SRC += lcd_backlight_hal_emulator.c endif UDEFS += -DLCD_BACKLIGHT_ENABLE endif From 07e412c53829c66e948eb147873a04cd27b0771b Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Tue, 17 May 2016 11:21:38 +0300 Subject: [PATCH 33/41] Fix crash when event listener not created --- visualizer.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/visualizer.c b/visualizer.c index ea84546fb6..ff99e960fd 100644 --- a/visualizer.c +++ b/visualizer.c @@ -458,7 +458,9 @@ void visualizer_init(void) { void update_status(bool changed) { if (changed) { GSourceListener* listener = geventGetSourceListener(layer_changed_event, NULL); - geventSendEvent(listener); + if (listener) { + geventSendEvent(listener); + } } #ifdef USE_SERIAL_LINK static systime_t last_update = 0; From fa8feb21a4709dba552df4a96205c50a319f5e3b Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Wed, 18 May 2016 08:40:36 +0300 Subject: [PATCH 34/41] Add custom led and lcd display support --- visualizer.c | 18 +++++++++++++++--- visualizer.h | 5 +++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/visualizer.c b/visualizer.c index ff99e960fd..bbb00debcf 100644 --- a/visualizer.c +++ b/visualizer.c @@ -85,6 +85,15 @@ static remote_object_t* remote_objects[] = { GDisplay* LCD_DISPLAY = 0; GDisplay* LED_DISPLAY = 0; +__attribute__((weak)) +GDisplay* get_lcd_display(void) { + return gdispGetDisplay(0); +} + +__attribute__((weak)) +GDisplay* get_led_display(void) { + return gdispGetDisplay(1); +} void start_keyframe_animation(keyframe_animation_t* animation) { animation->current_frame = -1; @@ -444,10 +453,13 @@ void visualizer_init(void) { #ifdef USE_SERIAL_LINK add_remote_objects(remote_objects, sizeof(remote_objects) / sizeof(remote_object_t*) ); #endif - // TODO: Make sure these works when either of these are disabled - LCD_DISPLAY = gdispGetDisplay(0); - LED_DISPLAY = gdispGetDisplay(1); +#ifdef LCD_ENABLE + LCD_DISPLAY = get_lcd_display(); +#endif +#ifdef LED_ENABLE + LED_DISPLAY = get_led_display(); +#endif // We are using a low priority thread, the idea is to have it run only // when the main thread is sleeping during the matrix scanning diff --git a/visualizer.h b/visualizer.h index 8a2772c6da..4d6a61dda3 100644 --- a/visualizer.h +++ b/visualizer.h @@ -45,6 +45,11 @@ void visualizer_suspend(void); // This should be called when the keyboard wakes up from suspend state void visualizer_resume(void); +// These functions are week, so they can be overridden by the keyboard +// if needed +GDisplay* get_lcd_display(void); +GDisplay* get_led_display(void); + // If you need support for more than 16 keyframes per animation, you can change this #define MAX_VISUALIZER_KEY_FRAMES 16 From 94519e387a85e3b4ab72bd7d837ff590cc690eb9 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Wed, 18 May 2016 09:03:42 +0300 Subject: [PATCH 35/41] Add callback function for emulator drawing --- visualizer.c | 4 ++++ visualizer.h | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/visualizer.c b/visualizer.c index bbb00debcf..607a64566a 100644 --- a/visualizer.c +++ b/visualizer.c @@ -413,6 +413,10 @@ static DECLARE_THREAD_FUNCTION(visualizerThread, arg) { #ifdef LED_ENABLE gdispGFlush(LED_DISPLAY); #endif + +#if EMULATOR + draw_emulator(); +#endif // The animation can enable the visualizer // And we might need to update the state when that happens // so don't sleep diff --git a/visualizer.h b/visualizer.h index 4d6a61dda3..a3828e35f4 100644 --- a/visualizer.h +++ b/visualizer.h @@ -50,6 +50,11 @@ void visualizer_resume(void); GDisplay* get_lcd_display(void); GDisplay* get_led_display(void); +// For emulator builds, this function need to be implemented +#if EMULATOR +void draw_emulator(void); +#endif + // If you need support for more than 16 keyframes per animation, you can change this #define MAX_VISUALIZER_KEY_FRAMES 16 @@ -134,7 +139,7 @@ bool keyframe_enable_lcd_and_backlight(keyframe_animation_t* animation, visualiz // directly from the initalize_user_visualizer function (the animation can be null) bool enable_visualization(keyframe_animation_t* animation, visualizer_state_t* state); -// These two functions have to be implemented by the user +// These functions have to be implemented by the user void initialize_user_visualizer(visualizer_state_t* state); void update_user_visualizer_state(visualizer_state_t* state); void user_visualizer_suspend(visualizer_state_t* state); From 4d7e4a4780bc779a3960497a9084ef6c9d6ef87e Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sat, 28 May 2016 11:13:08 +0300 Subject: [PATCH 36/41] Don't include lcd_backlight_hal for emulator --- visualizer.mk | 2 -- 1 file changed, 2 deletions(-) diff --git a/visualizer.mk b/visualizer.mk index 3e361491c9..56525ffd93 100644 --- a/visualizer.mk +++ b/visualizer.mk @@ -34,8 +34,6 @@ ifdef LCD_BACKLIGHT_ENABLE SRC += $(VISUALIZER_DIR)/lcd_backlight.c ifndef EMULATOR SRC += lcd_backlight_hal.c -else -SRC += lcd_backlight_hal_emulator.c endif UDEFS += -DLCD_BACKLIGHT_ENABLE endif From 9c955145f50abebcbe5ad5e203a91ca83249fee6 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 29 May 2016 02:08:46 +0300 Subject: [PATCH 37/41] Fix emulator #ifdef check --- visualizer.c | 2 +- visualizer.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/visualizer.c b/visualizer.c index 607a64566a..0e587221fc 100644 --- a/visualizer.c +++ b/visualizer.c @@ -414,7 +414,7 @@ static DECLARE_THREAD_FUNCTION(visualizerThread, arg) { gdispGFlush(LED_DISPLAY); #endif -#if EMULATOR +#ifdef EMULATOR draw_emulator(); #endif // The animation can enable the visualizer diff --git a/visualizer.h b/visualizer.h index a3828e35f4..45cfa9aa97 100644 --- a/visualizer.h +++ b/visualizer.h @@ -51,7 +51,7 @@ GDisplay* get_lcd_display(void); GDisplay* get_led_display(void); // For emulator builds, this function need to be implemented -#if EMULATOR +#ifdef EMULATOR void draw_emulator(void); #endif From 0c3189055f049e6023471e75139d488b288aead9 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 29 May 2016 17:42:32 +0300 Subject: [PATCH 38/41] Make LED visualization times configurable --- led_test.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/led_test.c b/led_test.c index 6d3f3b2ed7..d53f0b7fb7 100644 --- a/led_test.c +++ b/led_test.c @@ -25,6 +25,9 @@ SOFTWARE. #include "gfx.h" #include "math.h" +#define CROSSFADE_TIME 1000 +#define GRADIENT_TIME 3000 + keyframe_animation_t led_test_animation = { .num_frames = 14, .loop = true, @@ -32,17 +35,17 @@ keyframe_animation_t led_test_animation = { gfxMillisecondsToTicks(1000), // fade in gfxMillisecondsToTicks(1000), // no op (leds on) gfxMillisecondsToTicks(1000), // fade out - gfxMillisecondsToTicks(1000), // crossfade - gfxMillisecondsToTicks(3000), // left to rigt (outside in) - gfxMillisecondsToTicks(1000), // crossfade - gfxMillisecondsToTicks(3000), // top_to_bottom + gfxMillisecondsToTicks(CROSSFADE_TIME), // crossfade + gfxMillisecondsToTicks(GRADIENT_TIME), // left to rigt (outside in) + gfxMillisecondsToTicks(CROSSFADE_TIME), // crossfade + gfxMillisecondsToTicks(GRADIENT_TIME), // top_to_bottom 0, // mirror leds - gfxMillisecondsToTicks(1000), // crossfade - gfxMillisecondsToTicks(3000), // left_to_right (mirrored, so inside out) - gfxMillisecondsToTicks(1000), // crossfade - gfxMillisecondsToTicks(3000), // top_to_bottom + gfxMillisecondsToTicks(CROSSFADE_TIME), // crossfade + gfxMillisecondsToTicks(GRADIENT_TIME), // left_to_right (mirrored, so inside out) + gfxMillisecondsToTicks(CROSSFADE_TIME), // crossfade + gfxMillisecondsToTicks(GRADIENT_TIME), // top_to_bottom 0, // normal leds - gfxMillisecondsToTicks(1000), // crossfade + gfxMillisecondsToTicks(CROSSFADE_TIME), // crossfade }, .frame_functions = { From 489288f67462e0c6fae8e54644af2fa4593e4406 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Sun, 29 May 2016 18:27:32 +0300 Subject: [PATCH 39/41] Add a nicer sine based gradient for the LEDs --- led_test.c | 13 +++++-------- ugfx | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/led_test.c b/led_test.c index d53f0b7fb7..c2ea30b55b 100644 --- a/led_test.c +++ b/led_test.c @@ -89,14 +89,11 @@ static uint8_t crossfade_start_frame[NUM_ROWS][NUM_COLS]; static uint8_t crossfade_end_frame[NUM_ROWS][NUM_COLS]; static uint8_t compute_gradient_color(float t, float index, float num) { - const float target = t * (num - 1.0f); - const float half_num = num / 2.0f; - float d = fabs(index - target); - if (d > half_num) { - d = num - d; - } - d = 1.0f - (d / half_num); - return (uint8_t)(255.0f * d); + const float two_pi = 2.0f * PI; + float normalized_index = (1.0f - index / (num - 1)) * two_pi; + float x = t * two_pi + normalized_index; + float v = 0.5 * (cosf(x) + 1.0f); + return (uint8_t)(255.0f * v); } bool keyframe_fade_in_all_leds(keyframe_animation_t* animation, visualizer_state_t* state) { diff --git a/ugfx b/ugfx index 7d7eeef0ad..dc5786acc2 160000 --- a/ugfx +++ b/ugfx @@ -1 +1 @@ -Subproject commit 7d7eeef0ad0f1b28f4fb86ad931cb6774c7b9e81 +Subproject commit dc5786acc246fb23503517647c386e43f1bfb247 From 73d890a2c9c34b905cd5e74e7146fdd4578dcb96 Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Wed, 1 Jun 2016 09:22:47 +0300 Subject: [PATCH 40/41] Fix visualizer sleeping too long The documentation for ugfx gEventWait wait is wrong and the function takes the time in milliseconds, instead of system ticks. This caused the the thread to sleep way too long. It also caused somewhat random sleeping behaviour as the MS2ST function overflows at around 43 seconds sleep. The event source is also now initialized correctly, so that the thread actually can be woken up by events. --- visualizer.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/visualizer.c b/visualizer.c index 0e587221fc..c240734055 100644 --- a/visualizer.c +++ b/visualizer.c @@ -25,6 +25,9 @@ SOFTWARE. #include "visualizer.h" #include "config.h" #include +#ifdef PROTOCOL_CHIBIOS +#include "ch.h" +#endif #ifdef LCD_ENABLE #include "gfx.h" @@ -67,7 +70,6 @@ static bool same_status(visualizer_keyboard_status_t* status1, visualizer_keyboa status1->suspended == status2->suspended; } -static GSourceHandle layer_changed_event; static bool visualizer_enabled = false; #define MAX_SIMULTANEOUS_ANIMATIONS 4 @@ -185,8 +187,8 @@ static bool update_keyframe_animation(keyframe_animation_t* animation, visualize animation->first_update_of_frame = false; } - int wanted_sleep = animation->need_update ? 10 : animation->time_left_in_frame; - if ((unsigned)wanted_sleep < *sleep_time) { + systemticks_t wanted_sleep = animation->need_update ? gfxMillisecondsToTicks(10) : (unsigned)animation->time_left_in_frame; + if (wanted_sleep < *sleep_time) { *sleep_time = wanted_sleep; } @@ -345,7 +347,7 @@ static DECLARE_THREAD_FUNCTION(visualizerThread, arg) { GListener event_listener; geventListenerInit(&event_listener); - geventAttachSource(&event_listener, layer_changed_event, 0); + geventAttachSource(&event_listener, (GSourceHandle)¤t_status, 0); visualizer_keyboard_status_t initial_status = { .default_layer = 0xFFFFFFFF, @@ -435,6 +437,16 @@ static DECLARE_THREAD_FUNCTION(visualizerThread, arg) { } } dprintf("Update took %d, last delta %d, sleep_time %d\n", update_delta, delta, sleep_time); +#ifdef PROTOCOL_CHIBIOS + // The gEventWait function really takes milliseconds, even if the documentation says ticks. + // Unfortunately there's no generic ugfx conversion from system time to milliseconds, + // so let's do it in a platform dependent way. + + // On windows the system ticks is the same as milliseconds anyway + if (sleep_time != TIME_INFINITE) { + sleep_time = ST2MS(sleep_time); + } +#endif geventEventWait(&event_listener, sleep_time); } #ifdef LCD_ENABLE @@ -473,7 +485,7 @@ void visualizer_init(void) { void update_status(bool changed) { if (changed) { - GSourceListener* listener = geventGetSourceListener(layer_changed_event, NULL); + GSourceListener* listener = geventGetSourceListener((GSourceHandle)¤t_status, NULL); if (listener) { geventSendEvent(listener); } From f727801bc69b3db28f84b7b8986756193bbfd21e Mon Sep 17 00:00:00 2001 From: Fred Sundvik Date: Wed, 6 Jul 2016 20:17:16 +0300 Subject: [PATCH 41/41] Delete .gitmodules from visualizer --- quantum/visualizer/.gitmodules | 3 --- quantum/visualizer/ugfx | 1 - 2 files changed, 4 deletions(-) delete mode 100644 quantum/visualizer/.gitmodules delete mode 160000 quantum/visualizer/ugfx diff --git a/quantum/visualizer/.gitmodules b/quantum/visualizer/.gitmodules deleted file mode 100644 index b320458c0e..0000000000 --- a/quantum/visualizer/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "ugfx"] - path = ugfx - url = https://bitbucket.org/fredizzimo/ugfx.git diff --git a/quantum/visualizer/ugfx b/quantum/visualizer/ugfx deleted file mode 160000 index e221a69061..0000000000 --- a/quantum/visualizer/ugfx +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e221a690616e20f87e0b0088baffdbd5427be862