浏览代码

Merge remote-tracking branch 'origin/master' into develop

QMK Bot 3 年之前
父节点
当前提交
e969420a85
共有 45 个文件被更改,包括 1360 次插入134 次删除
  1. 9 10
      keyboards/9key/keymaps/bcat/keymap.c
  2. 3 3
      keyboards/9key/keymaps/bcat/readme.md
  3. 2 0
      keyboards/eco/keymaps/bcat/config.h
  4. 1 18
      keyboards/eco/keymaps/bcat/keymap.c
  5. 1 1
      keyboards/eco/keymaps/bcat/readme.md
  6. 4 9
      keyboards/keebio/bdn9/keymaps/bcat/keymap.c
  7. 4 9
      keyboards/keebio/quefrency/keymaps/bcat/keymap.c
  8. 1 1
      keyboards/keebio/quefrency/keymaps/bcat/readme.md
  9. 2 0
      keyboards/lily58/keymaps/bcat/config.h
  10. 17 16
      keyboards/lily58/keymaps/bcat/keymap.c
  11. 1 1
      keyboards/lily58/keymaps/bcat/readme.md
  12. 2 0
      keyboards/lily58/keymaps/bcat/rules.mk
  13. 138 0
      keyboards/reviung53/config.h
  14. 68 0
      keyboards/reviung53/info.json
  15. 55 0
      keyboards/reviung53/keymaps/default/keymap.c
  16. 1 0
      keyboards/reviung53/keymaps/default/readme.md
  17. 55 0
      keyboards/reviung53/keymaps/via/keymap.c
  18. 3 0
      keyboards/reviung53/keymaps/via/readme.md
  19. 1 0
      keyboards/reviung53/keymaps/via/rules.mk
  20. 27 0
      keyboards/reviung53/readme.md
  21. 4 0
      keyboards/reviung53/reviung53.c
  22. 30 0
      keyboards/reviung53/reviung53.h
  23. 19 0
      keyboards/reviung53/rules.mk
  24. 2 9
      layouts/community/60_ansi_split_bs_rshift/bcat/keymap.c
  25. 1 1
      layouts/community/60_ansi_split_bs_rshift/bcat/readme.md
  26. 2 9
      layouts/community/60_tsangan_hhkb/bcat/keymap.c
  27. 1 1
      layouts/community/60_tsangan_hhkb/bcat/readme.md
  28. 4 9
      layouts/community/65_ansi_blocker_split_bs/bcat/keymap.c
  29. 1 1
      layouts/community/65_ansi_blocker_split_bs/bcat/readme.md
  30. 5 4
      layouts/community/split_3x6_3/bcat/config.h
  31. 17 16
      layouts/community/split_3x6_3/bcat/keymap.c
  32. 1 1
      layouts/community/split_3x6_3/bcat/readme.md
  33. 4 0
      layouts/community/split_3x6_3/bcat/rules.mk
  34. 6 4
      users/bcat/bcat.c
  35. 35 1
      users/bcat/bcat.h
  36. 216 0
      users/bcat/bcat_oled.c
  37. 55 0
      users/bcat/bcat_oled.h
  38. 73 0
      users/bcat/bcat_oled_pet.h
  39. 134 0
      users/bcat/bcat_oled_pet_isda.c
  40. 168 0
      users/bcat/bcat_oled_pet_luna.c
  41. 22 0
      users/bcat/bcat_rgblight.c
  42. 51 0
      users/bcat/compile.sh
  43. 75 4
      users/bcat/config.h
  44. 2 0
      users/bcat/readme.md
  45. 37 6
      users/bcat/rules.mk

+ 9 - 10
keyboards/9key/keymaps/bcat/keymap.c

@@ -16,22 +16,21 @@
 
 #include QMK_KEYBOARD_H
 
-enum layer {
-    LAYER_DEFAULT,
-    LAYER_FUNCTION,
-};
+#include "bcat.h"
 
-#define LY_FUNC MO(LAYER_FUNCTION)
-#define KY_LOCK LCA(KC_L) /* Cinnamon lock screen */
+#define KY_LOCK LGUI(KC_L) /* Chrome OS: Lock screen */
+#define KY_MICM LSG(KC_1)  /* Meet Shortcuts: Mute mic */
+#define KY_MICU LSG(KC_2)  /* Meet Shortcuts: Unmute mic */
+#define KY_RHAND LSG(KC_3) /* Meet Shortcuts: Raise/lower hand */
 
 const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
     // clang-format off
     [LAYER_DEFAULT] = LAYOUT(
-        KC_MPLY,  KC_VOLU,  KC_MSTP,
-        KC_MPRV,  KC_VOLD,  KC_MNXT,
-        KY_LOCK,  KC_MUTE,  LY_FUNC
+        KC_MPLY,  KC_VOLU,  KY_RHAND,
+        KY_LOCK,  KC_VOLD,  KY_MICU,
+        LY_FN1,   KC_MUTE,  KY_MICM
     ),
-    [LAYER_FUNCTION] = LAYOUT(
+    [LAYER_FUNCTION_1] = LAYOUT(
         EEP_RST,  _______,  RESET,
         _______,  _______,  _______,
         _______,  _______,  _______

+ 3 - 3
keyboards/9key/keymaps/bcat/readme.md

@@ -1,5 +1,5 @@
 # bcat's 9-Key layout
 
-This is a super simple PCB-mount macropad with nine keys, used at work for
-media keys and quick access to screen lock on Linux (Cinnamon desktop
-environment).
+This is a super simple PCB-mount macropad with nine keys, used on my
+work-from-home Chromebox for media/volume control and to activate some global
+shortcuts for Google Meet.

+ 2 - 0
keyboards/eco/keymaps/bcat/config.h

@@ -16,6 +16,8 @@
 
 #pragma once
 
+#define BCAT_ORTHO_LAYERS
+
 /* WS2812B RGB LED strip hand wired to Elite-C. */
 #define RGB_DI_PIN B7
 #define RGBLED_NUM 15

+ 1 - 18
keyboards/eco/keymaps/bcat/keymap.c

@@ -18,21 +18,6 @@
 
 #include "bcat.h"
 
-enum layer {
-    LAYER_DEFAULT,
-    LAYER_LOWER,
-    LAYER_RAISE,
-    LAYER_ADJUST,
-};
-
-#define LY_LWR MO(LAYER_LOWER)
-#define LY_RSE MO(LAYER_RAISE)
-
-#define KY_CSPC LCTL(KC_SPC)
-#define KY_ZMIN LCTL(KC_EQL)
-#define KY_ZMOUT LCTL(KC_MINS)
-#define KY_ZMRST LCTL(KC_0)
-
 const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
     // clang-format off
     /* Default layer: http://www.keyboard-layout-editor.com/#/gists/2c11371c7a5f7cd08a0132631d3d3281 */
@@ -58,12 +43,10 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
     ),
     /* Adjust layer: http://www.keyboard-layout-editor.com/#/gists/b18aafa0327d7e83eaf485546c067a21 */
     [LAYER_ADJUST] = LAYOUT(
-        _______,  NK_TOGG,  KC_MPLY,  KC_VOLU,  KC_MSTP,  _______,  _______,  _______,  EEP_RST,  RESET,    _______,  _______,  _______,  _______,
+        _______,  _______,  KC_MPLY,  KC_VOLU,  KC_MSTP,  _______,  _______,  _______,  EEP_RST,  RESET,    _______,  _______,  _______,  _______,
         _______,  _______,  KC_MPRV,  KC_VOLD,  KC_MNXT,  _______,  _______,  _______,  RGB_RMOD, RGB_VAD,  RGB_VAI,  RGB_MOD,  RGB_SPI,  _______,
         _______,  _______,  _______,  KC_MUTE,  _______,  _______,  _______,  _______,  RGB_HUI,  RGB_SAD,  RGB_SAI,  RGB_HUD,  RGB_SPD,  _______,
         _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  RGB_TOG,  _______,  _______,  _______,  _______,  _______
     ),
     // clang-format on
 };
-
-layer_state_t layer_state_set_keymap(layer_state_t state) { return update_tri_layer_state(state, LAYER_LOWER, LAYER_RAISE, LAYER_ADJUST); }

+ 1 - 1
keyboards/eco/keymaps/bcat/readme.md

@@ -39,6 +39,6 @@ nothing really useful to bind them to anyway.
 
 ## Adjust layer
 
