Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Keymap] Update bcat's keymaps/userspace to share logic, add OLED functionality, and set up one of my macropads for WFH #14702

Merged
merged 23 commits into from
Dec 27, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8084c71
Add script to build all bcat keymaps at once
bcat Jun 20, 2021
8083630
Move userspace RGB to separate source file
bcat Jun 20, 2021
fb73247
Move layer handling logic into userspace
bcat Jun 20, 2021
0e27a3e
Move keycap aliases into userspace
bcat Jun 20, 2021
38eaf30
Add OLED userspace library and Lily58 OLED setup
bcat Jun 20, 2021
2c940d5
Add Luna keyboard pet, generic OLED pet framework
bcat Jun 20, 2021
d42d567
Use OLED on bcat's Crkbd
bcat Jun 20, 2021
eed58f2
Remove vestigial NK_TOGG keybindings
bcat Jun 20, 2021
0f0f71d
Add post-render hook to OLED pet API
bcat Jun 21, 2021
a68cdac
Add Isda keyboard pet
bcat Jun 21, 2021
45d6909
Replace OLED timeout implementation with custom
bcat Jun 23, 2021
bc95ebb
Move keyboard state for OLED functions into struct
bcat Jun 23, 2021
e07d941
Enable continuously running OLED pet (for Luna)
bcat Jun 26, 2021
23930f6
Sync OLED state; enable Bootmagic only when needed
bcat Oct 4, 2021
bf46471
Update 9-Key macropad keymap for working from home
bcat Oct 4, 2021
e0f292e
Remove includes redundant with quantum.h
bcat Oct 5, 2021
2ab419b
Simplify BCAT_OLED_PET makefile logic
bcat Oct 5, 2021
aada1c1
Swap some keys on my 9-Key macropad around
bcat Oct 8, 2021
d13f869
Inline spurious variable in OLED code
bcat Nov 24, 2021
1d18c9d
Remove max brightness that's now set by default
bcat Nov 24, 2021
cfcffdd
Enable specific RGBLIGHT modes instead of default
bcat Nov 28, 2021
0b9ae7b
Reenable RGB_MATRIX animations after #15018
bcat Nov 28, 2021
a3efc6b
Use new get_u8_str function for WPM display
bcat Nov 28, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Enable continuously running OLED pet (for Luna)
  • Loading branch information
bcat committed Dec 2, 2021
commit e07d9411e10693c025f958ce4225dcf1b9124594
60 changes: 26 additions & 34 deletions users/bcat/bcat_oled.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,44 +154,40 @@ void process_record_oled(uint16_t keycode, const keyrecord_t *record) {
}
}

