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
Add Luna keyboard pet, generic OLED pet framework
Luna artwork and original implementation by HellSingCoder, licensed
under GPL v2.0.

See also: https://github.com/qmk/qmk_firmware/blob/6dfe915e26d7147e6c2bed495d3b01cf5b21e6ec/keyboards/sofle/keymaps/helltm/keymap.c
  • Loading branch information
bcat committed Dec 2, 2021
commit 2c940d50121591e5579c897c581f58e8ddfef965
10 changes: 8 additions & 2 deletions keyboards/lily58/keymaps/bcat/keymap.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,18 @@ oled_rotation_t oled_init_user(oled_rotation_t rotation) { return is_keyboard_ma

void oled_task_user(void) {
if (is_keyboard_master()) {
uint8_t mods = get_mods();
led_t leds = host_keyboard_led_state();
uint8_t wpm = get_current_wpm();

render_oled_layers();
oled_advance_page(/*clearPageRemainder=*/false);
render_oled_indicators();
render_oled_indicators(leds);
oled_advance_page(/*clearPageRemainder=*/false);
oled_advance_page(/*clearPageRemainder=*/false);
render_oled_wpm();
render_oled_wpm(wpm);

render_oled_pet(/*col=*/0, /*line=*/12, mods, leds, wpm);
} else {
render_oled_logo();
}
Expand Down
2 changes: 2 additions & 0 deletions keyboards/lily58/keymaps/bcat/rules.mk
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
BCAT_OLED_PET = luna

BOOTLOADER = atmel-dfu # Elite-C
2 changes: 2 additions & 0 deletions users/bcat/bcat.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@

static int8_t alt_tab_layer = -1;

__attribute__((weak)) void process_record_oled(uint16_t keycode, const keyrecord_t *record) {}
__attribute__((weak)) bool process_record_keymap(uint16_t keycode, keyrecord_t *record) { return true; }

bool process_record_user(uint16_t keycode, keyrecord_t *record) {
process_record_oled(keycode, record);
if (!process_record_keymap(keycode, record)) {
return false;
}
Expand Down
2 changes: 2 additions & 0 deletions users/bcat/bcat.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

#pragma once

#include <stdbool.h>

#include "keymap.h"

/* Layer numbers shared across keymaps. */
Expand Down
120 changes: 106 additions & 14 deletions users/bcat/bcat_oled.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,23 @@
#include <stdint.h>

#include "bcat.h"
#include "keycode.h"
#include "led.h"
#include "oled_driver.h"
#include "progmem.h"
#include "quantum.h"
#include "timer.h"
#include "wpm.h"

static const char TRIANGLE_UP = 0x1e;
static const char TRIANGLE_DOWN = 0x1f;
#if defined(BCAT_OLED_PET)
# include "bcat_oled_pet.h"
#endif

#define TRIANGLE_UP 0x1e
#define TRIANGLE_DOWN 0x1f

#if defined(BCAT_OLED_PET)
static bool oled_pet_should_jump = false;
#endif

void render_oled_logo(void) {
static const char PROGMEM logo[] = {
Expand Down Expand Up @@ -65,26 +73,23 @@ void render_oled_layers(void) {
#endif
}

void render_oled_indicators(void) {
led_t led_state = host_keyboard_led_state();
void render_oled_indicators(led_t leds) {
oled_advance_char();
oled_advance_char();
oled_write_P(led_state.num_lock ? PSTR("NUM") : PSTR(" "), /*invert=*/false);
oled_write_P(leds.num_lock ? PSTR("NUM") : PSTR(" "), /*invert=*/false);
oled_advance_char();
oled_advance_char();
oled_write_P(led_state.caps_lock ? PSTR("CAP") : PSTR(" "), /*invert=*/false);
oled_write_P(leds.caps_lock ? PSTR("CAP") : PSTR(" "), /*invert=*/false);
oled_advance_char();
oled_advance_char();
oled_write_P(led_state.scroll_lock ? PSTR("SCR") : PSTR(" "), /*invert=*/false);
oled_write_P(leds.scroll_lock ? PSTR("SCR") : PSTR(" "), /*invert=*/false);
}

void render_oled_wpm(void) {
static const uint16_t UPDATE_INTERVAL_MILLIS = 100;
static uint32_t update_timeout = 0;
void render_oled_wpm(uint8_t wpm) {
static const uint16_t UPDATE_MILLIS = 100;
static uint32_t update_timeout = 0;

if (timer_expired32(timer_read32(), update_timeout)) {
uint8_t wpm = get_current_wpm();

char wpm_str[] = " ";
if (wpm > 0) {
wpm_str[2] = '0' + wpm % 10;
Expand All @@ -103,6 +108,93 @@ void render_oled_wpm(void) {
oled_advance_char();
oled_write(wpm_str, /*invert=*/false);

update_timeout = timer_read32() + UPDATE_INTERVAL_MILLIS;
update_timeout = timer_read32() + UPDATE_MILLIS;
}
}

#if defined(BCAT_OLED_PET)
void process_record_oled(uint16_t keycode, const keyrecord_t *record) {
switch (keycode) {
case KC_SPACE:
if (oled_pet_can_jump()) {
oled_pet_should_jump = record->event.pressed;
}
break;
default:
break;
}
}

static void redraw_oled_pet(uint8_t col, uint8_t line, bool jumping, oled_pet_state_t state, uint8_t frame) {
oled_set_cursor(col, line);
if (jumping) {
oled_write_raw_P(oled_pet_frame(state, frame), 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_set_cursor(col, line + oled_pet_frame_lines() + 1);
}
}

bool render_oled_pet(uint8_t col, uint8_t line, uint8_t mods, led_t leds, uint8_t wpm) {
/* 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.
*/
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;

/* 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;

/* Time until next animation state/frame change. */
static uint32_t update_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;
if (oled_pet_should_jump && !jumping) {
redraw = true;
jumping = true;
jump_timeout = timer_read32() + JUMP_MILLIS;
} else if (!oled_pet_should_jump && jumping && timer_expired32(timer_read32(), jump_timeout)) {
redraw = true;
jumping = false;
}

if (redraw) {
redraw_oled_pet(col, line, jumping, state, frame);
}

/* If the update timer expired, recompute the pet's animation state and
* possibly advance to the next frame.
*/
animation_changed = false;
if (timer_expired32(timer_read32(), update_timeout)) {
oled_pet_state_t new_state = oled_pet_state(mods, leds, wpm);
if (state != new_state) {
state = new_state;
animation_changed = true;
}
/* If the user stopped typing, cycle around to the initial frame. */
if (wpm > 0 || state != OLED_PET_IDLE || frame != 0) {
frame = (frame + 1) % oled_pet_num_frames();
update_timeout = timer_read32() + oled_pet_update_millis(wpm);
animation_changed = true;
}
}

return redraw;
}
#endif
23 changes: 21 additions & 2 deletions users/bcat/bcat_oled.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@

#pragma once

#include <stdbool.h>
#include <stdint.h>

#include "led.h"

/* Renders the logo embedded at the "standard" location in the OLED font at the
* cursor. By default, this is a "QMK Firmware" logo, but many keyboards put
* their own logo here instead. Occupies 21x3 character cells.
Expand All @@ -30,7 +35,21 @@ void render_oled_layers(void);
/* Renders LED indicators (Num/Caps/Scroll Lock) at the cursor. Occupies 5x3
* character cells.
*/
void render_oled_indicators(void);
void render_oled_indicators(led_t leds);

/* Renders calculated WPM count at the cursor. Occupies 5x2 character cells. */
void render_oled_wpm(void);
void render_oled_wpm(uint8_t wpm);

#if defined(BCAT_OLED_PET)
/* Renders an animated critter at the cursor that can respond to keystrokes,
* typing speed, etc. Should be about 5 character cells wide, but exact height
* varies depending on the specific OLED pet implementation linked in.
*
* The rendered image will be one line taller than the OLED pet's animation
* frame height to accommodate pets that "jump" when the spacebar is pressed.
*
* Returns whether or not a new frame of the animation was displayed, in case
* the caller wants to draw atop the pet animation (e.g., in empty space).
*/
bool render_oled_pet(uint8_t col, uint8_t line, uint8_t mods, led_t leds, uint8_t wpm);
#endif
68 changes: 68 additions & 0 deletions users/bcat/bcat_oled_pet.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* Copyright 2021 Jonathan Rascher
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

/* Common interface for an OLED pet (animated critter that reacts to typing).
* Please link exactly one accompanying .c file to implement these functions.
*/

#pragma once

#include <stdbool.h>
#include <stdint.h>

#include "led.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.
*/
uint8_t oled_pet_num_frames(void);

/* 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
* to have the same frame size.
*/
uint16_t oled_pet_frame_bytes(void);

/* Returns the number of lines of the OLED occupied by the animation. Note that
* every state the pet supports is expected to have the same frame size. The
* returned value does not include the one line of padding that render_oled_pet
* uses to account for "jumping".
*/
uint8_t oled_pet_frame_lines(void);

/* Returns whether or not the OLED pet should "jump" when the spacebar is
* pressed. (The render_oled_pet implementation shifts the animation frame up
* one line when this happens.)
*/
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(uint8_t mods, led_t leds, uint8_t wpm);

/* Returns the delay before the next animation frame should be displayed. */
uint16_t oled_pet_update_millis(uint8_t wpm);

/* 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.
*/
const char *oled_pet_frame(oled_pet_state_t state, uint8_t frame);
Loading