-![Adjust layer layout](https://i.imgur.com/J2rOshm.png)
+![Adjust layer layout](https://i.imgur.com/63vm0ke.png)
 
 ([KLE](http://www.keyboard-layout-editor.com/#/gists/b18aafa0327d7e83eaf485546c067a21))

+ 4 - 9
keyboards/keebio/bdn9/keymaps/bcat/keymap.c

@@ -16,21 +16,16 @@
 
 #include QMK_KEYBOARD_H
 
-enum layer {
-    LAYER_FIRST,
-    LAYER_SECOND,
-};
-
-#define LY_SECND MO(LAYER_SECOND)
+#include "bcat.h"
 
 const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
     // clang-format off
-    [LAYER_FIRST] = LAYOUT(
-        KC_MUTE,  LY_SECND, BL_BRTG,
+    [LAYER_DEFAULT] = LAYOUT(
+        KC_MUTE,  LY_FN1,   BL_BRTG,
         KC_F4,    KC_F5,    KC_F6,
         KC_F1,    KC_F2,    KC_F3
     ),
-    [LAYER_SECOND] = LAYOUT(
+    [LAYER_FUNCTION_1] = LAYOUT(
         EEP_RST,  _______,  RESET,
         KC_F10,   KC_F11,   KC_F12,
         KC_F7,    KC_F8,    KC_F9

+ 4 - 9
keyboards/keebio/quefrency/keymaps/bcat/keymap.c

@@ -16,12 +16,7 @@
 
 #include QMK_KEYBOARD_H
 
-enum layer {
-    LAYER_DEFAULT,
-    LAYER_FUNCTION,
-};
-
-#define LY_FN MO(LAYER_FUNCTION)
+#include "bcat.h"
 
 const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
     // clang-format off
@@ -31,12 +26,12 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
         KC_TAB,   KC_Q,     KC_W,     KC_E,     KC_R,     KC_T,     KC_Y,     KC_U,     KC_I,     KC_O,     KC_P,     KC_LBRC,  KC_RBRC,  KC_BSPC,            KC_PGUP,
         KC_LCTL,  KC_A,     KC_S,     KC_D,     KC_F,     KC_G,     KC_H,     KC_J,     KC_K,     KC_L,     KC_SCLN,  KC_QUOT,            KC_ENT,             KC_PGDN,
         KC_LSFT,  KC_Z,     KC_X,     KC_C,     KC_V,     KC_B,     KC_N,     KC_M,     KC_COMM,  KC_DOT,   KC_SLSH,  KC_RSFT,                      KC_UP,    KC_END,
-        KC_LCTL,  KC_LGUI,  KC_LALT,  LY_FN,    KC_SPC,                       KC_SPC,   XXXXXXX,  KC_RALT,  LY_FN,    KC_RCTL,            KC_LEFT,  KC_DOWN,  KC_RGHT
+        KC_LCTL,  KC_LGUI,  KC_LALT,  LY_FN1,   KC_SPC,                       KC_SPC,   XXXXXXX,  KC_RALT,  LY_FN1,   KC_RCTL,            KC_LEFT,  KC_DOWN,  KC_RGHT
     ),
     /* Function layer: http://www.keyboard-layout-editor.com/#/gists/59636898946da51f91fb290f8e078b4d */
-    [LAYER_FUNCTION] = LAYOUT_65(
+    [LAYER_FUNCTION_1] = LAYOUT_65(
         _______,  KC_F1,    KC_F2,    KC_F3,    KC_F4,    KC_F5,    KC_F6,    KC_F7,    KC_F8,    KC_F9,    KC_F10,   KC_F11,   KC_F12,   KC_INS,   KC_DEL,   RGB_HUI,
-        KC_CAPS,  NK_TOGG,  KC_MPLY,  KC_VOLU,  KC_MSTP,  _______,  EEP_RST,  RESET,    KC_PSCR,  KC_SLCK,  KC_PAUS,  _______,  _______,  _______,            RGB_SAI,
+        KC_CAPS,  _______,  KC_MPLY,  KC_VOLU,  KC_MSTP,  _______,  EEP_RST,  RESET,    KC_PSCR,  KC_SLCK,  KC_PAUS,  _______,  _______,  _______,            RGB_SAI,
         _______,  _______,  KC_MPRV,  KC_VOLD,  KC_MNXT,  _______,  _______,  _______,  _______,  _______,  _______,  _______,            RGB_TOG,            RGB_SAD,
         _______,  KC_APP,   _______,  KC_MUTE,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,                      RGB_VAI,  RGB_HUD,
         _______,  _______,  _______,  _______,  _______,                      _______,  _______,  _______,  _______,  _______,            RGB_RMOD, RGB_VAD,  RGB_MOD

+ 1 - 1
keyboards/keebio/quefrency/keymaps/bcat/readme.md

@@ -12,6 +12,6 @@ ESDF cluster), and RGB controls in the function layer (on the arrow/nav keys).
 
 ## Function layer
 
-![Function layer layout](https://i.imgur.com/Fzshl0F.png)
+![Function layer layout](https://i.imgur.com/7oCdN86.png)
 
 ([KLE](http://www.keyboard-layout-editor.com/#/gists/59636898946da51f91fb290f8e078b4d))

+ 2 - 0
keyboards/lily58/keymaps/bcat/config.h

@@ -16,4 +16,6 @@
 
 #pragma once
 
+#define BCAT_ORTHO_LAYERS
+
 #define EE_HANDS

+ 17 - 16
keyboards/lily58/keymaps/bcat/keymap.c

@@ -18,20 +18,9 @@
 
 #include "bcat.h"
 
-enum layer {
-    LAYER_DEFAULT,
-    LAYER_LOWER,
-    LAYER_RAISE,
-    LAYER_ADJUST,
-};
-
-#define LY_LWR MO(LAYER_LOWER)
-#define LY_RSE MO(LAYER_RAISE)
-
-#define KY_CSPC LCTL(KC_SPC)
-#define KY_ZMIN LCTL(KC_EQL)
-#define KY_ZMOUT LCTL(KC_MINS)
-#define KY_ZMRST LCTL(KC_0)
+#if defined(OLED_ENABLE)
+#    include "bcat_oled.h"
+#endif
 
 const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
     // clang-format off
@@ -62,7 +51,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
     /* Adjust layer: http://www.keyboard-layout-editor.com/#/gists/8f6a3f08350a9bbe1d414b22bca4e6c7 */
     [LAYER_ADJUST] = LAYOUT(
         _______,  _______,  _______,  _______,  _______,  _______,                      _______,  _______,  _______,  _______,  _______,  _______,
-        _______,  NK_TOGG,  KC_MPLY,  KC_VOLU,  KC_MSTP,  _______,                      EEP_RST,  RESET,    _______,  _______,  _______,  _______,
+        _______,  _______,  KC_MPLY,  KC_VOLU,  KC_MSTP,  _______,                      EEP_RST,  RESET,    _______,  _______,  _______,  _______,
         _______,  _______,  KC_MPRV,  KC_VOLD,  KC_MNXT,  _______,                      _______,  _______,  _______,  _______,  _______,  _______,
         _______,  _______,  _______,  KC_MUTE,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,
                                       _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______
@@ -70,4 +59,16 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
     // clang-format on
 };
 
-layer_state_t layer_state_set_keymap(layer_state_t state) { return update_tri_layer_state(state, LAYER_LOWER, LAYER_RAISE, LAYER_ADJUST); }
+#if defined(OLED_ENABLE)
+oled_rotation_t oled_init_user(oled_rotation_t rotation) { return is_keyboard_master() ? OLED_ROTATION_270 : OLED_ROTATION_180; }
+
+void oled_task_keymap(const oled_keyboard_state_t *keyboard_state) {
+    render_oled_layers();
+    oled_advance_page(/*clearPageRemainder=*/false);
+    render_oled_indicators(keyboard_state->leds);
+    oled_advance_page(/*clearPageRemainder=*/false);
+    oled_advance_page(/*clearPageRemainder=*/false);
+    render_oled_wpm(keyboard_state->wpm);
+    render_oled_pet(/*col=*/0, /*line=*/12, keyboard_state);
+}
+#endif

+ 1 - 1
keyboards/lily58/keymaps/bcat/readme.md

@@ -37,6 +37,6 @@ back/forward navigation keys.
 
 ## Adjust layer
 
-![Adjust layer layout](https://i.imgur.com/Q3PGsiK.png)
+![Adjust layer layout](https://i.imgur.com/XC2eR2M.png)
 
 ([KLE](http://www.keyboard-layout-editor.com/#/gists/8f6a3f08350a9bbe1d414b22bca4e6c7))

+ 2 - 0
keyboards/lily58/keymaps/bcat/rules.mk

@@ -1 +1,3 @@
+BCAT_OLED_PET = luna
+
 BOOTLOADER = atmel-dfu  # Elite-C

+ 138 - 0
keyboards/reviung53/config.h

@@ -0,0 +1,138 @@
+// Copyright 2021 gtips (@gtips)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "config_common.h"
+
+/* USB Device descriptor parameter */
+#define VENDOR_ID    0x4E94
+#define PRODUCT_ID   0x4E09
+#define DEVICE_VER   0x0001
+#define MANUFACTURER gtips
+#define PRODUCT      reviung53
+
+/* key matrix size */
+#define MATRIX_ROWS 7
+#define MATRIX_COLS 8
+
+/*
+ * Keyboard Matrix Assignments
+ *
+ * Change this to how you wired your keyboard
+ * COLS: AVR pins used for columns, left to right
+ * ROWS: AVR pins used for rows, top to bottom
+ * DIODE_DIRECTION: COL2ROW = COL = Anode (+), ROW = Cathode (-, marked on diode)
+ *                  ROW2COL = ROW = Anode (+), COL = Cathode (-, marked on diode)
+ *
+ */
+#define MATRIX_ROW_PINS { D0, D4, C6, D7, E6, B4, B5 }
+#define MATRIX_COL_PINS { F4, F5, F6, F7, B1, B3, B2, B6 }
+#define UNUSED_PINS
+
+/* COL2ROW, ROW2COL */
+#define DIODE_DIRECTION COL2ROW
+
+/*
+ * Split Keyboard specific options, make sure you have 'SPLIT_KEYBOARD = yes' in your rules.mk, and define SOFT_SERIAL_PIN.
+ */
+// #define SOFT_SERIAL_PIN D0
+
+//#define LED_NUM_LOCK_PIN B0
+//#define LED_CAPS_LOCK_PIN B1
+//#define LED_SCROLL_LOCK_PIN B2
+//#define LED_COMPOSE_PIN B3
+//#define LED_KANA_PIN B4
+
+//#define BACKLIGHT_PIN B7
+//#define BACKLIGHT_LEVELS 3
+//#define BACKLIGHT_BREATHING
+
+#define RGB_DI_PIN D3
+#ifdef RGB_DI_PIN
+#define RGBLED_NUM 10
+#define RGBLIGHT_HUE_STEP 16
+#define RGBLIGHT_SAT_STEP 16
+#define RGBLIGHT_VAL_STEP 16
+#define RGBLIGHT_LIMIT_VAL 255 /* The maximum brightness level */
+#define RGBLIGHT_SLEEP  /* If defined, the RGB lighting will be switched off when the host goes to sleep */
+/*== all animations enable ==*/
+#define RGBLIGHT_ANIMATIONS
+/*== or choose animations ==*/
+//#    define RGBLIGHT_EFFECT_BREATHING
+//#    define RGBLIGHT_EFFECT_RAINBOW_MOOD
+//#    define RGBLIGHT_EFFECT_RAINBOW_SWIRL
+//#    define RGBLIGHT_EFFECT_SNAKE
+//#    define RGBLIGHT_EFFECT_KNIGHT
+//#    define RGBLIGHT_EFFECT_CHRISTMAS
+//#    define RGBLIGHT_EFFECT_STATIC_GRADIENT
+//#    define RGBLIGHT_EFFECT_RGB_TEST
+//#    define RGBLIGHT_EFFECT_ALTERNATING
+/*== customize breathing effect ==*/
+/*==== (DEFAULT) use fixed table instead of exp() and sin() ====*/
+//#    define RGBLIGHT_BREATHE_TABLE_SIZE 256      // 256(default) or 128 or 64
+/*==== use exp() and sin() ====*/
+//#    define RGBLIGHT_EFFECT_BREATHE_CENTER 1.85  // 1 to 2.7
+//#    define RGBLIGHT_EFFECT_BREATHE_MAX    255   // 0 to 255
+#endif
+
+/* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed */
+#define DEBOUNCE 5
+
+/* define if matrix has ghost (lacks anti-ghosting diodes) */
+//#define MATRIX_HAS_GHOST
+
+/* Mechanical locking support. Use KC_LCAP, KC_LNUM or KC_LSCR instead in keymap */
+#define LOCKING_SUPPORT_ENABLE
+/* Locking resynchronize hack */
+#define LOCKING_RESYNC_ENABLE
+
+/* If defined, GRAVE_ESC will always act as ESC when CTRL is held.
+ * This is useful for the Windows task manager shortcut (ctrl+shift+esc).
+ */
+//#define GRAVE_ESC_CTRL_OVERRIDE
+
+/*
+ * Force NKRO
+ *
+ * Force NKRO (nKey Rollover) to be enabled by default, regardless of the saved
+ * state in the bootmagic EEPROM settings. (Note that NKRO must be enabled in the
+ * makefile for this to work.)
+ *
+ * If forced on, NKRO can be disabled via magic key (default = LShift+RShift+N)
+ * until the next keyboard reset.
+ *
+ * NKRO may prevent your keystrokes from being detected in the BIOS, but it is
+ * fully operational during normal computer usage.
+ *
+ * For a less heavy-handed approach, enable NKRO via magic key (LShift+RShift+N)
+ * or via bootmagic (hold SPACE+N while plugging in the keyboard). Once set by
+ * bootmagic, NKRO mode will always be enabled until it is toggled again during a
+ * power-up.
+ *
+ */
+//#define FORCE_NKRO
+
+/*
+ * Feature disable options
+ *  These options are also useful to firmware size reduction.
+ */
+
+/* disable debug print */
+//#define NO_DEBUG
+
+/* disable print */
+//#define NO_PRINT
+
+/* disable action features */
+//#define NO_ACTION_LAYER
+//#define NO_ACTION_TAPPING
+//#define NO_ACTION_ONESHOT
+
+/* disable these deprecated features by default */
+#define NO_ACTION_MACRO
+#define NO_ACTION_FUNCTION
+
+/* Bootmagic Lite key configuration */
+//#define BOOTMAGIC_LITE_ROW 0
+//#define BOOTMAGIC_LITE_COLUMN 0

+ 68 - 0
keyboards/reviung53/info.json

@@ -0,0 +1,68 @@
+{
+    "keyboard_name": "reviung53",
+    "url": "",
+    "maintainer": "gtips",
+    "layouts": {
+        "LAYOUT_reviung53": {
+            "layout": [
+                {"label": "k02", "x":2.25,  "y":0},
+                {"label": "k03", "x":3.5,   "y":0},
+                {"label": "k04", "x":4.5,   "y":0},
+                {"label": "k05", "x":5.5,   "y":0},
+                {"label": "k06", "x":6.5,   "y":0},
+                {"label": "k07", "x":7.75,  "y":0},
+                {"label": "k40", "x":8.75,  "y":0},
+                {"label": "k41", "x":9.75,  "y":0},
+                {"label": "k42", "x":10.75, "y":0},
+                {"label": "k43", "x":12,    "y":0},
+
+                {"label": "k10", "x":0,     "y":1.25, "w":1.5},
+                {"label": "k11", "x":1.5,   "y":1.25},
+                {"label": "k12", "x":2.5,   "y":1.25},
+                {"label": "k13", "x":3.5,   "y":1.25},
+                {"label": "k14", "x":4.5,   "y":1.25},
+                {"label": "k15", "x":5.5,   "y":1.25},
+                {"label": "k16", "x":6.5,   "y":1.25},
+                {"label": "k17", "x":7.5,   "y":1.25},
+                {"label": "k44", "x":8.5,   "y":1.25},
+                {"label": "k45", "x":9.5,   "y":1.25},
+                {"label": "k46", "x":10.5,  "y":1.25},
+                {"label": "k47", "x":11.5,  "y":1.25, "w":1.5},
+
+                {"label": "k20", "x":0,     "y":2.25, "w":1.75},
+                {"label": "k21", "x":1.75,  "y":2.25},
+                {"label": "k22", "x":2.75,  "y":2.25},
+                {"label": "k23", "x":3.75,  "y":2.25},
+                {"label": "k24", "x":4.75,  "y":2.25},
+                {"label": "k25", "x":5.75,  "y":2.25},
+                {"label": "k26", "x":6.75,  "y":2.25},
+                {"label": "k27", "x":7.75,  "y":2.25},
+                {"label": "k50", "x":8.75,  "y":2.25},
+                {"label": "k51", "x":9.75,  "y":2.25},
+                {"label": "k52", "x":10.75, "y":2.25},
+                {"label": "k53", "x":11.75, "y":2.25, "w":1.25},
+
+                {"label": "k30", "x":0,     "y":3.25, "w":2.25},
+                {"label": "k31", "x":2.25,  "y":3.25},
+                {"label": "k32", "x":3.25,  "y":3.25},
+                {"label": "k33", "x":4.25,  "y":3.25},
+                {"label": "k34", "x":5.25,  "y":3.25},
+                {"label": "k35", "x":6.25,  "y":3.25},
+                {"label": "k36", "x":7.25,  "y":3.25},
+                {"label": "k37", "x":8.25,  "y":3.25},
+                {"label": "k54", "x":9.25,  "y":3.25},
+                {"label": "k55", "x":10.25, "y":3.25},
+                {"label": "k56", "x":11.25, "y":3.25, "w":1.75},
+
+                {"label": "k60", "x":0,     "y":4.25, "w":1.5},
+                {"label": "k61", "x":1.5,   "y":4.25, "w":1.25},
+                {"label": "k62", "x":2.75,  "y":4.25, "w":1.25},
+                {"label": "k63", "x":4,     "y":4.25, "w":2.75},
+                {"label": "k64", "x":6.75,  "y":4.25, "w":2.25},
+                {"label": "k65", "x":9,     "y":4.25, "w":1.25},
+                {"label": "k66", "x":10.25, "y":4.25, "w":1.25},
+                {"label": "k67", "x":11.5,  "y":4.25, "w":1.5}
+            ]
+        }
+    }
+}

+ 55 - 0
keyboards/reviung53/keymaps/default/keymap.c

@@ -0,0 +1,55 @@
+// Copyright 2021 gtips (@gtips)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include QMK_KEYBOARD_H
+
+// Defines names for use in layer keycodes and the keymap
+enum layer_names {
+	_BASE,
+	_LOWER,
+	_RAISE,
+	_ADJUST
+};
+
+#define LOWER  MO(_LOWER)
+#define RAISE  MO(_RAISE)
+#define ADJUST MO(_ADJUST)
+
+#define SF_SS RSFT_T(KC_SLSH)
+#define SP_LO LT(LOWER, KC_SPC)
+#define SP_RA LT(RAISE, KC_SPC)
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+	[_BASE] = LAYOUT_reviung53(
+                        KC_ESC,   KC_F1,    KC_F2,    KC_F3,    KC_F4,    KC_F5,    KC_F6,    KC_F7,    KC_F8,    KC_DEL,
+    KC_TAB,   KC_Q,     KC_W,     KC_E,     KC_R,     KC_T,     KC_Y,     KC_U,     KC_I,     KC_O,     KC_P,     KC_BSPC,
+    KC_CAPS,  KC_A,     KC_S,     KC_D,     KC_F,     KC_G,     KC_H,     KC_J,     KC_K,     KC_L,     KC_SCLN,  KC_ENT,
+    KC_LSFT,  KC_Z,     KC_X,     KC_C,     KC_V,     KC_B,     KC_N,     KC_M,     KC_COMM,  KC_DOT,             SF_SS,
+    KC_LCTL,  KC_LGUI,  KC_LALT,               SP_LO,                   SP_RA,                KC_LALT,  KC_RGUI,  KC_RCTL
+	),
+	[_LOWER] = LAYOUT_reviung53(
+                        KC_GESC,  KC_F9,    KC_F10,   KC_F11,   KC_F12,   KC_INS,   KC_PAUS,  KC_SLCK,  KC_PSCR,  _______,
+    _______,  KC_EXLM,  KC_AT,    KC_HASH,  KC_DLR,   KC_PERC,  KC_CIRC,  KC_AMPR,  KC_ASTR,  KC_LPRN,  KC_RPRN,  _______,
+    _______,  KC_UNDS,  KC_PLUS,  KC_LCBR,  KC_RCBR,  KC_PIPE,  KC_LEFT,  KC_DOWN,  KC_UP,    KC_RGHT,  XXXXXXX,  KC_QUOT,
+    _______,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  KC_HOME,  KC_END,   KC_PGUP,  KC_PGDN,            KC_SLSH,
+    _______,  _______,  _______,               _______,                 _______,              _______,  _______,  _______
+	),
+	[_RAISE] = LAYOUT_reviung53(
+                        KC_TILD,  KC_F9,    KC_F10,   KC_F11,   KC_F12,   KC_MUTE,  KC_VOLU,  KC_VOLD,  KC_MPLY,  _______,
+    _______,  KC_1,     KC_2,     KC_3,     KC_4,     KC_5,     KC_6,     KC_7,     KC_8,     KC_9,     KC_0,     _______,
+    _______,  KC_MINS,  KC_EQL,   KC_LBRC,  KC_RBRC,  KC_BSLS,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  KC_DQUO,
+    _______,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,            KC_QUES,
+    _______,  _______,  _______,               _______,                 _______,              _______,  _______,  _______
+	),
+	[_ADJUST] = LAYOUT_reviung53(
+                        XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  RESET,
+    RGB_VAI,   RGB_SAI, RGB_HUI,  RGB_MOD,  XXXXXXX,  RGB_TOG,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,
+    RGB_VAD,   RGB_SAD, RGB_HUD,  RGB_RMOD, XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,
+    XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,            XXXXXXX,
+    XXXXXXX,  XXXXXXX,  XXXXXXX,               _______,                 _______,              XXXXXXX,  XXXXXXX,  XXXXXXX
+	),
+};
+
+layer_state_t layer_state_set_user(layer_state_t state) {
+  return update_tri_layer_state(state, _LOWER, _RAISE, _ADJUST);
+}

+ 1 - 0
keyboards/reviung53/keymaps/default/readme.md

@@ -0,0 +1 @@
+# The default keymap for reviung53

+ 55 - 0
keyboards/reviung53/keymaps/via/keymap.c

@@ -0,0 +1,55 @@
+// Copyright 2021 gtips (@gtips)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include QMK_KEYBOARD_H
+
+// Defines names for use in layer keycodes and the keymap
+enum layer_names {
+	_BASE,
+	_LOWER,
+	_RAISE,
+	_ADJUST
+};
+
+#define LOWER  MO(_LOWER)
+#define RAISE  MO(_RAISE)
+#define ADJUST MO(_ADJUST)
+
+#define SF_SS RSFT_T(KC_SLSH)
+#define SP_LO LT(LOWER, KC_SPC)
+#define SP_RA LT(RAISE, KC_SPC)
+
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+	[_BASE] = LAYOUT_reviung53(
+                        KC_ESC,   KC_F1,    KC_F2,    KC_F3,    KC_F4,    KC_F5,    KC_F6,    KC_F7,    KC_F8,    KC_DEL,
+    KC_TAB,   KC_Q,     KC_W,     KC_E,     KC_R,     KC_T,     KC_Y,     KC_U,     KC_I,     KC_O,     KC_P,     KC_BSPC,
+    KC_CAPS,  KC_A,     KC_S,     KC_D,     KC_F,     KC_G,     KC_H,     KC_J,     KC_K,     KC_L,     KC_SCLN,  KC_ENT,
+    KC_LSFT,  KC_Z,     KC_X,     KC_C,     KC_V,     KC_B,     KC_N,     KC_M,     KC_COMM,  KC_DOT,             SF_SS,
+    KC_LCTL,  KC_LGUI,  KC_LALT,               SP_LO,                   SP_RA,                KC_LALT,  KC_RGUI,  KC_RCTL
+	),
+	[_LOWER] = LAYOUT_reviung53(
+                        KC_GESC,  KC_F9,    KC_F10,   KC_F11,   KC_F12,   KC_INS,   KC_PAUS,  KC_SLCK,  KC_PSCR,  _______,
+    _______,  KC_EXLM,  KC_AT,    KC_HASH,  KC_DLR,   KC_PERC,  KC_CIRC,  KC_AMPR,  KC_ASTR,  KC_LPRN,  KC_RPRN,  _______,
+    _______,  KC_UNDS,  KC_PLUS,  KC_LCBR,  KC_RCBR,  KC_PIPE,  KC_LEFT,  KC_DOWN,  KC_UP,    KC_RGHT,  XXXXXXX,  KC_QUOT,
+    _______,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  KC_HOME,  KC_END,   KC_PGUP,  KC_PGDN,            KC_SLSH,
+    _______,  _______,  _______,               _______,                 _______,              _______,  _______,  _______
+	),
+	[_RAISE] = LAYOUT_reviung53(
+                        KC_TILD,  KC_F9,    KC_F10,   KC_F11,   KC_F12,   KC_MUTE,  KC_VOLU,  KC_VOLD,  KC_MPLY,  _______,
+    _______,  KC_1,     KC_2,     KC_3,     KC_4,     KC_5,     KC_6,     KC_7,     KC_8,     KC_9,     KC_0,     _______,
+    _______,  KC_MINS,  KC_EQL,   KC_LBRC,  KC_RBRC,  KC_BSLS,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  KC_DQUO,
+    _______,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,            KC_QUES,
+    _______,  _______,  _______,               _______,                 _______,              _______,  _______,  _______
+	),
+	[_ADJUST] = LAYOUT_reviung53(
+                        XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  RESET,
+    RGB_VAI,   RGB_SAI, RGB_HUI,  RGB_MOD,  XXXXXXX,  RGB_TOG,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,
+    RGB_VAD,   RGB_SAD, RGB_HUD,  RGB_RMOD, XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,
+    XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,  XXXXXXX,            XXXXXXX,
+    XXXXXXX,  XXXXXXX,  XXXXXXX,               _______,                 _______,              XXXXXXX,  XXXXXXX,  XXXXXXX
+	),
+};
+
+layer_state_t layer_state_set_user(layer_state_t state) {
+  return update_tri_layer_state(state, _LOWER, _RAISE, _ADJUST);
+}

+ 3 - 0
keyboards/reviung53/keymaps/via/readme.md

@@ -0,0 +1,3 @@
+# The default keymap for reviung53
+
+For use with VIA configurator and compatible keymap editors.  

+ 1 - 0
keyboards/reviung53/keymaps/via/rules.mk

@@ -0,0 +1 @@
+VIA_ENABLE = yes

+ 27 - 0
keyboards/reviung53/readme.md

@@ -0,0 +1,27 @@
+# reviung53
+
+![reviung53](https://github.com/gtips/reviung/blob/master/reviung53/image/reviung53-04.jpg) 
+
+The REVIUNG53 is 53-key keyboard.
+
+* Keyboard Maintainer: [gtips](https://github.com/gtips)
+* Hardware Supported: REVIUNG53 PCB. 
+* Hardware Availability: [PCB & Case Data](https://github.com/gtips/reviung/tree/master/reviung53) 
+
+Make example for this keyboard (after setting up your build environment):
+
+    make reviung53:default
+
+Flashing example for this keyboard:
+
+    make reviung53:default:flash
+
+See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).
+
+## Bootloader
+
+Enter the bootloader in 3 ways:
+
+* **Bootmagic reset**: Hold down the key at (0,0) in the matrix (usually the top left key or Escape) and plug in the keyboard
+* **Physical reset button**: Briefly press the button on the back of the PCB - some may have pads you must short instead
+* **Keycode in layout**: Press the key mapped to `RESET` if it is available

+ 4 - 0
keyboards/reviung53/reviung53.c

@@ -0,0 +1,4 @@
+// Copyright 2021 gtips (@gtips)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "reviung53.h"

+ 30 - 0
keyboards/reviung53/reviung53.h

@@ -0,0 +1,30 @@
+// Copyright 2021 gtips (@gtips)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "quantum.h"
+
+/* This is a shortcut to help you visually see your layout.
+ *
+ * The first section contains all of the arguments representing the physical
+ * layout of the board and position of the keys.
+ *
+ * The second converts the arguments into a two-dimensional array which
+ * represents the switch matrix.
+ */
+#define LAYOUT_reviung53( \
+              K02, K03, K04, K05, K06, K07, K40, K41, K42, K43,  \
+    K10, K11, K12, K13, K14, K15, K16, K17, K44, K45, K46, K47, \
+    K20, K21, K22, K23, K24, K25, K26, K27, K50, K51, K52, K53, \
+    K30, K31, K32, K33, K34, K35, K36, K37, K54, K55, K56,\
+    K60, K61, K62, K63, K64, K65, K66, K67 \
+) { \
+    { KC_NO, KC_NO, K02,   K03,   K04,   K05,   K06,   K07 }, \
+    { K10,   K11,   K12,   K13,   K14,   K15,   K16,   K17 }, \
+    { K20,   K21,   K22,   K23,   K24,   K25,   K26,   K27 }, \
+    { K30,   K31,   K32,   K33,   K34,   K35,   K36,   K37 }, \
+    { K40,   K41,   K42,   K43,   K44,   K45,   K46,   K47 }, \
+    { K50,   K51,   K52,   K53,   K54,   K55,   K56,   KC_NO }, \
+    { K60,   K61,   K62,   K63,   K64,   K65,   K66,   K67 } \
+}

+ 19 - 0
keyboards/reviung53/rules.mk

@@ -0,0 +1,19 @@
+# MCU name
+MCU = atmega32u4
+
+# Bootloader selection
+BOOTLOADER = caterina
+
+# Build Options
+#   change yes to no to disable
+#
+BOOTMAGIC_ENABLE = yes      # Enable Bootmagic Lite
+MOUSEKEY_ENABLE = no        # Mouse keys
+EXTRAKEY_ENABLE = yes       # Audio control and System control
+CONSOLE_ENABLE = no         # Console for debug
+COMMAND_ENABLE = no         # Commands for debug and configuration
+# if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
+NKRO_ENABLE = no            # USB Nkey Rollover
+BACKLIGHT_ENABLE = no       # Enable keyboard backlight functionality
+RGBLIGHT_ENABLE = yes       # Enable keyboard RGB underglow
+AUDIO_ENABLE = no           # Audio output

+ 2 - 9
layouts/community/60_ansi_split_bs_rshift/bcat/keymap.c

@@ -16,14 +16,7 @@
 
 #include QMK_KEYBOARD_H
 
-enum layer {
-    LAYER_DEFAULT,
-    LAYER_FUNCTION_1,
-    LAYER_FUNCTION_2,
-};
-
-#define LY_FN1 MO(LAYER_FUNCTION_1)
-#define LY_FN2 MO(LAYER_FUNCTION_2)
+#include "bcat.h"
 
 const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
     // clang-format off
@@ -48,7 +41,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
     /* Function 2 layer: http://www.keyboard-layout-editor.com/#/gists/6e1068e4f91bbacccaf5ac0acbeec79c */
     [LAYER_FUNCTION_2] = LAYOUT_60_ansi_split_bs_rshift(
         _______,  KC_F1,    KC_F2,    KC_F3,    KC_F4,    KC_F5,    KC_F6,    KC_F7,    KC_F8,    KC_F9,    KC_F10,   KC_F11,   KC_F12,   KC_INS,   KC_DEL,
-        _______,  NK_TOGG,  KC_MPLY,  KC_VOLU,  KC_MSTP,  BL_BRTG,  EEP_RST,  RESET,    _______,  _______,  _______,  RGB_VAI,  _______,  _______,
+        _______,  _______,  KC_MPLY,  KC_VOLU,  KC_MSTP,  BL_BRTG,  EEP_RST,  RESET,    _______,  _______,  _______,  RGB_VAI,  _______,  _______,
         _______,  _______,  KC_MPRV,  KC_VOLD,  KC_MNXT,  BL_INC,   _______,  RGB_SPI,  RGB_HUI,  RGB_SAI,  RGB_RMOD, RGB_MOD,            RGB_TOG,
         _______,  _______,  _______,  KC_MUTE,  _______,  BL_DEC,   _______,  RGB_SPD,  RGB_HUD,  RGB_SAD,  RGB_VAD,  _______,                      _______,
         _______,  _______,  _______,                                _______,                                          _______,  _______,  _______,  _______

+ 1 - 1
layouts/community/60_ansi_split_bs_rshift/bcat/readme.md

@@ -19,6 +19,6 @@ layout](https://github.com/qmk/qmk_firmware/tree/master/layouts/community/60_tsa
 
 ## Function 2 layer
 
-![Function 2 layer layout](https://i.imgur.com/vJaCzVo.png)
+![Function 2 layer layout](https://i.imgur.com/DW03vvJ.png)
 
 ([KLE](http://www.keyboard-layout-editor.com/#/gists/6e1068e4f91bbacccaf5ac0acbeec79c))

+ 2 - 9
layouts/community/60_tsangan_hhkb/bcat/keymap.c

@@ -16,14 +16,7 @@
 
 #include QMK_KEYBOARD_H
 
-enum layer {
-    LAYER_DEFAULT,
-    LAYER_FUNCTION_1,
-    LAYER_FUNCTION_2,
-};
-
-#define LY_FN1 MO(LAYER_FUNCTION_1)
-#define LY_FN2 MO(LAYER_FUNCTION_2)
+#include "bcat.h"
 
 const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
     // clang-format off
@@ -46,7 +39,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
     /* Function 2 layer: http://www.keyboard-layout-editor.com/#/gists/65ac939caec878401603bc36290852d4 */
     [LAYER_FUNCTION_2] = LAYOUT_60_tsangan_hhkb(
         _______,  KC_F1,    KC_F2,    KC_F3,    KC_F4,    KC_F5,    KC_F6,    KC_F7,    KC_F8,    KC_F9,    KC_F10,   KC_F11,   KC_F12,   KC_INS,   KC_DEL,
-        _______,  NK_TOGG,  KC_MPLY,  KC_VOLU,  KC_MSTP,  BL_BRTG,  EEP_RST,  RESET,    _______,  _______,  _______,  RGB_VAI,  _______,  _______,
+        _______,  _______,  KC_MPLY,  KC_VOLU,  KC_MSTP,  BL_BRTG,  EEP_RST,  RESET,    _______,  _______,  _______,  RGB_VAI,  _______,  _______,
         _______,  _______,  KC_MPRV,  KC_VOLD,  KC_MNXT,  BL_INC,   _______,  RGB_SPI,  RGB_HUI,  RGB_SAI,  RGB_RMOD, RGB_MOD,            RGB_TOG,
         _______,  _______,  _______,  KC_MUTE,  _______,  BL_DEC,   _______,  RGB_SPD,  RGB_HUD,  RGB_SAD,  RGB_VAD,  _______,                      _______,
         _______,  _______,  _______,                                _______,                                          _______,  _______,            _______

+ 1 - 1
layouts/community/60_tsangan_hhkb/bcat/readme.md

@@ -39,6 +39,6 @@ and/or blockers, so there aren't switches installed in those positions.
 
 ## Function 2 layer
 
-![Function 2 layer layout](https://i.imgur.com/vdNpFae.png)
+![Function 2 layer layout](https://i.imgur.com/4Jdw9eL.png)
 
 ([KLE](http://www.keyboard-layout-editor.com/#/gists/65ac939caec878401603bc36290852d4))

+ 4 - 9
layouts/community/65_ansi_blocker_split_bs/bcat/keymap.c

@@ -16,12 +16,7 @@
 
 #include QMK_KEYBOARD_H
 
-enum layer {
-    LAYER_DEFAULT,
-    LAYER_FUNCTION,
-};
-
-#define LY_FN MO(LAYER_FUNCTION)
+#include "bcat.h"
 
 const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
     // clang-format off
@@ -31,13 +26,13 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
         KC_TAB,   KC_Q,     KC_W,     KC_E,     KC_R,     KC_T,     KC_Y,     KC_U,     KC_I,     KC_O,     KC_P,     KC_LBRC,  KC_RBRC,  KC_BSPC,            KC_PGUP,
         KC_LCTL,  KC_A,     KC_S,     KC_D,     KC_F,     KC_G,     KC_H,     KC_J,     KC_K,     KC_L,     KC_SCLN,  KC_QUOT,            KC_ENT,             KC_PGDN,
         KC_LSFT,  KC_Z,     KC_X,     KC_C,     KC_V,     KC_B,     KC_N,     KC_M,     KC_COMM,  KC_DOT,   KC_SLSH,  KC_RSFT,                      KC_UP,    KC_END,
-        KC_LCTL,  KC_LGUI,  KC_LALT,                      KC_SPC,                                 KC_RALT,  LY_FN,                        KC_LEFT,  KC_DOWN,  KC_RGHT
+        KC_LCTL,  KC_LGUI,  KC_LALT,                      KC_SPC,                                 KC_RALT,  LY_FN1,                       KC_LEFT,  KC_DOWN,  KC_RGHT
     ),
 
     /* Function layer: http://www.keyboard-layout-editor.com/#/gists/f29128427f674c43777f045e363d1b44 */
-    [LAYER_FUNCTION] = LAYOUT_65_ansi_blocker_split_bs(
+    [LAYER_FUNCTION_1] = LAYOUT_65_ansi_blocker_split_bs(
         _______,  KC_F1,    KC_F2,    KC_F3,    KC_F4,    KC_F5,    KC_F6,    KC_F7,    KC_F8,    KC_F9,    KC_F10,   KC_F11,   KC_F12,   KC_INS,   KC_DEL,   _______,
-        KC_CAPS,  NK_TOGG,  KC_MPLY,  KC_VOLU,  KC_MSTP,  _______,  EEP_RST,  RESET,    KC_PSCR,  KC_SLCK,  KC_PAUS,  _______,  _______,  _______,            _______,
+        KC_CAPS,  _______,  KC_MPLY,  KC_VOLU,  KC_MSTP,  _______,  EEP_RST,  RESET,    KC_PSCR,  KC_SLCK,  KC_PAUS,  _______,  _______,  _______,            _______,
         _______,  _______,  KC_MPRV,  KC_VOLD,  KC_MNXT,  _______,  _______,  _______,  _______,  _______,  _______,  _______,            _______,            _______,
         _______,  KC_APP,   _______,  KC_MUTE,  _______,  _______,  _______,  _______,  _______,  _______,  _______,  _______,                      _______,  _______,
         _______,  _______,  _______,                      _______,                                _______,  _______,                      _______,  _______,  _______

+ 1 - 1
layouts/community/65_ansi_blocker_split_bs/bcat/readme.md

@@ -12,6 +12,6 @@ keys, an HHKB-style (split) backspace, and media controls in the function layer
 
 ## Function layer
 
-![Function layer layout](https://i.imgur.com/CsxfVfd.png)
+![Function layer layout](https://i.imgur.com/s4hS9ZK.png)
 
 ([KLE](http://www.keyboard-layout-editor.com/#/gists/f29128427f674c43777f045e363d1b44))

+ 5 - 4
layouts/community/split_3x6_3/bcat/config.h

@@ -16,12 +16,13 @@
 
 #pragma once
 
+#define BCAT_ORTHO_LAYERS
+
 #if defined(KEYBOARD_crkbd_rev1)
 #    define EE_HANDS
 
-#    if defined(RGB_MATRIX_ENABLE)
-/* Limit max RGB LED current to avoid tripping controller fuse. */
-#        undef RGB_MATRIX_MAXIMUM_BRIGHTNESS
-#        define RGB_MATRIX_MAXIMUM_BRIGHTNESS 150
+#    if defined(OLED_ENABLE)
+#        undef OLED_FONT_H
+#        define OLED_FONT_H "lib/glcdfont.c"
 #    endif
 #endif

+ 17 - 16
layouts/community/split_3x6_3/bcat/keymap.c

@@ -18,20 +18,9 @@
 
 #include "bcat.h"
 
-enum layer {
-    LAYER_DEFAULT,
-    LAYER_LOWER,
-    LAYER_RAISE,
-    LAYER_ADJUST,
-};
-
-#define LY_LWR MO(LAYER_LOWER)
-#define LY_RSE MO(LAYER_RAISE)
-
-#define KY_CSPC LCTL(KC_SPC)
-#define KY_ZMIN LCTL(KC_EQL)
-#define KY_ZMOUT LCTL(KC_MINS)
-#define KY_ZMRST LCTL(KC_0)
+#if defined(OLED_ENABLE)
+#    include "bcat_oled.h"
+#endif
 
 const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
     // clang-format off
@@ -58,7 +47,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
     ),
     /* Adjust layer: http://www.keyboard-layout-editor.com/#/gists/77e7572e077b36a23eb2086017e16fee */
     [LAYER_ADJUST] = LAYOUT_split_3x6_3(
-        _______,  NK_TOGG,  KC_MPLY,  KC_VOLU,  KC_MSTP,  _______,                      EEP_RST,  RESET,    _______,  _______,  _______,  _______,
+        _______,  _______,  KC_MPLY,  KC_VOLU,  KC_MSTP,  _______,                      EEP_RST,  RESET,    _______,  _______,  _______,  _______,
         _______,  _______,  KC_MPRV,  KC_VOLD,  KC_MNXT,  _______,                      RGB_RMOD, RGB_VAD,  RGB_VAI,  RGB_MOD,  RGB_SPI,  _______,
         _______,  _______,  _______,  KC_MUTE,  _______,  _______,                      RGB_HUI,  RGB_SAD,  RGB_SAI,  RGB_HUD,  RGB_SPD,  _______,
                                                 _______,  _______,  _______,  RGB_TOG,  _______,  _______
@@ -66,4 +55,16 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
     // clang-format on
 };
 
-layer_state_t layer_state_set_keymap(layer_state_t state) { return update_tri_layer_state(state, LAYER_LOWER, LAYER_RAISE, LAYER_ADJUST); }
+#if defined(OLED_ENABLE)
+oled_rotation_t oled_init_user(oled_rotation_t rotation) { return is_keyboard_master() ? OLED_ROTATION_270 : OLED_ROTATION_180; }
+
+void oled_task_keymap(const oled_keyboard_state_t *keyboard_state) {
+    render_oled_layers();
+    oled_advance_page(/*clearPageRemainder=*/false);
+    render_oled_indicators(keyboard_state->leds);
+    oled_advance_page(/*clearPageRemainder=*/false);
+    oled_advance_page(/*clearPageRemainder=*/false);
+    render_oled_wpm(keyboard_state->wpm);
+    render_oled_pet(/*col=*/0, /*line=*/12, keyboard_state);
+}
+#endif

+ 1 - 1
layouts/community/split_3x6_3/bcat/readme.md

@@ -117,7 +117,7 @@ better location.
 
 ## Adjust layer
 
-![Adjust layer layout](https://i.imgur.com/fZouko5.png)
+![Adjust layer layout](https://i.imgur.com/Q4rN6cQ.png)
 
 ([KLE](http://www.keyboard-layout-editor.com/#/gists/77e7572e077b36a23eb2086017e16fee))
 

+ 4 - 0
layouts/community/split_3x6_3/bcat/rules.mk

@@ -1,5 +1,9 @@
+BCAT_OLED_PET = luna
+
 ifeq ($(strip $(KEYBOARD)), crkbd/rev1)
 	BOOTLOADER = atmel-dfu  # Elite-C
 
+	OLED_ENABLE = yes  # dual 128x32 OLED screens
+	OLED_DRIVER = SSD1306
 	RGB_MATRIX_ENABLE = yes  # per-key RGB and underglow
 endif

+ 6 - 4
users/bcat/bcat.c

@@ -16,16 +16,15 @@
 
 #include "bcat.h"
 
-#if defined(RGBLIGHT_ENABLE)
-/* Adjust RGB static hue ranges for shorter gradients than default. */
-const uint8_t RGBLED_GRADIENT_RANGES[] PROGMEM = {255, 127, 63, 31, 15};
-#endif
+#include "quantum.h"
 
 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;
     }
@@ -51,6 +50,9 @@ __attribute__((weak)) layer_state_t layer_state_set_keymap(layer_state_t state)
 
 layer_state_t layer_state_set_user(layer_state_t state) {
     state = layer_state_set_keymap(state);
+#if defined(BCAT_ORTHO_LAYERS)
+    state = update_tri_layer_state(state, LAYER_LOWER, LAYER_RAISE, LAYER_ADJUST);
+#endif
     if (alt_tab_layer >= 0 && !layer_state_cmp(state, alt_tab_layer)) {
         unregister_code(KC_LALT);
         alt_tab_layer = -1;

+ 35 - 1
users/bcat/bcat.h

@@ -16,9 +16,43 @@
 
 #pragma once
 
-#include "quantum.h"
+#include <stdbool.h>
 
+#include "keymap.h"
+
+/* Layer numbers shared across keymaps. */
+enum user_layer {
+    /* Base layers: */
+    LAYER_DEFAULT,
+
+#if defined(BCAT_ORTHO_LAYERS)
+    /* Function layers for ortho (and ergo) boards: */
+    LAYER_LOWER,
+    LAYER_RAISE,
+    LAYER_ADJUST,
+#else
+    /* Function layers for traditional boards: */
+    LAYER_FUNCTION_1,
+    LAYER_FUNCTION_2,
+#endif
+};
+
+/* Custom keycodes shared across keymaps. */
 enum user_keycode {
     MC_ALTT = SAFE_RANGE,
     KEYMAP_SAFE_RANGE,
 };
+
+/* Keycode aliases shared across keymaps. */
+#define KY_CSPC LCTL(KC_SPC)
+#define KY_ZMIN LCTL(KC_EQL)
+#define KY_ZMOUT LCTL(KC_MINS)
+#define KY_ZMRST LCTL(KC_0)
+
+#if defined(BCAT_ORTHO_LAYERS)
+#    define LY_LWR MO(LAYER_LOWER)
+#    define LY_RSE MO(LAYER_RAISE)
+#else
+#    define LY_FN1 MO(LAYER_FUNCTION_1)
+#    define LY_FN2 MO(LAYER_FUNCTION_2)
+#endif

+ 216 - 0
users/bcat/bcat_oled.c

@@ -0,0 +1,216 @@
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "bcat_oled.h"
+
+#include "quantum.h"
+#include "bcat.h"
+
+#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
+
+/* Should be overridden by the keymap to render the OLED contents. For split
+ * keyboards, this function is only called on the master side.
+ */
+__attribute__((weak)) void oled_task_keymap(const oled_keyboard_state_t *keyboard_state) {}
+
+bool oled_task_user(void) {
+#if defined(SPLIT_KEYBOARD)
+    if (is_keyboard_master()) {
+#endif
+        /* Custom OLED timeout implementation that only considers user activity.
+         * Allows the OLED to turn off in the middle of a continuous animation.
+         */
+        static const uint16_t TIMEOUT_MILLIS = 60000 /* 1 min */;
+
+        if (last_input_activity_elapsed() < TIMEOUT_MILLIS) {
+            if (!is_oled_on()) {
+                oled_on();
+            }
+            oled_keyboard_state_t keyboard_state = {
+                .mods = get_mods(),
+                .leds = host_keyboard_led_state(),
+                .wpm  = get_current_wpm(),
+            };
+            oled_task_keymap(&keyboard_state);
+        } else if (is_oled_on()) {
+            oled_off();
+        }
+#if defined(SPLIT_KEYBOARD)
+    } else {
+        /* Display logo embedded at standard location in the OLED font on the
+         * slave side. By default, this is a "QMK firmware" logo, but many
+         * keyboards substitute their own logo. Occupies 21x3 character cells.
+         *
+         * Since the slave display buffer never changes, we don't need to worry
+         * about oled_render incorrectly turning the OLED on. Instead, we rely
+         * on SPLIT_OLED_ENABLE to propagate OLED on/off status from master.
+         */
+        static const char PROGMEM logo[] = {
+            // clang-format off
+            0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94,
+            0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4,
+            0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4,
+            0x00,
+            // clang-format on
+        };
+
+        oled_write_P(logo, /*invert=*/false);
+    }
+#endif
+
+    return false;
+}
+
+void render_oled_layers(void) {
+    oled_advance_char();
+    oled_advance_char();
+#if defined(BCAT_ORTHO_LAYERS)
+    oled_write_char(IS_LAYER_ON(LAYER_LOWER) ? TRIANGLE_DOWN : ' ', /*invert=*/false);
+    oled_advance_char();
+    oled_write_char(IS_LAYER_ON(LAYER_RAISE) ? TRIANGLE_UP : ' ', /*invert=*/false);
+#else
+    switch (get_highest_layer(layer_state)) {
+        case LAYER_FUNCTION_1:
+            oled_write_P(PSTR("FN1"), /*invert=*/false);
+            break;
+        case LAYER_FUNCTION_2:
+            oled_write_P(PSTR("FN2"), /*invert=*/false);
+            break;
+        default:
+            oled_write_P(PSTR("   "), /*invert=*/false);
+            break;
+    }
+#endif
+}
+
+void render_oled_indicators(led_t leds) {
+    oled_advance_char();
+    oled_advance_char();
+    oled_write_P(leds.num_lock ? PSTR("NUM") : PSTR("   "), /*invert=*/false);
+    oled_advance_char();
+    oled_advance_char();
+    oled_write_P(leds.caps_lock ? PSTR("CAP") : PSTR("   "), /*invert=*/false);
+    oled_advance_char();
+    oled_advance_char();
+    oled_write_P(leds.scroll_lock ? PSTR("SCR") : PSTR("   "), /*invert=*/false);
+}
+
+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)) {
+        oled_advance_char();
+        oled_advance_char();
+        oled_write_P(wpm > 0 ? PSTR("WPM") : PSTR("   "), /*invert=*/false);
+        if (wpm > 0) {
+            oled_advance_char();
+            oled_advance_char();
+            oled_write(get_u8_str(wpm, ' '), /*invert=*/false);
+        } else {
+            oled_advance_page(/*clearPageRemainder=*/true);
+        }
+
+        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) {
+    oled_set_cursor(col, line);
+    if (jumping) {
+        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), oled_pet_frame_bytes());
+    }
+}
+
+void render_oled_pet(uint8_t col, uint8_t line, const oled_keyboard_state_t *keyboard_state) {
+    /* 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 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;
+
+    /* 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 = state_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;
+    }
+
+    /* 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);
+    }
+    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. */
+    if (timer_expired32(timer_read32(), update_timeout)) {
+        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

+ 55 - 0
users/bcat/bcat_oled.h

@@ -0,0 +1,55 @@
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "led.h"
+
+/* Keyboard status passed to the oled_task_keymap function and used by the
+ * various keyboard pet implementations.
+ */
+typedef struct {
+    uint8_t mods;
+    led_t   leds;
+    uint8_t wpm;
+} oled_keyboard_state_t;
+
+/* Note: Functions below assume a vertical OLED that is 32px (5 chars) wide. */
+
+/* Renders layer status at the cursor. Occupies 5x1 character cells. */
+void render_oled_layers(void);
+
+/* Renders LED indicators (Num/Caps/Scroll Lock) at the cursor. Occupies 5x3
+ * character cells.
+ */
+void render_oled_indicators(led_t leds);
+
+/* Renders calculated WPM count at the cursor. Occupies 5x2 character cells. */
+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.
+ */
+void render_oled_pet(uint8_t col, uint8_t line, const oled_keyboard_state_t *keyboard_state);
+#endif

+ 73 - 0
users/bcat/bcat_oled_pet.h

@@ -0,0 +1,73 @@
+/* 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 <http://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 "bcat_oled.h"
+
+/* 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.
+ */
+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
+ * 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 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
+ * pet implementation to draw custom things atop its animation frames.
+ *
+ * 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 redraw);
+
+/* 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);

+ 134 - 0
users/bcat/bcat_oled_pet_isda.c

@@ -0,0 +1,134 @@
+/* Copyright 2018 sparrow666
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* OLED pet "Isda" (animated unicorn) featuring artwork by OpenGameArt user
+ * sparrow666, licensed under GPL v2.0.
+ *
+ * The animation is 32x72 pixels (9 lines tall).
+ *
+ * Runs faster the quicker you type. Shows LED indicator (Num/Caps/Scroll Lock)
+ * status in the bottom-right corner.
+ *
+ * Named after the goddess Ehlonna's personal unicorn in the first D&D campaign
+ * I ever played. :)
+ *
+ * Artwork source: https://opengameart.org/content/unicorn-2
+ */
+
+#include "bcat_oled_pet.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "bcat_oled.h"
+#include "led.h"
+#include "oled_driver.h"
+#include "progmem.h"
+
+#define NUM_FRAMES 4
+#define FRAME_BYTES 288 /* (32 pixel) * (72 pixel) / (8 pixel/byte) */
+
+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; }
+
+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;
+    static const uint8_t  MAX_WPM    = 150;
+    uint8_t               wpm        = keyboard_state->wpm;
+    if (wpm > MAX_WPM) {
+        wpm = MAX_WPM;
+    }
+    return MAX_MILLIS - (MAX_MILLIS - MIN_MILLIS) * wpm / MAX_WPM;
+}
+
+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
+     * previously drawn indicators).
+     */
+    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 + 4);
+        oled_write_char(leds.num_lock ? 'N' : ' ', /*invert=*/false);
+        oled_set_cursor(col + 4, line + 6);
+        oled_write_char(leds.caps_lock ? 'C' : ' ', /*invert=*/false);
+        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) {
+    static const char PROGMEM FRAMES[NUM_FRAMES][FRAME_BYTES] = {
+        // clang-format off
+        {
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0xa0, 0x60, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x88, 0xd0, 0x78, 0x04, 0x28, 0x70, 0x60, 0x90, 0x88, 0xc4, 0x22, 0x19, 0x04, 0x03, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x84, 0x8c, 0x08, 0x01, 0x01, 0x02, 0x02, 0x04, 0x88, 0xf0, 0x00,
+            0xc0, 0xe0, 0xe0, 0x10, 0x10, 0x08, 0x04, 0x02, 0x01, 0xff, 0xff, 0xff, 0x7f, 0x0f, 0x1f, 0x00, 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+            0x00, 0x0f, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0xe0, 0x18, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0xfc, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0x3c, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x01, 0x03, 0xc6, 0x3c, 0x00, 0x80, 0x70, 0x1c, 0x0f, 0x03, 0x0f, 0x3f, 0xff, 0xff, 0xfc, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x80, 0xe0, 0xf8, 0xfe, 0x7f, 0x1f, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x01, 0x0e, 0x30, 0x40, 0x47, 0x4f, 0x77, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        },
+        {
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0xc0, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x10, 0xa0, 0xf0, 0x08, 0x50, 0xe0, 0xc0, 0x20, 0x10, 0x88, 0x44, 0x32, 0x09, 0x06, 0x01, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf8, 0xfe, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x19, 0x11, 0x03, 0x02, 0x04, 0x04, 0x08, 0x10, 0xe0, 0x00,
+            0xc0, 0xe0, 0xe0, 0x10, 0x10, 0x08, 0x04, 0x02, 0x01, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x3f, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x00,
+            0x00, 0x1f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x03, 0x00, 0x00, 0x80, 0xc0, 0x20, 0x10, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0x3c, 0x0f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x01, 0x03, 0xc6, 0x3c, 0x00, 0x80, 0x70, 0x18, 0x0f, 0x03, 0x0f, 0x3f, 0xff, 0xff, 0xfc, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0xc0, 0x38, 0x07, 0xc0, 0x38, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0f, 0x7f, 0xff, 0xff, 0xe0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x21, 0x20, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x0f, 0x0f, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        },
+        {
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0xc0, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x10, 0xa0, 0xf0, 0x08, 0x50, 0xe0, 0xc0, 0x20, 0x10, 0x88, 0x44, 0x32, 0x09, 0x06, 0x01, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf8, 0xfe, 0xff, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x19, 0x11, 0x03, 0x02, 0x04, 0x04, 0x08, 0x10, 0xe0, 0x00,
+            0xc0, 0xc0, 0xc0, 0x20, 0x20, 0x10, 0x08, 0x04, 0x03, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x3f, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x00,
+            0x00, 0x1f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x03, 0x00, 0x00, 0x80, 0xc0, 0x20, 0x10, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x01, 0x03, 0xc3, 0xfe, 0xfe, 0xfc, 0x7c, 0x1c, 0x0c, 0x0c, 0x08, 0x10, 0x60, 0x83, 0x07, 0x18, 0x60, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x71, 0x0e, 0x80, 0x70, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x01, 0x0f, 0x3f, 0x7f, 0x7f, 0x78, 0xe0, 0x90, 0x88, 0x66, 0x11, 0x08, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        },
+        {
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0xa0, 0x60, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x88, 0xd0, 0x78, 0x04, 0x28, 0x70, 0x60, 0x90, 0x88, 0xc4, 0x22, 0x19, 0x04, 0x03, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x84, 0x8c, 0x08, 0x01, 0x01, 0x02, 0x02, 0x04, 0x88, 0xf0, 0x00,
+            0xc0, 0xe0, 0xe0, 0x10, 0x10, 0x08, 0x04, 0x02, 0x01, 0xff, 0xff, 0xff, 0x7f, 0x0f, 0x1f, 0x00, 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
+            0x00, 0x0f, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x80, 0xe0, 0x18, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0xe0, 0xfc, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x01, 0x03, 0xc6, 0xfc, 0xfc, 0xfc, 0x7c, 0x18, 0x08, 0x08, 0x08, 0x30, 0xc0, 0x03, 0x0c, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0xc0, 0xf8, 0xff, 0xff, 0x3f, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x70, 0x80, 0x1f, 0x60, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x18, 0x3e, 0x3f, 0x3f, 0x1f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x0c, 0x09, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        },
+        // clang-format on
+    };
+    return FRAMES[state];
+}

+ 168 - 0
users/bcat/bcat_oled_pet_luna.c

@@ -0,0 +1,168 @@
+/* Copyright 2021 HellSingCoder
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* OLED pet "Luna" (animated doggo) originally by HellSingCoder
+ * (https://www.simonepellegrino.com/) and licensed under GPL v2.0, adapted to
+ * fit the OLED pet framework in bcat's userspace.
+ *
+ * The animation is 32x24 pixels (3 lines tall).
+ *
+ * Walks or runs in response to typing speed. Sneaks when Ctrl is pressed and
+ * barks when Caps Lock is on. Jumps when space is pressed.
+ *
+ * Original source:
+ * https://github.com/qmk/qmk_firmware/blob/6dfe915e26d7147e6c2bed495d3b01cf5b21e6ec/keyboards/sofle/keymaps/helltm/keymap.c
+ */
+
+#include "bcat_oled_pet.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "bcat_oled.h"
+#include "keycode.h"
+#include "progmem.h"
+
+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) */
+
+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; }
+
+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) {
+        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;
+    }
+    luna_state.frame = (luna_state.frame + 1) % NUM_FRAMES;
+    return luna_state.raw;
+}
+
+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) {
+    static const char PROGMEM IDLE_FRAMES[NUM_FRAMES][FRAME_BYTES] = {
+        // clang-format off
+        {
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x1c, 0x02, 0x05, 0x02, 0x24, 0x04, 0x04, 0x02, 0xa9, 0x1e, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x10, 0x08, 0x68, 0x10, 0x08, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x82, 0x7c, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x0c, 0x10, 0x10, 0x20, 0x20, 0x20, 0x28, 0x3e, 0x1c, 0x20, 0x20, 0x3e, 0x0f, 0x11, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        },
+        {
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x1c, 0x02, 0x05, 0x02, 0x24, 0x04, 0x04, 0x02, 0xa9, 0x1e, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x90, 0x08, 0x18, 0x60, 0x10, 0x08, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0e, 0x82, 0x7c, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x0c, 0x10, 0x10, 0x20, 0x20, 0x20, 0x28, 0x3e, 0x1c, 0x20, 0x20, 0x3e, 0x0f, 0x11, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        },
+        // clang-format on
+    };
+    static const char PROGMEM WALK_FRAMES[NUM_FRAMES][FRAME_BYTES] = {
+        // clang-format off
+        {
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x90, 0x90, 0x90, 0xa0, 0xc0, 0x80, 0x80, 0x80, 0x70, 0x08, 0x14, 0x08, 0x90, 0x10, 0x10, 0x08, 0xa4, 0x78, 0x80, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x08, 0xfc, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x18, 0xea, 0x10, 0x0f, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x1c, 0x20, 0x20, 0x3c, 0x0f, 0x11, 0x1f, 0x03, 0x06, 0x18, 0x20, 0x20, 0x3c, 0x0c, 0x12, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        },
+        {
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x20, 0x20, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x10, 0x28, 0x10, 0x20, 0x20, 0x20, 0x10, 0x48, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x20, 0xf8, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x30, 0xd5, 0x20, 0x1f, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x20, 0x30, 0x0c, 0x02, 0x05, 0x09, 0x12, 0x1e, 0x02, 0x1c, 0x14, 0x08, 0x10, 0x20, 0x2c, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        },
+        // clang-format on
+    };
+    static const char PROGMEM RUN_FRAMES[NUM_FRAMES][FRAME_BYTES] = {
+        // clang-format off
+        {
+            0x00, 0x00, 0x00, 0x00, 0xe0, 0x10, 0x08, 0x08, 0xc8, 0xb0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x40, 0x40, 0x3c, 0x14, 0x04, 0x08, 0x90, 0x18, 0x04, 0x08, 0xb0, 0x40, 0x80, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0xc4, 0xa4, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc8, 0x58, 0x28, 0x2a, 0x10, 0x0f, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x09, 0x04, 0x04, 0x04, 0x04, 0x02, 0x03, 0x02, 0x01, 0x01, 0x02, 0x02, 0x04, 0x08, 0x10, 0x26, 0x2b, 0x32, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+        },
+        {
+            0x00, 0x00, 0x00, 0xe0, 0x10, 0x10, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x78, 0x28, 0x08, 0x10, 0x20, 0x30, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x03, 0x04, 0x08, 0x10, 0x11, 0xf9, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0xb0, 0x50, 0x55, 0x20, 0x1f, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x0c, 0x10, 0x20, 0x28, 0x37, 0x02, 0x1e, 0x20, 0x20, 0x18, 0x0c, 0x14, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        },
+        // clang-format on
+    };
+    static const char PROGMEM SNEAK_FRAMES[NUM_FRAMES][FRAME_BYTES] = {
+        // clang-format off
+        {
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x40, 0x40, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x40, 0x40, 0x80, 0x00, 0x80, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x1e, 0x21, 0xf0, 0x04, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, 0x04, 0x04, 0x04, 0x03, 0x01, 0x00, 0x00, 0x09, 0x01, 0x80, 0x80, 0xab, 0x04, 0xf8, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x1c, 0x20, 0x20, 0x3c, 0x0f, 0x11, 0x1f, 0x02, 0x06, 0x18, 0x20, 0x20, 0x38, 0x08, 0x10, 0x18, 0x04, 0x04, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00,
+        },
+        {
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x40, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xa0, 0x20, 0x40, 0x80, 0xc0, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x3e, 0x41, 0xf0, 0x04, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, 0x02, 0x04, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x40, 0x40, 0x55, 0x82, 0x7c, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x20, 0x30, 0x0c, 0x02, 0x05, 0x09, 0x12, 0x1e, 0x04, 0x18, 0x10, 0x08, 0x10, 0x20, 0x28, 0x34, 0x06, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+        },
+        // clang-format on
+    };
+    static const char PROGMEM BARK_FRAMES[NUM_FRAMES][FRAME_BYTES] = {
+        // clang-format off
+        {
+            0x00, 0xc0, 0x20, 0x10, 0xd0, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x40, 0x3c, 0x14, 0x04, 0x08, 0x90, 0x18, 0x04, 0x08, 0xb0, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x03, 0x04, 0x08, 0x10, 0x11, 0xf9, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc8, 0x48, 0x28, 0x2a, 0x10, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x0c, 0x10, 0x20, 0x28, 0x37, 0x02, 0x02, 0x04, 0x08, 0x10, 0x26, 0x2b, 0x32, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        },
+        {
+            0x00, 0xe0, 0x10, 0x10, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x40, 0x40, 0x2c, 0x14, 0x04, 0x08, 0x90, 0x18, 0x04, 0x08, 0xb0, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x03, 0x04, 0x08, 0x10, 0x11, 0xf9, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0x48, 0x28, 0x2a, 0x10, 0x0f, 0x20, 0x4a, 0x09, 0x10,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x0c, 0x10, 0x20, 0x28, 0x37, 0x02, 0x02, 0x04, 0x08, 0x10, 0x26, 0x2b, 0x32, 0x04, 0x05, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        },
+        // clang-format on
+    };
+    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[luna_state.frame];
+    }
+}

+ 22 - 0
users/bcat/bcat_rgblight.c

@@ -0,0 +1,22 @@
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+
+#include "progmem.h"
+
+/* Adjust RGB static hue ranges for shorter gradients than default. */
+const uint8_t RGBLED_GRADIENT_RANGES[] PROGMEM = {255, 127, 63, 31, 15};

+ 51 - 0
users/bcat/compile.sh

@@ -0,0 +1,51 @@
+#!/bin/bash
+
+set -o errexit -o nounset
+
+usage () {
+  printf "\
+usage: ./users/bcat/compile.sh [-c] [-j N]
+
+Compiles all keyboards for which bcat maintains keymaps.
+
+optional arguments:
+  -c                    performs a clean build
+  -j N                  runs N make tasks in parallel
+  -v                    shows verbose output
+"
+}
+
+compile () {
+    local keyboard=$1 layout=${2:-}
+    FORCE_LAYOUT="$layout" SILENT="$opt_silent" make -j "$opt_parallel" "$keyboard":bcat
+}
+
+opt_parallel=1
+opt_silent=true
+
+while getopts :chj:v opt; do
+  case $opt in
+    c) opt_clean=1 ;;
+    j) opt_parallel=$OPTARG ;;
+    v) opt_silent=false ;;
+    h) usage; exit 0 ;;
+    \?) usage >&2; exit 2 ;;
+  esac
+done
+
+if [[ -n ${opt_clean:-} ]]; then
+  SILENT="$opt_silent" make clean
+fi
+
+compile 9key
+compile ai03/polaris 60_tsangan_hhkb
+compile cannonkeys/an_c 60_tsangan_hhkb
+compile cannonkeys/instant60 60_tsangan_hhkb
+compile crkbd/rev1 split_3x6_3
+compile dz60 60_ansi_split_bs_rshift
+compile dz60 60_tsangan_hhkb
+compile eco/rev2
+compile kbdfans/kbd67/hotswap 65_ansi_blocker_split_bs
+compile keebio/bdn9/rev1
+compile keebio/quefrency/rev1
+compile lily58/rev1

+ 75 - 4
users/bcat/config.h

@@ -14,6 +14,12 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+/* Enable NKRO by default. All my devices support this, and it enables me to
+ * dispense with the NK_TOGG key, thus saving firmware space by not compiling
+ * magic keycode support.
+ */
+#define FORCE_NKRO
+
 /* Wait between tap_code register and unregister to fix flaky media keys. */
 #undef TAP_CODE_DELAY
 
@@ -31,6 +37,22 @@
  */
 #define TAPPING_FORCE_HOLD
 
+#if defined(OLED_ENABLE)
+/* The built-in OLED timeout wakes the OLED screen every time the buffer is
+ * updated, even if no user activity has occurred recently. This prevents the
+ * OLED from ever turning off during a continuously running animation. To avoid
+ * this, we disable the default timeout and implement our own in
+ * oled_task_user.
+ */
+#    undef OLED_TIMEOUT
+#    define OLED_DISABLE_TIMEOUT
+
+#    if defined(SPLIT_KEYBOARD)
+/* Sync OLED on/off state between halves of split keyboards. */
+#        define SPLIT_OLED_ENABLE
+#    endif
+#endif
+
 #if defined(RGB_MATRIX_ENABLE)
 /* Turn off per-key RGB when the host goes to sleep. */
 #    define RGB_DISABLE_WHEN_USB_SUSPENDED
@@ -46,9 +68,42 @@
 #    define RGB_MATRIX_VAL_STEP 17
 #    define RGB_MATRIX_SPD_STEP 17
 
-/* Turn on additional RGB animations. */
+/* Enable specific per-key animation modes. */
+#    define ENABLE_RGB_MATRIX_ALPHAS_MODS
+#    define ENABLE_RGB_MATRIX_BAND_PINWHEEL_SAT
+#    define ENABLE_RGB_MATRIX_BAND_PINWHEEL_VAL
+#    define ENABLE_RGB_MATRIX_BAND_SAT
+#    define ENABLE_RGB_MATRIX_BAND_SPIRAL_SAT
+#    define ENABLE_RGB_MATRIX_BAND_SPIRAL_VAL
+#    define ENABLE_RGB_MATRIX_BAND_VAL
+#    define ENABLE_RGB_MATRIX_BREATHING
+#    define ENABLE_RGB_MATRIX_CYCLE_ALL
+#    define ENABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
+#    define ENABLE_RGB_MATRIX_CYCLE_OUT_IN
+#    define ENABLE_RGB_MATRIX_CYCLE_OUT_IN_DUAL
+#    define ENABLE_RGB_MATRIX_CYCLE_PINWHEEL
+#    define ENABLE_RGB_MATRIX_CYCLE_SPIRAL
+#    define ENABLE_RGB_MATRIX_CYCLE_UP_DOWN
+#    define ENABLE_RGB_MATRIX_DUAL_BEACON
+#    define ENABLE_RGB_MATRIX_GRADIENT_LEFT_RIGHT
+#    define ENABLE_RGB_MATRIX_GRADIENT_UP_DOWN
+#    define ENABLE_RGB_MATRIX_HUE_BREATHING
+#    define ENABLE_RGB_MATRIX_HUE_PENDULUM
+#    define ENABLE_RGB_MATRIX_HUE_WAVE
+#    define ENABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
+#    define ENABLE_RGB_MATRIX_PIXEL_FRACTAL
+#    define ENABLE_RGB_MATRIX_PIXEL_RAIN
+#    define ENABLE_RGB_MATRIX_RAINBOW_BEACON
+#    define ENABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
+#    define ENABLE_RGB_MATRIX_RAINBOW_PINWHEELS
+#    define ENABLE_RGB_MATRIX_RAINDROPS
+
+/* Enable additional per-key animation modes that require a copy of the
+ * framebuffer (with accompanying storage cost).
+ */
 #    define RGB_MATRIX_FRAMEBUFFER_EFFECTS
-#    define RGB_MATRIX_KEYPRESSES
+#    define ENABLE_RGB_MATRIX_DIGITAL_RAIN
+#    define ENABLE_RGB_MATRIX_TYPING_HEATMAP
 #endif
 
 #if defined(RGBLIGHT_ENABLE)
@@ -64,8 +119,18 @@
 #    define RGBLIGHT_SAT_STEP 17
 #    define RGBLIGHT_VAL_STEP 17
 
-/* Turn on additional RGB animations. */
-#    define RGBLIGHT_ANIMATIONS
+/* Enable specific underglow animation modes. (Skip TWINKLE because it seems to
+ * be broken on ARM: https://github.com/qmk/qmk_firmware/issues/15345.)
+ */
+#    define RGBLIGHT_EFFECT_ALTERNATING
+#    define RGBLIGHT_EFFECT_BREATHING
+#    define RGBLIGHT_EFFECT_CHRISTMAS
+#    define RGBLIGHT_EFFECT_KNIGHT
+#    define RGBLIGHT_EFFECT_RAINBOW_MOOD
+#    define RGBLIGHT_EFFECT_RAINBOW_SWIRL
+#    define RGBLIGHT_EFFECT_RGB_TEST
+#    define RGBLIGHT_EFFECT_SNAKE
+#    define RGBLIGHT_EFFECT_STATIC_GRADIENT
 #endif
 
 #if defined(BACKLIGHT_ENABLE)
@@ -77,3 +142,9 @@
 
 #    define BACKLIGHT_LEVELS 7
 #endif
+
+/* Turn off unused config options to reduce firmware size. */
+#define LAYER_STATE_8BIT
+#define NO_ACTION_ONESHOT
+#undef LOCKING_RESYNC_ENABLE
+#undef LOCKING_SUPPORT_ENABLE

+ 2 - 0
users/bcat/readme.md

@@ -6,6 +6,8 @@ keyboard-specific keymaps for boards without standard layout support. I derive
 my keymaps from two canonical ones (preferred for typing and gaming,
 respectively).
 
+You can build all keymaps I maintain at once using `./users/bcat/compile.sh`.
+
 ## Canonical keymaps
 
 * [Split 3x6 + 3 thumb

+ 37 - 6
users/bcat/rules.mk

@@ -1,7 +1,10 @@
-SRC += bcat.c
-
-# Enable Bootmagic Lite to consistently reset to bootloader and clear EEPROM.
-BOOTMAGIC_ENABLE = yes      # Enable Bootmagic Lite
+# Enable Bootmagic Lite for keyboards that don't have an easily accessible
+# reset button, but keep it disabled for all others to reduce firmware size.
+ifneq ($(filter $(strip $(KEYBOARD)),ai03/polaris dz60 kbdfans/kbd67/hotswap),)
+	BOOTMAGIC_ENABLE = yes
+else
+	BOOTMAGIC_ENABLE = no
+endif
 
 # Enable media keys on all keyboards.
 EXTRAKEY_ENABLE = yes
@@ -16,21 +19,49 @@ NKRO_ENABLE = yes
 # Enable link-time optimization to reduce binary size.
 LTO_ENABLE = yes
 
-# Disable unused build options on all keyboards.
+# Include common utilities shared across all our keymaps.
+SRC += bcat.c
+
+# Include additional utilities that extend optional QMK features only enabled
+# on some keyboards.
+ifeq ($(strip $(OLED_ENABLE)), yes)
+	SRC += bcat_oled.c
+	WPM_ENABLE = yes  # for WPM and animated "keyboard pet" widgets
+
+	# OLED pets (animated critters that react to typing) take up a lot of
+	# firmware space, so only compile one, and only if requested.
+	BCAT_OLED_PET ?= no
+	ifneq ($(strip $(BCAT_OLED_PET)), no)
+		SRC += bcat_oled_pet_$(strip $(BCAT_OLED_PET)).c
+		OPT_DEFS += -DBCAT_OLED_PET
+	endif
+endif
+
+ifeq ($(strip $(RGBLIGHT_ENABLE)), yes)
+	SRC += bcat_rgblight.c
+endif
+
+# Disable unwanted build options on all keyboards. (Mouse keys are turned off
+# due to https://github.com/qmk/qmk_firmware/issues/8323, and the rest are
+# turned off to reduce firmware size.)
 COMMAND_ENABLE = no
 CONSOLE_ENABLE = no
 MOUSEKEY_ENABLE = no
 TERMINAL_ENABLE = no
 
-# Disable unused hardware options on all keyboards.
+# Disable unwanted hardware options on all keyboards. (Some keyboards turn
+# these features on by default even though they aren't actually required.)
 MIDI_ENABLE = no
 SLEEP_LED_ENABLE = no
 
 # Disable other unused options on all keyboards.
 AUTO_SHIFT_ENABLE = no
 COMBO_ENABLE = no
+GRAVE_ESC_ENABLE = no
 KEY_LOCK_ENABLE = no
 LEADER_ENABLE = no
+MAGIC_ENABLE = no
+SPACE_CADET_ENABLE = no
 SWAP_HANDS_ENABLE = no
 TAP_DANCE_ENABLE = no
 UCIS_ENABLE = no