diff options
author | druotoni <89318351+druotoni@users.noreply.github.com> | 2022-01-13 20:00:35 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-13 11:00:35 -0800 |
commit | b3c0548ed3cdb31c47d1a9e06c446c0804d6e1b6 (patch) | |
tree | 87ba1062ffd5d3c77e7ea95fe537d32c3337da94 | |
parent | 8a6da095d22d66c0ba36ce8731c9d821ca4262e9 (diff) | |
download | qmk_firmware-b3c0548ed3cdb31c47d1a9e06c446c0804d6e1b6.tar.gz qmk_firmware-b3c0548ed3cdb31c47d1a9e06c446c0804d6e1b6.zip |
[Keymap] Lily58 : HELL0 NAVI. Interface (#15469)
Co-authored-by: Drashna Jaelre <drashna@live.com>
Co-authored-by: Joel Challis <git@zvecr.com>
21 files changed, 2879 insertions, 0 deletions
diff --git a/keyboards/lily58/keymaps/druotoni/boot.c b/keyboards/lily58/keymaps/druotoni/boot.c new file mode 100644 index 0000000000..ba46f8e361 --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/boot.c @@ -0,0 +1,309 @@ +// Copyright 2021 Nicolas Druoton (druotoni) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include QMK_KEYBOARD_H + +#include "boot.h" +#include "fast_random.h" +#include "draw_helper.h" +#include "gui_state.h" + +// boot +#define ANIM_BOOT_FRAME_DURATION 8 +uint16_t anim_boot_timer = 0; +uint8_t anim_boot_current_frame = 0; + +#define NAVI_DURATION 55 + +// terminal stuff +#define TERMINAL_DURATION 25 +#define TERMINAL_LINE_NUMBER 19 +#define TERMINAL_LINE_MAX 14 + +#define LILY_DURATION 50 + +// halt +#define ANIM_HALT_FRAME_DURATION 55 +uint16_t anim_halt_timer = 0; + +void reset_boot(void) { + // frame zero + anim_boot_current_frame = 0; +} + +static void draw_lily_key(uint8_t x, uint8_t y, uint8_t *key_number, unsigned long key_state, uint8_t color) { + uint8_t v = *key_number; + unsigned long mask = 1; + mask = mask << v; + + // ligth the key according to the mask + if (((key_state & mask) == mask)) { + color = !color; + } + + draw_rectangle_fill(x, y, 3, 3, color); + *key_number = v + 1; +} + +static void draw_lily_key_row(uint8_t x, uint8_t y, int w, uint8_t *key_number, unsigned long key_state, uint8_t color) { + // row of rectangle + for (uint8_t i = 0; i < w; i++) { + draw_lily_key(x + (i * 4), y, key_number, key_state, color); + } +} + +static void draw_lily_render(unsigned long key_state) { + // different orientation base on side +#if IS_LEFT + + uint8_t x = 0; + uint8_t y = 56; + uint8_t x_ref = 10 + x; + uint8_t y_ref = 2 + y; + uint8_t i_key_number = 0; + + for (uint8_t i = 0; i < 4; i++) { + draw_lily_key_row(x_ref, y_ref + (i * 4), 4, &i_key_number, key_state, true); + draw_lily_key_row(x_ref - 8, y_ref + 2 + (i * 4), 2, &i_key_number, key_state, true); + } + + draw_lily_key_row(x_ref + 2, y_ref + (4 * 4), 3, &i_key_number, key_state, true); + + uint8_t x_side = x_ref + (4 * 4); + + draw_lily_key(x_side, y_ref + (2 * 4) + 2, &i_key_number, key_state, true); + draw_lily_key(x_side, y_ref + (4 * 4), &i_key_number, key_state, true); + + // screen + draw_rectangle(x_side, y_ref, 4, 8, true); + + // frame + drawline_hr(x + 1, y + 2, 8, true); + oled_write_pixel(x + 8, y + 1, true); + drawline_hr(x + 8, y, 23, true); + + drawline_hr(x + 1, y + 20, 10, true); + oled_write_pixel(x + 10, y + 21, true); + drawline_hr(x + 10, y + 22, 16, true); + + drawline_vb(x, y + 3, 17, true); + drawline_vb(x + 31, y + 1, 20, true); + oled_write_pixel(x + 30, y + 21, true); + oled_write_pixel(x + 29, y + 22, true); + oled_write_pixel(x + 28, y + 23, true); + oled_write_pixel(x + 27, y + 24, true); + oled_write_pixel(x + 26, y + 23, true); +#endif + +#if IS_RIGHT + uint8_t i_key_number = 0; + + for (uint8_t i = 0; i < 4; i++) { + draw_lily_key_row(7, 58 + (i * 4), 4, &i_key_number, key_state, true); + draw_lily_key_row(23, 60 + (i * 4), 2, &i_key_number, key_state, true); + } + + draw_lily_key_row(9, 74, 3, &i_key_number, key_state, true); + + draw_lily_key(3, 68, &i_key_number, key_state, true); + draw_lily_key(3, 74, &i_key_number, key_state, true); + + // screen + draw_rectangle(2, 58, 4, 8, true); + + // frame + drawline_hr(23, 58, 8, true); + oled_write_pixel(23, 57, true); + drawline_hr(1, 56, 23, true); + + drawline_hr(21, 76, 10, true); + oled_write_pixel(21, 77, true); + drawline_hr(6, 78, 16, true); + + drawline_vb(31, 59, 17, true); + drawline_vb(0, 57, 20, true); + oled_write_pixel(1, 77, true); + oled_write_pixel(2, 78, true); + oled_write_pixel(3, 79, true); + oled_write_pixel(4, 80, true); + oled_write_pixel(5, 79, true); +#endif +} + +static void draw_lily(uint8_t f) { + // frame for the events + uint8_t tres_stroke = 10; + uint8_t tres_boom = 30; + uint8_t y_start = 56; + + if (f == 0 || f == tres_stroke || f == tres_boom) { + // clean screen + oled_clear(); + } + + // simple lily58 with all the keys + if (f < tres_stroke) { + draw_lily_render(0); + } + + // increase number of random keys pressed + if (f >= tres_stroke && f < tres_boom) { + int inter_f = interpo_pourcent(tres_stroke, tres_boom, f); + + unsigned long key_state = fastrand_long(); + for (int r = 100 - inter_f; r > 0; r = r - 10) { + key_state &= fastrand_long(); + } + draw_lily_render(key_state); + } + + // statir explosion + if (f >= tres_boom) { + oled_clear(); + uint8_t density = (f - tres_boom); + if (density > 4) density = 4; + draw_static(0, y_start - 8, 32, 32, true, density); + } +} + +static void draw_startup_navi(uint8_t f) { + // text + oled_write_cursor(0, 5, "HELL0", false); + oled_write_cursor(0, 7, "NAVI.", false); + + // prompt + if ((f % 8) > 4) { + oled_write_cursor(0, 12, "> ", false); + } else { + oled_write_cursor(0, 12, ">_", false); + } + + // frame threshold + uint8_t tres_shell = 15; + uint8_t tres_load = 35; + + // rand text to init display + if (f > tres_shell) { + int inter_f = interpo_pourcent(tres_shell, tres_load, f); + + draw_random_char(1, 12, 'i', 60 + inter_f, 0); + draw_random_char(2, 12, 'n', 20 + inter_f, 0); + draw_random_char(3, 12, 'i', inter_f, 0); + draw_random_char(4, 12, 't', 20 + inter_f, 0); + } + + // loading propress bar + if (f > tres_load) { + int inter_f = interpo_pourcent(tres_load, 50, f); + + // ease + float fv = inter_f / 100.00; + fv = fv * fv * fv * fv; + inter_f = fv * 100; + + draw_rectangle(0, (15 * 8), 32, 8, 1); + draw_progress(0 + 3, (15 * 8) + 3, 26, 2, inter_f, 0, 1); + } +} + +// text dispayed on terminal +static char *boot_ref[TERMINAL_LINE_NUMBER] = {"LT:", "RT:", "M :", " ", "cnx:", "A0:", "B0:", " ", "0x40", "0x60", "0x85", "0x0F", " ", "> run", "x ", "y ", " 100%", " ", "> key"}; + +// prompt style for char in the font +char scan_font[5] = {'>', 1, 1, 1, 1}; + +static char *get_terminal_line(uint8_t i) { + // display text + if (i < TERMINAL_LINE_NUMBER) { + return boot_ref[i]; + } + + // blank line every 3 lines + if (i % 3 == 0) { + return " "; + } + + // display consecutive chars in the font + i = (i - TERMINAL_LINE_NUMBER) * 4; + + scan_font[1] = i; + scan_font[2] = i + 1; + scan_font[3] = i + 2; + scan_font[4] = i + 3; + + return scan_font; +} + +static void draw_startup_terminal(uint8_t f) { + // ease for printing on screen + f = f * 2; + f += (f / 5); + + // scroll text + uint8_t i_start = 0; + uint8_t i_nb_char = f; + + if (f > TERMINAL_LINE_MAX) { + i_start = f - TERMINAL_LINE_MAX; + i_nb_char = TERMINAL_LINE_MAX; + } + + // display lines + oled_clear(); + for (uint8_t i = 0; i < i_nb_char; i++) { + char *s = get_terminal_line(i + i_start); + oled_write_cursor(0, i, s, false); + } +} + +bool render_boot(void) { + // end of the boot sequence + if (anim_boot_current_frame >= NAVI_DURATION + TERMINAL_DURATION + LILY_DURATION) { + anim_boot_current_frame = 0; + oled_clear(); + return true; + } + + if (timer_elapsed(anim_boot_timer) > ANIM_BOOT_FRAME_DURATION) { + anim_boot_timer = timer_read(); + if (anim_boot_current_frame < NAVI_DURATION) { + // 55 frames + draw_startup_navi(anim_boot_current_frame); + } else { + if (anim_boot_current_frame >= NAVI_DURATION && anim_boot_current_frame < NAVI_DURATION + TERMINAL_DURATION) { + // 25 + draw_startup_terminal(anim_boot_current_frame - NAVI_DURATION); + } else { + if (anim_boot_current_frame >= NAVI_DURATION + TERMINAL_DURATION) { + // 25 + draw_lily(anim_boot_current_frame - NAVI_DURATION - TERMINAL_DURATION); + } + } + } + + anim_boot_current_frame++; + } + return false; +} + +void render_halt(void) { + if (timer_elapsed(anim_halt_timer) > ANIM_HALT_FRAME_DURATION) { + anim_halt_timer = timer_read(); + + // comb glitch for all the screen + draw_glitch_comb(0, 0, 32, 128, 3, true); + + // random moving blocks of pixels + for (uint8_t i = 0; i < 6; i++) { + int r = fastrand(); + int rr = fastrand(); + uint8_t x = 4 + r % 28; + uint8_t y = rr % 128; + + uint8_t w = 7 + r % 20; + uint8_t h = 3 + rr % 10; + int s = (fastrand() % 20) - 10; + move_block(x, y, w, h, s); + } + } +} diff --git a/keyboards/lily58/keymaps/druotoni/boot.h b/keyboards/lily58/keymaps/druotoni/boot.h new file mode 100644 index 0000000000..7897e17923 --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/boot.h @@ -0,0 +1,9 @@ +// Copyright 2021 Nicolas Druoton (druotoni) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +bool render_boot(void); +void render_halt(void); + +void reset_boot(void);
\ No newline at end of file diff --git a/keyboards/lily58/keymaps/druotoni/burst.c b/keyboards/lily58/keymaps/druotoni/burst.c new file mode 100644 index 0000000000..6dd6579e73 --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/burst.c @@ -0,0 +1,252 @@ +// Copyright 2021 Nicolas Druoton (druotoni) +// Copyright 2020 Richard Sutherland (rich@brickbots.com) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include QMK_KEYBOARD_H + +#include "gui_state.h" +#include "fast_random.h" +#include "burst.h" +#include "draw_helper.h" + +// burst stuff +static int current_burst = 0; +static uint16_t burst_timer = 0; + +// WPM stuff +static int current_wpm = 0; +static uint16_t wpm_timer = 0; + +// This smoothing is 40 keystrokes +static const float wpm_smoothing = WPM_SMOOTHING; + +// store values +uint8_t burst_scope[SIZE_SCOPE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +uint8_t wpm_scope[SIZE_SCOPE] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +// current max wpm +int max_wpm = MAX_WPM_INIT; + +// scope animation stuff +#define ANIM_SCOPE_FRAME_DURATION 40 +#define ANIM_SLEEP_SCOPE_FRAME_NUMBER 10 + +uint16_t anim_scope_timer = 0; +uint16_t anim_scope_idle_timer = 0; +uint16_t anim_sleep_scope_timer = 0; + +uint8_t anim_sleep_scope_duration[ANIM_SLEEP_SCOPE_FRAME_NUMBER] = {30, 30, 30, 30, 20, 20, 30, 30, 32, 35}; +uint8_t current_sleep_scope_frame = 0; +uint8_t sleep_scope_frame_destination = ANIM_SLEEP_SCOPE_FRAME_NUMBER - 1; + +// glitch animation +int current_glitch_scope_time = 150; +uint32_t glitch_scope_timer = 0; +uint8_t current_glitch_scope_index = 0; + +static void update_wpm(void) { + if (wpm_timer > 0) { + current_wpm += ((60000 / timer_elapsed(wpm_timer) / WPM_ESTIMATED_WORD_SIZE) - current_wpm) * wpm_smoothing; + if (current_wpm > LIMIT_MAX_WPM) { + current_wpm = LIMIT_MAX_WPM; + } + } + wpm_timer = timer_read(); +} + +void update_scope(void) { + update_wpm(); + + uint16_t temps_ecoule = timer_elapsed(burst_timer); + + if (temps_ecoule > BURST_FENETRE) { + // 1er frappe après longtemps + current_burst = 40; + } else { + int time_pourcent = ((100 * (temps_ecoule)) / (BURST_FENETRE)); + current_burst = 100 - time_pourcent; + } + burst_timer = timer_read(); +} + +static void update_scope_array(void) { + // shift array + for (uint8_t i = 0; i < SIZE_SCOPE - 1; i++) { + burst_scope[i] = burst_scope[i + 1]; + wpm_scope[i] = wpm_scope[i + 1]; + } + + int burst = current_burst; + int wpm = current_wpm; + + // compute max wpm + max_wpm = (wpm == 0) ? MAX_WPM_INIT : ((wpm > max_wpm) ? wpm : max_wpm); + + // current wpm ratio VS max + wpm = (100 * wpm) / max_wpm; + if (wpm > 100) wpm = 100; + + // update last slot of the arrays + burst_scope[SIZE_SCOPE - 1] = burst; + wpm_scope[SIZE_SCOPE - 1] = wpm; + + // apply decay to burst chart + uint8_t pBaisse = 0; + for (uint8_t i = 0; i < SIZE_SCOPE - (SIZE_SCOPE / 4); i++) { + pBaisse = 2 + ((SIZE_SCOPE - 1 - i)) / 2; + burst_scope[i] -= ((burst_scope[i] * pBaisse) / 100); + } +} + +static void RenderScopeBlack(void) { + // clean central zone + draw_rectangle_fill(3, 82, 28, 120, false); + + // redraw some parts of the frame + drawline_hr(1, SCOPE_Y_BOTTOM, 32, 1); + drawline_vt(0, SCOPE_Y_BOTTOM - 1, 42, 1); + drawline_vt(31, SCOPE_Y_BOTTOM - 1, 47, 1); +} + +static void render_scope_white(void) { + static const char PROGMEM raw_logo[] = { + 240, 8, 4, 226, 241, 248, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 252, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 127, 128, 128, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 159, 128, 128, 127, + }; + oled_write_raw_P_cursor(0, 10, raw_logo, sizeof(raw_logo)); +} + +static void render_scope_chart(void) { + // clean the frame + render_scope_white(); + + uint8_t y_offset = SCOPE_Y_BOTTOM - 3; + + for (uint8_t i = 0; i < SIZE_SCOPE; i++) { + // offset + uint8_t x = 3 + i; + + // new black vertical line for burst + uint8_t iCurrentBurst = burst_scope[i]; + drawline_vt(x, y_offset, (iCurrentBurst * 4) / 10, 0); + + // new black point for wpm, white if it's on the burst line + uint8_t iCurrentWpm = wpm_scope[i]; + uint8_t yWpm = y_offset - ((iCurrentWpm * 4) / 10); + oled_write_pixel(x, yWpm, !(iCurrentWpm > iCurrentBurst)); + } +} + +void reset_scope(void) { + // scope need wakeup + anim_sleep_scope_timer = timer_read(); + current_sleep_scope_frame = ANIM_SLEEP_SCOPE_FRAME_NUMBER - 1; + + sleep_scope_frame_destination = 0; +} + +static void render_glitch_square(void) { + if (timer_elapsed(anim_scope_idle_timer) > 60) { + anim_scope_idle_timer = timer_read(); + RenderScopeBlack(); + + uint8_t color = 0; + uint8_t size = 0; + for (uint8_t i = 0; i < 4; i++) { + size = 4 + (fastrand() % 6); + + draw_gradient(3 + (fastrand() % 19), 85 + (fastrand() % 20), size, size, 255, 255, 4); + + size = (fastrand() % 6); + color = 100 + (fastrand() % 100); + draw_gradient(3 + (fastrand() % 19), 100 + (fastrand() % 20), size, size, color, color, 4); + } + } +} + +void render_scope_idle(void) { + uint8_t glitch_prob = get_glitch_probability(); + get_glitch_index(&glitch_scope_timer, ¤t_glitch_scope_time, ¤t_glitch_scope_index, 150, 350, glitch_prob, 2); + + switch (current_glitch_scope_index) { + case 0: + RenderScopeBlack(); + return; + case 1: + render_glitch_square(); + return; + } +} + +static void RenderScopeSleep(void) { + if (current_sleep_scope_frame == sleep_scope_frame_destination) { + // animation finished + render_scope_idle(); + return; + } + + if (timer_elapsed(anim_sleep_scope_timer) > anim_sleep_scope_duration[current_sleep_scope_frame]) { + anim_sleep_scope_timer = timer_read(); + + // clean scope + RenderScopeBlack(); + + // render animation + render_tv_animation(current_sleep_scope_frame, 3, 80, 25, 48); + + // update frame number + if (sleep_scope_frame_destination > current_sleep_scope_frame) { + current_sleep_scope_frame++; + } else { + current_sleep_scope_frame--; + } + } +} + +void render_scope(gui_state_t t) { + if (timer_elapsed(anim_scope_timer) > ANIM_SCOPE_FRAME_DURATION) { + anim_scope_timer = timer_read(); + + // shift arrays + update_scope_array(); + + // oled_set_cursor(0, 10); + + if (t == _WAKINGUP) { + RenderScopeSleep(); + return; + } + + if (t == _IDLE) { + sleep_scope_frame_destination = ANIM_SLEEP_SCOPE_FRAME_NUMBER - 1; + RenderScopeSleep(); + return; + } + + render_scope_chart(); + } +} + +static void decay_burst(void) { + uint16_t temps_ecoule = timer_elapsed(burst_timer); + + int poucentageEcoule = 100; + + if (temps_ecoule <= BURST_FENETRE * 4) { + poucentageEcoule = ((100 * (temps_ecoule)) / (BURST_FENETRE * 4)); + } + + current_burst = current_burst - poucentageEcoule; + if (current_burst <= 0) current_burst = 0; +} + +static void decay_wpm(void) { + if (timer_elapsed(wpm_timer) > 1000) { + wpm_timer = timer_read(); + current_wpm += (-current_wpm) * wpm_smoothing; + } +} + +void decay_scope(void) { + decay_burst(); + decay_wpm(); +} diff --git a/keyboards/lily58/keymaps/druotoni/burst.h b/keyboards/lily58/keymaps/druotoni/burst.h new file mode 100644 index 0000000000..8bc8153508 --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/burst.h @@ -0,0 +1,24 @@ +// Copyright 2021 Nicolas Druoton (druotoni) +// Copyright 2020 Richard Sutherland (rich@brickbots.com) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +// burst +#define MAX_WPM_INIT 40 +#define BURST_FENETRE 500 + +// wpm +#define LIMIT_MAX_WPM 150 +#define WPM_ESTIMATED_WORD_SIZE 5 +#define WPM_SMOOTHING 0.0487 + +// scope +#define SIZE_SCOPE 26 +#define SCOPE_Y_BOTTOM 127 + +void update_scope(void); +void render_scope(gui_state_t t); + +void reset_scope(void); +void decay_scope(void); diff --git a/keyboards/lily58/keymaps/druotoni/config.h b/keyboards/lily58/keymaps/druotoni/config.h new file mode 100644 index 0000000000..37124fcaaf --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/config.h @@ -0,0 +1,55 @@ +// Copyright 2021 Nicolas Druoton (druotoni) +// Copyright 2012 Jun Wako <wakojun@gmail.com> +// Copyright 2015 Jack Humbert +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#define MASTER_LEFT +#define OLED_DRIVER_ENABLE + +// tapping toggle for my layers +#define TAPPING_TOGGLE 2 + +// choose IS_LEFT or IS_RIGHT for compilation and flash firmware +#define IS_LEFT 1 +//#define IS_RIGHT 1 + +// logo glitch +#define WITH_GLITCH +// boot sequence +#define WITH_BOOT + +// custom transport for displaying on both side +#define SPLIT_TRANSACTION_IDS_USER USER_SYNC_A + +// custom font +#ifdef OLED_FONT_H +# undef OLED_FONT_H +#endif +#define OLED_FONT_H "navi_font.c" +#undef OLED_FONT_END +#define OLED_FONT_END 125 + +// more space +#define NO_ACTION_MACRO +#define NO_ACTION_FUNCTION +#define NO_ACTION_ONESHOT +#define DISABLE_LEADER + +// ??? +#undef LOCKING_SUPPORT_ENABLE +#undef LOCKING_RESYNC_ENABLE + +// small layer state +#define LAYER_STATE_8BIT + +// no debug or trace +#ifndef NO_DEBUG +# define NO_DEBUG +#endif +#if !defined(NO_PRINT) && !defined(CONSOLE_ENABLE) +# define NO_PRINT +#endif + + diff --git a/keyboards/lily58/keymaps/druotoni/draw_helper.c b/keyboards/lily58/keymaps/druotoni/draw_helper.c new file mode 100644 index 0000000000..c6761d725f --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/draw_helper.c @@ -0,0 +1,768 @@ +// Copyright 2021 Nicolas Druoton (druotoni) +// Copyright 2021 ugfx +// SPDX-License-Identifier: GPL-2.0-or-later + +#include QMK_KEYBOARD_H + +#include "draw_helper.h" +#include "fast_random.h" + +void drawline(uint8_t x, uint8_t y, uint8_t width, bool bHorizontal, bool bPositiveDirection, bool color) { + if (width <= 0) return; + uint8_t yPlus = 0; + uint8_t yMois = 0; + uint8_t nbtour = 0; + + if (!bPositiveDirection) { + if (bHorizontal) { + x -= width; + } else { + y -= width; + } + } + + yMois = (width / 2) - 1 + (width % 2); + + yPlus = (width / 2); + nbtour = (width / 4) + 1; + + bool bWhite = color; + + if (bHorizontal) { + for (uint8_t i = 0; i < nbtour; i++) { + oled_write_pixel(x + yPlus + i, y, bWhite); + oled_write_pixel(x + yMois - i, y, bWhite); + + oled_write_pixel(x + i, y, bWhite); + oled_write_pixel(x + width - 1 - i, y, bWhite); + } + } else { + for (uint8_t i = 0; i < nbtour; i++) { + oled_write_pixel(x, y + yPlus + i, bWhite); + oled_write_pixel(x, y + yMois - i, bWhite); + + oled_write_pixel(x, y + i, bWhite); + + oled_write_pixel(x, y + width - 1 - i, bWhite); + } + } +} + +void drawline_vb(uint8_t x, uint8_t y, uint8_t width, bool color) { drawline(x, y, width, false, true, color); } + +void drawline_vt(uint8_t x, uint8_t y, uint8_t width, bool color) { drawline(x, y, width, false, false, color); } + +void drawline_hr(uint8_t x, uint8_t y, uint8_t width, bool color) { drawline(x, y, width, true, true, color); } + +void drawline_hl(uint8_t x, uint8_t y, uint8_t width, bool color) { drawline(x, y, width, true, false, color); } + +void draw_rectangle(uint8_t x, uint8_t y, uint8_t width, uint8_t heigth, bool color) { + drawline_hr(x, y, width, color); + drawline_hr(x, y + heigth - 1, width, color); + drawline_vb(x, y, heigth, color); + drawline_vb(x + width - 1, y, heigth, color); +} + +void draw_rectangle_fill(uint8_t x, uint8_t y, uint8_t width, uint8_t heigth, bool color) { + for (uint8_t i = 0; i < heigth; i++) { + drawline_hr(x, y + i, width, color); + } +} + +void drawline_hr_heigth(uint8_t x, uint8_t y, uint8_t width, uint8_t heigth, bool color) { + for (int i = 0; i < heigth; i++) { + drawline_hr(x, y - i, width, color); + drawline_hr(x, y + i, width, color); + } +} + +void drawline_point_hr(short x, short y, short x1, bool color) { + if (y < 0 || y > 127) return; + + if (x1 < x) { + short iTemp = x; + x = x1; + x1 = iTemp; + } + + if (x1 > 31) x1 = 31; + if (x < 0) x = 0; + if (x > 31) x = 31; + + drawline(x, y, x1 - x, true, true, color); +} + +void flip_flap_x(short px, short py, uint8_t val, bool color) { + oled_write_pixel(px + val, py, color); + oled_write_pixel(px - val, py, color); +} + +void draw_circle(uint8_t x, uint8_t y, uint8_t radius, bool color) { + short a, b, P; + + // Calculate intermediates + a = 1; + b = radius; + P = 4 - radius; + + short py, px; + + // Away we go using Bresenham's circle algorithm + // Optimized to prevent double drawing + px = x; + py = y + b; + oled_write_pixel(px, py, color); + px = x; + py = y - b; + oled_write_pixel(px, py, color); + + flip_flap_x(x, y, b, color); + + do { + flip_flap_x(x, y + b, a, color); + flip_flap_x(x, y - b, a, color); + flip_flap_x(x, y + a, b, color); + flip_flap_x(x, y - a, b, color); + + if (P < 0) + P += 3 + 2 * a++; + else + P += 5 + 2 * (a++ - b--); + } while (a < b); + + flip_flap_x(x, y + b, a, color); + flip_flap_x(x, y - b, a, color); +} + +void draw_ellipse(uint8_t x, uint8_t y, uint8_t a, uint8_t b, bool color) { + int dx, dy; + int a2, b2; + int err, e2; + + // short py, px; + // Calculate intermediates + dx = 0; + dy = b; + a2 = a * a; + b2 = b * b; + err = b2 - (2 * b - 1) * a2; + + // Away we go using Bresenham's ellipse algorithm + do { + flip_flap_x(x, y + dy, dx, color); + flip_flap_x(x, y - dy, dx, color); + + e2 = 2 * err; + if (e2 < (2 * dx + 1) * b2) { + dx++; + err += (2 * dx + 1) * b2; + } + if (e2 > -(2 * dy - 1) * a2) { + dy--; + err -= (2 * dy - 1) * a2; + } + } while (dy >= 0); +} + +void draw_ellipse_fill(uint8_t x, uint8_t y, uint8_t a, uint8_t b, bool color) { return; } +// void draw_ellipse_fill(uint8_t x, uint8_t y, uint8_t a, uint8_t b, uint8_t color) { +// int dx, dy; +// int a2, b2; +// int err, e2; + +// // Calculate intermediates +// dx = 0; +// dy = b; +// a2 = a * a; +// b2 = b * b; +// err = b2 - (2 * b - 1) * a2; + +// short py, px, px1; + +// // Away we go using Bresenham's ellipse algorithm +// // This is optimized to prevent overdrawing by drawing a line only when a y is about to change value +// do { +// e2 = 2 * err; +// if (e2 < (2 * dx + 1) * b2) { +// dx++; +// err += (2 * dx + 1) * b2; +// } +// if (e2 > -(2 * dy - 1) * a2) { +// py = y + dy; +// px = x - dx; +// px1 = x + dx; +// drawline_point_hr(px, py, px1, color); +// if (y) { +// py = y - dy; +// px = x - dx; +// px1 = x + dx; +// drawline_point_hr(px, py, px1, color); +// } +// dy--; +// err -= (2 * dy - 1) * a2; +// } +// } while (dy >= 0); +// } + +bool test_limit(short x, short y) { return !(y < 0 || y > 127 || x < 0 || x > 31); } + +void flip_flap_y_point(short px, short py, short px1, uint8_t val, bool color) { + // firmware size optimisation : one fonction for 2 lines of code + drawline_point_hr(px, py + val, px1, color); + drawline_point_hr(px, py - val, px1, color); +} + +void draw_fill_circle(short x, short y, uint8_t radius, bool color) { + short a, b, P; + + // Calculate intermediates + a = 1; + b = radius; + P = 4 - radius; + + // Away we go using Bresenham's circle algorithm + // This is optimized to prevent overdrawing by drawing a line only when a variable is about to change value + short py, px, px1; + + py = y; + px = x - b; + px1 = x + b; + drawline_point_hr(px, py, px1, color); + + py = y + b; + px = x; + if (test_limit(px, py)) oled_write_pixel(px, py, color); + py = y - b; + px = x; + if (test_limit(px, py)) oled_write_pixel(px, py, color); + do { + flip_flap_y_point(x - b, y, x + b, a, color); + + if (P < 0) { + P += 3 + 2 * a++; + } else { + flip_flap_y_point(x - a, y, x + a, b, color); + + P += 5 + 2 * (a++ - b--); + } + } while (a < b); + + flip_flap_y_point(x - b, y, x + b, a, color); +} + +bool apres_moitie(int a, int b) { return (a > b / 2); } +bool arrive_moitie(int a, int b) { return (a > b / 2); } +bool avant_moitie(int a, int b) { return (a <= b / 2 && !apres_moitie(a, b)); } + +void draw_arc_sector(uint8_t x, uint8_t y, uint8_t radius, unsigned char sectors, unsigned char half, bool color) { + short a, b, P; + short py, px; + // Calculate intermediates + a = 1; // x in many explanations + b = radius; // y in many explanations + P = 4 - radius; + + if (half != 2) { + // Away we go using Bresenham's circle algorithm + // Optimized to prevent double drawing + if (sectors & 0x06) { + px = x; + py = y - b; + oled_write_pixel(px, py, color); + } // Upper upper + if (sectors & 0x60) { + px = x; + py = y + b; + oled_write_pixel(px, py, color); + } // Lower lower + if (sectors & 0x81) { + px = x + b; + py = y; + oled_write_pixel(px, py, color); + } // Right right + if (sectors & 0x18) { + px = x - b; + py = y; + oled_write_pixel(px, py, color); + } // Left left + } + + bool dessiner = false; + + do { + if (half == 1 && arrive_moitie(a, b)) break; + + if (half == 2 && avant_moitie(a, b)) { + dessiner = false; + } else { + dessiner = true; + } + + if (dessiner) { + if (sectors & 0x01) { + px = x + b; + py = y - a; + oled_write_pixel(px, py, color); + } // Upper right right + if (sectors & 0x02) { + px = x + a; + py = y - b; + oled_write_pixel(px, py, color); + } // Upper upper right + if (sectors & 0x04) { + px = x - a; + py = y - b; + oled_write_pixel(px, py, color); + } // Upper upper left + if (sectors & 0x08) { + px = x - b; + py = y - a; + oled_write_pixel(px, py, color); + } // Upper left left + if (sectors & 0x10) { + px = x - b; + py = y + a; + oled_write_pixel(px, py, color); + } // Lower left left + if (sectors & 0x20) { + px = x - a; + py = y + b; + oled_write_pixel(px, py, color); + } // Lower lower left + if (sectors & 0x40) { + px = x + a; + py = y + b; + oled_write_pixel(px, py, color); + } // Lower lower right + if (sectors & 0x80) { + px = x + b; + py = y + a; + oled_write_pixel(px, py, color); + } // Lower right right + } + + if (P < 0) + P += 3 + 2 * a++; + else + P += 5 + 2 * (a++ - b--); + } while (a < b); + + if (half != 1) { + if (sectors & 0xC0) { + px = x + a; + py = y + b; + oled_write_pixel(px, py, color); + } // Lower right + if (sectors & 0x03) { + px = x + a; + py = y - b; + oled_write_pixel(px, py, color); + } // Upper right + if (sectors & 0x30) { + px = x - a; + py = y + b; + oled_write_pixel(px, py, color); + } // Lower left + if (sectors & 0x0C) { + px = x - a; + py = y - b; + oled_write_pixel(px, py, color); + } // Upper left + } +} + +void draw_static(uint8_t x, uint8_t y, uint8_t width, uint8_t heigth, int color, uint8_t density) { + unsigned long rx = fastrand_long(); + unsigned long ry = fastrand_long(); + unsigned long maskx = 1; + unsigned long masky = 1; + unsigned long mask_base = 1; + + // more 1 in the octet + for (int r = 0; r < density; r++) { + rx &= fastrand_long(); + ry &= fastrand_long(); + } + + color = ((rx >> 1) % 2) == 0; + + for (uint8_t i = 0; i < width; i++) { + for (uint8_t j = 0; j < heigth; j++) { + // new mask based on ij loop + maskx = (mask_base << i); + masky = (mask_base << j); + + // logic AND with the masks + if (((rx & maskx) == maskx) && ((ry & masky) == masky)) { + oled_write_pixel(x + i, y + j, color); + } + } + } +} + +void copy_pixel(int from, int shift, unsigned char mask) { + if (shift == 0) return; + + // pixel cluster from + char c_from = get_oled_char(from); + char extract = c_from & mask; + + // pixel cluster shift + char c_from_shift = get_oled_char(from + shift); + c_from_shift &= ~(mask); + c_from_shift |= extract; + oled_write_raw_byte(c_from_shift, from + shift); + + // fill blank with black + c_from &= ~(mask); + oled_write_raw_byte(c_from, from); +} + +void draw_glitch_comb(uint8_t x, uint8_t y, uint8_t width, uint16_t height, uint8_t iSize, bool odd) { + // work only on row + uint16_t y_start = (y / 8) * 32; + uint8_t nb_h = height / 8; + + uint8_t w_max = width; + uint16_t index = y_start + x; + + // shift pair even pixel + int mask_1 = 85; + int mask_2 = 170; + + if (!odd) { + // shift odd pixel + mask_1 = 170; + mask_2 = 85; + } + + // wobble + uint16_t pos = 0; + for (uint16_t j = 0; j < nb_h; j++) { + // next line + index = (y_start + x) + (j * 32); + + for (uint16_t i = 0; i < w_max; i++) { + if (i + iSize < w_max) { + pos = index + i; + copy_pixel(pos + iSize, iSize * -1, mask_1); + } + + if (w_max - 1 - i - iSize >= 0) { + pos = (index + w_max - 1) - i; + copy_pixel(pos - iSize, iSize, mask_2); + } + } + } +} + +void draw_random_char(uint8_t column, uint8_t row, char final_char, int value, uint8_t style) { + if (value < 0) return; + + char c = final_char; + + if (value < 100) { + c = ((fastrand() % 15) + 1); + } + + oled_set_cursor(column, row); + oled_write_char(c, false); +} + +void get_glitch_index_new(uint16_t *glitch_timer, uint8_t *current_glitch_scope_time, uint8_t *glitch_index, uint8_t min_time, uint16_t max_time, uint8_t glitch_probobility, uint8_t glitch_frame_number) { + if (timer_elapsed(*glitch_timer) > *current_glitch_scope_time) { + // end of the last glitch period + *glitch_timer = timer_read(); + + // new random glich period + *current_glitch_scope_time = min_time + fastrand() % (max_time - min_time); + + bool bGenerateGlitch = (fastrand() % 100) < glitch_probobility; + if (!bGenerateGlitch) { + // no glitch + *glitch_index = 0; + return; + } + + // get a new glitch index + *glitch_index = fastrand() % glitch_frame_number; + } +} + +uint8_t get_glitch_frame_index(uint8_t glitch_probobility, uint8_t glitch_frame_number) { + bool bGenerateGlitch = (fastrand() % 100) < glitch_probobility; + if (!bGenerateGlitch) { + // no glitch + return 0; + } + + // get a new glitch index + return fastrand() % glitch_frame_number; +} + +uint8_t get_glitch_duration(uint8_t min_time, uint16_t max_time) { return min_time + fastrand() % (max_time - min_time); } + +void get_glitch_index(uint32_t *glitch_timer, int *current_glitch_scope_time, uint8_t *glitch_index, uint8_t min_time, uint16_t max_time, uint8_t glitch_probobility, uint8_t glitch_frame_number) { + if (timer_elapsed32(*glitch_timer) > *current_glitch_scope_time) { + // end of the last glitch period + *glitch_timer = timer_read32(); + + // new random glich period + *current_glitch_scope_time = min_time + fastrand() % (max_time - min_time); + + bool bGenerateGlitch = (fastrand() % 100) < glitch_probobility; + if (!bGenerateGlitch) { + // no glitch + *glitch_index = 0; + return; + } + + // get a new glitch index + *glitch_index = fastrand() % glitch_frame_number; + } +} + +void draw_progress(uint8_t x, uint8_t y, uint8_t width, uint8_t heigth, int value, uint8_t style, bool color) { + if (value > 100) { + value = 100; + } + int lenght = (width * value) / 100; + for (uint8_t i = 0; i < lenght; i++) { + switch (style) { + case 0: + drawline_vb(x + i, y, heigth - 1, color); + break; + + // case 1: + // drawline_vb(x + i, y + 1, heigth - 3, ((i % 3) < 2)); + // break; + // case 2: + // // . . . . . + // drawline_vb(x + i, y + 3, 2, ((i % 2) == 0)); + // break; + } + } +} + +void oled_write_raw_P_cursor(uint8_t col, uint8_t line, const char *data, uint16_t size) { + // raw_P at cursor position + oled_set_cursor(col, line); + oled_write_raw_P(data, size); +} + +void oled_write_cursor(uint8_t col, uint8_t line, const char *data, bool invert) { + // write at cursor position + oled_set_cursor(col, line); + oled_write(data, invert); +} + +void draw_label(const char *data, uint8_t len, uint8_t row, int value) { + if (value < 0) return; + if (row >= 16 || row < 0) return; + oled_write_cursor(0, row, data, false); +} + +void draw_box(const char *data, uint8_t len, uint8_t row, long value, uint8_t style) { + if (value < 0) return; + if (row >= 16 || row < 0) return; + + oled_write_cursor(0, row, data, false); + + uint8_t y = row * 8; + + uint8_t x = 6 * len; + uint8_t w = 32 - x; + + if (value < 0) value = 0; + if (value > 100) value = 100; + draw_progress(x, y, w, 7, value, style, 1); +} + +char get_oled_char(uint16_t start_index) { + oled_buffer_reader_t reader; + reader = oled_read_raw(start_index); + return *reader.current_element; +} + +static int get_index_first_block(uint8_t y) { return ((y / 8) * 32); } + +void move_block(uint8_t x, uint8_t y, uint8_t width, uint8_t heigth, int shift) { + // clip + if (x >= 31) return; + if (y >= 127) return; + + int max_screen = 32 - 1; + if ((width + x) > max_screen + 1) width = max_screen + 1 - x; + + if (width <= 1) return; + + if ((heigth + y) > 127) heigth = 127 - y; + if (heigth <= 1) return; + + // [-32 & +32] + if (shift > max_screen) shift = max_screen; + if (shift < -1 * max_screen) shift = -1 * max_screen; + + if ((width + x + shift) > max_screen) width = width - shift; + + int pixelTop = 8 - (y % 8); + int pixelBottom = (y + heigth) % 8; + + unsigned char cMastTop = ~((unsigned)255 >> (pixelTop)); + unsigned char cMastBottom = ~((unsigned)255 << (pixelBottom)); + + int indexFirstBloc = get_index_first_block(y) + x; + int indexFirstBlocFull = get_index_first_block(y + pixelTop) + x; + int indexFirstBlocEnd = get_index_first_block(y + heigth) + x; + + int nbBlockHeigth = (heigth - pixelTop - pixelBottom) / 8; + + if (nbBlockHeigth < 0) { + // just single row + nbBlockHeigth = 0; + cMastBottom = 0; + } + + if (shift < 0) { + for (uint16_t i = 0; i < width; i++) { + copy_pixel(indexFirstBloc + i, shift, cMastTop); + copy_pixel(indexFirstBlocEnd + i, shift, cMastBottom); + + for (uint16_t j = 0; j < nbBlockHeigth; j++) { + copy_pixel(indexFirstBlocFull + i + (j * 32), shift, 255); + } + } + + } else { + for (int i = width - 1; i >= 0; i--) { + copy_pixel(indexFirstBloc + i, shift, cMastTop); + copy_pixel(indexFirstBlocEnd + i, shift, cMastBottom); + + for (uint16_t j = 0; j < nbBlockHeigth; j++) { + copy_pixel(indexFirstBlocFull + i + (j * 32), shift, 255); + } + } + } +} + +int interpo_pourcent(int min, int max, int v) { + // interpolation + float x0 = min; + float x1 = max; + float y0 = 0; + float y1 = 100; + float xp = v; + float yp = y0 + ((y1 - y0) / (x1 - x0)) * (xp - x0); + + return (int)yp; +} + +uint8_t BAYER_PATTERN_4[4][4] = {{15, 135, 45, 165}, {195, 75, 225, 105}, {60, 180, 30, 150}, {240, 120, 210, 90}}; + +void draw_gradient(uint8_t x, uint8_t y, uint8_t width, uint8_t heigth, uint8_t color_start, uint8_t color_end, uint8_t tres) { + bool invert = color_start > color_end; + + if (invert) { + color_start = 255 - color_start; + color_end = 255 - color_end; + } + + int step = (100 / tres); + int step_minus = (100 / (tres - 1)); + int distance = color_end - color_start; + + for (uint8_t i = 0; i < width; i++) { + int position = interpo_pourcent(0, width, i); + + float color = position; + color = ((int)(color / step)) * step_minus; + + color = color_start + ((distance * color) / 100); + + for (uint8_t j = 0; j < heigth; j++) { + uint8_t m = BAYER_PATTERN_4[i % 4][j % 4]; + unsigned char color_d = (color > m) ? !invert : invert; + + oled_write_pixel(x + i, y + j, color_d); + } + } +} + +void render_tv_animation(uint8_t frame_number, uint8_t x, uint8_t y, uint8_t width, uint8_t heigth) { + uint8_t xCenter = x + (width / 2); + uint8_t yCenter = y + (heigth / 2); + + switch (frame_number) { + case 0: + // a fond : allume + drawline_hr_heigth(x, yCenter, width, 17, true); + break; + + case 1: + drawline_hr_heigth(x, yCenter, width, 12, true); + draw_ellipse_fill(xCenter, yCenter, 7, 15, true); + break; + + case 2: + drawline_hr_heigth(x, yCenter, width, 5, true); + draw_ellipse_fill(xCenter, yCenter, 5, 8, true); + break; + + case 3: + drawline_hr_heigth(x, yCenter, width, 3, true); + draw_ellipse_fill(xCenter, yCenter, 3, 4, true); + break; + + case 4: + drawline_hr_heigth(x, yCenter, width, 2, true); + draw_fill_circle(xCenter, yCenter, 3, true); + break; + + case 5: + // central line + drawline_hr(x, yCenter, width, true); + draw_fill_circle(xCenter, yCenter, 2, true); + break; + + case 6: + // cross + drawline_hr(xCenter, yCenter + 1, 2, true); + drawline_hr(xCenter, yCenter - 1, 2, true); + + // central line + drawline_hr(x, yCenter, width, true); + break; + + case 7: + // cross + drawline_hr(xCenter, yCenter + 1, 2, true); + drawline_hr(xCenter, yCenter - 1, 2, true); + // central line + drawline_hr(xCenter - 8, yCenter, 18, true); + // static + oled_write_pixel(xCenter - 11, yCenter, true); + oled_write_pixel(xCenter + 12, yCenter, true); + break; + + case 8: + // cross + drawline_hr(xCenter, yCenter + 1, 2, true); + drawline_hr(xCenter, yCenter - 1, 2, true); + // central line + drawline_hr(xCenter - 2, yCenter, 4, true); + // static + drawline_hr(xCenter - 7, yCenter, 2, true); + drawline_hr(xCenter + 6, yCenter, 3, true); + + // oled_write_pixel(xCenter - 11, yCenter, true); + oled_write_pixel(xCenter - 9, yCenter, true); + oled_write_pixel(xCenter + 12, yCenter, true); + oled_write_pixel(xCenter + 14, yCenter, true); + break; + + case 9: + // central line + drawline_hr(xCenter, yCenter, 2, true); + break; + } +}
\ No newline at end of file diff --git a/keyboards/lily58/keymaps/druotoni/draw_helper.h b/keyboards/lily58/keymaps/druotoni/draw_helper.h new file mode 100644 index 0000000000..991ab0d0a6 --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/draw_helper.h @@ -0,0 +1,47 @@ +// Copyright 2021 Nicolas Druoton (druotoni) +// Copyright 2021 ugfx +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +// line +void drawline_vb(uint8_t x, uint8_t y, uint8_t width, bool color); +void drawline_vt(uint8_t x, uint8_t y, uint8_t width, bool color); +void drawline_hr(uint8_t x, uint8_t y, uint8_t width, bool color); +void drawline_hl(uint8_t x, uint8_t y, uint8_t width, bool color); +void drawline_hr_heigth(uint8_t x, uint8_t y, uint8_t width, uint8_t heigth, bool color); + +// rectangle +void draw_rectangle(uint8_t x, uint8_t y, uint8_t width, uint8_t heigth, bool color); +void draw_rectangle_fill(uint8_t x, uint8_t y, uint8_t width, uint8_t heigth, bool color); +void draw_gradient(uint8_t x, uint8_t y, uint8_t width, uint8_t heigth, uint8_t color_start, uint8_t color_end, uint8_t tres); + +// circle +void draw_fill_circle(short x, short y, uint8_t radius, bool color); +void draw_circle(uint8_t x, uint8_t y, uint8_t radius, bool color); +void draw_ellipse(uint8_t x, uint8_t y, uint8_t a, uint8_t b, bool color); +void draw_ellipse_fill(uint8_t x, uint8_t y, uint8_t a, uint8_t b, bool color); +void draw_arc_sector(uint8_t x, uint8_t y, uint8_t radius, unsigned char sectors, unsigned char half, bool color); +void draw_static(uint8_t x, uint8_t y, uint8_t width, uint8_t heigth, int color, uint8_t density); + +// text +void draw_random_char(uint8_t column, uint8_t row, char final_char, int value, uint8_t style); +void draw_label(const char *data, uint8_t len, uint8_t row, int value); +void draw_box(const char *data, uint8_t len, uint8_t row, long value, uint8_t style); +void draw_progress(uint8_t x, uint8_t y, uint8_t width, uint8_t heigth, int value, uint8_t style, bool color); + +// oled drivers stuff +char get_oled_char(uint16_t start_index); +void oled_write_cursor(uint8_t col, uint8_t line, const char *data, bool invert); +void oled_write_raw_P_cursor(uint8_t col, uint8_t line, const char *data, uint16_t size); + +// pixel manipulation +void copy_pixel(int from, int shift, unsigned char mask); +void move_block(uint8_t x, uint8_t y, uint8_t width, uint8_t heigth, int shift); +void draw_glitch_comb(uint8_t x, uint8_t y, uint8_t width, uint16_t height, uint8_t iSize, bool odd); + +// misc +void render_tv_animation(uint8_t frame_number, uint8_t x, uint8_t y, uint8_t width, uint8_t heigth); +int interpo_pourcent(int min, int max, int v); +void get_glitch_index(uint32_t *glitch_timer, int *current_glitch_scope_time, uint8_t *glitch_index, uint8_t min_time, uint16_t max_time, uint8_t glitch_probobility, uint8_t glitch_frame_number); +void get_glitch_index_new(uint16_t *glitch_timer, uint8_t *current_glitch_scope_time, uint8_t *glitch_index, uint8_t min_time, uint16_t max_time, uint8_t glitch_probobility, uint8_t glitch_frame_number); diff --git a/keyboards/lily58/keymaps/druotoni/fast_random.c b/keyboards/lily58/keymaps/druotoni/fast_random.c new file mode 100644 index 0000000000..3028b57ac6 --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/fast_random.c @@ -0,0 +1,17 @@ +// Copyright 2021 Nicolas Druoton (druotoni) +// SPDX-License-Identifier: GPL-2.0-or-later +#include "fast_random.h" + +// seed for random +static unsigned long g_seed = 0; + +int fastrand(void) { + // todo : try with random16(); + g_seed = (214013 * g_seed + 2531011); + return (g_seed >> 16) & 0x7FFF; +} + +unsigned long fastrand_long(void) { + g_seed = (214013 * g_seed + 2531011); + return g_seed; +} diff --git a/keyboards/lily58/keymaps/druotoni/fast_random.h b/keyboards/lily58/keymaps/druotoni/fast_random.h new file mode 100644 index 0000000000..fe8c4a40a2 --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/fast_random.h @@ -0,0 +1,7 @@ +// Copyright 2021 Nicolas Druoton (druotoni) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +int fastrand(void); +unsigned long fastrand_long(void);
\ No newline at end of file diff --git a/keyboards/lily58/keymaps/druotoni/gui_state.c b/keyboards/lily58/keymaps/druotoni/gui_state.c new file mode 100644 index 0000000000..d86e67ec7b --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/gui_state.c @@ -0,0 +1,71 @@ +// Copyright 2021 Nicolas Druoton (druotoni) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include QMK_KEYBOARD_H + +#include "gui_state.h" +#include "draw_helper.h" + +// timer for the gui state +uint32_t global_sleep_timer = 0; +uint32_t global_waking_up_timer = 0; +uint32_t global_booting_timer = 0; + +// timers test for states +#ifdef WITH_BOOT +static bool IsBooting(void) { return (timer_elapsed32(global_booting_timer) < BOOTING_TIME_TRESHOLD); } +#else +static bool IsBooting(void) { return false; } +#endif + +// state test +static bool IsWakingUp(void) { return (timer_elapsed32(global_waking_up_timer) < WAKING_UP_TIME_TRESHOLD); } +static bool IsIdle(void) { return (timer_elapsed32(global_sleep_timer) > IDLE_TIME_TRESHOLD && timer_elapsed32(global_sleep_timer) < HALTING_TIME_TRESHOLD); } +static bool IsSleep(void) { return (timer_elapsed32(global_sleep_timer) >= SLEEP_TIME_TRESHOLD); } +static bool IsHalting(void) { return (timer_elapsed32(global_sleep_timer) >= HALTING_TIME_TRESHOLD && timer_elapsed32(global_sleep_timer) < SLEEP_TIME_TRESHOLD); } + +gui_state_t get_gui_state(void) { + // get gui states by testing timers + if (IsBooting()) return _BOOTING; + if (IsWakingUp()) return _WAKINGUP; + if (IsIdle()) return _IDLE; + if (IsHalting()) return _HALTING; + if (IsSleep()) return _SLEEP; + + return _UP; +} + +void update_gui_state(void) { + // what to do when a key is pressed + gui_state_t t = get_gui_state(); + +#ifdef WITH_BOOT + if (t == _SLEEP) { + // booting + global_booting_timer = timer_read32(); + } + + if (t == _BOOTING) { + // cancel booting + global_booting_timer = 1000000; + } +#else + if (t == _SLEEP) { + // waking up + global_waking_up_timer = timer_read32(); + } +#endif + + if (t == _IDLE || t == _HALTING || t == _BOOTING) { + // waking up + global_waking_up_timer = timer_read32(); + } + + // no sleep + global_sleep_timer = timer_read32(); +} + +uint8_t get_glitch_probability(void) { + // more gliches could occur when halting time is near + return interpo_pourcent(IDLE_TIME_TRESHOLD, HALTING_TIME_TRESHOLD, timer_elapsed32(global_sleep_timer)); +} diff --git a/keyboards/lily58/keymaps/druotoni/gui_state.h b/keyboards/lily58/keymaps/druotoni/gui_state.h new file mode 100644 index 0000000000..190e02d303 --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/gui_state.h @@ -0,0 +1,18 @@ +// Copyright 2021 Nicolas Druoton (druotoni) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +// states timing +#define BOOTING_TIME_TRESHOLD 7000 +#define WAKING_UP_TIME_TRESHOLD 300 +#define IDLE_TIME_TRESHOLD 4000 +#define HALTING_TIME_TRESHOLD IDLE_TIME_TRESHOLD + 6000 +#define SLEEP_TIME_TRESHOLD HALTING_TIME_TRESHOLD + 8000 + +typedef uint8_t gui_state_t; +enum gui_state { _WAKINGUP = 0, _IDLE, _SLEEP, _UP, _BOOTING, _HALTING }; + +gui_state_t get_gui_state(void); +void update_gui_state(void); +uint8_t get_glitch_probability(void);
\ No newline at end of file diff --git a/keyboards/lily58/keymaps/druotoni/keymap.c b/keyboards/lily58/keymaps/druotoni/keymap.c new file mode 100644 index 0000000000..2db32047e0 --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/keymap.c @@ -0,0 +1,253 @@ +// Copyright 2021 Nicolas Druoton (druotoni) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include QMK_KEYBOARD_H +#include "keymap_french.h" +#include "transactions.h" + +// global +#include "gui_state.h" +#include "boot.h" +#include "navi_logo.h" + +#include "draw_helper.h" +#include "fast_random.h" + +// left side +#include "layer_frame.h" +#include "burst.h" + +// right side +#include "ring.h" + +// clang-format off +const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { +/* QWERTY +* ,-----------------------------------------. ,-----------------------------------------. +* | ESC | 1 | 2 | 3 | 4 | 5 | | 6 | 7 | 8 | 9 | 0 | DEL | +* |------+------+------+------+------+------| |------+------+------+------+------+------| +* | Tab | Q | W | E | R | T | | Y | U | I | O | P | ^ | +* |------+------+------+------+------+------| |------+------+------+------+------+------| +* |LShift| A | S | D | F | G |-------. ,-------| H | J | K | L | ; |RShift| +* |------+------+------+------+------+------| " | | ) |------+------+------+------+------+------| +* |LCTRL | Z | X | C | V | B |-------| |-------| N | M | , | . | / | $ | +* `-----------------------------------------/ / \ \-----------------------------------------' +* | LAlt | SPE | Space| / NAV / \Enter \ |BackSP| ] | RGUI | +* | | | |/ / \ \ | | | | +* `----------------------------' '------''--------------------' +*/ + [_QWERTY] = LAYOUT( + KC_ESC, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_DELETE, + KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, FR_CIRC, + KC_LSFT, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_RSFT, + KC_LCTRL, KC_Z, KC_X, KC_C, KC_V, KC_B, S(KC_Z), FR_RPRN, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_QUOT, + KC_LALT, TT(_RAISE), KC_SPC, TT(_LOWER), KC_ENT, KC_BSPC, KC_RBRC, KC_RGUI + ), + + /* LOWER +* ,---------------------------------------------. ,------------------------------------------------. +* | ESC | F1 | F2 | F3 | F4 | F5 | | F6 | F7 | F8 | F9 | F10 | DEL | +* |------+-------+-------+------+-------+-------| |--------+--------+--------+-------+------+------| +* | RST | F11 | F12 | DEL | paste | copy | | home | pg up | print | redo | w | | +* |------+-------+-------+------+-------+-------| |--------+--------+--------+-------+------+------| +* | | all | | SAV | undo | BackSP|-------. ,-------| left | down | up | right | | | +* |------+-------+-------+------+-------+-------| enter| | |--------+--------+--------+-------+------+------| +* | F9 | F11 | F10 | F5 | TAB | cut |-------| |-------| end | pg dw | | | | | +* `---------------------------------------------/ / \ \-----------------------------------------------' +* | | SPE | | / / \ \ | | MENU | | +* | | | |/ / \ \ | | | | +* `--------------------------------' '-------''--------------------' +*/ + [_LOWER] = LAYOUT( + KC_ESC, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_DELETE, + RESET, KC_F11, KC_F12, KC_DELETE, RCTL(FR_V), RCTL(FR_C), KC_HOME, KC_PGUP, KC_PSCR, RCTL(FR_Y), RCTL(KC_RIGHT), _______, + _______,RCTL(FR_A), _______,RCTL(FR_S), RCTL(FR_Z), KC_BSPC, KC_LEFT, KC_DOWN, KC_UP, KC_RIGHT, _______, _______, + KC_F9, KC_F11, KC_F10, KC_F5, LALT(KC_TAB), RCTL(FR_X), KC_ENT, _______, KC_END, KC_PGDN, _______, _______, _______, _______, + _______,TT(_RAISE), _______, _______, _______, _______, KC_APP, _______), + +/* RAISE +* ,-----------------------------------------. ,-------------------------------------------. +* | | | | | | | | | | / | * | - | RGB TOG| +* |------+------+------+------+------+------| |------+------+------+------+-----+--------| +* | ` | [ | ] | | | | | ^ | 7 | 8 | 9 | + | RGB HUI| +* |------+------+------+------+------+------| |------+------+------+------+-----+--------| +* | | @ | | | & | € | # |-------. ,-------| $ | 4 | 5 | 6 | | | +* |------+------+------+------+------+------| | | |------+------+------+------+-----+--------| +* | F7 | F8 | F9 | F10 | # | F12 |-------| |-------| | 1 | 2 | 3 | | | +* `-----------------------------------------/ / \ \------------------------------------------' +* | | | | / / \ \ | | 0 | . | +* | | | |/ / \ \ | | | | +* `----------------------------' '------''---------------------' +*/ + [_RAISE] = LAYOUT( + _______, _______, _______, _______, _______, _______, _______, _______, KC_PSLS, KC_PAST, KC_PMNS, RGB_TOG, + KC_GRV, FR_LBRC, FR_RBRC, _______, _______, _______, FR_EQL, KC_KP_7, KC_KP_8, KC_KP_9, KC_PPLS, RGB_HUI, + _______, FR_AT, FR_PIPE, ALGR(KC_1), FR_EURO, FR_HASH, S(FR_EQL), KC_KP_4, KC_KP_5, KC_KP_6, _______, _______, + KC_F7, KC_F8, KC_F9, KC_F10, FR_HASH, KC_F12, _______, _______, _______, KC_KP_1, KC_KP_2, KC_KP_3, _______, _______, + _______, _______, _______, _______, _______, _______, KC_KP_0, KC_KP_DOT) +}; +// clang-format on + +// sync transport +typedef struct _sync_keycode_t { + uint16_t keycode; +} sync_keycode_t; + +// force rigth side to update +bool b_sync_need_send = false; + +// last keycode typed +sync_keycode_t last_keycode; + +oled_rotation_t oled_init_user(oled_rotation_t rotation) { + // vertical orientation + return OLED_ROTATION_270; +} + +void render(gui_state_t t) { + // logo + render_logo(t); + +#if IS_LEFT + // left side + render_layer_frame(t); + render_gears(); + + decay_scope(); + render_scope(t); +#endif + +#if IS_RIGHT + // right side + render_circle(t); +#endif +} + +void update(uint16_t keycode) { +#if IS_LEFT + update_scope(); +#endif + +#if IS_RIGHT + update_circle(keycode); +#endif +} + +void reset(void) { +#if IS_LEFT + reset_scope(); +#endif + +#if IS_RIGHT + reset_ring(); +#endif +} + +void set_wackingup_mode_clean(void) { + oled_clear(); + reset(); +} + +bool oled_task_user(void) { + gui_state_t t = get_gui_state(); + + // in sleep mode => turn display off + if (t == _SLEEP) { + oled_off(); + return false; + } + + // not in sleep mode => screen is on + oled_on(); + +#ifdef WITH_BOOT + // in booting mode => display booting animation + if (t == _BOOTING) { + bool boot_finished = render_boot(); + if (boot_finished) { + // end of the boot : wacking up + set_wackingup_mode_clean(); + update_gui_state(); + } + return false; + } +#endif + + // in halting mode => display booting animation + if (t == _HALTING) { + render_halt(); + return false; + } + + render(t); + return false; +} + +void process_key(uint16_t keycode) { + // update screen with the new key + update(keycode); + + gui_state_t t = get_gui_state(); + + if (t == _IDLE) { + // wake up animation + reset(); + } + + if (t == _BOOTING || t == _HALTING) { + // cancel booting or halting : waking_up + set_wackingup_mode_clean(); + } + + if (t == _SLEEP) { + // boot sequence + set_wackingup_mode_clean(); + reset_boot(); + } + + update_gui_state(); +} + +void user_sync_a_slave_handler(uint8_t in_buflen, const void* in_data, uint8_t out_buflen, void* out_data) { + const sync_keycode_t* m2s = (const sync_keycode_t*)in_data; + // get the last char typed on left side and update the right side + process_key(m2s->keycode); +} + +void keyboard_post_init_user(void) { + // callback for tranport sync data + transaction_register_rpc(USER_SYNC_A, user_sync_a_slave_handler); +} + +void housekeeping_task_user(void) { + // only for master side + if (!is_keyboard_master()) return; + + // only if a new char was typed + if (!b_sync_need_send) return; + + // send the char to the slave side : sync is done + if (transaction_rpc_send(USER_SYNC_A, sizeof(last_keycode), &last_keycode)) { + b_sync_need_send = false; + } +} + +bool process_record_user(uint16_t keycode, keyrecord_t* record) { + if (record->event.pressed) { + // master : store keycode to sent to the other side to be process_key + last_keycode.keycode = keycode; + b_sync_need_send = true; + + // gui process the input + process_key(keycode); + } + return true; +} + +#if IS_LEFT +layer_state_t layer_state_set_user(layer_state_t state) { + // update the frame with the layer name + update_layer_frame(state); + return state; +} +#endif diff --git a/keyboards/lily58/keymaps/druotoni/layer_frame.c b/keyboards/lily58/keymaps/druotoni/layer_frame.c new file mode 100644 index 0000000000..6f7ae1a253 --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/layer_frame.c @@ -0,0 +1,105 @@ +// Copyright 2021 Nicolas Druoton (druotoni) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include QMK_KEYBOARD_H + +#include "gui_state.h" +#include "layer_frame.h" +#include "draw_helper.h" + +#define ANIM_LAYER_FRAME_DURATION 2 +#define ANIM_LAYER_FRAME_MAX 7 + +// current layer +uint8_t current_layer = _QWERTY; + +// layer animation stuff +uint16_t anim_layer_frame_timer = 0; +uint8_t current_layer_frame = ANIM_LAYER_FRAME_MAX; +uint8_t layer_frame_destination = ANIM_LAYER_FRAME_MAX; + +// layer name for display +const char* layer_name; +static const char* layer_ref[3] = {LAYER_NAME_0, LAYER_NAME_1, LAYER_NAME_2}; + +void update_layer_frame(layer_state_t state) { + // reset timer + anim_layer_frame_timer = timer_read(); + + // direction for animation base on layer selected + current_layer = get_highest_layer(state); + if (current_layer == _QWERTY) { + layer_frame_destination = 0; + } else { + layer_frame_destination = ANIM_LAYER_FRAME_MAX; + } +} + +static void draw_black_screen(void) { + // clean frame center + draw_rectangle_fill(3, 42, 26, 20, false); + drawline_hr(17, 62, 12, false); +} + +void render_gears(void) { + // 64 bytes, 8x8 font, 8 characters, 32x16 image, 4 columns, 2 rows + static const char PROGMEM raw_logo[] = { + 0, 6, 6, 54, 118, 96, 230, 192, 192, 128, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 18, 226, 2, 18, 226, 2, 18, 226, 2, 1, 0, 0, 0, 0, 0, 128, 128, 128, 185, 187, 187, 131, 128, 184, 128, 128, 128, 128, 128, 128, 128, 128, 128, 191, 128, 128, 191, 128, 128, 191, 128, 0, + }; + + // extra line for complete the gui + oled_write_raw_P_cursor(0, 8, raw_logo, sizeof(raw_logo)); +} + +void render_layer_frame(gui_state_t t) { + // 96 bytes, 8x8 font, 12 characters, 32x24 image, 4 columns, 3 rows + static const char PROGMEM raw_logo[] = { + 62, 1, 0, 56, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 124, 248, 241, 226, 4, 8, 240, 0, 28, 28, 28, 0, 0, 127, 4, 8, 16, 127, 0, 124, 18, 17, 18, 124, 0, 31, 32, 64, 32, 31, 0, 0, 0, 0, 255, 255, 0, 0, 255, 62, 64, 64, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 142, 30, 62, 126, 126, 70, 70, 126, 70, 70, 126, 70, 70, 127, 127, 0, 0, 255, + }; + oled_write_raw_P_cursor(0, 5, raw_logo, sizeof(raw_logo)); + + // extra line for complete the gui + drawline_hr(2, 39, 25, 1); + + if (current_layer_frame != layer_frame_destination) { + if (timer_elapsed(anim_layer_frame_timer) > ANIM_LAYER_FRAME_DURATION) { + anim_layer_frame_timer = timer_read(); + + if (layer_frame_destination > current_layer_frame) { + current_layer_frame++; + } else { + current_layer_frame--; + } + } + + // black screen + draw_black_screen(); + + // gradient animation on layer selection + draw_gradient(3, 42, current_layer_frame * 4, 10, 0, 255, 7); + draw_gradient(3 + (27 - current_layer_frame * 4), 57, current_layer_frame * 4, 6, 255, 0, 7); + + drawline_hr(3, 46, 22, false); + drawline_hr(3, 47, 23, false); + + draw_rectangle_fill(3, 55, 24, 2, false); + draw_rectangle_fill(24, 48, 3, 7, false); + + draw_rectangle_fill(3, 60, 12, 2, false); + oled_write_pixel(15, 61, false); + drawline_hr(14, 62, 3, false); + drawline_hr(14, 62, 3, false); + drawline_hr(3, 62, 11, true); + } + + // get current layer name + layer_name = layer_ref[current_layer]; + + // gui on pause : no layer name on screen + if (t == _IDLE || t == _SLEEP || t == _WAKINGUP) { + layer_name = " "; + } + + // display layer name in the frame + oled_write_cursor(1, 6, layer_name, false); +} diff --git a/keyboards/lily58/keymaps/druotoni/layer_frame.h b/keyboards/lily58/keymaps/druotoni/layer_frame.h new file mode 100644 index 0000000000..abbd948395 --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/layer_frame.h @@ -0,0 +1,15 @@ +// Copyright 2021 Nicolas Druoton (druotoni) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +// layer name : must be 3 chars +#define LAYER_NAME_0 "ABC" +#define LAYER_NAME_1 "NAV" +#define LAYER_NAME_2 "SPE" + +enum layer_number { _QWERTY = 0, _LOWER, _RAISE }; + +void render_gears(void); +void render_layer_frame(gui_state_t t); +void update_layer_frame(layer_state_t state);
\ No newline at end of file diff --git a/keyboards/lily58/keymaps/druotoni/navi_font.c b/keyboards/lily58/keymaps/druotoni/navi_font.c new file mode 100644 index 0000000000..2412256e61 --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/navi_font.c @@ -0,0 +1,139 @@ +// Copyright 2021 Nicolas Druoton (druotoni) +// SPDX-License-Identifier: GPL-2.0-or-later + +// This is the 'classic' fixed-space bitmap font for Adafruit_GFX since 1.0. +// See gfxfont.h for newer custom bitmap font info. + +#include "progmem.h" + +// Standard ASCII 5x7 font +const unsigned char font[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3A, 0x12, 0x12, 0x0E, 0x00, + 0x0C, 0x44, 0x47, 0x24, 0x1C, 0x00, + 0x24, 0x24, 0x14, 0x7F, 0x04, 0x00, + 0x42, 0x3F, 0x02, 0x22, 0x1E, 0x00, + 0x0A, 0x0A, 0x7F, 0x0A, 0x0A, 0x00, + 0x02, 0x47, 0x42, 0x22, 0x1F, 0x00, + 0x21, 0x15, 0x09, 0x15, 0x63, 0x00, + 0x44, 0x44, 0x3F, 0x04, 0x04, 0x00, + 0x22, 0x1A, 0x02, 0x7F, 0x12, 0x00, + 0x22, 0x22, 0x12, 0x0A, 0x06, 0x00, + 0x08, 0x47, 0x42, 0x22, 0x1E, 0x00, + 0x10, 0x52, 0x54, 0x30, 0x16, 0x00, + 0x40, 0x3A, 0x02, 0x3E, 0x42, 0x00, + 0x5E, 0x52, 0x52, 0x52, 0x5E, 0x00, + 0x04, 0x27, 0x44, 0x44, 0x3C, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, + 0x14, 0x22, 0x7F, 0x22, 0x14, 0x00, + 0x5F, 0x5F, 0x00, 0x5F, 0x5F, 0x00, + 0x06, 0x09, 0x7F, 0x01, 0x7F, 0x00, + 0x00, 0x66, 0x89, 0x95, 0x6A, 0x00, + 0x60, 0x60, 0x60, 0x60, 0x60, 0x00, + 0x94, 0xA2, 0xFF, 0xA2, 0x94, 0x00, + 0x08, 0x04, 0x7E, 0x04, 0x08, 0x00, + 0x10, 0x20, 0x7E, 0x20, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1C, 0x1C, 0x1C, 0x00, 0x00, + 0x00, 0x08, 0x1C, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, + 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, + 0x14, 0x7F, 0x14, 0x7F, 0x14, 0x00, + 0x24, 0x2A, 0x7F, 0x2A, 0x12, 0x00, + 0x23, 0x13, 0x08, 0x64, 0x62, 0x00, + 0x36, 0x49, 0x56, 0x20, 0x50, 0x00, + 0x00, 0x08, 0x07, 0x03, 0x00, 0x00, + 0x00, 0x1C, 0x22, 0x41, 0x00, 0x00, + 0x00, 0x41, 0x22, 0x1C, 0x00, 0x00, + 0x2A, 0x1C, 0x7F, 0x1C, 0x2A, 0x00, + 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, + 0x00, 0x80, 0x70, 0x30, 0x00, 0x00, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, + 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, + 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, + 0x3E, 0x51, 0x49, 0x45, 0x3E, 0x00, + 0x00, 0x42, 0x7F, 0x40, 0x00, 0x00, + 0x72, 0x49, 0x49, 0x49, 0x46, 0x00, + 0x21, 0x41, 0x49, 0x4D, 0x33, 0x00, + 0x18, 0x14, 0x12, 0x7F, 0x10, 0x00, + 0x27, 0x45, 0x45, 0x45, 0x39, 0x00, + 0x3C, 0x4A, 0x49, 0x49, 0x31, 0x00, + 0x41, 0x21, 0x11, 0x09, 0x07, 0x00, + 0x36, 0x49, 0x49, 0x49, 0x36, 0x00, + 0x46, 0x49, 0x49, 0x29, 0x1E, 0x00, + 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x00, 0x40, 0x34, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x14, 0x22, 0x41, 0x00, + 0x14, 0x14, 0x14, 0x14, 0x14, 0x00, + 0x00, 0x41, 0x22, 0x14, 0x08, 0x00, + 0x02, 0x01, 0x59, 0x09, 0x06, 0x00, + 0x3E, 0x41, 0x5D, 0x59, 0x4E, 0x00, + 0x7C, 0x12, 0x11, 0x12, 0x7C, 0x00, + 0x7F, 0x49, 0x49, 0x49, 0x36, 0x00, + 0x3E, 0x41, 0x41, 0x41, 0x22, 0x00, + 0x7F, 0x41, 0x41, 0x41, 0x3E, 0x00, + 0x7F, 0x49, 0x49, 0x49, 0x41, 0x00, + 0x7F, 0x09, 0x09, 0x09, 0x01, 0x00, + 0x3E, 0x41, 0x41, 0x51, 0x73, 0x00, + 0x7F, 0x08, 0x08, 0x08, 0x7F, 0x00, + 0x00, 0x41, 0x7F, 0x41, 0x00, 0x00, + 0x20, 0x40, 0x41, 0x3F, 0x01, 0x00, + 0x7F, 0x08, 0x14, 0x22, 0x41, 0x00, + 0x7F, 0x40, 0x40, 0x40, 0x40, 0x00, + 0x7F, 0x02, 0x1C, 0x02, 0x7F, 0x00, + 0x7F, 0x04, 0x08, 0x10, 0x7F, 0x00, + 0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00, + 0x7F, 0x09, 0x09, 0x09, 0x06, 0x00, + 0x3E, 0x41, 0x51, 0x21, 0x5E, 0x00, + 0x7F, 0x09, 0x19, 0x29, 0x46, 0x00, + 0x26, 0x49, 0x49, 0x49, 0x32, 0x00, + 0x03, 0x01, 0x7F, 0x01, 0x03, 0x00, + 0x3F, 0x40, 0x40, 0x40, 0x3F, 0x00, + 0x1F, 0x20, 0x40, 0x20, 0x1F, 0x00, + 0x3F, 0x40, 0x38, 0x40, 0x3F, 0x00, + 0x63, 0x14, 0x08, 0x14, 0x63, 0x00, + 0x03, 0x04, 0x78, 0x04, 0x03, 0x00, + 0x61, 0x59, 0x49, 0x4D, 0x43, 0x00, + 0x00, 0x7F, 0x41, 0x41, 0x41, 0x00, + 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, + 0x00, 0x41, 0x41, 0x41, 0x7F, 0x00, + 0x04, 0x02, 0x01, 0x02, 0x04, 0x00, + 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, + 0x00, 0x03, 0x07, 0x08, 0x00, 0x00, + 0x20, 0x54, 0x54, 0x78, 0x40, 0x00, + 0x7F, 0x28, 0x44, 0x44, 0x38, 0x00, + 0x38, 0x44, 0x44, 0x44, 0x28, 0x00, + 0x38, 0x44, 0x44, 0x28, 0x7F, 0x00, + 0x38, 0x54, 0x54, 0x54, 0x18, 0x00, + 0x00, 0x08, 0x7E, 0x09, 0x02, 0x00, + 0x18, 0x24, 0x24, 0x1C, 0x78, 0x00, + 0x7F, 0x08, 0x04, 0x04, 0x78, 0x00, + 0x00, 0x44, 0x7D, 0x40, 0x00, 0x00, + 0x20, 0x40, 0x40, 0x3D, 0x00, 0x00, + 0x7F, 0x10, 0x28, 0x44, 0x00, 0x00, + 0x00, 0x41, 0x7F, 0x40, 0x00, 0x00, + 0x7C, 0x04, 0x78, 0x04, 0x78, 0x00, + 0x7C, 0x08, 0x04, 0x04, 0x78, 0x00, + 0x38, 0x44, 0x44, 0x44, 0x38, 0x00, + 0x7C, 0x18, 0x24, 0x24, 0x18, 0x00, + 0x18, 0x24, 0x24, 0x18, 0x7C, 0x00, + 0x7C, 0x08, 0x04, 0x04, 0x08, 0x00, + 0x48, 0x54, 0x54, 0x54, 0x24, 0x00, + 0x04, 0x04, 0x3F, 0x44, 0x24, 0x00, + 0x3C, 0x40, 0x40, 0x20, 0x7C, 0x00, + 0x1C, 0x20, 0x40, 0x20, 0x1C, 0x00, + 0x3C, 0x40, 0x30, 0x40, 0x3C, 0x00, + 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, + 0x4C, 0x90, 0x90, 0x90, 0x7C, 0x00, + 0x44, 0x64, 0x54, 0x4C, 0x44, 0x00, + 0x00, 0x08, 0x36, 0x41, 0x00, 0x00, + 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, + 0x00, 0x41, 0x36, 0x08, 0x00, 0x00, + 0x02, 0x01, 0x02, 0x04, 0x02, 0x00, + 0x3C, 0x26, 0x23, 0x26, 0x3C, 0x00 +}; diff --git a/keyboards/lily58/keymaps/druotoni/navi_logo.c b/keyboards/lily58/keymaps/druotoni/navi_logo.c new file mode 100644 index 0000000000..6041aa5ebc --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/navi_logo.c @@ -0,0 +1,117 @@ +// Copyright 2021 Nicolas Druoton (druotoni) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include QMK_KEYBOARD_H + +#include "gui_state.h" +#include "navi_logo.h" +#include "fast_random.h" +#include "draw_helper.h" + +#define LOGO_SIZE 128 + +// glitch stuff +#define GLITCH_FRAME_NUMBER 11 + +uint8_t current_glitch_index = 0; +int current_glitch_time = 150; +uint32_t glitch_timer = 0; + +static void render_logo_clean(void) { + // your logo here + static const char PROGMEM raw_logo[] = { + 0, 0, 0, 0, 0, 0, 128, 128, 0, 0, 128, 128, 192, 192, 204, 222, 222, 204, 192, 192, 128, 0, 0, 0, 128, 128, 0, 0, 0, 0, 0, 0, 192, 240, 248, 28, 14, 7, 3, 249, 252, 255, 15, 7, 3, 225, 241, 241, 241, 241, 225, 3, 7, 15, 255, 252, 249, 3, 7, 14, 28, 248, 240, 192, 192, 227, 231, 206, 28, 56, 112, 99, 15, 31, 60, 120, 240, 225, 227, 3, 3, 227, 225, 240, 120, 60, 31, 15, 103, 112, 56, 28, 206, 231, 227, 192, 0, 1, 1, 0, 0, 0, 56, 120, 96, 192, 192, 192, 96, 127, 63, 0, 0, 63, 127, 96, 192, 192, 192, 96, 120, 56, 0, 0, 0, 1, 1, 0, + }; + oled_write_raw_P(raw_logo, sizeof(raw_logo)); +} + +void render_glitch_bar(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t iProb) { + // random horizontal scanlines + for (uint8_t i = 0; i < height; i++) { + bool bGenerateGlitch = (fastrand() % 100) < iProb; + + if (bGenerateGlitch) { + drawline_hr(x, y + i, width, true); + } + } +} + +void render_misc_glitch(uint8_t algo) { + char c = 0; + switch (algo) { + case 7: + // invert + for (uint8_t i = 0; i < LOGO_SIZE; i++) { + c = get_oled_char(i); + oled_write_raw_byte(~(c), i); + } + break; + + case 8: + // wobble + for (uint8_t i = 0; i < LOGO_SIZE; i++) { + if (i < LOGO_SIZE - 1) { + copy_pixel(i + 1, -1, 85); + + copy_pixel(LOGO_SIZE - 1 - 1 - i, 1, 170); + } + } + break; + } +} + +static void render_logo_glitch(void) { +#ifdef WITH_GLITCH + // get a random glitch index + uint8_t glitch_prob = get_glitch_probability(); + get_glitch_index(&glitch_timer, ¤t_glitch_time, ¤t_glitch_index, 0, 150, glitch_prob, GLITCH_FRAME_NUMBER); + + // no glitch + if (current_glitch_index <= 3) { + render_logo_clean(); + return; + } + + // glitch time ! + switch (current_glitch_index) { + case 4: + move_block(1, 11, 24, 3, 5); + move_block(2, 19, 14, 3, 4); + move_block(9, 22, 7, 4, 4); + return; + + case 5: + move_block(6, 25, 20, 7, 4); + move_block(0, 8, 32, 8, 7); + return; + case 6: + move_block(3, 7, 27, 4, -3); + move_block(13, 23, 19, 4, -4); + return; + + case 7: + case 8: + render_misc_glitch(current_glitch_index); + return; + + case 9: + render_glitch_bar(0, 0, 32, 32, 25); + return; + + case 10: + draw_static(0, 0, 32, 32, true, 0); + return; + } +#endif +} + +void render_logo(gui_state_t t) { + if (t == _IDLE) { + // on idle : glitch time ! + render_logo_glitch(); + return; + } + + // standart logo + render_logo_clean(); +} diff --git a/keyboards/lily58/keymaps/druotoni/navi_logo.h b/keyboards/lily58/keymaps/druotoni/navi_logo.h new file mode 100644 index 0000000000..3504568e5d --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/navi_logo.h @@ -0,0 +1,7 @@ +// Copyright 2021 Nicolas Druoton (druotoni) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +void render_logo(gui_state_t t); + diff --git a/keyboards/lily58/keymaps/druotoni/readme.md b/keyboards/lily58/keymaps/druotoni/readme.md new file mode 100644 index 0000000000..c6d47da5d7 --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/readme.md @@ -0,0 +1,133 @@ +# HELL0 NAVI. Interface + +HELL0 NAVI. Interface is a GUI based en [Serial Experiments Lain](https://en.wikipedia.org/wiki/Serial_Experiments_Lain). Turn your [Lily58](https://github.com/kata0510/Lily58) keyboard into a Navi computer with its own Copland OS. + + +Ready to dive into the Wired ? + + +HELL0 NAVI provides interactive animations for both sides : +- a scope on left side for burst, WPM and active layer +- a ring on right side for the last key stroke + + + + + + + +## Typing animation + +The scope displays your burst time on a chart. The WPM is represented by an horizontal line. + +The ring display the last letter in the upper frame. Each time you enter a key, the Navi searches into the circular database and locks the position. A special animation is displayed when Enter, Backspce or Escape are struck. + +<img src="https://imgur.com/Yf7D6UN.gif" height="400" > + +## Startup animation + +Your Navi boots when it leaves the sleep mode. The animation can be canceled by typing. + + + +<img src="https://imgur.com/EXU92Ev.gif" height="400" > + + + +## Waking up animation + +After a period of inactivity, the scope and the ring turn off and the Navi runs in Idle mode. A new key stroke wakes them up. + + +<img src="https://imgur.com/9GWa7rR.gif" height="400" > + + +## Idle animation + +The Copland OS is still in beta test. After a while, some visual glitches will occur. + + +<img src="https://imgur.com/eKZ7qgC.gif" height="400" > + + + +## Shutdown animation +The Navi runs in sleep mode after 10 seconds in Idle mode. A nice (and difficul to render in a gif) animation is run. The OLED display turns off. + +# How to build & flash + +You need to flash each side with a specific version based on config.h configuration. + + ## Left side (master) + +IS_RIGHT needs to be commented in config.h +``` +#define IS_LEFT 1 +//#define IS_RIGHT 1 +``` +Connect the left side and flash + + ## Right side (slave) + +Comment IS_LEFT and uncomment IS_RIGHT in config.h +``` +//#define IS_LEFT 1 +#define IS_RIGHT 1 +``` +Connect the right side and flash + +# Customization + +## Logo +Logo can be change in navi_logo.c. +The new logo must be 32x32 pixels. +``` +static void render_logo_clean(void) { + // your logo here + static const char PROGMEM logo_raw[] = { + 0, 0, 0, 0, 0, 0, 128, 128, 0, 0, 128, 128, 192, 192, 204, 222, 222, 204, 192, 192, 128, 0, 0, 0, 128, 128, 0, 0, + 0, 0, 0, 0, 192, 240, 248, 28, 14, 7, 3, 249, 252, 255, 15, 7, 3, 225, 241, 241, 241, 241, 225, 3, 7, 15, 255, 252, + 249, 3, 7, 14, 28, 248, 240, 192, 192, 227, 231, 206, 28, 56, 112, 99, 15, 31, 60, 120, 240, 225, 227, 3, 3, 227, + 225, 240, 120, 60, 31, 15, 103, 112, 56, 28, 206, 231, 227, 192, 0, 1, 1, 0, 0, 0, 56, 120, 96, 192, 192, 192, + 96, 127, 63, 0, 0, 63, 127, 96, 192, 192, 192, 96, 120, 56, 0, 0, 0, 1, 1, 0, + }; + oled_write_raw_P(logo_raw, sizeof(logo_raw)); +} +``` +## Layer names + +The current version handle 3 differents layers. Names can be changed in layer_frame.h. +``` +// layer name : must be 3 chars +#define LAYER_NAME_0 "ABC" +#define LAYER_NAME_1 "NAV" +#define LAYER_NAME_2 "SPE" +``` + +## Timing + +You can tweak states timing in gui_state.h. +``` +// states timing +#define BOOTING_TIME_TRESHOLD 7000 +#define WAKING_UP_TIME_TRESHOLD 300 +#define IDLE_TIME_TRESHOLD 4000 +#define HALTING_TIME_TRESHOLD IDLE_TIME_TRESHOLD + 6000 +#define SLEEP_TIME_TRESHOLD HALTING_TIME_TRESHOLD + 8000 +``` + +## Need space ? +Boot and gliches can be commented in config.h +``` +// states timing +// logo glitch +//#define WITH_GLITCH +// boot sequence +//#define WITH_BOOT +``` + +![My Navi](https://imgur.com/eYkgoZJ.png) +> Keyboard : https://github.com/kata0510/Lily58 +> +> Case : https://github.com/BoardSodie/Lily58-Acrylic-Case + diff --git a/keyboards/lily58/keymaps/druotoni/ring.c b/keyboards/lily58/keymaps/druotoni/ring.c new file mode 100644 index 0000000000..e3b7472872 --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/ring.c @@ -0,0 +1,494 @@ +// Copyright 2021 Nicolas Druoton (druotoni) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include QMK_KEYBOARD_H +#include "gui_state.h" +#include "ring.h" + +#include "fast_random.h" +#include "draw_helper.h" + +char tListeTotal[SIZE_ARRAY_1] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'}; +char tListeTotal2[SIZE_ARRAY_1] = {'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ',', '.', '/', '1', '2', '3'}; + +static char tRefArc[SIZE_ARRAY_1] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'}; +static char tRefArc2[SIZE_ARRAY_1] = {'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ',', '.', '/', '1', '2', '3'}; + +// ring target and previous char +char c_target = 'A'; +char c_target2 = 'Q'; +char c_last = ' '; +char c_previous = ' '; + +static const char PROGMEM code_to_name[60] = {' ', ' ', ' ', ' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'R', 'E', 'B', 'T', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ';', '\'', ' ', ',', '.', '/', ' ', ' ', ' '}; + +// main circle +#define CIRCLE_ANIM_FRAME_DURATION 40 +uint16_t circle_timer = 0; + +// special animation for special keys +#define ANIM_CENTER_FRAME_NUMBER 5 +#define ANIM_CENTER_FRAME_DURATION 40 +uint16_t anim_center_timer = 0; +uint8_t anim_center_current_frame = 0; + +// sleep animation +#define ANIM_SLEEP_RING_FRAME_NUMBER 9 +#define ANIM_SLEEP_RING_FRAME_DURATION 20 +uint16_t anim_sleep_ring_timer = 0; +uint8_t current_sleep_ring_frame = 0; +uint8_t sleep_ring_frame_destination = ANIM_SLEEP_RING_FRAME_NUMBER - 1; + +// glitch animation +uint16_t anim_ring_idle_timer = 0; +int current_glitch_ring_time = 150; +uint32_t glitch_ring_timer = 0; +uint8_t current_glitch_ring_index = 0; + +// central frame keylog animation +#define ANIM_KEYLOG_FRAME_NUMBER 8 +#define ANIM_KEYLOG_FRAME_DURATION 20 +uint8_t anim_keylog_current_frame = 0; +uint16_t anim_keylog_timer = 0; + +static const char PROGMEM raw_ring_sleep[4][64] = {{ + 192, 32, 16, 8, 4, 4, 4, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 8, 16, 32, 192, 3, 4, 8, 16, 32, 32, 32, 64, 64, 64, 64, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 64, 32, 32, 32, 16, 8, 4, 3, + }, + + { + 128, 64, 32, 32, 16, 16, 16, 16, 16, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16, 16, 32, 32, 64, 128, 0, 1, 2, 2, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, 2, 2, 1, 0, + }, + + { + 248, 192, 128, 128, 128, 128, 128, 128, 128, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 128, 128, 128, 128, 128, 128, 128, 192, 248, 15, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 15, + }, + + { + 255, 240, 128, 128, 0, 128, 128, 0, 0, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 0, 0, 128, 128, 0, 128, 128, 248, 255, 255, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 255, + }}; + +static const char PROGMEM raw_circle[4][128] = {{ + 0, 0, 0, 192, 32, 16, 8, 8, 4, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 4, 8, 8, 16, 32, 192, 0, 0, 0, 240, 14, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 129, 238, 240, 15, 112, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 192, 240, 255, 127, 15, 0, 0, 0, 3, 4, 8, 16, 16, 32, 64, 64, 64, 128, 128, 192, 192, 224, 224, 224, 240, 112, 120, 124, 60, 30, 30, 15, 7, 3, 0, 0, 0, + }, + { + 0, 0, 0, 192, 32, 16, 8, 8, 4, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 4, 8, 8, 48, 224, 192, 0, 0, 0, 240, 14, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 255, 255, 254, 240, 15, 112, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 192, 224, 240, 248, 248, 254, 255, 255, 127, 15, 0, 0, 0, 3, 7, 15, 31, 30, 62, 126, 126, 126, 254, 254, 254, 254, 254, 255, 255, 255, 127, 127, 127, 63, 31, 31, 15, 7, 3, 0, 0, 0, + }, + { + 0, 0, 0, 192, 32, 16, 8, 8, 4, 2, 2, 2, 1, 1, 1, 1, 3, 15, 255, 255, 254, 254, 254, 252, 248, 248, 240, 224, 192, 0, 0, 0, 240, 14, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 192, 240, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 240, 15, 126, 252, 248, 248, 248, 248, 248, 252, 252, 254, 254, 255, 255, 255, 255, 255, 255, 255, 127, 63, 31, 15, 7, 7, 3, 3, 3, 7, 143, 127, 15, 0, 0, 0, 3, 7, 15, 31, 31, 63, 127, 127, 127, 255, 255, 255, 255, 255, 255, 195, 128, 64, 64, 64, 32, 16, 16, 8, 4, 3, 0, 0, 0, + }, + { + 0, 0, 0, 192, 224, 240, 248, 248, 124, 62, 30, 14, 15, 7, 7, 3, 3, 3, 1, 1, 2, 2, 2, 4, 8, 8, 16, 32, 192, 0, 0, 0, 240, 254, 255, 31, 15, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 14, 240, 15, 115, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 112, 15, 0, 0, 0, 3, 4, 8, 16, 16, 32, 64, 64, 64, 128, 128, 128, 128, 128, 128, 128, 128, 64, 64, 64, 32, 16, 16, 8, 4, 3, 0, 0, 0, + }}; + +static const char PROGMEM raw_bottom[] = { + 127, 192, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 192, 127, +}; + +static const char PROGMEM raw_middle[] = { + 240, 8, 4, 226, 241, 248, 124, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 56, 0, 1, 62, 255, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 28, 28, 0, 255, 0, 0, 127, 127, 70, 70, 126, 70, 70, 126, 70, 70, 126, 126, 62, 30, 142, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, 64, 64, 62, 1, 2, 114, 114, 2, 2, 114, 114, 2, 2, 114, 114, 2, 2, 2, 2, 1, 0, 0, 0, 128, 64, 64, 64, 64, 64, 64, 64, 64, 64, 128, 0, 0, 128, 131, 131, 132, 136, 179, 131, 132, 184, 131, 131, 188, 128, 128, 128, 128, 128, 128, 128, 143, 144, 149, 144, 149, 144, 149, 144, 149, 144, 143, 0, +}; + +static void rotate_right(char str[]) { + uint8_t iSize = SIZE_ARRAY_1; + char cFist = str[0]; + + // rotate array to the right + for (uint8_t i = 0; i < iSize - 1; i++) { + str[i] = str[i + 1]; + } + str[iSize - 1] = cFist; +} + +static void rotate_left(char str[]) { + uint8_t iSize = SIZE_ARRAY_1; + char cLast = str[iSize - 1]; + + // rotate array to the left + for (uint8_t i = iSize - 1; i > 0; i--) { + str[i] = str[i - 1]; + } + str[0] = cLast; +} + +static signed char GetPosition(char c, char tListe[]) { + uint8_t iSize = SIZE_ARRAY_1; + + // find position of c in the array + for (uint8_t i = 0; i < iSize; i++) { + if (tListe[i] == c) return i; + } + + // not found + return -1; +} + +static signed char GetDistance(char cNew, char tListe[]) { + signed char iPositionNew = GetPosition(cNew, tListe); + if (iPositionNew == -1) { + // not found + return 0; + } + + return iPositionNew - CURSOR_1; +} + +static bool TesterEstDansListe(char c, char tListe[]) { + // char in the list ? + return GetPosition(c, tListe) != -1; +} + +static void SmartRotation(char c, char tListe[]) { + signed char i = GetDistance(c, tListe); + if (i == 0) return; + + // rotate in the shorter way + if (i < 0) { + rotate_left(tListe); + return; + } + + if (i > 0) { + rotate_right(tListe); + return; + } +} + +static void update_list(char cNouveau, char tListe[]) { + signed char iDistance = GetDistance(cNouveau, tListe); + if (iDistance != 0) { + // the new char is in the list : rotation + SmartRotation(cNouveau, tListe); + } +} + +static void draw_arc_sector_16(uint8_t x, uint8_t y, uint8_t radius, int position, bool color) { + unsigned int s = 1; + s = s << (position / 2); + + if (position % 4 == 0 || position % 4 == 3) { + draw_arc_sector(x, y, radius, s, 1, color); + } else { + draw_arc_sector(x, y, radius, s, 2, color); + } +} + +static void render_set(uint8_t x, uint8_t y, uint8_t r, int p, bool color) { + // 2 pixels arc sector + draw_arc_sector_16(x, y, r, p, color); + draw_arc_sector_16(x, y, r - 1, p, color); +} + +static void draw_letter_circle(char t[], char tRef[], char ct, uint8_t x, uint8_t y, uint8_t r, bool invert) { + char c = t[CURSOR_1]; + + signed char p = GetPosition(c, tRef); + signed char pt = GetPosition(ct, tRef); + + if (!invert) { + draw_fill_circle(x, y, r, false); + draw_circle(x, y, r, false); + draw_circle(x, y, r - 1, false); + draw_circle(x, y, r - 2, false); + draw_circle(x, y, r - 4, true); + draw_circle(x, y, r - 5, true); + } + + int pafter = (pt + 1) % SIZE_ARRAY_1; + int pbefore = (pt + SIZE_ARRAY_1 - 1) % SIZE_ARRAY_1; + render_set(x, y, r, pt, true); + render_set(x, y, r, pafter, true); + render_set(x, y, r, pbefore, true); + + pafter = (pt + 2) % SIZE_ARRAY_1; + pbefore = (pt + SIZE_ARRAY_1 - 2) % SIZE_ARRAY_1; + render_set(x, y, r, pafter, true); + render_set(x, y, r, pbefore, true); + + r -= 4; + + pafter = (p + 1) % SIZE_ARRAY_1; + pbefore = (p + SIZE_ARRAY_1 - 1) % SIZE_ARRAY_1; + + render_set(x, y, r, p, false); + render_set(x, y, r, pafter, false); + render_set(x, y, r, pbefore, false); + + draw_circle(x, y, r - 6, true); +} + +static void draw_center_circle_frame(uint8_t x, uint8_t y, uint8_t r, uint8_t f) { + draw_fill_circle(x, y, r, 0); + draw_circle(x, y, r, 0); + + if (f == 0) { + draw_circle(x, y, r, 1); + } else { + // animation + oled_write_raw_P_cursor(0, 11, raw_circle[f - 1], sizeof(raw_circle[f - 1])); + } +} + +static void render_anim_center_circle(uint8_t x, uint8_t y, uint8_t r) { + if (anim_center_current_frame == ANIM_CENTER_FRAME_NUMBER) { + // last frame : no animation + return; + } + + if (timer_elapsed(anim_center_timer) > ANIM_CENTER_FRAME_DURATION) { + anim_center_timer = timer_read(); + + draw_center_circle_frame(x, y, r, anim_center_current_frame); + + anim_center_current_frame++; + } +} + +static void write_char(char c) { + // write keylog char in the frame then offset to center + oled_set_cursor(2, 6); + oled_write_char(c, false); + move_block(12, 48, 6, 8, 2); +} + +static void render_keylog(gui_state_t t) { + if (anim_keylog_current_frame != ANIM_KEYLOG_FRAME_NUMBER) { + if (timer_elapsed(anim_keylog_timer) > ANIM_KEYLOG_FRAME_DURATION) { + // update frame number + anim_keylog_timer = timer_read(); + anim_keylog_current_frame++; + } + + // clean frame + draw_rectangle_fill(7, 46, 21, 11, false); + + // comb motion to merge current and previous + if (anim_keylog_current_frame < ANIM_KEYLOG_FRAME_NUMBER / 2) { + // expand the previous char + write_char(c_previous); + draw_glitch_comb(9, 6 * 8, 18, 8, anim_keylog_current_frame + 1, true); + } else { + // shrink the current char + write_char(c_last); + draw_glitch_comb(9, 6 * 8, 18, 8, ANIM_KEYLOG_FRAME_NUMBER - anim_keylog_current_frame, false); + } + + return; + } + + write_char(c_last); +} + +void reset_ring(void) { + // need to open + anim_sleep_ring_timer = timer_read(); + current_sleep_ring_frame = ANIM_SLEEP_RING_FRAME_NUMBER - 1; + sleep_ring_frame_destination = 0; +} + +static void render_tv_circle(uint8_t x, uint8_t y, uint8_t r, uint8_t f) { + // raw image + if (f == 2 || f == 3) { + oled_write_raw_P_cursor(0, 12, raw_ring_sleep[f - 2], sizeof(raw_ring_sleep[f - 2])); + return; + } + + // raw image + if (f == 5 || f == 6) { + oled_write_raw_P_cursor(0, 12, raw_ring_sleep[f - 3], sizeof(raw_ring_sleep[f - 3])); + return; + } + + // other frames : lighter to draw than using raw image + switch (f) { + case 1: + draw_circle(x, y, r, 1); + break; + + case 4: + drawline_hr(1, y, 12, 1); + drawline_hr(19, y, 12, 1); + drawline_vb(0, y - 1, 3, true); + drawline_vb(31, y - 1, 3, true); + break; + + case 7: + + oled_write_pixel(1, y, true); + oled_write_pixel(3, y, true); + oled_write_pixel(28, y, true); + oled_write_pixel(30, y, true); + + drawline_vb(0, y - 12, 26, true); + drawline_vb(31, y - 12, 26, true); + break; + + case 8: + drawline_vb(0, 88, 32, true); + drawline_vb(31, 88, 32, true); + break; + } +} + +static void render_circle_white(void) { + // top + oled_write_raw_P_cursor(0, 5, raw_middle, sizeof(raw_middle)); + drawline_hr(5, 39, 25, 1); + + // clean center + draw_rectangle_fill(0, 80, 32, 40, false); + + // bottom + drawline_vb(0, 80, 8, 1); + drawline_vb(31, 80, 8, 1); + oled_write_pixel(1, 80, true); + oled_write_pixel(30, 80, true); + + oled_write_raw_P_cursor(0, 15, raw_bottom, sizeof(raw_bottom)); +} + +static void render_ring_clean_close(void) { + render_circle_white(); + drawline_vb(0, 88, 32, true); + drawline_vb(31, 88, 32, true); +} + +static void render_glitch_square(void) { + if (timer_elapsed(anim_ring_idle_timer) > 60) { + anim_ring_idle_timer = timer_read(); + + render_ring_clean_close(); + + uint8_t size = 0; + for (uint8_t i = 0; i < 4; i++) { + size = 4 + (fastrand() % 6); + draw_rectangle_fill(3 + (fastrand() % 19), 85 + (fastrand() % 20), size, size, true); + + size = (fastrand() % 6); + draw_rectangle_fill(3 + (fastrand() % 19), 100 + (fastrand() % 20), size, size, true); + } + } +} + +static void render_ring_idle(void) { + uint8_t glitch_prob = get_glitch_probability(); + get_glitch_index(&glitch_ring_timer, ¤t_glitch_ring_time, ¤t_glitch_ring_index, 150, 350, glitch_prob, 2); + + switch (current_glitch_ring_index) { + case 0: + // no glitch + render_ring_clean_close(); + return; + case 1: + // square gliches + render_glitch_square(); + return; + } +} + +static void render_ring_sleep(void) { + if (current_sleep_ring_frame == sleep_ring_frame_destination) { + // no more animation needes : render the idle animation + render_ring_idle(); + return; + } + + // display wacking up / sleep animation + if (timer_elapsed(anim_sleep_ring_timer) > ANIM_SLEEP_RING_FRAME_DURATION) { + anim_sleep_ring_timer = timer_read(); + + // clean + new frame + render_circle_white(); + render_tv_circle(15, 103, 11, current_sleep_ring_frame); + + // update frame number + if (sleep_ring_frame_destination > current_sleep_ring_frame) { + current_sleep_ring_frame++; + } else { + current_sleep_ring_frame--; + } + } +} + +static void render_circle_middle(void) { + // clean + render_circle_white(); + + // center special animation + if (anim_center_current_frame < ANIM_CENTER_FRAME_NUMBER) { + render_anim_center_circle(15, 103, 15 - 4); + return; + } + + // ring render + if (anim_center_current_frame == ANIM_CENTER_FRAME_NUMBER) { + draw_letter_circle(tListeTotal, tRefArc, c_target, 15, 103, 15, false); + draw_letter_circle(tListeTotal2, tRefArc2, c_target2, 15, 103, 15, true); + } +} + +void render_circle(gui_state_t t) { + if (timer_elapsed(circle_timer) > CIRCLE_ANIM_FRAME_DURATION) { + // new frame + circle_timer = timer_read(); + + // shift rings + update_list(c_target, tListeTotal); + update_list(c_target2, tListeTotal2); + + // waking up animation + if (t == _WAKINGUP) { + render_ring_sleep(); + return; + } + + // idle animation + if (t == _IDLE) { + sleep_ring_frame_destination = ANIM_SLEEP_RING_FRAME_NUMBER - 1; + render_ring_sleep(); + return; + } + + // render on display + render_circle_middle(); + render_keylog(t); + } +} + +void update_circle(uint16_t keycode) { + // special animation for special keys + if (keycode == KC_ESC || keycode == KC_SPACE || keycode == KC_ENTER) { + anim_center_timer = timer_read(); + anim_center_current_frame = 0; + return; + } + + // cancel special animation on a new key + anim_center_current_frame = ANIM_CENTER_FRAME_NUMBER; + + // out of scope key + if (keycode >= 60) { + return; + } + + // keycode to char + char c = pgm_read_byte(&code_to_name[keycode]); + + // stock previous char + c_previous = c_last; + c_last = c; + + // start keylog animation + anim_keylog_current_frame = 0; + + // update target in ring #1 position + if (TesterEstDansListe(c, tListeTotal)) { + c_target = c; + return; + } + + // update target in #2 position + if (TesterEstDansListe(c, tListeTotal2)) { + c_target2 = c; + return; + } +} diff --git a/keyboards/lily58/keymaps/druotoni/ring.h b/keyboards/lily58/keymaps/druotoni/ring.h new file mode 100644 index 0000000000..9ce4520e64 --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/ring.h @@ -0,0 +1,11 @@ +// Copyright 2021 Nicolas Druoton (druotoni) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#define SIZE_ARRAY_1 16 +#define CURSOR_1 9 + +void update_circle(uint16_t); +void render_circle(gui_state_t t); +void reset_ring(void); diff --git a/keyboards/lily58/keymaps/druotoni/rules.mk b/keyboards/lily58/keymaps/druotoni/rules.mk new file mode 100644 index 0000000000..c07761108e --- /dev/null +++ b/keyboards/lily58/keymaps/druotoni/rules.mk @@ -0,0 +1,28 @@ +# Build Options +# change to "no" to disable the options, or define them in the Makefile in +# the appropriate keymap folder that will get included automatically +# +RGBLIGHT_ENABLE = yes # Enable WS2812 RGB underlight. + +# Bootloader selection +BOOTLOADER = atmel-dfu + +# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE +SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend +# EXTRAFLAGS += -flto +LTO_ENABLE = yes +SPACE_CADET_ENABLE = no +GRAVE_ESC_ENABLE = no +MAGIC_ENABLE = no + +# If you want to change the display of OLED, you need to change here +SRC += ./lib/rgb_state_reader.c \ + ./burst.c \ + ./navi_logo.c \ + ./gui_state.c \ + ./fast_random.c \ + ./layer_frame.c \ + ./ring.c \ + ./boot.c \ + ./draw_helper.c \ + |