static void redraw_oled_pet(uint8_t col, uint8_t line, bool jumping, oled_pet_state_t state, uint8_t frame) {
static void redraw_oled_pet(uint8_t col, uint8_t line, bool jumping, oled_pet_state_t state) {
oled_set_cursor(col, line);
if (jumping) {
oled_write_raw_P(oled_pet_frame(state, frame), oled_pet_frame_bytes());
oled_write_raw_P(oled_pet_frame(state), oled_pet_frame_bytes());
oled_set_cursor(col, line + oled_pet_frame_lines());
oled_advance_page(/*clearPageRemainder=*/true);
} else {
oled_advance_page(/*clearPageRemainder=*/true);
oled_write_raw_P(oled_pet_frame(state, frame), oled_pet_frame_bytes());
oled_write_raw_P(oled_pet_frame(state), oled_pet_frame_bytes());
}
}

void render_oled_pet(uint8_t col, uint8_t line, const oled_keyboard_state_t *keyboard_state) {
/* Whether or not the animation state or frame has changed since the pet
* was last drawn. We track this to avoid redrawing the same frame
* repeatedly during idle. This allows the caller to draw on top of the pet
* without preventing the OLED from ever going to sleep.
/* Current animation to draw. We track changes to avoid redrawing the same
* frame repeatedly, allowing oled_pet_post_render to draw over the
* animation frame.
*/
static bool animation_changed = true;

/* Current animation state and frame to redraw. */
static oled_pet_state_t state = OLED_PET_IDLE;
static uint8_t frame = 0;
static oled_pet_state_t state = 0;
static bool state_changed = true;

/* Minimum time until the pet comes down after jumping. */
static const uint16_t JUMP_MILLIS = 200;
static bool jumping = false;
static uint32_t jump_timeout = 0;
static const uint16_t JUMP_MILLIS = 200;
static bool jumping = false;

/* Time until next animation state/frame change. */
/* Time until the next animation or jump state change. */
static uint32_t update_timeout = 0;
static uint32_t jump_timeout = 0;

/* If the user pressed the jump key, immediately redraw instead of waiting
* for the animation frame to update. That way, the pet appears to respond
* to jump commands quickly rather than lagging. If the user released the
* jump key, wait for the jump timeout to avoid overly brief jumps.
*/
bool redraw = animation_changed;
bool redraw = state_changed;
if (oled_pet_should_jump && !jumping) {
redraw = true;
jumping = true;
Expand All @@ -201,28 +197,24 @@ void render_oled_pet(uint8_t col, uint8_t line, const oled_keyboard_state_t *key
jumping = false;
}

/* Draw the actual animation, then move the cursor to the end of the
* rendered area. (Note that we take up an extra line to account for
* jumping, which shifts the animation up or down a line.)
*/
if (redraw) {
redraw_oled_pet(col, line, jumping, state, frame);
redraw_oled_pet(col, line, jumping, state);
}
oled_pet_post_render(col, line, keyboard_state, jumping, redraw);
oled_pet_post_render(col, line + !jumping, keyboard_state, redraw);
oled_set_cursor(col, line + oled_pet_frame_lines() + 1);

/* If the update timer expired, recompute the pet's animation state and
* possibly advance to the next frame.
*/
animation_changed = false;
/* If the update timer expired, recompute the pet's animation state. */
if (timer_expired32(timer_read32(), update_timeout)) {
oled_pet_state_t new_state = oled_pet_state(keyboard_state);
if (state != new_state) {
state = new_state;
animation_changed = true;
}
/* If the user stopped typing, cycle around to the initial frame. */
if (keyboard_state->wpm > 0 || state != OLED_PET_IDLE || frame != 0) {
frame = (frame + 1) % oled_pet_num_frames();
update_timeout = timer_read32() + oled_pet_update_millis(keyboard_state);
animation_changed = true;
}
oled_pet_state_t new_state = oled_pet_next_state(state, keyboard_state);
state_changed = new_state != state;
state = new_state;
update_timeout = timer_read32() + oled_pet_update_millis(keyboard_state);
} else {
state_changed = false;
}
}
#endif
29 changes: 12 additions & 17 deletions users/bcat/bcat_oled_pet.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,11 @@

#include "bcat_oled.h"

/* Opaque token identifying what animation state the pet is currently in. */
typedef uint8_t oled_pet_state_t;

/* The default animation state that every OLED pet must support. */
#define OLED_PET_IDLE 0

/* Returns the number of frames in the animation. Note that every state the pet
* supports is expected to have the same number of frames.
/* Opaque token representing a single frame of the OLED pet animation.
* Different pet implementations have different valid state values, but the
* zero value must always represent the default state of the pet at startup.
*/
uint8_t oled_pet_num_frames(void);
typedef uint16_t oled_pet_state_t;

/* Returns the number of bytes used to represent the animation frame (in
* oled_write_raw_P format). Note that every state the pet supports is expected
Expand All @@ -55,12 +50,12 @@ uint8_t oled_pet_frame_lines(void);
*/
bool oled_pet_can_jump(void);

/* Returns the current state to be animated based on current keyboard state. */
oled_pet_state_t oled_pet_state(const oled_keyboard_state_t *keyboard_state);

/* Returns the delay before the next animation frame should be displayed. */
uint16_t oled_pet_update_millis(const oled_keyboard_state_t *keyboard_state);

/* Returns the state of the pet to be animated on the next animation tick. */
oled_pet_state_t oled_pet_next_state(oled_pet_state_t state, const oled_keyboard_state_t *keyboard_state);

/* Called after the OLED pet is rendered during each OLED task invocation.
* Receives the same keyboard state as render_oled_pet. The redraw param
* indicates whether or not an OLED frame was just redrawn, allowing a specific
Expand All @@ -69,10 +64,10 @@ uint16_t oled_pet_update_millis(const oled_keyboard_state_t *keyboard_state);
* When this function is called, the cursor will be in an unspecified location,
* not necessarily the top-left corner of the OLED pet.
*/
void oled_pet_post_render(uint8_t col, uint8_t line, const oled_keyboard_state_t *keyboard_state, bool jumping, bool redraw);
void oled_pet_post_render(uint8_t col, uint8_t line, const oled_keyboard_state_t *keyboard_state, bool redraw);

/* Returns a PROGMEM pointer to the specified animation frame buffer for the
* specified state. The animation frame has length given by
* oled_pet_frame_bytes and is formatted as expected by oled_write_raw_P.
/* Returns a PROGMEM pointer to the specified frame buffer for the specified
* state. The animation frame has length given by oled_pet_frame_bytes and is
* formatted as expected by oled_write_raw_P.
*/
const char *oled_pet_frame(oled_pet_state_t state, uint8_t frame);
const char *oled_pet_frame(oled_pet_state_t state);
20 changes: 11 additions & 9 deletions users/bcat/bcat_oled_pet_isda.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,10 @@
#define NUM_FRAMES 4
#define FRAME_BYTES 288 /* (32 pixel) * (72 pixel) / (8 pixel/byte) */

uint8_t oled_pet_num_frames(void) { return NUM_FRAMES; }
uint16_t oled_pet_frame_bytes(void) { return FRAME_BYTES; }
uint8_t oled_pet_frame_lines(void) { return 9 /* (72 pixel) / (8 pixel/line) */; }
bool oled_pet_can_jump(void) { return false; }

oled_pet_state_t oled_pet_state(const oled_keyboard_state_t *keyboard_state) { return OLED_PET_IDLE; }

uint16_t oled_pet_update_millis(const oled_keyboard_state_t *keyboard_state) {
static const uint16_t MIN_MILLIS = 75;
static const uint16_t MAX_MILLIS = 300;
Expand All @@ -60,7 +57,12 @@ uint16_t oled_pet_update_millis(const oled_keyboard_state_t *keyboard_state) {
return MAX_MILLIS - (MAX_MILLIS - MIN_MILLIS) * wpm / MAX_WPM;
}

void oled_pet_post_render(uint8_t col, uint8_t line, const oled_keyboard_state_t *keyboard_state, bool jumping, bool redraw) {
oled_pet_state_t oled_pet_next_state(oled_pet_state_t state, const oled_keyboard_state_t *keyboard_state) {
/* When the user stops typing, cycle the animation to frame 0 and stop. */
return state != 0 || keyboard_state->wpm > 0 ? (state + 1) % NUM_FRAMES : 0;
}

void oled_pet_post_render(uint8_t col, uint8_t line, const oled_keyboard_state_t *keyboard_state, bool redraw) {
/* Draws LED indicator status in the bottom-right corner of the OLED pet,
* atop the animation frame. Redrawn only when necessary, e.g., when LED
* status changes or the animation itself updated (which overwrites any
Expand All @@ -69,17 +71,17 @@ void oled_pet_post_render(uint8_t col, uint8_t line, const oled_keyboard_state_t
static led_t prev_leds = {.raw = 0};
led_t leds = keyboard_state->leds;
if (redraw || leds.raw != prev_leds.raw) {
oled_set_cursor(col + 4, line + !jumping + 4);
oled_set_cursor(col + 4, line + 4);
oled_write_char(leds.num_lock ? 'N' : ' ', /*invert=*/false);
oled_set_cursor(col + 4, line + !jumping + 6);
oled_set_cursor(col + 4, line + 6);
oled_write_char(leds.caps_lock ? 'C' : ' ', /*invert=*/false);
oled_set_cursor(col + 4, line + !jumping + 8);
oled_set_cursor(col + 4, line + 8);
oled_write_char(leds.scroll_lock ? 'S' : ' ', /*invert=*/false);
prev_leds = leds;
}
}

const char *oled_pet_frame(oled_pet_state_t state, uint8_t frame) {
const char *oled_pet_frame(oled_pet_state_t state) {
static const char PROGMEM FRAMES[NUM_FRAMES][FRAME_BYTES] = {
// clang-format off
{
Expand Down Expand Up @@ -128,5 +130,5 @@ const char *oled_pet_frame(oled_pet_state_t state, uint8_t frame) {
},
// clang-format on
};
return FRAMES[frame];
return FRAMES[state];
}
74 changes: 42 additions & 32 deletions users/bcat/bcat_oled_pet_luna.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,42 +37,51 @@
#include "keycode.h"
#include "progmem.h"

enum state {
OLED_PET_WALK = OLED_PET_IDLE + 1,
OLED_PET_RUN,
OLED_PET_SNEAK,
OLED_PET_BARK,
enum image {
IMAGE_IDLE,
IMAGE_WALK,
IMAGE_RUN,
IMAGE_SNEAK,
IMAGE_BARK,
};

typedef union {
oled_pet_state_t raw;
struct {
uint8_t image;
uint8_t frame;
};
} luna_state_t;

#define NUM_FRAMES 2
#define FRAME_BYTES 96 /* (32 pixel) * (24 pixel) / (8 pixel/byte) */

uint8_t oled_pet_num_frames(void) { return NUM_FRAMES; }
uint16_t oled_pet_frame_bytes(void) { return FRAME_BYTES; }
uint8_t oled_pet_frame_lines(void) { return 3 /* (24 pixel) / (8 pixel/line) */; }
bool oled_pet_can_jump(void) { return true; }

oled_pet_state_t oled_pet_state(const oled_keyboard_state_t *keyboard_state) {
uint16_t oled_pet_update_millis(const oled_keyboard_state_t *keyboard_state) { return 200; }

oled_pet_state_t oled_pet_next_state(oled_pet_state_t state, const oled_keyboard_state_t *keyboard_state) {
luna_state_t luna_state = {.raw = state};
if (keyboard_state->leds.caps_lock) {
return OLED_PET_BARK;
}
if (keyboard_state->mods & MOD_MASK_CTRL) {
return OLED_PET_SNEAK;
luna_state.image = IMAGE_BARK;
} else if (keyboard_state->mods & MOD_MASK_CTRL) {
luna_state.image = IMAGE_SNEAK;
} else if (keyboard_state->wpm >= 100) {
luna_state.image = IMAGE_RUN;
} else if (keyboard_state->wpm >= 25) {
luna_state.image = IMAGE_WALK;
} else {
luna_state.image = IMAGE_IDLE;
}
if (keyboard_state->wpm >= 100) {
return OLED_PET_RUN;
}
if (keyboard_state->wpm >= 25) {
return OLED_PET_WALK;
}
return OLED_PET_IDLE;
luna_state.frame = (luna_state.frame + 1) % NUM_FRAMES;
return luna_state.raw;
}

uint16_t oled_pet_update_millis(const oled_keyboard_state_t *keyboard_state) { return 200; }

void oled_pet_post_render(uint8_t col, uint8_t line, const oled_keyboard_state_t *keyboard_state, bool jumping, bool redraw) {}
void oled_pet_post_render(uint8_t col, uint8_t line, const oled_keyboard_state_t *keyboard_state, bool redraw) {}

const char *oled_pet_frame(oled_pet_state_t state, uint8_t frame) {
const char *oled_pet_frame(oled_pet_state_t state) {
static const char PROGMEM IDLE_FRAMES[NUM_FRAMES][FRAME_BYTES] = {
// clang-format off
{
Expand Down Expand Up @@ -143,16 +152,17 @@ const char *oled_pet_frame(oled_pet_state_t state, uint8_t frame) {
},
// clang-format on
};
switch (state) {
case OLED_PET_WALK:
return WALK_FRAMES[frame];
case OLED_PET_RUN:
return RUN_FRAMES[frame];
case OLED_PET_SNEAK:
return SNEAK_FRAMES[frame];
case OLED_PET_BARK:
return BARK_FRAMES[frame];
luna_state_t luna_state = {.raw = state};
switch (luna_state.image) {
case IMAGE_WALK:
return WALK_FRAMES[luna_state.frame];
case IMAGE_RUN:
return RUN_FRAMES[luna_state.frame];
case IMAGE_SNEAK:
return SNEAK_FRAMES[luna_state.frame];
case IMAGE_BARK:
return BARK_FRAMES[luna_state.frame];
default:
return IDLE_FRAMES[frame];
return IDLE_FRAMES[luna_state.frame];
}
}