aboutsummaryrefslogtreecommitdiff
path: root/jack
diff options
context:
space:
mode:
Diffstat (limited to 'jack')
-rw-r--r--jack/color.c140
-rw-r--r--jack/config.h64
-rw-r--r--jack/introspection.c33
-rw-r--r--jack/jack.c67
-rw-r--r--jack/jack.h16
-rw-r--r--jack/oled.c1
-rw-r--r--jack/picosdk_math.mk153
-rw-r--r--jack/post_rules.mk14
-rw-r--r--jack/rgb_matrix_user.inc835
-rw-r--r--jack/rules.mk31
-rw-r--r--jack/tapping.c2
11 files changed, 1285 insertions, 71 deletions
diff --git a/jack/color.c b/jack/color.c
new file mode 100644
index 0000000..9728d99
--- /dev/null
+++ b/jack/color.c
@@ -0,0 +1,140 @@
+// Copyright 2025 [email protected]
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "color.h"
+#include <lib/lib8tion/lib8tion.h>
+
+#define K255 255
+#define K171 171
+#define K170 170
+#define K85 85
+
+// Adapted from https://fastled.io/docs/hsv2rgb_8cpp_source.html
+// void hsv2rgb_rainbow() function
+RGB rgb_matrix_hsv_to_rgb(const HSV hsv) {
+ // Yellow has a higher inherent brightness than
+ // any other color; 'pure' yellow is perceived to
+ // be 93% as bright as white. In order to make
+ // yellow appear the correct relative brightness,
+ // it has to be rendered brighter than all other
+ // colors.
+ const uint8_t hue = hsv.h;
+ const uint8_t sat = hsv.s;
+ uint8_t val = hsv.v;
+
+ // offset8 = (hue [0..31]) * 8
+ uint8_t offset8 = hue & 0x1F;
+ offset8 <<= 3;
+
+ uint8_t third = scale8(offset8, (256 / 3)); // max = 85
+ uint8_t r, g, b;
+ if (!(hue & 0x80)) {
+ // 0XX
+ if (!(hue & 0x40)) {
+ // 00X
+ // section 0-1
+ if (!(hue & 0x20)) {
+ // 000
+ // case 0: // R -> O
+ r = K255 - third;
+ g = third;
+ b = 0;
+ } else {
+ // 001
+ // case 1: // O -> Y
+ r = K171;
+ g = K85 + third;
+ b = 0;
+ }
+ } else {
+ // 01X
+ // section 2-3
+ if (!(hue & 0x20)) {
+ // 010
+ // case 2: // Y -> G
+ // uint8_t twothirds = (third << 1);
+ uint8_t twothirds = scale8(offset8, ((256 * 2) / 3)); // max=170
+ r = K171 - twothirds;
+ g = K170 + third;
+ b = 0;
+ } else {
+ // 011
+ // case 3: // G -> A
+ r = 0;
+ g = K255 - third;
+ b = third;
+ }
+ }
+ } else {
+ // section 4-7
+ // 1XX
+ if (!(hue & 0x40)) {
+ // 10X
+ if (!(hue & 0x20)) {
+ // 100
+ // case 4: // A -> B
+ r = 0;
+ // uint8_t twothirds = (third << 1);
+ uint8_t twothirds = scale8(offset8, ((256 * 2) / 3)); // max=170
+ g = K171 - twothirds; // K170?
+ b = K85 + twothirds;
+ } else {
+ // 101
+ // case 5: // B -> P
+ r = third;
+ g = 0;
+ b = K255 - third;
+ }
+ } else {
+ if (!(hue & 0x20)) {
+ // 110
+ // case 6: // P -- K
+ r = K85 + third;
+ g = 0;
+ b = K171 - third;
+ } else {
+ // 111
+ // case 7: // K -> R
+ r = K170 + third;
+ g = 0;
+ b = K85 - third;
+ }
+ }
+ }
+ // This is one of the good places to scale the green down,
+ // although the client can scale green down as well.
+ g >>= 1;
+ // Scale down colors if we're desaturated at all
+ // and add the brightness_floor to r, g, and b.
+ if (sat != 255) {
+ if (sat == 0) {
+ r = 255;
+ b = 255;
+ g = 255;
+ } else {
+ uint8_t desat = 255 - sat;
+ desat = scale8_video(desat, desat);
+ uint8_t satscale = 255 - desat;
+ r = scale8(r, satscale);
+ g = scale8(g, satscale);
+ b = scale8(b, satscale);
+ uint8_t brightness_floor = desat;
+ r += brightness_floor;
+ g += brightness_floor;
+ b += brightness_floor;
+ }
+ }
+ // Now scale everything down if we're at value < 255.
+ if (val != 255) {
+ val = scale8_video(val, val);
+ if (val == 0) {
+ r = 0;
+ g = 0;
+ b = 0;
+ } else {
+ r = scale8(r, val);
+ g = scale8(g, val);
+ b = scale8(b, val);
+ }
+ }
+ return (RGB){.r = r, .g = g, .b = b};
+}
diff --git a/jack/config.h b/jack/config.h
index 0c34ee0..72347f8 100644
--- a/jack/config.h
+++ b/jack/config.h
@@ -35,19 +35,55 @@
#ifdef RGB_MATRIX_ENABLE
#define RGB_MATRIX_SLEEP
#define RGB_MATRIX_TIMEOUT 300000
-#undef RGB_MATRIX_DEFAULT_MODE
-#define RGB_MATRIX_DEFAULT_MODE RGB_MATRIX_ALPHAS_MODS
-#define ENABLE_RGB_MATRIX_ALPHAS_MODS
-#define ENABLE_RGB_MATRIX_GRADIENT_LEFT_RIGHT
-#define ENABLE_RGB_MATRIX_CYCLE_SPIRAL
-#define ENABLE_RGB_MATRIX_RAINDROPS
-#define ENABLE_RGB_MATRIX_FRAMEBUFFER_EFFECTS
-#define ENABLE_RGB_MATRIX_TYPING_HEATMAP
-#define ENABLE_RGB_MATRIX_KEYPRESSES
-#define ENABLE_RGB_MATRIX_SOLID_REACTIVE
-#define ENABLE_RGB_MATRIX_SOLID_MULTISPLASH
-#define ENABLE_RGB_MATRIX_SOLID_REACTIVE_CROSS
-#define ENABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
#define RGB_MATRIX_KEYPRESSES
-#define ENABLE_RGB_MATRIX_SOLID_REACTIVE
+#define PALETTEFX_ENABLE_ALL_EFFECTS
+#define PALETTEFX_ENABLE_ALL_PALETTES
+#undef ENABLE_RGB_MATRIX_ALPHAS_MODS
+#undef ENABLE_RGB_MATRIX_GRADIENT_UP_DOWN
+#undef ENABLE_RGB_MATRIX_GRADIENT_LEFT_RIGHT
+#undef ENABLE_RGB_MATRIX_BREATHING
+#undef ENABLE_RGB_MATRIX_BAND_SAT
+#undef ENABLE_RGB_MATRIX_BAND_VAL
+#undef ENABLE_RGB_MATRIX_BAND_PINWHEEL_SAT
+#undef ENABLE_RGB_MATRIX_BAND_PINWHEEL_VAL
+#undef ENABLE_RGB_MATRIX_BAND_SPIRAL_SAT
+#undef ENABLE_RGB_MATRIX_BAND_SPIRAL_VAL
+#undef ENABLE_RGB_MATRIX_CYCLE_ALL
+#undef ENABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
+#undef ENABLE_RGB_MATRIX_CYCLE_UP_DOWN
+#undef ENABLE_RGB_MATRIX_CYCLE_OUT_IN
+#undef ENABLE_RGB_MATRIX_CYCLE_OUT_IN_DUAL
+#undef ENABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
+#undef ENABLE_RGB_MATRIX_CYCLE_PINWHEEL
+#undef ENABLE_RGB_MATRIX_CYCLE_SPIRAL
+#undef ENABLE_RGB_MATRIX_DUAL_BEACON
+#undef ENABLE_RGB_MATRIX_RAINBOW_BEACON
+#undef ENABLE_RGB_MATRIX_RAINBOW_PINWHEELS
+#undef ENABLE_RGB_MATRIX_FLOWER_BLOOMING
+#undef ENABLE_RGB_MATRIX_RAINDROPS
+#undef ENABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
+#undef ENABLE_RGB_MATRIX_HUE_BREATHING
+#undef ENABLE_RGB_MATRIX_HUE_PENDULUM
+#undef ENABLE_RGB_MATRIX_HUE_WAVE
+#undef ENABLE_RGB_MATRIX_PIXEL_FRACTAL
+#undef ENABLE_RGB_MATRIX_PIXEL_FLOW
+#undef ENABLE_RGB_MATRIX_PIXEL_RAIN
+#undef ENABLE_RGB_MATRIX_TYPING_HEATMAP
+#undef ENABLE_RGB_MATRIX_DIGITAL_RAIN
+#undef ENABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE
+#undef ENABLE_RGB_MATRIX_SOLID_REACTIVE
+#undef ENABLE_RGB_MATRIX_SOLID_REACTIVE_WIDE
+#undef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTIWIDE
+#undef ENABLE_RGB_MATRIX_SOLID_REACTIVE_CROSS
+#undef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTICROSS
+#undef ENABLE_RGB_MATRIX_SOLID_REACTIVE_NEXUS
+#undef ENABLE_RGB_MATRIX_SOLID_REACTIVE_MULTINEXUS
+#undef ENABLE_RGB_MATRIX_SPLASH
+#undef ENABLE_RGB_MATRIX_MULTISPLASH
+#undef ENABLE_RGB_MATRIX_SOLID_SPLASH
+#undef ENABLE_RGB_MATRIX_SOLID_MULTISPLASH
+#undef ENABLE_RGB_MATRIX_STARLIGHT
+#undef ENABLE_RGB_MATRIX_STARLIGHT_DUAL_HUE
+#undef ENABLE_RGB_MATRIX_STARLIGHT_DUAL_SAT
+#undef ENABLE_RGB_MATRIX_RIVERFLOW
#endif
diff --git a/jack/introspection.c b/jack/introspection.c
new file mode 100644
index 0000000..86f453b
--- /dev/null
+++ b/jack/introspection.c
@@ -0,0 +1,33 @@
+// Copyright 2025 [email protected]
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "jack.h"
+
+#ifdef TAP_DANCE_ENABLE
+tap_dance_action_t tap_dance_actions[] = {
+ [EM_DASH_MINS] = ACTION_TAP_DANCE_FN(em_dash_mins),
+ [PLY_NXT_PRV] = ACTION_TAP_DANCE_FN(ply_nxt_prv),
+ [PASTE_RAISE] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, raise_paste, td_reset),
+ [CBRACKET] = ACTION_TAP_DANCE_DOUBLE(KC_LCBR, KC_RCBR),
+ [SBRACKET] = ACTION_TAP_DANCE_DOUBLE(KC_LBRC, KC_RBRC),
+};
+#endif
+
+#ifdef ENCODER_MAP_ENABLE
+const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][NUM_DIRECTIONS] = {
+ [_BASE] = {{KC_VOLU, KC_VOLD}, {KC_MNXT, KC_MPRV}},
+ [_LOWER] = {{XRGB_SAI, XRGB_SAD}, {XRGB_HUI, XRGB_HUD}},
+ [_RAISE] = {{C(KC_LEFT), C(KC_RGHT)}, {C(S(KC_TAB)), C(KC_TAB)}}};
+#endif
+
+#ifdef COMBO_ENABLE
+#define CMB(name, action, ...) \
+ uint16_t const name##_combo[] PROGMEM = {__VA_ARGS__, COMBO_END};
+#include "combos.def"
+#undef CMB
+
+#define CMB(name, action, ...) [name] = COMBO(name##_combo, action),
+combo_t key_combos[] = {
+#include "combos.def"
+};
+#undef CMB
+#endif
diff --git a/jack/jack.c b/jack/jack.c
index f41f50a..274a011 100644
--- a/jack/jack.c
+++ b/jack/jack.c
@@ -2,31 +2,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "jack.h"
-tap_dance_action_t tap_dance_actions[] = {
- [EM_DASH_MINS] = ACTION_TAP_DANCE_FN(em_dash_mins),
- [PLY_NXT_PRV] = ACTION_TAP_DANCE_FN(ply_nxt_prv),
- [PASTE_RAISE] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, raise_paste, td_reset),
- [CBRACKET] = ACTION_TAP_DANCE_DOUBLE(KC_LCBR, KC_RCBR),
- [SBRACKET] = ACTION_TAP_DANCE_DOUBLE(KC_LBRC, KC_RBRC),
-};
-
-#define CMB(name, action, ...) name,
-enum combos {
-#include "combos.def"
-};
-#undef CMB
-
-#define CMB(name, action, ...) \
- uint16_t const name##_combo[] PROGMEM = {__VA_ARGS__, COMBO_END};
-#include "combos.def"
-#undef CMB
-
-#define CMB(name, action, ...) [name] = COMBO(name##_combo, action),
-combo_t key_combos[] = {
-#include "combos.def"
-};
-#undef CMB
-
#if defined(SPLIT_KEYBOARD) && defined(OLED_ENABLE)
#include "transactions.h"
extern char keylog_str[KEYLOG_LEN + 1];
@@ -37,25 +12,31 @@ void keylogger_sync(uint8_t initiator2target_buffer_size,
if (initiator2target_buffer_size == 5)
memcpy(&keylog_str, initiator2target_buffer, initiator2target_buffer_size);
}
+#endif
+__attribute__((weak)) void housekeeping_task_keymap(void) {}
void housekeeping_task_user(void) {
+#if defined(SPLIT_KEYBOARD) && defined(OLED_ENABLE)
if (is_keyboard_master()) {
static uint32_t last_sync;
bool needs_sync = false;
static char tmp_keylog[5] = {0};
- if (memcmp(&keylog_str, &tmp_keylog, 5)) {
+ if (memcmp(&keylog_str, &tmp_keylog, sizeof(tmp_keylog))) {
needs_sync = true;
- memcpy(&tmp_keylog, &keylog_str, 5);
+ memcpy(&tmp_keylog, &keylog_str, sizeof(tmp_keylog));
}
if (timer_elapsed32(last_sync) > 250)
needs_sync = true;
if (needs_sync)
- if (transaction_rpc_send(RPC_ID_USER_KEYLOG_STR, 5, &keylog_str))
+ if (transaction_rpc_send(RPC_ID_USER_KEYLOG_STR, sizeof(tmp_keylog),
+ &keylog_str))
last_sync = timer_read32();
}
-}
#endif
+ housekeeping_task_keymap();
+}
+__attribute__((weak)) void keyboard_post_init_keymap(void) {}
void keyboard_post_init_user(void) {
#if defined(SPLIT_KEYBOARD) && defined(OLED_ENABLE)
transaction_register_rpc(RPC_ID_USER_KEYLOG_STR, keylogger_sync);
@@ -67,7 +48,8 @@ void keyboard_post_init_user(void) {
rgblight_enable_noeeprom();
rgblight_sethsv_noeeprom(HSV_CYAN);
rgblight_mode_noeeprom(RGBLIGHT_MODE_STATIC_LIGHT);
-#elif RGB_MATRIX_ENABLE
+#endif
+#ifdef RGB_MATRIX_ENABLE
rgb_matrix_enable_noeeprom();
rgb_matrix_sethsv_noeeprom(HSV_CYAN);
rgb_matrix_mode_noeeprom(RGB_MATRIX_SOLID_COLOR);
@@ -76,6 +58,7 @@ void keyboard_post_init_user(void) {
if (!autocorrect_is_enabled())
autocorrect_enable();
#endif
+ keyboard_post_init_keymap();
}
void matrix_scan_user(void) {
@@ -84,17 +67,23 @@ void matrix_scan_user(void) {
#endif
}
-#ifdef ENCODER_MAP_ENABLE
-const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][NUM_DIRECTIONS] = {
- [_BASE] = {{KC_VOLU, KC_VOLD}, {KC_MNXT, KC_MPRV}},
- [_LOWER] = {{RGB_SAI, RGB_SAD}, {RGB_HUI, RGB_HUD}},
- [_RAISE] = {{C(KC_LEFT), C(KC_RGHT)}, {C(S(KC_TAB)), C(KC_TAB)}}};
-#endif
-
#ifndef MAGIC_ENABLE
uint16_t keycode_config(uint16_t keycode) { return keycode; }
+uint8_t mod_config(uint8_t mod) { return mod; }
#endif
-#ifndef MAGIC_ENABLE
-uint8_t mod_config(uint8_t mod) { return mod; }
+#ifdef RP2040_MATH_IN_ROM
+typedef void (*init_fn)(void);
+extern init_fn __preinit_array_base__;
+extern init_fn __preinit_array_end__;
+#endif
+
+__attribute__((weak)) void keyboard_pre_init_keymap(void) {}
+void keyboard_pre_init_user(void) {
+#ifdef RP2040_MATH_IN_ROM
+ for (init_fn *func = &__preinit_array_base__; func < &__preinit_array_end__;
+ func++)
+ (*func)();
#endif
+ keyboard_pre_init_keymap();
+}
diff --git a/jack/jack.h b/jack/jack.h
index ec7ae69..3d3b1c9 100644
--- a/jack/jack.h
+++ b/jack/jack.h
@@ -21,6 +21,12 @@ enum custom_keycodes {
UC_SUPER
};
+#define CMB(name, action, ...) name,
+enum combos {
+#include "combos.def"
+};
+#undef CMB
+
bool process_record_unicode(uint16_t keycode, keyrecord_t *record);
bool process_record_taps(uint16_t keycode, keyrecord_t *record);
extern uint8_t extract_mod_bits(uint16_t code);
@@ -29,12 +35,15 @@ void tap_code_register(uint8_t code, uint8_t mods, uint16_t delay,
bool register_tap);
void process_tap_code_buffer(void);
void tap_string(const char *str);
+
+#ifdef TAP_DANCE_ENABLE
void td_reset(tap_dance_state_t *state, void *user_data);
void em_dash_mins(tap_dance_state_t *state, void *user_data);
void ply_nxt_prv(tap_dance_state_t *state, void *user_data);
void raise_paste(tap_dance_state_t *state, void *user_data);
+#endif
+
void oled_timer_reset(void);
-bool oled_task_keymap(void);
void anim_frame(uint16_t size, char const action[][size]);
void wpm_graph(void);
void felix_dog(void);
@@ -46,6 +55,11 @@ void render_keylog(void);
void render_wpm(void);
void render_scan_rate(void);
+void keyboard_pre_init_keymap(void);
+void keyboard_post_init_keymap(void);
+void housekeeping_task_keymap(void);
+bool oled_task_keymap(void);
+
#define KEYLOG_LEN 5
#define PNP TD(PLY_NXT_PRV)
#define PSTRSE TD(PASTE_RAISE)
diff --git a/jack/oled.c b/jack/oled.c
index adaad3d..ec52705 100644
--- a/jack/oled.c
+++ b/jack/oled.c
@@ -11,7 +11,6 @@ extern OLED_BLOCK_TYPE oled_dirty;
void oled_timer_reset(void) { oled_timer = timer_read32(); }
__attribute__((weak)) bool oled_task_keymap(void) { return true; }
-
bool oled_task_user(void) {
if (is_keyboard_master()) {
if (timer_elapsed32(oled_timer) > 300000) {
diff --git a/jack/picosdk_math.mk b/jack/picosdk_math.mk
new file mode 100644
index 0000000..82f18a3
--- /dev/null
+++ b/jack/picosdk_math.mk
@@ -0,0 +1,153 @@
+OPT_DEFS += -DPICO_DOUBLE_IN_RAM=1 \
+ -DPICO_FLOAT_IN_RAM=1
+
+# bit
+SRC += $(PICOSDKROOT)/src/rp2_common/pico_bit_ops/bit_ops_aeabi.S
+EXTRALDFLAGS += \
+ -Wl,--wrap=__clzsi2 \
+ -Wl,--wrap=__clzdi2 \
+ -Wl,--wrap=__ctzsi2 \
+ -Wl,--wrap=__ctzdi2 \
+ -Wl,--wrap=__popcountsi2 \
+ -Wl,--wrap=__popcountdi2
+
+# double
+VPATH += $(PICOSDKROOT)/src/rp2_common/pico_double/include
+SRC += \
+ $(PICOSDKROOT)/src/rp2_common/pico_double/double_aeabi.S \
+ $(PICOSDKROOT)/src/rp2_common/pico_double/double_init_rom.c \
+ $(PICOSDKROOT)/src/rp2_common/pico_double/double_math.c \
+ $(PICOSDKROOT)/src/rp2_common/pico_double/double_v1_rom_shim.S
+EXTRALDFLAGS += \
+ -Wl,--wrap=__aeabi_dadd \
+ -Wl,--wrap=__aeabi_ddiv \
+ -Wl,--wrap=__aeabi_dmul \
+ -Wl,--wrap=__aeabi_drsub \
+ -Wl,--wrap=__aeabi_dsub \
+ -Wl,--wrap=__aeabi_cdcmpeq \
+ -Wl,--wrap=__aeabi_cdrcmple \
+ -Wl,--wrap=__aeabi_cdcmple \
+ -Wl,--wrap=__aeabi_dcmpeq \
+ -Wl,--wrap=__aeabi_dcmplt \
+ -Wl,--wrap=__aeabi_dcmple \
+ -Wl,--wrap=__aeabi_dcmpge \
+ -Wl,--wrap=__aeabi_dcmpgt \
+ -Wl,--wrap=__aeabi_dcmpun \
+ -Wl,--wrap=__aeabi_i2d \
+ -Wl,--wrap=__aeabi_l2d \
+ -Wl,--wrap=__aeabi_ui2d \
+ -Wl,--wrap=__aeabi_ul2d \
+ -Wl,--wrap=__aeabi_d2iz \
+ -Wl,--wrap=__aeabi_d2lz \
+ -Wl,--wrap=__aeabi_d2uiz \
+ -Wl,--wrap=__aeabi_d2ulz \
+ -Wl,--wrap=__aeabi_d2f \
+ -Wl,--wrap=sqrt \
+ -Wl,--wrap=cos \
+ -Wl,--wrap=sin \
+ -Wl,--wrap=tan \
+ -Wl,--wrap=atan2 \
+ -Wl,--wrap=exp \
+ -Wl,--wrap=log \
+ -Wl,--wrap=ldexp \
+ -Wl,--wrap=copysign \
+ -Wl,--wrap=trunc \
+ -Wl,--wrap=floor \
+ -Wl,--wrap=ceil \
+ -Wl,--wrap=round \
+ -Wl,--wrap=sincos \
+ -Wl,--wrap=asin \
+ -Wl,--wrap=acos \
+ -Wl,--wrap=atan \
+ -Wl,--wrap=sinh \
+ -Wl,--wrap=cosh \
+ -Wl,--wrap=tanh \
+ -Wl,--wrap=asinh \
+ -Wl,--wrap=acosh \
+ -Wl,--wrap=atanh \
+ -Wl,--wrap=exp2 \
+ -Wl,--wrap=log2 \
+ -Wl,--wrap=exp10 \
+ -Wl,--wrap=log10 \
+ -Wl,--wrap=pow \
+ -Wl,--wrap=powint \
+ -Wl,--wrap=hypot \
+ -Wl,--wrap=cbrt \
+ -Wl,--wrap=fmod \
+ -Wl,--wrap=drem \
+ -Wl,--wrap=remainder \
+ -Wl,--wrap=remquo \
+ -Wl,--wrap=expm1 \
+ -Wl,--wrap=log1p \
+ -Wl,--wrap=fma
+
+# float
+VPATH += $(PICOSDKROOT)/src/rp2_common/pico_float/include
+SRC += \
+ $(PICOSDKROOT)/src/rp2_common/pico_float/float_aeabi.S \
+ $(PICOSDKROOT)/src/rp2_common/pico_float/float_init_rom.c \
+ $(PICOSDKROOT)/src/rp2_common/pico_float/float_math.c \
+ $(PICOSDKROOT)/src/rp2_common/pico_float/float_v1_rom_shim.S
+
+EXTRALDFLAGS += \
+ -Wl,--wrap=__aeabi_fadd \
+ -Wl,--wrap=__aeabi_fdiv \
+ -Wl,--wrap=__aeabi_fmul \
+ -Wl,--wrap=__aeabi_frsub \
+ -Wl,--wrap=__aeabi_fsub \
+ -Wl,--wrap=__aeabi_cfcmpeq \
+ -Wl,--wrap=__aeabi_cfrcmple \
+ -Wl,--wrap=__aeabi_cfcmple \
+ -Wl,--wrap=__aeabi_fcmpeq \
+ -Wl,--wrap=__aeabi_fcmplt \
+ -Wl,--wrap=__aeabi_fcmple \
+ -Wl,--wrap=__aeabi_fcmpge \
+ -Wl,--wrap=__aeabi_fcmpgt \
+ -Wl,--wrap=__aeabi_fcmpun \
+ -Wl,--wrap=__aeabi_i2f \
+ -Wl,--wrap=__aeabi_l2f \
+ -Wl,--wrap=__aeabi_ui2f \
+ -Wl,--wrap=__aeabi_ul2f \
+ -Wl,--wrap=__aeabi_f2iz \
+ -Wl,--wrap=__aeabi_f2lz \
+ -Wl,--wrap=__aeabi_f2uiz \
+ -Wl,--wrap=__aeabi_f2ulz \
+ -Wl,--wrap=__aeabi_f2d \
+ -Wl,--wrap=sqrtf \
+ -Wl,--wrap=cosf \
+ -Wl,--wrap=sinf \
+ -Wl,--wrap=tanf \
+ -Wl,--wrap=atan2f \
+ -Wl,--wrap=expf \
+ -Wl,--wrap=logf \
+ -Wl,--wrap=ldexpf \
+ -Wl,--wrap=copysignf \
+ -Wl,--wrap=truncf \
+ -Wl,--wrap=floorf \
+ -Wl,--wrap=ceilf \
+ -Wl,--wrap=roundf \
+ -Wl,--wrap=sincosf \
+ -Wl,--wrap=asinf \
+ -Wl,--wrap=acosf \
+ -Wl,--wrap=atanf \
+ -Wl,--wrap=sinhf \
+ -Wl,--wrap=coshf \
+ -Wl,--wrap=tanhf \
+ -Wl,--wrap=asinhf \
+ -Wl,--wrap=acoshf \
+ -Wl,--wrap=atanhf \
+ -Wl,--wrap=exp2f \
+ -Wl,--wrap=log2f \
+ -Wl,--wrap=exp10f \
+ -Wl,--wrap=log10f \
+ -Wl,--wrap=powf \
+ -Wl,--wrap=powintf \
+ -Wl,--wrap=hypotf \
+ -Wl,--wrap=cbrtf \
+ -Wl,--wrap=fmodf \
+ -Wl,--wrap=dremf \
+ -Wl,--wrap=remainderf \
+ -Wl,--wrap=remquof \
+ -Wl,--wrap=expm1f \
+ -Wl,--wrap=log1pf \
+ -Wl,--wrap=fmaf
diff --git a/jack/post_rules.mk b/jack/post_rules.mk
new file mode 100644
index 0000000..fb10049
--- /dev/null
+++ b/jack/post_rules.mk
@@ -0,0 +1,14 @@
+ifeq ($(MCU_SERIES), RP2040)
+ include picosdk_math.mk
+endif
+
+ifeq ($(strip $(RP2040_MATH_IN_ROM)), yes)
+ OPT_DEFS += -DRP2040_MATH_IN_ROM
+ MCU_LDSCRIPT = RP2040_FLASH_TIMECRIT_ROM_MATH
+endif
+
+ifeq ($(strip $(MOUSE_JIGGLE_ENABLE)), yes)
+ OPT_DEFS += -DMOUSE_JIGGLE_ENABLE
+ MOUSEKEY_ENABLE = yes
+ DEFERRED_EXEC_ENABLE = yes
+endif
diff --git a/jack/rgb_matrix_user.inc b/jack/rgb_matrix_user.inc
new file mode 100644
index 0000000..2ff9e95
--- /dev/null
+++ b/jack/rgb_matrix_user.inc
@@ -0,0 +1,835 @@
+// Copyright 2025 Google LLC
+// SPDX-License-Identifier: Apache-2.0
+#if defined(PALETTEFX_ENABLE_ALL_EFFECTS) || defined(PALETTEFX_GRADIENT_ENABLE)
+RGB_MATRIX_EFFECT(PALETTEFX_GRADIENT)
+#endif
+#if defined(PALETTEFX_ENABLE_ALL_EFFECTS) || defined(PALETTEFX_FLOW_ENABLE)
+RGB_MATRIX_EFFECT(PALETTEFX_FLOW)
+#endif
+#if defined(PALETTEFX_ENABLE_ALL_EFFECTS) || defined(PALETTEFX_RIPPLE_ENABLE)
+RGB_MATRIX_EFFECT(PALETTEFX_RIPPLE)
+#endif
+#if defined(PALETTEFX_ENABLE_ALL_EFFECTS) || defined(PALETTEFX_SPARKLE_ENABLE)
+RGB_MATRIX_EFFECT(PALETTEFX_SPARKLE)
+#endif
+#if defined(PALETTEFX_ENABLE_ALL_EFFECTS) || defined(PALETTEFX_VORTEX_ENABLE)
+RGB_MATRIX_EFFECT(PALETTEFX_VORTEX)
+#endif
+#if defined(RGB_MATRIX_KEYREACTIVE_ENABLED) && \
+ (defined(PALETTEFX_ENABLE_ALL_EFFECTS) || \
+ defined(PALETTEFX_REACTIVE_ENABLE))
+RGB_MATRIX_EFFECT(PALETTEFX_REACTIVE)
+#endif
+
+#ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
+
+///////////////////////////////////////////////////////////////////////////////
+// PaletteFx function definitions
+///////////////////////////////////////////////////////////////////////////////
+
+/** Gets the selected palette. */
+static const uint16_t *palettefx_get_palette_data(void);
+
+/**
+ * @brief Computes the interpolated HSV palette color at 0 <= x < 256.
+ *
+ * Looks up and linearly interpolates `palette` at 0 <= x < 256. The color
+ * saturation and value are scaled according to rgb_matrix_config.
+ *
+ * @note `palette` must point to a PROGMEM address.
+ *
+ * @param palette Pointer to PROGMEM of a size-16 table of HSV16 colors.
+ * @param x Palette lookup position, a value in 0-255.
+ * @return HSV color.
+ */
+static hsv_t palettefx_interp_color(const uint16_t *palette, uint8_t x);
+
+/** Gets the color data for the selected palette. */
+static const uint16_t *palettefx_get_palette_data(void);
+
+/**
+ * @brief Compute a scaled 16-bit time that wraps smoothly.
+ *
+ * Computes `timer` scaled by `scale`, returning the result as a uint16.
+ * Generally, the scaled time would have a discontinuity every ~65 seconds when
+ * `timer` wraps 16-bit range. This is corrected for to wrap smoothly mod 2^16.
+ *
+ * @param timer A 32-bit timer.
+ * @param scale Scale value in 0-255.
+ * @return Scaled time.
+ */
+inline static uint16_t palettefx_scaled_time(uint32_t timer, uint8_t scale);
+
+///////////////////////////////////////////////////////////////////////////////
+// PaletteFx effects
+///////////////////////////////////////////////////////////////////////////////
+
+#if defined(PALETTEFX_ENABLE_ALL_EFFECTS) || defined(PALETTEFX_GRADIENT_ENABLE)
+// "Gradient" static effect. This is essentially a palette-colored version of
+// RGB_MATRIX_GRADIENT_UP_DOWN. A vertically-sloping gradient is made, with the
+// highest color on the top keys of keyboard and the lowest color at the bottom.
+static bool PALETTEFX_GRADIENT(effect_params_t *params) {
+ // On first call, compute and cache the slope of the gradient.
+ static uint8_t gradient_slope = 0;
+ if (!gradient_slope) {
+ uint8_t y_max = 64; // To avoid overflow below, x_max must be at least 64.
+ for (uint8_t i = 0; i < RGB_MATRIX_LED_COUNT; ++i) {
+ if (g_led_config.point[i].y > y_max) {
+ y_max = g_led_config.point[i].y;
+ }
+ }
+ // Compute the quotient `255 / y_max` with 6 fractional bits and rounding.
+ gradient_slope = (64 * 255 + y_max / 2) / y_max;
+ }
+
+ RGB_MATRIX_USE_LIMITS(led_min, led_max);
+ const uint16_t *palette = palettefx_get_palette_data();
+
+ for (uint8_t i = led_min; i < led_max; ++i) {
+ RGB_MATRIX_TEST_LED_FLAGS();
+ const uint8_t y = g_led_config.point[i].y;
+ const uint8_t value = 255 - (((uint16_t)y * (uint16_t)gradient_slope) >> 6);
+ rgb_t rgb = rgb_matrix_hsv_to_rgb(palettefx_interp_color(palette, value));
+ rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+ }
+
+ return rgb_matrix_check_finished_leds(led_max);
+}
+#endif
+
+#if defined(PALETTEFX_ENABLE_ALL_EFFECTS) || defined(PALETTEFX_FLOW_ENABLE)
+// "Flow" animated effect. Draws moving wave patterns mimicking the appearance
+// of flowing liquid. For interesting variety of patterns, space coordinates are
+// slowly rotated and a function of several sine waves is evaluated.
+static bool PALETTEFX_FLOW(effect_params_t *params) {
+ RGB_MATRIX_USE_LIMITS(led_min, led_max);
+ const uint16_t *palette = palettefx_get_palette_data();
+ const uint16_t time =
+ palettefx_scaled_time(g_rgb_timer, 1 + rgb_matrix_config.speed / 8);
+ // Compute rotation coefficients with 7 fractional bits.
+ const int8_t rot_c = cos8(time / 4) - 128;
+ const int8_t rot_s = sin8(time / 4) - 128;
+ const uint8_t omega = 32 + sin8(time) / 4;
+
+ for (uint8_t i = led_min; i < led_max; ++i) {
+ RGB_MATRIX_TEST_LED_FLAGS();
+ const uint8_t x = g_led_config.point[i].x;
+ const uint8_t y = g_led_config.point[i].y;
+
+ // Rotate (x, y) by the 2x2 rotation matrix described by rot_c, rot_s.
+ const uint8_t x1 = (uint8_t)((((int16_t)rot_c) * ((int16_t)x)) / 128) -
+ (uint8_t)((((int16_t)rot_s) * ((int16_t)y)) / 128);
+ const uint8_t y1 = (uint8_t)((((int16_t)rot_s) * ((int16_t)x)) / 128) +
+ (uint8_t)((((int16_t)rot_c) * ((int16_t)y)) / 128);
+
+ uint8_t value = scale8(sin8(x1 - 2 * time), omega) + y1 + time / 4;
+ // Evaluate `sawtooth(value)`.
+ value = 2 * ((value <= 127) ? value : (255 - value));
+
+ rgb_t rgb = rgb_matrix_hsv_to_rgb(palettefx_interp_color(palette, value));
+ rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+ }
+
+ return rgb_matrix_check_finished_leds(led_max);
+}
+#endif
+
+#if defined(PALETTEFX_ENABLE_ALL_EFFECTS) || defined(PALETTEFX_RIPPLE_ENABLE)
+// "Ripple" animated effect. Draws circular rings emanating from random points,
+// simulating water drops falling in a quiet pool.
+static bool PALETTEFX_RIPPLE(effect_params_t *params) {
+ RGB_MATRIX_USE_LIMITS(led_min, led_max);
+ const uint16_t *palette = palettefx_get_palette_data();
+
+ // Each instance of this struct represents one water drop. For efficiency, at
+ // most 3 drops are active at any time.
+ static struct {
+ uint16_t time;
+ uint8_t x;
+ uint8_t y;
+ uint8_t amplitude;
+ uint8_t scale;
+ uint8_t phase;
+ } drops[3];
+ static uint32_t drop_timer = 0;
+ static uint8_t drops_tail = 0;
+
+ if (params->iter == 0) {
+ if (params->init) {
+ for (uint8_t j = 0; j < 3; ++j) {
+ drops[j].amplitude = 0;
+ }
+ drop_timer = g_rgb_timer;
+ }
+
+ if (drops[drops_tail].amplitude == 0 &&
+ timer_expired32(g_rgb_timer, drop_timer)) {
+ // Spawn a new drop, located at a random LED.
+ const uint8_t i = random8_max(RGB_MATRIX_LED_COUNT);
+ drops[drops_tail].time = (uint16_t)g_rgb_timer;
+ drops[drops_tail].x = g_led_config.point[i].x;
+ drops[drops_tail].y = g_led_config.point[i].y;
+ drops[drops_tail].amplitude = 1;
+ ++drops_tail;
+ if (drops_tail == 3) {
+ drops_tail = 0;
+ }
+ drop_timer = g_rgb_timer + 1000;
+ }
+
+ uint8_t amplitude(uint8_t t) { // Drop amplitude as a function of time.
+ if (t <= 55) {
+ return (t < 32) ? (3 + 5 * t) : 192;
+ } else {
+ t = (((uint16_t)(255 - t)) * UINT16_C(123)) >> 7;
+ return scale8(t, t);
+ }
+ }
+
+ for (uint8_t j = 0; j < 3; ++j) {
+ if (drops[j].amplitude == 0) {
+ continue;
+ }
+ const uint16_t tick = scale16by8(g_rgb_timer - drops[j].time,
+ 1 + rgb_matrix_config.speed / 4);
+ if (tick < 4 * 255) {
+ const uint8_t t = (uint8_t)(tick / 4);
+ drops[j].amplitude = amplitude(t);
+ drops[j].scale = 255 / (1 + t / 2);
+ drops[j].phase = (uint8_t)tick;
+ } else {
+ drops[j].amplitude = 0; // Animation for this drop is complete.
+ }
+ }
+ }
+
+ for (uint8_t i = led_min; i < led_max; ++i) {
+ RGB_MATRIX_TEST_LED_FLAGS();
+ int16_t value = 128;
+
+ for (uint8_t j = 0; j < 3; ++j) {
+ if (drops[j].amplitude == 0) {
+ continue;
+ }
+
+ const uint8_t x = abs8((g_led_config.point[i].x - drops[j].x) / 2);
+ const uint8_t y = abs8((g_led_config.point[i].y - drops[j].y) / 2);
+ const uint8_t r = sqrt16(x * x + y * y);
+ const uint16_t r_scaled = (uint16_t)r * (uint16_t)drops[j].scale;
+
+ if (r_scaled < 255) {
+ // The drop is made from a radial sine wave modulated by a smooth bump
+ // to localize its spatial extent.
+ const uint8_t bump = scale8(ease8InOutApprox(255 - (uint8_t)r_scaled),
+ drops[j].amplitude);
+ const int8_t wave = (int16_t)cos8(8 * r - drops[j].phase) - 128;
+ value += ((int16_t)wave * (int16_t)bump) / 128;
+ }
+ }
+
+ // Clip `value` to 0-255 range.
+ if (value < 0) {
+ value = 0;
+ }
+ if (value > 255) {
+ value = 255;
+ }
+ rgb_t rgb =
+ rgb_matrix_hsv_to_rgb(palettefx_interp_color(palette, (uint8_t)value));
+ rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+ }
+
+ return rgb_matrix_check_finished_leds(led_max);
+}
+#endif
+
+#if defined(PALETTEFX_ENABLE_ALL_EFFECTS) || defined(PALETTEFX_SPARKLE_ENABLE)
+// "Sparkle" effect. Each LED is animated by a sine wave with pseudorandom
+// phase, so that the matrix "sparkles." All the LED sines are modulated by a
+// global amplitude factor, which varies by a slower sine wave, so that the
+// matrix as a whole periodically brightens and dims.
+static bool PALETTEFX_SPARKLE(effect_params_t *params) {
+ RGB_MATRIX_USE_LIMITS(led_min, led_max);
+ const uint16_t *palette = palettefx_get_palette_data();
+ const uint8_t time =
+ palettefx_scaled_time(g_rgb_timer, 1 + rgb_matrix_config.speed / 8);
+ const uint8_t amplitude = 128 + sin8(time) / 2;
+ uint16_t rand_state = 1 + params->iter;
+
+ for (uint8_t i = led_min; i < led_max; ++i) {
+ RGB_MATRIX_TEST_LED_FLAGS();
+ // Multiplicative congruential generator for a random phase for each LED.
+ rand_state *= UINT16_C(36563);
+ const uint8_t phase = (uint8_t)(rand_state >> 8);
+
+ const uint8_t value = scale8(sin8(2 * time + phase), amplitude);
+
+ rgb_t rgb = rgb_matrix_hsv_to_rgb(palettefx_interp_color(palette, value));
+ rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+ }
+
+ return rgb_matrix_check_finished_leds(led_max);
+}
+#endif
+
+#if defined(PALETTEFX_ENABLE_ALL_EFFECTS) || defined(PALETTEFX_SPARKLE_ENABLE)
+// "Vortex" animated effect. LEDs are animated according to a polar function
+// with the appearance of a spinning vortex centered on k_rgb_matrix_center.
+static bool PALETTEFX_VORTEX(effect_params_t *params) {
+ RGB_MATRIX_USE_LIMITS(led_min, led_max);
+ const uint16_t *palette = palettefx_get_palette_data();
+ const uint16_t time =
+ palettefx_scaled_time(g_rgb_timer, 1 + rgb_matrix_config.speed / 4);
+
+ for (uint8_t i = led_min; i < led_max; ++i) {
+ RGB_MATRIX_TEST_LED_FLAGS();
+ const int16_t x = g_led_config.point[i].x - k_rgb_matrix_center.x;
+ const int16_t y = g_led_config.point[i].y - k_rgb_matrix_center.y;
+ uint8_t value = sin8(atan2_8(y, x) + time - sqrt16(x * x + y * y) / 2);
+
+ rgb_t rgb = rgb_matrix_hsv_to_rgb(palettefx_interp_color(palette, value));
+ rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+ }
+
+ return rgb_matrix_check_finished_leds(led_max);
+}
+#endif
+
+#if defined(RGB_MATRIX_KEYREACTIVE_ENABLED) && \
+ (defined(PALETTEFX_ENABLE_ALL_EFFECTS) || \
+ defined(PALETTEFX_REACTIVE_ENABLE))
+// Reactive animated effect. This effect is "reactive," it responds to key
+// presses. For each key press, LEDs near the key change momentarily.
+static bool PALETTEFX_REACTIVE(effect_params_t *params) {
+ RGB_MATRIX_USE_LIMITS(led_min, led_max);
+ const uint16_t *palette = palettefx_get_palette_data();
+ const uint8_t count = g_last_hit_tracker.count;
+
+ uint8_t amplitude(uint8_t t) { // Bump amplitude as a function of time.
+ if (t <= 55) {
+ return (t < 32) ? (4 + 8 * t) : 255;
+ } else {
+ t = (((uint16_t)(255 - t)) * UINT16_C(164)) >> 7;
+ return scale8(t, t);
+ }
+ }
+
+ uint8_t hit_amplitude[LED_HITS_TO_REMEMBER] = {0};
+ for (uint8_t j = 0; j < count; ++j) {
+ const uint16_t tick =
+ scale16by8(g_last_hit_tracker.tick[j], 1 + rgb_matrix_config.speed / 4);
+ if (tick <= 255) {
+ hit_amplitude[j] = amplitude((uint8_t)tick);
+ }
+ }
+
+ for (uint8_t i = led_min; i < led_max; ++i) {
+ RGB_MATRIX_TEST_LED_FLAGS();
+ uint8_t value = 0;
+
+ for (uint8_t j = 0; j < count; ++j) {
+ if (hit_amplitude[j] == 0) {
+ continue;
+ }
+
+ uint8_t dx =
+ abs8((g_led_config.point[i].x - g_last_hit_tracker.x[j]) / 2);
+ uint8_t dy =
+ abs8((g_led_config.point[i].y - g_last_hit_tracker.y[j]) / 2);
+ if (dx < 21 && dy < 21) {
+ const uint16_t dist_sqr = dx * dx + dy * dy;
+ if (dist_sqr < 21 * 21) { // Accumulate a radial bump for each hit.
+ const uint8_t dist = sqrt16(dist_sqr);
+ value = qadd8(value, scale8(255 - 12 * dist, hit_amplitude[j]));
+ // Early loop exit where the value has saturated.
+ if (value == 255) {
+ break;
+ }
+ }
+ }
+ }
+
+ hsv_t hsv = palettefx_interp_color(palette, value);
+ if (value < 32) { // Make the background dark regardless of palette.
+ hsv.v = scale8(hsv.v, 64 + 6 * value);
+ }
+
+ const rgb_t rgb = rgb_matrix_hsv_to_rgb(hsv);
+ rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
+ }
+ return rgb_matrix_check_finished_leds(led_max);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// PaletteFx implementation
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief 16-bit HSV color.
+ *
+ * Represents a color with a 16-bit value in hue-saturation-value (HSV) space.
+ * Components are packed as:
+ *
+ * - Hue: lowest 8 bits.
+ * - Saturation: middle 4 bits.
+ * - Value: highest 4 bits.
+ */
+#define HSV16(h, s, v) ((((v) >> 4) << 12) | (((s) >> 4) << 8) | ((h) & 0xff))
+
+/** Unpacks 16-bit HSV color to hsv_t. */
+static hsv_t unpack_hsv16(uint16_t hsv16) {
+ return (hsv_t){
+ .h = (uint8_t)hsv16,
+ .s = ((uint8_t)(hsv16 >> 8) & 0x0f) * 17,
+ .v = ((uint8_t)(hsv16 >> 12) & 0x0f) * 17,
+ };
+}
+
+/** PaletteFx palette color data. */
+static const uint16_t palettefx_palettes[][16] PROGMEM = {
+#if defined(PALETTEFX_ENABLE_ALL_PALETTES) || \
+ defined(PALETTEFX_AFTERBURN_ENABLE)
+ // "Afterburn" palette.
+ {
+ HSV16(139, 255, 85),
+ HSV16(134, 255, 85),
+ HSV16(131, 255, 102),
+ HSV16(128, 255, 102),
+ HSV16(127, 187, 102),
+ HSV16(125, 119, 102),
+ HSV16(124, 51, 102),
+ HSV16(125, 0, 119),
+ HSV16(15, 17, 119),
+ HSV16(17, 51, 153),
+ HSV16(18, 102, 170),
+ HSV16(19, 153, 204),
+ HSV16(21, 187, 238),
+ HSV16(23, 238, 255),
+ HSV16(26, 255, 255),
+ HSV16(30, 255, 255),
+ },
+#endif
+#if defined(PALETTEFX_ENABLE_ALL_PALETTES) || defined(PALETTEFX_AMBER_ENABLE)
+ // "Amber" palette.
+ {
+ HSV16(5, 187, 170),
+ HSV16(15, 221, 170),
+ HSV16(22, 238, 187),
+ HSV16(23, 238, 187),
+ HSV16(25, 255, 204),
+ HSV16(27, 255, 221),
+ HSV16(29, 255, 238),
+ HSV16(29, 255, 255),
+ HSV16(28, 187, 255),
+ HSV16(30, 68, 255),
+ HSV16(32, 34, 255),
+ HSV16(32, 34, 255),
+ HSV16(26, 119, 255),
+ HSV16(24, 238, 255),
+ HSV16(21, 221, 255),
+ HSV16(16, 204, 255),
+ },
+#endif
+#if defined(PALETTEFX_ENABLE_ALL_PALETTES) || defined(PALETTEFX_BADWOLF_ENABLE)
+ // "Bad Wolf" palette. Inspired by the Bad Wolf theme by Steve Losh, which
+ // is
+ // distributed under MIT/X11 license.
+ // https://github.com/sjl/badwolf/tree/61d75affa51d40213d671edc9c8ff83992d7fd6f
+ {
+ HSV16(14, 34, 0),
+ HSV16(245, 255, 255),
+ HSV16(245, 255, 255),
+ HSV16(245, 255, 255),
+ HSV16(14, 34, 0),
+ HSV16(14, 34, 0),
+ HSV16(14, 34, 0),
+ HSV16(14, 34, 0),
+ HSV16(14, 34, 0),
+ HSV16(14, 34, 0),
+ HSV16(24, 17, 136),
+ HSV16(28, 0, 255),
+ HSV16(24, 17, 136),
+ HSV16(14, 34, 0),
+ HSV16(14, 34, 0),
+ HSV16(14, 34, 0),
+ },
+#endif
+#if defined(PALETTEFX_ENABLE_ALL_PALETTES) || defined(PALETTEFX_CARNIVAL_ENABLE)
+ // "Carnival" palette.
+ {
+ HSV16(132, 255, 85),
+ HSV16(121, 187, 85),
+ HSV16(108, 170, 102),
+ HSV16(90, 153, 119),
+ HSV16(70, 187, 136),
+ HSV16(57, 255, 153),
+ HSV16(50, 255, 187),
+ HSV16(44, 255, 204),
+ HSV16(39, 255, 238),
+ HSV16(32, 255, 255),
+ HSV16(27, 255, 255),
+ HSV16(22, 255, 255),
+ HSV16(18, 255, 255),
+ HSV16(8, 221, 238),
+ HSV16(252, 204, 238),
+ HSV16(241, 255, 221),
+ },
+#endif
+#if defined(PALETTEFX_ENABLE_ALL_PALETTES) || defined(PALETTEFX_CLASSIC_ENABLE)
+ // "Classic" palette.
+ {
+ HSV16(150, 255, 85),
+ HSV16(151, 238, 119),
+ HSV16(160, 136, 119),
+ HSV16(176, 102, 136),
+ HSV16(193, 85, 136),
+ HSV16(216, 85, 136),
+ HSV16(234, 102, 153),
+ HSV16(247, 119, 187),
+ HSV16(3, 153, 204),
+ HSV16(10, 204, 221),
+ HSV16(15, 255, 255),
+ HSV16(18, 255, 255),
+ HSV16(23, 255, 255),
+ HSV16(24, 204, 255),
+ HSV16(25, 136, 255),
+ HSV16(26, 85, 255),
+ },
+#endif
+#if defined(PALETTEFX_ENABLE_ALL_PALETTES) || defined(PALETTEFX_DRACULA_ENABLE)
+ // "Dracula" palette. Inspired by the Dracula theme by Zeno Rocha, which is
+ // distributed under MIT license.
+ // https://github.com/dracula/dracula-theme/tree/ac4dc82dab2a3c35e5cac0cd80c97fbf4c2ca986
+ {
+ HSV16(165, 153, 119),
+ HSV16(167, 153, 136),
+ HSV16(170, 170, 187),
+ HSV16(173, 170, 221),
+ HSV16(177, 170, 238),
+ HSV16(183, 170, 255),
+ HSV16(190, 170, 255),
+ HSV16(200, 153, 255),
+ HSV16(216, 153, 255),
+ HSV16(230, 170, 255),
+ HSV16(2, 153, 255),
+ HSV16(29, 153, 238),
+ HSV16(44, 255, 255),
+ HSV16(53, 238, 255),
+ HSV16(63, 238, 238),
+ HSV16(85, 238, 238),
+ },
+#endif
+#if defined(PALETTEFX_ENABLE_ALL_PALETTES) || defined(PALETTEFX_GROOVY_ENABLE)
+ // "Groovy" palette. Inspired by the Gruvbox theme by Pavel Pertsev, which
+ // is
+ // distributed under MIT/X11 license.
+ // https://github.com/morhetz/gruvbox/tree/f1ecde848f0cdba877acb0c740320568252cc482
+ {
+ HSV16(26, 136, 187),
+ HSV16(23, 85, 102),
+ HSV16(21, 85, 85),
+ HSV16(21, 85, 85),
+ HSV16(21, 85, 85),
+ HSV16(17, 102, 102),
+ HSV16(5, 204, 255),
+ HSV16(4, 255, 255),
+ HSV16(4, 255, 255),
+ HSV16(7, 204, 255),
+ HSV16(23, 136, 187),
+ HSV16(26, 136, 187),
+ HSV16(28, 136, 187),
+ HSV16(50, 238, 238),
+ HSV16(54, 255, 238),
+ HSV16(54, 255, 238),
+ },
+#endif
+#if defined(PALETTEFX_ENABLE_ALL_PALETTES) || defined(PALETTEFX_NOTPINK_ENABLE)
+ // "Not Pink" palette.
+ {
+ HSV16(9, 255, 153),
+ HSV16(3, 221, 187),
+ HSV16(252, 204, 187),
+ HSV16(248, 204, 187),
+ HSV16(247, 187, 204),
+ HSV16(245, 187, 238),
+ HSV16(244, 170, 255),
+ HSV16(245, 153, 255),
+ HSV16(252, 102, 255),
+ HSV16(16, 68, 255),
+ HSV16(40, 34, 255),
+ HSV16(32, 51, 255),
+ HSV16(6, 85, 255),
+ HSV16(248, 136, 255),
+ HSV16(247, 187, 255),
+ HSV16(249, 221, 255),
+ },
+#endif
+#if defined(PALETTEFX_ENABLE_ALL_PALETTES) || defined(PALETTEFX_PHOSPHOR_ENABLE)
+ // "Phosphor" palette.
+ {
+ HSV16(116, 102, 34),
+ HSV16(113, 170, 51),
+ HSV16(113, 255, 68),
+ HSV16(110, 255, 68),
+ HSV16(109, 255, 85),
+ HSV16(105, 255, 102),
+ HSV16(95, 238, 102),
+ HSV16(85, 238, 119),
+ HSV16(84, 255, 136),
+ HSV16(84, 255, 153),
+ HSV16(83, 255, 187),
+ HSV16(80, 238, 204),
+ HSV16(69, 221, 238),
+ HSV16(46, 204, 255),
+ HSV16(42, 153, 255),
+ HSV16(40, 102, 255),
+ },
+#endif
+#if defined(PALETTEFX_ENABLE_ALL_PALETTES) || \
+ defined(PALETTEFX_POLARIZED_ENABLE)
+ // "Polarized" palette. Inspired by the Solarized theme by Ethan Schoonover,
+ // which is distributed under MIT license.
+ // https://github.com/altercation/solarized/tree/62f656a02f93c5190a8753159e34b385588d5ff3
+ {
+ HSV16(139, 255, 68),
+ HSV16(139, 221, 85),
+ HSV16(139, 204, 102),
+ HSV16(138, 204, 119),
+ HSV16(137, 204, 136),
+ HSV16(137, 187, 170),
+ HSV16(137, 153, 187),
+ HSV16(132, 170, 187),
+ HSV16(126, 187, 238),
+ HSV16(124, 102, 255),
+ HSV16(124, 102, 255),
+ HSV16(124, 187, 255),
+ HSV16(130, 170, 204),
+ HSV16(137, 153, 187),
+ HSV16(137, 153, 204),
+ HSV16(137, 136, 221),
+ },
+#endif
+#if defined(PALETTEFX_ENABLE_ALL_PALETTES) || defined(PALETTEFX_ROSEGOLD_ENABLE)
+ // "Rose Gold" palette.
+ {
+ HSV16(246, 255, 204),
+ HSV16(250, 238, 221),
+ HSV16(253, 221, 238),
+ HSV16(1, 221, 255),
+ HSV16(6, 221, 255),
+ HSV16(12, 221, 255),
+ HSV16(18, 204, 255),
+ HSV16(22, 170, 255),
+ HSV16(20, 204, 255),
+ HSV16(17, 221, 255),
+ HSV16(12, 221, 255),
+ HSV16(7, 221, 255),
+ HSV16(255, 204, 255),
+ HSV16(248, 204, 255),
+ HSV16(241, 238, 255),
+ HSV16(235, 255, 255),
+ },
+#endif
+#if defined(PALETTEFX_ENABLE_ALL_PALETTES) || defined(PALETTEFX_SPORT_ENABLE)
+ // "Sport" palette.
+ {
+ HSV16(156, 102, 51),
+ HSV16(155, 102, 68),
+ HSV16(155, 102, 68),
+ HSV16(154, 102, 68),
+ HSV16(155, 102, 85),
+ HSV16(156, 85, 102),
+ HSV16(158, 68, 119),
+ HSV16(159, 51, 136),
+ HSV16(158, 17, 153),
+ HSV16(130, 0, 170),
+ HSV16(49, 17, 204),
+ HSV16(42, 51, 238),
+ HSV16(39, 85, 255),
+ HSV16(37, 119, 255),
+ HSV16(36, 170, 255),
+ HSV16(36, 255, 255),
+ },
+#endif
+#if defined(PALETTEFX_ENABLE_ALL_PALETTES) || \
+ defined(PALETTEFX_SYNTHWAVE_ENABLE)
+ // "Synthwave" palette.
+ {
+ HSV16(170, 221, 119),
+ HSV16(180, 221, 153),
+ HSV16(196, 238, 153),
+ HSV16(209, 255, 153),
+ HSV16(220, 255, 170),
+ HSV16(227, 255, 204),
+ HSV16(233, 255, 238),
+ HSV16(245, 204, 255),
+ HSV16(3, 170, 255),
+ HSV16(13, 187, 255),
+ HSV16(21, 204, 255),
+ HSV16(28, 221, 255),
+ HSV16(43, 255, 255),
+ HSV16(42, 255, 255),
+ HSV16(68, 68, 255),
+ HSV16(132, 255, 255),
+ },
+#endif
+#if defined(PALETTEFX_ENABLE_ALL_PALETTES) || defined(PALETTEFX_THERMAL_ENABLE)
+ // "Thermal" palette.
+ {
+ HSV16(7, 0, 17),
+ HSV16(8, 0, 17),
+ HSV16(12, 0, 34),
+ HSV16(13, 0, 51),
+ HSV16(15, 17, 51),
+ HSV16(16, 17, 68),
+ HSV16(20, 34, 85),
+ HSV16(95, 0, 102),
+ HSV16(126, 85, 119),
+ HSV16(112, 85, 153),
+ HSV16(54, 136, 187),
+ HSV16(39, 238, 221),
+ HSV16(33, 255, 255),
+ HSV16(25, 238, 255),
+ HSV16(13, 238, 255),
+ HSV16(5, 238, 255),
+ },
+#endif
+#if defined(PALETTEFX_ENABLE_ALL_PALETTES) || defined(PALETTEFX_VIRIDIS_ENABLE)
+ // "Viridis" palette. Inspired by the Viridis colormap by Stefan van der
+ // Walt
+ // and Nathaniel Smith, which is distributed under CC0 license.
+ // https://github.com/BIDS/colormap/blob/bc549477db0c12b54a5928087552ad2cf274980f/colormaps.py
+ {
+ HSV16(204, 221, 102),
+ HSV16(194, 187, 119),
+ HSV16(183, 153, 136),
+ HSV16(169, 119, 153),
+ HSV16(155, 153, 153),
+ HSV16(145, 170, 153),
+ HSV16(137, 187, 153),
+ HSV16(130, 204, 153),
+ HSV16(123, 221, 153),
+ HSV16(117, 221, 170),
+ HSV16(108, 187, 187),
+ HSV16(93, 153, 204),
+ HSV16(71, 170, 221),
+ HSV16(56, 221, 221),
+ HSV16(46, 255, 238),
+ HSV16(39, 255, 255),
+ },
+#endif
+#if defined(PALETTEFX_ENABLE_ALL_PALETTES) || \
+ defined(PALETTEFX_WATERMELON_ENABLE)
+ // "Watermelon" palette.
+ {
+ HSV16(67, 255, 102),
+ HSV16(55, 255, 153),
+ HSV16(47, 255, 204),
+ HSV16(48, 136, 238),
+ HSV16(48, 68, 255),
+ HSV16(45, 34, 255),
+ HSV16(39, 34, 255),
+ HSV16(17, 85, 255),
+ HSV16(8, 187, 255),
+ HSV16(1, 255, 255),
+ HSV16(2, 238, 255),
+ HSV16(2, 238, 255),
+ HSV16(0, 238, 255),
+ HSV16(253, 255, 238),
+ HSV16(254, 255, 238),
+ HSV16(255, 255, 221),
+ },
+#endif
+#if __has_include("palettefx_user.inc") // Include user palettes if present.
+#include "palettefx_user.inc"
+#endif
+};
+/** Number of palettes. User palettes, if any, are included in the count. */
+#define NUM_PALETTEFX_PALETTES \
+ (sizeof(palettefx_palettes) / sizeof(*palettefx_palettes))
+
+// Validate at compile time that `1 <= NUM_PALETTEFX_PALETTES <= 32`. The upper
+// limit is due to using RGB Matrix's hue config to select palettes.
+_Static_assert(
+ NUM_PALETTEFX_PALETTES >= 1,
+ "palettefx: No palettes are enabled. To fix: enable all built-in palettes "
+ "by adding in config.h `#define PALETTE_ENABLE_ALL_PALETTES`, or enable at "
+ "least one with `#define PALETTE_<name>_ENABLE`, or define at least one "
+ "custom palette in palettefx_user.inc.");
+_Static_assert(
+ NUM_PALETTEFX_PALETTES <= 256 / RGB_MATRIX_HUE_STEP,
+ "palettefx: Too many palettes. Up to 32 (= 256 / RGB_MATRIX_HUE_STEP) "
+ "palettes are supported. Otherwise, some palettes would be unreachable.");
+
+/** Gets the index of the selected palette. */
+static uint8_t palettefx_get_palette(void) {
+ uint8_t i =
+ (rgb_matrix_get_hue() / RGB_MATRIX_HUE_STEP) % NUM_PALETTEFX_PALETTES;
+
+ if (256 % (RGB_MATRIX_HUE_STEP * NUM_PALETTEFX_PALETTES) != 0) {
+ // Hue wraps mod 256. If NUM_PALETTEFX_PALETTES is not a power of 2, modulo
+ // 256 wraps would jump, and adjustment is needed to cycle as desired:
+ //
+ // * If the hue is a step or less from 256, assume it has wrapped down
+ // from 0 and the last palette is selected.
+ // * Otherwise, i = ((hue / step) % NUM_PALETTEFX_PALETTES) is used. If
+ // 256 - 2 * step <= hue < 256 - step, hue is set to (step * i).
+ hsv_t hsv = rgb_matrix_get_hsv();
+ if (hsv.h >= 256 - 2 * RGB_MATRIX_HUE_STEP) {
+ if (hsv.h >= 256 - RGB_MATRIX_HUE_STEP) {
+ i = NUM_PALETTEFX_PALETTES - 1;
+ hsv.h = RGB_MATRIX_HUE_STEP *
+ ((256 / (RGB_MATRIX_HUE_STEP * NUM_PALETTEFX_PALETTES)) *
+ NUM_PALETTEFX_PALETTES -
+ 1);
+ } else {
+ i %= NUM_PALETTEFX_PALETTES;
+ hsv.h = RGB_MATRIX_HUE_STEP * i;
+ }
+ rgb_matrix_sethsv_noeeprom(hsv.h, hsv.s, hsv.v);
+ }
+ }
+
+ return i;
+}
+
+static const uint16_t *palettefx_get_palette_data(void) {
+ return palettefx_palettes[palettefx_get_palette()];
+}
+
+static hsv_t palettefx_interp_color(const uint16_t *palette, uint8_t x) {
+ // Clamp `x` to [8, 247] and subtract 8, mapping to the range [0, 239].
+ x = (x <= 8) ? 0 : ((x < 247) ? (x - 8) : 239);
+ // Get index into the palette, 0 <= i <= 14.
+ const uint8_t i = x >> 4;
+ // Fractional position between i and (i + 1).
+ const uint8_t frac = x << 4;
+
+ // Look up palette colors at i and (i + 1).
+ hsv_t a = unpack_hsv16(pgm_read_word(&palette[i]));
+ hsv_t b = unpack_hsv16(pgm_read_word(&palette[i + 1]));
+
+ // Linearly interpolate in HSV, accounting for wrapping in hue.
+ const uint8_t hue_wrap = 128 & (a.h >= b.h ? (a.h - b.h) : (b.h - a.h));
+ return (hsv_t){
+ .h = lerp8by8(a.h ^ hue_wrap, b.h ^ hue_wrap, frac) ^ hue_wrap,
+ .s = scale8(lerp8by8(a.s, b.s, frac), rgb_matrix_config.hsv.s),
+ .v = scale8(lerp8by8(a.v, b.v, frac), rgb_matrix_config.hsv.v),
+ };
+}
+
+static uint16_t palettefx_scaled_time(uint32_t timer, uint8_t scale) {
+ static uint16_t wrap_correction = 0;
+ static uint8_t last_high_byte = 0;
+ const uint8_t high_byte = (uint8_t)(timer >> 16);
+
+ if (last_high_byte != high_byte) {
+ last_high_byte = high_byte;
+ wrap_correction += ((uint16_t)scale) << 8;
+ }
+
+ return scale16by8(timer, scale) + wrap_correction;
+}
+
+#endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS
diff --git a/jack/rules.mk b/jack/rules.mk
index c6f1b2b..71ddad7 100644
--- a/jack/rules.mk
+++ b/jack/rules.mk
@@ -16,35 +16,34 @@ MOUSE_JIGGLE_ENABLE = yes
CUSTOM_TAP_CODE_ENABLE = no
DEBOUNCE_TYPE = asym_eager_defer_pk
AVR_CFLAGS += -mrelax
-INTROSPECTION_KEYMAP_C = jack.c
+INTROSPECTION_KEYMAP_C = introspection.c
-SRC += tapping.c
+SRC += $(USER_PATH)/jack.c $(USER_PATH)/tapping.c
ifeq ($(strip $(OLED_ENABLE)), yes)
- SRC += oled.c
+ SRC += $(USER_PATH)/oled.c
WPM_ENABLE = yes
endif
+ifeq ($(strip $(RGB_MATRIX_ENABLE)), yes)
+ RGB_MATRIX_CUSTOM_USER = yes
+ SRC += $(USER_PATH)/color.c
+endif
+
ifeq ($(strip $(UNICODE_COMMON)), yes)
UNICODE_ENABLE = no
- SRC += unicode.c
+ SRC += $(USER_PATH)/unicode.c
endif
ifeq ($(strip $(CUSTOM_TAP_CODE_ENABLE)), yes)
OPT_DEFS += -DCUSTOM_TAP_CODE_ENABLE
- SRC += tap_code.c
+ SRC += $(USER_PATH)tap_code.c
endif
ifeq ($(PLATFORM), CHIBIOS)
- EXTRAFLAGS += -Os
-endif
-
-ifneq ($(findstring Gentoo, $(shell arm-none-eabi-gcc --version)),)
- EXTRAFLAGS += -U_FORTIFY_SOURCE
-endif
-
-ifeq ($(strip $(MOUSE_JIGGLE_ENABLE)), yes)
- OPT_DEFS += -DMOUSE_JIGGLE_ENABLE
- MOUSEKEY_ENABLE = yes
- DEFERRED_EXEC_ENABLE = yes
+ ifeq ($(MCU_SERIES), RP2040)
+ RP2040_MATH_IN_ROM = no
+ PROGRAM_CMD = doas picotool load $(BUILD_DIR)/$(TARGET).uf2
+ endif
+ EXTRAFLAGS += -O3
endif
diff --git a/jack/tapping.c b/jack/tapping.c
index 800d67d..d58b70b 100644
--- a/jack/tapping.c
+++ b/jack/tapping.c
@@ -30,6 +30,7 @@ uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) {
}
}
+#ifdef TAP_DANCE_ENABLE
void td_reset(tap_dance_state_t *state, void *user_data) {
clear_keyboard();
layer_clear();
@@ -70,6 +71,7 @@ void raise_paste(tap_dance_state_t *state, void *user_data) {
else if (state->count == 1)
tap_code16(C(S(KC_V)));
}
+#endif
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
#ifdef UNICODE_COMMON_ENABLE