Compare commits

...

4 Commits

Author SHA1 Message Date
61b29ad1d9 Fix RULE_CANCEL_ON_KEYPRESS to restore original modifier before
applying per-key rules, so Ctrl+key combos still remap correctly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 19:32:15 -07:00
061baa1cf0 Add new remap rules: Ctrl+D, Ctrl(+Shift)+P with Xcode-specific
bindings, Ctrl+Shift word selection, and Ctrl-as-Cmd for mouse
clicks in Xcode/VSCode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 19:30:59 -07:00
0e370b1abd Add list of built in LED effects in order they appear from CK_RGBMOD 2026-03-24 19:00:18 -07:00
1eae6ba455 Refactor keymap remaps into a declarative rules table
Replace the per-keycode switch statement in process_record_user with a
remap_rule_t table evaluated first-match-wins. Each rule specifies
os_mask, app_mask, mods_from, key_from, mods_to, key_to.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 18:28:42 -07:00
2 changed files with 216 additions and 212 deletions

View File

@@ -14,58 +14,127 @@ enum custom_keycodes {
CK_NOREMAP, // hold to bypass all remaps CK_NOREMAP, // hold to bypass all remaps
}; };
// --- Remap rules engine -------------------------------------------------
//
// Each rule defines a single key remap: when the trigger key is pressed with
// exactly the specified modifiers, on a matching OS and focused app, the
// modifiers are swapped and (optionally) the keycode is changed.
//
// Rules are evaluated in order; the first match wins.
// OS match bitmasks
#define OS_ANY 0xFF
#define OS_LINUX (1 << OS_MODE_LINUX)
#define OS_MAC (1 << OS_MODE_MAC)
#define OS_WIN (1 << OS_MODE_WINDOWS)
// App match bitmasks
#define APP_ANY 0xFF
#define APP(a) (1 << (a))
#define APP_NOT(a) (APP_ANY & ~APP(a))
// Rule flags
#define RULE_CANCEL_ON_KEYPRESS (1 << 0) // teardown rewrite if any other key is pressed
typedef struct { typedef struct {
uint8_t from; uint8_t os_mask; // bitmask of OS modes that match
uint8_t to; uint8_t app_mask; // bitmask of focused apps that match
} mod_rewrite_t; uint8_t mods_from; // required modifiers (exact match after normalization)
uint16_t key_from; // trigger keycode
uint8_t mods_to; // replacement modifiers
uint16_t key_to; // output keycode (0 = same as trigger key)
uint8_t flags; // RULE_* flags (0 for most rules via partial init)
} remap_rule_t;
static const mod_rewrite_t CTRL_TO_SUPER = { MOD_MASK_CTRL, MOD_MASK_GUI }; static const remap_rule_t remap_rules[] = {
static const mod_rewrite_t CTRL_SHIFT_TO_SUPER = { MOD_MASK_CTRL | MOD_MASK_SHIFT, MOD_MASK_GUI }; // --- Ctrl -> Cmd for mouse clicks (Xcode/VSCode on Mac) ---
static const mod_rewrite_t CTRL_SHIFT_TO_CMD_SHIFT = { MOD_MASK_CTRL | MOD_MASK_SHIFT, MOD_MASK_GUI | MOD_MASK_SHIFT }; // Registers Cmd while Ctrl is held alone; any keyboard key cancels it.
static const mod_rewrite_t CTRL_ALT_TO_CTRL = { MOD_MASK_CTRL | MOD_MASK_ALT, MOD_MASK_CTRL }; // Enables Cmd+click (jump to symbol) via the Ctrl key.
static const mod_rewrite_t CTRL_TO_ALT = { MOD_MASK_CTRL, MOD_MASK_ALT }; { OS_MAC, APP(APP_XCODE) | APP(APP_VSCODE), 0, KC_LCTL, 0, KC_LGUI, RULE_CANCEL_ON_KEYPRESS },
static const mod_rewrite_t ALT_TO_SUPER = { MOD_MASK_ALT, MOD_MASK_GUI }; { OS_MAC, APP(APP_XCODE) | APP(APP_VSCODE), 0, KC_RCTL, 0, KC_LGUI, RULE_CANCEL_ON_KEYPRESS },
static const mod_rewrite_t SUPER_TO_SUPER = { MOD_MASK_GUI, MOD_MASK_GUI };
static const mod_rewrite_t SUPER_TO_CTRL = { MOD_MASK_GUI, MOD_MASK_CTRL };
static const mod_rewrite_t NONE_TO_SUPER = { 0, MOD_MASK_GUI };
static const mod_rewrite_t NONE_TO_CTRL = { 0, MOD_MASK_CTRL };
// --- Text editing (Mac) ---
// Ctrl+Shift+C/V -> copy/paste (Cmd on Mac, Ctrl on Linux/Windows)
{ OS_MAC, APP_ANY, MOD_MASK_CTRL | MOD_MASK_SHIFT, KC_C, MOD_MASK_GUI, 0 },
{ OS_MAC, APP_ANY, MOD_MASK_CTRL | MOD_MASK_SHIFT, KC_V, MOD_MASK_GUI, 0 },
// Ctrl+C/V -> Cmd+C/V (not in terminal — let Ctrl+C = SIGINT through)
{ OS_MAC, APP_NOT(APP_TERMINAL), MOD_MASK_CTRL, KC_C, MOD_MASK_GUI, 0 },
{ OS_MAC, APP_NOT(APP_TERMINAL), MOD_MASK_CTRL, KC_V, MOD_MASK_GUI, 0 },
{ OS_MAC, APP_ANY, MOD_MASK_CTRL, KC_X, MOD_MASK_GUI, 0 }, // cut
{ OS_MAC, APP_ANY, MOD_MASK_CTRL, KC_A, MOD_MASK_GUI, 0 }, // select all
{ OS_MAC, APP_NOT(APP_TERMINAL), MOD_MASK_CTRL, KC_D, MOD_MASK_GUI, 0 }, // bookmark / select next occurrence
{ OS_MAC, APP_ANY, MOD_MASK_CTRL, KC_F, MOD_MASK_GUI, 0 }, // find
{ OS_MAC, APP_ANY, MOD_MASK_CTRL | MOD_MASK_SHIFT, KC_F, MOD_MASK_GUI | MOD_MASK_SHIFT, 0 }, // find in files
// Word navigation: Ctrl(+Shift)+Left/Right -> Alt(+Shift)+Left/Right
{ OS_MAC, APP_ANY, MOD_MASK_CTRL | MOD_MASK_SHIFT, KC_LEFT, MOD_MASK_ALT | MOD_MASK_SHIFT, 0 }, // select word left
{ OS_MAC, APP_ANY, MOD_MASK_CTRL | MOD_MASK_SHIFT, KC_RGHT, MOD_MASK_ALT | MOD_MASK_SHIFT, 0 }, // select word right
{ OS_MAC, APP_ANY, MOD_MASK_CTRL, KC_LEFT, MOD_MASK_ALT, 0 }, // word left
{ OS_MAC, APP_ANY, MOD_MASK_CTRL, KC_RGHT, MOD_MASK_ALT, 0 }, // word right
// Delete word: Ctrl+Backspace -> Alt+Backspace
{ OS_MAC, APP_ANY, MOD_MASK_CTRL, KC_BSPC, MOD_MASK_ALT, 0 },
// Home/End: terminal -> Ctrl+A/E, general -> Cmd+Left/Right
{ OS_MAC, APP(APP_TERMINAL), 0, KC_HOME, MOD_MASK_CTRL, KC_A },
{ OS_MAC, APP(APP_TERMINAL), 0, KC_END, MOD_MASK_CTRL, KC_E },
{ OS_MAC, APP_ANY, 0, KC_HOME, MOD_MASK_GUI, KC_LEFT },
{ OS_MAC, APP_ANY, 0, KC_END, MOD_MASK_GUI, KC_RGHT },
// --- Common Ctrl -> Cmd shortcuts (Mac) ---
{ OS_MAC, APP_ANY, MOD_MASK_CTRL, KC_S, MOD_MASK_GUI, 0 }, // save
{ OS_MAC, APP_ANY, MOD_MASK_CTRL, KC_W, MOD_MASK_GUI, 0 }, // close tab
{ OS_MAC, APP_ANY, MOD_MASK_CTRL, KC_L, MOD_MASK_GUI, 0 }, // address bar
{ OS_MAC, APP_ANY, MOD_MASK_CTRL, KC_R, MOD_MASK_GUI, 0 }, // reload
{ OS_MAC, APP(APP_XCODE), MOD_MASK_CTRL, KC_P, MOD_MASK_GUI | MOD_MASK_SHIFT, KC_O }, // Xcode: Open Quickly
{ OS_MAC, APP_ANY, MOD_MASK_CTRL, KC_P, MOD_MASK_GUI, 0 }, // print / quick open
{ OS_MAC, APP(APP_XCODE), MOD_MASK_CTRL | MOD_MASK_SHIFT, KC_P, MOD_MASK_GUI | MOD_MASK_SHIFT, KC_A }, // Xcode: Quick Actions
{ OS_MAC, APP_ANY, MOD_MASK_CTRL | MOD_MASK_SHIFT, KC_P, MOD_MASK_GUI | MOD_MASK_SHIFT, 0 }, // command palette
{ OS_MAC, APP_ANY, MOD_MASK_CTRL, KC_Z, MOD_MASK_GUI, 0 }, // undo
{ OS_MAC, APP_ANY, MOD_MASK_CTRL | MOD_MASK_SHIFT, KC_Z, MOD_MASK_GUI | MOD_MASK_SHIFT, 0 }, // redo
{ OS_MAC, APP_ANY, MOD_MASK_CTRL, KC_T, MOD_MASK_GUI, 0 }, // new tab
{ OS_MAC, APP_ANY, MOD_MASK_CTRL | MOD_MASK_SHIFT, KC_T, MOD_MASK_GUI | MOD_MASK_SHIFT, 0 }, // reopen tab
{ OS_MAC, APP_ANY, MOD_MASK_CTRL, KC_N, MOD_MASK_GUI, 0 }, // new window
{ OS_MAC, APP_ANY, MOD_MASK_CTRL | MOD_MASK_SHIFT, KC_N, MOD_MASK_GUI | MOD_MASK_SHIFT, 0 }, // incognito
// --- Workspace switching (Mac) ---
{ OS_MAC, APP_ANY, MOD_MASK_CTRL | MOD_MASK_ALT, KC_LEFT, MOD_MASK_CTRL, 0 }, // switch workspace left
{ OS_MAC, APP_ANY, MOD_MASK_CTRL | MOD_MASK_ALT, KC_RGHT, MOD_MASK_CTRL, 0 }, // switch workspace right
// --- Alt+F4 -> Cmd+Q (Mac) ---
{ OS_MAC, APP_ANY, MOD_MASK_ALT, KC_F4, MOD_MASK_GUI, KC_Q },
// --- Alt+Tab -> Cmd+Tab (Mac) ---
{ OS_MAC, APP_ANY, MOD_MASK_ALT, KC_TAB, MOD_MASK_GUI, 0 },
// --- Workspace overview (all OS) ---
{ OS_MAC, APP_ANY, MOD_MASK_GUI, KC_UP, MOD_MASK_CTRL, 0 }, // Mission Control
{ OS_LINUX, APP_ANY, MOD_MASK_GUI, KC_UP, MOD_MASK_GUI, KC_W }, // KDE overview
{ OS_WIN, APP_ANY, MOD_MASK_GUI, KC_UP, MOD_MASK_GUI, KC_TAB }, // Task View
};
// --- Modifier normalization ----------------------------------------------
// Collapse left/right mods into modifier types for comparison. // Collapse left/right mods into modifier types for comparison.
// e.g. either LCTL (0x01) or RCTL (0x10) both become 0x01. // e.g. either LCTL (0x01) or RCTL (0x10) both become 0x01.
static uint8_t normalize_mods(uint8_t mods) { static uint8_t normalize_mods(uint8_t mods) {
return (mods | (mods >> 4)) & 0x0F; return (mods | (mods >> 4)) & 0x0F;
} }
// Check if current mods exactly match a rule's `from` mods. // --- Active rewrite tracking (hold-to-repeat support) --------------------
static bool mods_match(uint8_t mods, mod_rewrite_t rule) {
return normalize_mods(mods) == normalize_mods(rule.from);
}
// Active rewrite tracking for hold-to-repeat support.
// //
// The old tap_code() approach sent an immediate press+release, so holding // register_code() keeps the key held for auto-repeat. This struct tracks the
// e.g. Ctrl+Left (remapped to Alt+Left on Mac) produced a single word-jump // state needed to clean up correctly on release. Only one rewrite can be
// instead of repeating. Now rewrite_mods()/rewrite_mods_and_key() use // active at a time (only one key auto-repeats).
// register_code() to keep the key held, and this struct tracks the state
// needed to clean up correctly on release.
//
// Only one rewrite can be active at a time (only one key auto-repeats).
typedef struct { typedef struct {
uint16_t trigger_keycode; // physical key (e.g. KC_LEFT) 0 when inactive uint16_t trigger_keycode; // physical key (e.g. KC_LEFT) -- 0 when inactive
uint8_t output_keycode; // key registered with host (may differ from trigger) uint8_t output_keycode; // key registered with host (may differ from trigger)
uint8_t from_mods; // normalized mods that were removed uint8_t from_mods; // normalized mods that were removed
uint8_t to_mods; // normalized mods that were added uint8_t to_mods; // normalized mods that were added
uint8_t saved_mods; // exact get_mods() at press time, for restoration uint8_t saved_mods; // exact get_mods() at press time, for restoration
// updated as from-mods are released during hold // updated as from-mods are released during hold
uint8_t flags; // propagated from remap_rule_t
} active_rewrite_t; } active_rewrite_t;
static active_rewrite_t active_rw = {0}; static active_rewrite_t active_rw = {0};
// Undo an active rewrite: restore saved modifiers and unregister the held key. // Undo an active rewrite: restore saved modifiers and unregister the held key.
// saved_mods starts as the exact get_mods() at press time, but is updated as
// individual from-mods are released during the hold, so it always reflects
// what should actually be active after teardown. No phantom mods.
// Mods are restored before unregister_code so only one USB report is sent.
static void teardown_active_rewrite(void) { static void teardown_active_rewrite(void) {
if (!active_rw.trigger_keycode) return; if (!active_rw.trigger_keycode) return;
set_mods(active_rw.saved_mods); set_mods(active_rw.saved_mods);
@@ -73,14 +142,9 @@ static void teardown_active_rewrite(void) {
active_rw.trigger_keycode = 0; active_rw.trigger_keycode = 0;
} }
// Intercept release events for active rewrites. Called at the top of // Intercept release events for active rewrites.
// process_record_user (after OS_MODE_NONE/no_remap bail-outs). // 1. Rewritten key released -> clean up via teardown.
// // 2. Original modifier released while key held -> remove from saved_mods.
// Handles two cases:
// 1. The rewritten key itself is released — clean up via teardown_active_rewrite().
// 2. The original modifier is released while the key is still held —
// remove it from saved_mods so teardown won't restore a phantom modifier.
//
// Returns false to consume the event, true to continue normal processing. // Returns false to consume the event, true to continue normal processing.
static bool process_rewrite_release(uint16_t keycode, keyrecord_t *record) { static bool process_rewrite_release(uint16_t keycode, keyrecord_t *record) {
if (!active_rw.trigger_keycode) return true; if (!active_rw.trigger_keycode) return true;
@@ -98,59 +162,39 @@ static bool process_rewrite_release(uint16_t keycode, keyrecord_t *record) {
return true; return true;
} }
// Rewrite modifier combos: swap exact `from` mods to `to` mods and hold keycode. // Apply a rewrite unconditionally (matching already done by caller).
// Left/right variants of the same modifier are treated as equivalent. static void apply_rewrite(uint16_t trigger_keycode, uint8_t mods_from, uint8_t mods_to, uint16_t key_out, uint8_t flags) {
//
// Uses register_code() instead of tap_code() so the key stays held for
// auto-repeat. The key is unregistered later by process_rewrite_release()
// when the physical key is released. If another rewrite is already active,
// it is cleaned up first (only one key can auto-repeat at a time).
//
// Returns true if a rewrite was applied (caller should return false to
// suppress the original keycode).
static bool rewrite_mods(uint16_t keycode, mod_rewrite_t rule) {
uint8_t mods = get_mods(); uint8_t mods = get_mods();
if (mods_match(mods, rule)) {
if (active_rw.trigger_keycode) teardown_active_rewrite();
active_rw = (active_rewrite_t){
.trigger_keycode = keycode,
.output_keycode = keycode,
.from_mods = normalize_mods(rule.from),
.to_mods = normalize_mods(rule.to),
.saved_mods = mods,
};
set_mods(active_rw.to_mods);
register_code(keycode);
return true;
}
return false;
}
// Like rewrite_mods(), but also changes the keycode sent to the host.
// trigger_keycode is the physical key pressed (used to match the release
// event later), while keycode_out is what the host actually receives.
// e.g. Home (trigger) -> Cmd+Left (output): trigger_keycode=KC_HOME,
// keycode_out=KC_LEFT, rule=NONE_TO_SUPER.
static bool rewrite_mods_and_key(uint16_t trigger_keycode, mod_rewrite_t rule, uint16_t keycode_out) {
uint8_t mods = get_mods();
if (mods_match(mods, rule)) {
if (active_rw.trigger_keycode) teardown_active_rewrite(); if (active_rw.trigger_keycode) teardown_active_rewrite();
active_rw = (active_rewrite_t){ active_rw = (active_rewrite_t){
.trigger_keycode = trigger_keycode, .trigger_keycode = trigger_keycode,
.output_keycode = keycode_out, .output_keycode = key_out,
.from_mods = normalize_mods(rule.from), .from_mods = normalize_mods(mods_from),
.to_mods = normalize_mods(rule.to), .to_mods = normalize_mods(mods_to),
.saved_mods = mods, .saved_mods = mods,
.flags = flags,
}; };
set_mods(active_rw.to_mods); set_mods(active_rw.to_mods);
register_code(keycode_out); register_code(key_out);
}
// Walk the rule table; first matching rule wins.
static bool apply_remap_rules(uint16_t keycode) {
uint8_t norm = normalize_mods(get_mods());
for (uint8_t i = 0; i < sizeof(remap_rules) / sizeof(remap_rules[0]); i++) {
const remap_rule_t *r = &remap_rules[i];
if (r->key_from != keycode) continue;
if (!((1 << os_mode) & r->os_mask)) continue;
if (!((1 << focused_app) & r->app_mask)) continue;
if (norm != normalize_mods(r->mods_from)) continue;
apply_rewrite(keycode, r->mods_from, r->mods_to, r->key_to ? r->key_to : keycode, r->flags);
return true; return true;
} }
return false; return false;
} }
// --- Keymaps -------------------------------------------------------------
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
/* Layer 0, default layer /* Layer 0, default layer
@@ -202,7 +246,7 @@ ________________________________________________________________________________
| x | | | | | | | | | | | | PGUP | | x | | | | | | | | | | | | PGUP |
|________________|________|________|________|________|________|________|________|________|________|________|____________|________|_________ |________________|________|________|________|________|________|________|________|________|________|________|____________|________|_________
| | | | | | | | | | | | | | | | | | | | | | | | | | | |
| x | | x | x | | | x | x | x | | HOME | PGDN | END | | x | | x | x | | | x | x | NOREMAP | | HOME | PGDN | END |
|____________|________|_______|________|_________________|_________________|________|________|_____________| |________|________|________| |____________|________|_______|________|_________________|_________________|________|________|_____________| |________|________|________|
*/ */
@@ -212,7 +256,7 @@ ________________________________________________________________________________
KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO,
KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO,
KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_PGUP, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_PGUP,
KC_NO, KC_TRNS, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_HOME, KC_PGDN, KC_END KC_NO, KC_TRNS, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, CK_NOREMAP, KC_HOME, KC_PGDN, KC_END
), ),
[2] = LAYOUT( [2] = LAYOUT(
@@ -259,8 +303,6 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
} }
// Cycle RGB animation (FN+Backspace) // Cycle RGB animation (FN+Backspace)
// Must update layer_rgb for all layers because system76_ec_rgb_layer()
// resets rgb_matrix_config from layer_rgb on every layer change.
if (keycode == CK_RGBMOD) { if (keycode == CK_RGBMOD) {
if (record->event.pressed) { if (record->event.pressed) {
uint8_t mode = layer_rgb[0].mode + 1; uint8_t mode = layer_rgb[0].mode + 1;
@@ -282,7 +324,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
// No rewrites when noremap held // No rewrites when noremap held
if (no_remap) return true; if (no_remap) return true;
// Handle release events for active modifier rewrites (key repeat support) // Handle release events for active modifier rewrites
if (!process_rewrite_release(keycode, record)) return false; if (!process_rewrite_release(keycode, record)) return false;
// Any other key pressed while GUI held means it's being used as a modifier // Any other key pressed while GUI held means it's being used as a modifier
@@ -290,130 +332,35 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
super_tapped = false; super_tapped = false;
} }
switch (keycode) { // Mac: tap Super -> Cmd+Space (Spotlight)
// Mac mode: tap Super -> Cmd+Space (Spotlight) if (keycode == KC_LGUI && os_mode == OS_MODE_MAC) {
// Linux/Windows: tap Super passes through normally (KDE app menu)
case KC_LGUI:
if (os_mode == OS_MODE_MAC) {
if (record->event.pressed) { if (record->event.pressed) {
super_tapped = true; super_tapped = true;
register_mods(MOD_BIT(KC_LGUI)); register_mods(MOD_BIT(KC_LGUI));
} else { } else {
if (super_tapped) { if (super_tapped) {
tap_code(KC_SPC); // GUI still held, sends Super+Space tap_code(KC_SPC);
super_tapped = false; super_tapped = false;
} }
unregister_mods(MOD_BIT(KC_LGUI)); unregister_mods(MOD_BIT(KC_LGUI));
} }
return false; return false;
} }
break;
// Mac mode rewrites: // Apply remap rules on key press
// Ctrl+C/V -> Super+C/V (copy/paste)
// Ctrl+Shift+C/V -> Super+C/V (terminal copy/paste)
case KC_C:
case KC_V:
if (os_mode == OS_MODE_MAC && record->event.pressed) {
if (rewrite_mods(keycode, CTRL_SHIFT_TO_SUPER)) return false;
// Terminal: plain Ctrl+C/V passes through (SIGINT, etc.)
if (focused_app != APP_TERMINAL) {
if (rewrite_mods(keycode, CTRL_TO_SUPER)) return false;
}
}
break;
// Mac mode rewrites:
// Ctrl+X/A/S/F -> Super+X/A/S/F (cut, select all, save, find)
// Ctrl+W/L/R -> Super+W/L/R (close tab, address bar, reload)
case KC_X:
case KC_A:
case KC_S:
case KC_F:
case KC_W:
case KC_L:
case KC_R:
if (os_mode == OS_MODE_MAC && record->event.pressed) {
if (rewrite_mods(keycode, CTRL_TO_SUPER)) return false;
}
break;
// Mac mode rewrites:
// Ctrl+Z/T/N -> Super+Z/T/N (undo, new tab, new window)
// Ctrl+Shift+Z/T/N -> Super+Shift+Z/T/N (redo, reopen tab, incognito)
case KC_Z:
case KC_T:
case KC_N:
if (os_mode == OS_MODE_MAC && record->event.pressed) {
if (rewrite_mods(keycode, CTRL_TO_SUPER)) return false;
if (rewrite_mods(keycode, CTRL_SHIFT_TO_CMD_SHIFT)) return false;
}
break;
// Mac mode rewrites:
// Ctrl+Alt+Left/Right -> Ctrl+Left/Right (switch workspace)
// Ctrl+Left/Right -> Alt+Left/Right (word navigation)
case KC_LEFT:
case KC_RGHT:
if (os_mode == OS_MODE_MAC && record->event.pressed) {
if (rewrite_mods(keycode, CTRL_ALT_TO_CTRL)) return false;
if (rewrite_mods(keycode, CTRL_TO_ALT)) return false;
}
break;
// Mac mode rewrites:
// Ctrl+Backspace -> Alt+Backspace (delete word backward)
case KC_BSPC:
if (os_mode == OS_MODE_MAC && record->event.pressed) {
if (rewrite_mods(keycode, CTRL_TO_ALT)) return false;
}
break;
// Mac mode rewrites:
// Home -> Super+Left (line start)
// End -> Super+Right (line end)
case KC_HOME:
if (os_mode == OS_MODE_MAC && record->event.pressed) {
// Terminal: Home -> Ctrl+A (line start)
if (focused_app == APP_TERMINAL) {
if (rewrite_mods_and_key(keycode, NONE_TO_CTRL, KC_A)) return false;
}
if (rewrite_mods_and_key(keycode, NONE_TO_SUPER, KC_LEFT)) return false;
}
break;
case KC_END:
if (os_mode == OS_MODE_MAC && record->event.pressed) {
// Terminal: End -> Ctrl+E (line end)
if (focused_app == APP_TERMINAL) {
if (rewrite_mods_and_key(keycode, NONE_TO_CTRL, KC_E)) return false;
}
if (rewrite_mods_and_key(keycode, NONE_TO_SUPER, KC_RGHT)) return false;
}
break;
// Mac mode rewrites:
// Alt+F4 -> Super+Q (quit app)
case KC_F4:
if (os_mode == OS_MODE_MAC && record->event.pressed) {
if (rewrite_mods_and_key(keycode, ALT_TO_SUPER, KC_Q)) return false;
}
break;
// Mac mode rewrites:
// Alt+Tab -> Super+Tab (app switcher)
case KC_TAB:
if (os_mode == OS_MODE_MAC && record->event.pressed) {
if (rewrite_mods(keycode, ALT_TO_SUPER)) return false;
}
break;
// Workspace overview:
// Super+Up -> Super+W (KDE overview, linux)
// Super+Up -> Ctrl+Up (Mission Control, mac)
// Super+Up -> Super+Tab (Task View, windows)
case KC_UP:
if (record->event.pressed) { if (record->event.pressed) {
if (os_mode == OS_MODE_MAC) { // Cancel modifier-only rewrites (e.g. Ctrl->Cmd) and restore the
if (rewrite_mods(keycode, SUPER_TO_CTRL)) return false; // original modifier so per-key rules see the correct mod state.
} else if (os_mode == OS_MODE_LINUX) { if (active_rw.trigger_keycode &&
if (rewrite_mods_and_key(keycode, SUPER_TO_SUPER, KC_W)) return false; (active_rw.flags & RULE_CANCEL_ON_KEYPRESS) &&
} else if (os_mode == OS_MODE_WINDOWS) { keycode != active_rw.trigger_keycode) {
if (rewrite_mods_and_key(keycode, SUPER_TO_SUPER, KC_TAB)) return false; uint16_t original_key = active_rw.trigger_keycode;
teardown_active_rewrite();
register_code(original_key);
} }
if (apply_remap_rules(keycode)) return false;
} }
break;
}
return true; return true;
} }
@@ -421,4 +368,3 @@ void matrix_init_user(void) {
// Force dynamic keymap reset so PROGMEM keymaps always take effect // Force dynamic keymap reset so PROGMEM keymaps always take effect
dynamic_keymap_reset(); dynamic_keymap_reset();
} }

View File

@@ -0,0 +1,58 @@
# RGB Matrix Effects
Full list of enabled effects in cycle order (FN+Backspace).
| # | Effect | Source | Description |
|---|--------|--------|-------------|
| 0 | NONE | builtin | LEDs off (skipped in cycle) |
| 1 | SOLID_COLOR | builtin | Static single color |
| 2 | ALPHAS_MODS | config.h | Static dual hue, alpha vs modifier keys |
| 3 | GRADIENT_UP_DOWN | config.h | Static gradient top to bottom |
| 4 | GRADIENT_LEFT_RIGHT | config.h | Static gradient left to right |
| 5 | BREATHING | config.h | Single hue brightness pulse |
| 6 | BAND_SAT | config.h | Saturation band scrolling left to right |
| 7 | BAND_VAL | config.h | Brightness band scrolling left to right |
| 8 | BAND_PINWHEEL_SAT | config.h | Spinning pinwheel saturation band |
| 9 | BAND_PINWHEEL_VAL | config.h | Spinning pinwheel brightness band |
| 10 | BAND_SPIRAL_SAT | config.h | Spiral saturation band |
| 11 | BAND_SPIRAL_VAL | config.h | Spiral brightness band |
| 12 | CYCLE_ALL | board | All LEDs cycle through hues together |
| 13 | CYCLE_LEFT_RIGHT | board | Hue wave scrolling left to right |
| 14 | CYCLE_UP_DOWN | board | Hue wave scrolling top to bottom |
| 15 | RAINBOW_MOVING_CHEVRON | board | Moving chevron rainbow **(default)** |
| 16 | CYCLE_OUT_IN | board | Hue wave from edges to center |
| 17 | CYCLE_OUT_IN_DUAL | board | Dual directional hue wave |
| 18 | CYCLE_PINWHEEL | board | Spinning pinwheel hue |
| 19 | CYCLE_SPIRAL | board | Spiral hue animation |
| 20 | DUAL_BEACON | config.h | Two spinning beacons |
| 21 | RAINBOW_BEACON | config.h | Single spinning rainbow beacon |
| 22 | RAINBOW_PINWHEELS | config.h | Multiple rainbow pinwheels |
| 23 | RAINDROPS | board | Random color raindrops |
| 24 | JELLYBEAN_RAINDROPS | config.h | Random color jellybean raindrops |
| 25 | HUE_BREATHING | config.h | Hue shifts with breathing |
| 26 | HUE_PENDULUM | config.h | Hue swings back and forth |
| 27 | HUE_WAVE | config.h | Hue wave animation |
| 28 | PIXEL_RAIN | config.h | Random pixels light up and fade |
| 29 | PIXEL_FLOW | config.h | Pixels flow in a direction |
| 30 | PIXEL_FRACTAL | config.h | Fractal pixel pattern |
| 31 | TYPING_HEATMAP | config.h | Heatmap of recently pressed keys (framebuffer) |
| 32 | DIGITAL_RAIN | config.h | Matrix-style digital rain (framebuffer) |
| 33 | SOLID_REACTIVE_SIMPLE | config.h | Single color, lights up on keypress |
| 34 | SOLID_REACTIVE | config.h | Single color, fades on keypress |
| 35 | SOLID_REACTIVE_WIDE | config.h | Wide ripple on keypress |
| 36 | SOLID_REACTIVE_MULTIWIDE | config.h | Multiple wide ripples |
| 37 | SOLID_REACTIVE_CROSS | config.h | Cross pattern on keypress |
| 38 | SOLID_REACTIVE_MULTICROSS | config.h | Multiple cross patterns |
| 39 | SOLID_REACTIVE_NEXUS | config.h | Nexus ripple on keypress |
| 40 | SOLID_REACTIVE_MULTINEXUS | config.h | Multiple nexus ripples |
| 41 | SPLASH | board | Rainbow splash on keypress |
| 42 | MULTISPLASH | board | Multiple rainbow splashes |
| 43 | SOLID_SPLASH | config.h | Single color splash on keypress |
| 44 | SOLID_MULTISPLASH | config.h | Multiple single color splashes |
| 45 | *active_keys* | System76 | Highlights keys with bindings on current layer |
| 46 | *raw_rgb* | System76 | Per-key RGB set via USB HID (configurator) |
| 47 | *unlocked* | System76 | Spells U-N-L-O-C-K-E-D in sequence (bootloader indicator) |
"board" = defined in `qmk_firmware/keyboards/system76/launch_3/config.h`
"config.h" = defined in `keymaps/custom/config.h`
"System76" = custom effects in `qmk_firmware/keyboards/system76/launch_3/rgb_matrix_kb.inc`