Compare commits

..

8 Commits

Author SHA1 Message Date
57df438ae3 Update readme 2026-03-24 21:58:58 -07:00
1d8195ff6b Add license 2026-03-24 21:43:10 -07:00
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
e3fa6244a7 Support hold-to-repeat for rewritten modifier combos
Replace tap_code() with register_code()/unregister_code() in the modifier
rewrite system so that holding a rewritten key combo (e.g. Ctrl+Left →
Alt+Left on Mac) produces auto-repeat instead of a single tap. Track active
rewrites to correctly restore modifiers on release, handling edge cases like
the modifier being released before the trigger key.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 07:19:51 -07:00
8a6819effc Replace right FN with dedicated CK_NOREMAP key to bypass remaps 2026-03-23 19:53:18 -07:00
4 changed files with 300 additions and 184 deletions

7
LICENSE Normal file
View File

@@ -0,0 +1,7 @@
Copyright 2026 Zuckerberg
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -21,24 +21,36 @@ Four modes, indicated by RGB flash color:
| Input (Linux muscle memory) | Output (macOS) | Action |
|---|---|---|
| Ctrl+C/V | Cmd+C/V | Copy/Paste |
| Ctrl+C/V | Cmd+C/V | Copy/Paste (skipped in Terminal — Ctrl+C = SIGINT) |
| Ctrl+Shift+C/V | Cmd+C/V | Terminal copy/paste |
| Ctrl+X/A/S/F | Cmd+X/A/S/F | Cut, Select All, Save, Find |
| Ctrl+D | Cmd+D | Bookmark / select next occurrence (skipped in Terminal) |
| Ctrl+Shift+F | Cmd+Shift+F | Find in files |
| Ctrl+P | Cmd+Shift+O (Xcode) / Cmd+P (other) | Quick open |
| Ctrl+Shift+P | Cmd+Shift+A (Xcode) / Cmd+Shift+P (other) | Command palette / Quick Actions |
| Ctrl+W/L/R | Cmd+W/L/R | Close tab, Address bar, Reload |
| Ctrl+Z/T/N | Cmd+Z/T/N | Undo, New tab, New window |
| Ctrl+Shift+Z/T/N | Cmd+Shift+Z/T/N | Redo, Reopen tab, Incognito |
| Ctrl+Left/Right | Alt+Left/Right | Word navigation |
| Ctrl+Shift+Left/Right | Alt+Shift+Left/Right | Word selection |
| Ctrl+Alt+Left/Right | Ctrl+Left/Right | Switch workspace |
| Ctrl+Backspace | Alt+Backspace | Delete word |
| Home/End | Cmd+Left/Right | Line start/end |
| Home/End | Ctrl+A/E (Terminal) / Cmd+Left/Right (other) | Line start/end |
| Alt+F4 | Cmd+Q | Quit app |
| Alt+Tab | Cmd+Tab | App switcher |
| Super tap | Cmd+Space | Spotlight |
| Super+Up | Ctrl+Up | Mission Control |
| Ctrl hold (Xcode/VSCode) | Cmd hold | Ctrl-as-Cmd for mouse clicks (e.g. jump to symbol) |
#### Layer Override
#### App-Aware Remaps
When any modifier is held (in any mode except None), the FN layer is bypassed — keys always resolve from Layer 0. This allows bypassing shortcut rewrites when wanted. For example, if you want to send Ctrl+c on mac (which is normally rewritten to cmd+c), simply use fn+Ctrl+c instead.
Some rules are per-app: Terminal.app and iTerm2 let Ctrl+C/V/D pass through so signals and shell shortcuts work, while Xcode and VSCode get IDE-specific bindings for Ctrl+P and Ctrl+Shift+P.
App awareness requires the **mac-agent** ([`mac-agent/`](mac-agent/)) running on the host. It sends the focused app name to the keyboard over Raw HID. Without it, all apps are treated as "unknown" and generic rules apply.
#### NOREMAP Key
The right FN key has been replaced with a dedicated **NOREMAP** key. Hold it to bypass all shortcut remaps — the keypress is sent exactly as typed. Useful when you need the original Ctrl+key behavior on macOS.
### Layer 1 (FN Layer)
@@ -48,11 +60,10 @@ When any modifier is held (in any mode except None), the FN layer is bypassed
| F1/F2/F3 | Mute / Vol Down / Vol Up |
| 1/2/3 | Play-Pause / Prev / Next |
| 0/-/= | LED Toggle / LED Down / LED Up |
| Backspace | Cycle RGB Effect |
| Del | Cycle OS Mode |
| Arrows | Home / PgDn / PgUp / End |
Modifier keys (Shift, Ctrl, GUI, Alt) cannot be rebound on Layer 1 due to the layer override behavior.
## Building
Requires [Nix](https://nixos.org/) with flakes enabled.
@@ -79,10 +90,15 @@ qmk compile -kb system76/launch_3 -km custom
```
keymaps/custom/
├── keymap.c # Keymaps, shortcut rewrites, layer override
├── keymap.c # Keymaps, remap rules engine, process_record_user
├── os_detect.c # OS auto-detection, RGB flash indicator
├── os_mode.h # OS mode enum and shared state
├── app_focus.c # Raw HID handler for focused-app notifications
├── app_focus.h # Focused app enum and extern
├── config.h # RGB matrix effect enables
└── rules.mk # QMK build flags
mac-agent/ # macOS daemon that sends focused app to keyboard (see mac-agent/README.md)
patches/ # Patches applied to QMK submodule at build time
qmk_firmware/ # System76 QMK fork (git submodule)
flake.nix # Nix build system
```

View File

@@ -11,63 +11,189 @@ void system76_ec_rgb_eeprom(bool write);
enum custom_keycodes {
CK_OSMODE = SAFE_RANGE, // cycle OS mode
CK_RGBMOD, // cycle RGB animation
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 {
uint8_t from;
uint8_t to;
} mod_rewrite_t;
uint8_t os_mask; // bitmask of OS modes that match
uint8_t app_mask; // bitmask of focused apps that match
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 mod_rewrite_t CTRL_SHIFT_TO_SUPER = { MOD_MASK_CTRL | MOD_MASK_SHIFT, MOD_MASK_GUI };
static const mod_rewrite_t CTRL_SHIFT_TO_CMD_SHIFT = { MOD_MASK_CTRL | MOD_MASK_SHIFT, MOD_MASK_GUI | MOD_MASK_SHIFT };
static const mod_rewrite_t CTRL_ALT_TO_CTRL = { MOD_MASK_CTRL | MOD_MASK_ALT, MOD_MASK_CTRL };
static const mod_rewrite_t CTRL_TO_ALT = { MOD_MASK_CTRL, MOD_MASK_ALT };
static const mod_rewrite_t ALT_TO_SUPER = { MOD_MASK_ALT, MOD_MASK_GUI };
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 };
static const remap_rule_t remap_rules[] = {
// --- Ctrl -> Cmd for mouse clicks (Xcode/VSCode on Mac) ---
// Registers Cmd while Ctrl is held alone; any keyboard key cancels it.
// Enables Cmd+click (jump to symbol) via the Ctrl key.
{ OS_MAC, APP(APP_XCODE) | APP(APP_VSCODE), 0, KC_LCTL, 0, KC_LGUI, RULE_CANCEL_ON_KEYPRESS },
{ OS_MAC, APP(APP_XCODE) | APP(APP_VSCODE), 0, KC_RCTL, 0, KC_LGUI, RULE_CANCEL_ON_KEYPRESS },
// --- 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.
// e.g. either LCTL (0x01) or RCTL (0x10) both become 0x01.
static uint8_t normalize_mods(uint8_t mods) {
return (mods | (mods >> 4)) & 0x0F;
}
// Check if current mods exactly match a rule's `from` mods.
// Returns false if FN layer is active (skip rewrites when FN + any modifier is held).
static bool mods_match(uint8_t mods, mod_rewrite_t rule) {
if (layer_state_is(1) && get_mods() != 0) return false;
return normalize_mods(mods) == normalize_mods(rule.from);
// --- Active rewrite tracking (hold-to-repeat support) --------------------
//
// register_code() keeps the key held for auto-repeat. 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 {
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 from_mods; // normalized mods that were removed
uint8_t to_mods; // normalized mods that were added
uint8_t saved_mods; // exact get_mods() at press time, for restoration
// updated as from-mods are released during hold
uint8_t flags; // propagated from remap_rule_t
} active_rewrite_t;
static active_rewrite_t active_rw = {0};
// Undo an active rewrite: restore saved modifiers and unregister the held key.
static void teardown_active_rewrite(void) {
if (!active_rw.trigger_keycode) return;
set_mods(active_rw.saved_mods);
unregister_code(active_rw.output_keycode);
active_rw.trigger_keycode = 0;
}
// Rewrite modifier combos: swap exact `from` mod types to `to` mods, tap keycode, restore.
// Left/right variants of the same modifier are treated as equivalent.
// Returns true if rewrite occurred.
static bool rewrite_mods(uint16_t keycode, mod_rewrite_t rule) {
// Intercept release events for active rewrites.
// 1. Rewritten key released -> clean up via teardown.
// 2. Original modifier released while key held -> remove from saved_mods.
// Returns false to consume the event, true to continue normal processing.
static bool process_rewrite_release(uint16_t keycode, keyrecord_t *record) {
if (!active_rw.trigger_keycode) return true;
if (!record->event.pressed) {
if (keycode == active_rw.trigger_keycode) {
teardown_active_rewrite();
return false;
}
if (IS_MODIFIER_KEYCODE(keycode) &&
(normalize_mods(MOD_BIT(keycode)) & active_rw.from_mods)) {
active_rw.saved_mods &= ~MOD_BIT(keycode);
return false;
}
}
return true;
}
// Apply a rewrite unconditionally (matching already done by caller).
static void apply_rewrite(uint16_t trigger_keycode, uint8_t mods_from, uint8_t mods_to, uint16_t key_out, uint8_t flags) {
uint8_t mods = get_mods();
if (mods_match(mods, rule)) {
set_mods(normalize_mods(rule.to));
tap_code(keycode);
set_mods(mods);
if (active_rw.trigger_keycode) teardown_active_rewrite();
active_rw = (active_rewrite_t){
.trigger_keycode = trigger_keycode,
.output_keycode = key_out,
.from_mods = normalize_mods(mods_from),
.to_mods = normalize_mods(mods_to),
.saved_mods = mods,
.flags = flags,
};
set_mods(active_rw.to_mods);
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 false;
}
// Rewrite modifier combo AND change the keycode sent.
// Returns true if rewrite occurred.
static bool rewrite_mods_and_key(mod_rewrite_t rule, uint16_t keycode_out) {
uint8_t mods = get_mods();
if (mods_match(mods, rule)) {
set_mods(normalize_mods(rule.to));
tap_code(keycode_out);
set_mods(mods);
return true;
}
return false;
}
// --- Keymaps -------------------------------------------------------------
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
@@ -89,7 +215,7 @@ ________________________________________________________________________________
| SHIFT | Z | X | C | V | B | N | M | < | > | ? | SHIFT | UP |
|________________|________|________|________|________|________|________|________|________|________|________|____________|________|_________
| | | | | | | | | | | | | |
| CTRL | FN | LGUI | LALT | SPACE | SPACE | RALT | RCTRL | FN | | LEFT | DOWN | RIGHT |
| CTRL | FN | LGUI | LALT | SPACE | SPACE | RALT | RCTRL | NOREMAP | | LEFT | DOWN | RIGHT |
|____________|________|_______|________|_________________|_________________|________|________|_____________| |________|________|________|
*/
@@ -99,7 +225,7 @@ ________________________________________________________________________________
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_BSLS, KC_PGDN,
KC_CAPS, 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_END,
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_LCTL, MO(1), KC_LGUI, KC_LALT, KC_SPC, KC_SPC, KC_RALT, KC_RCTL, MO(1), KC_LEFT, KC_DOWN, KC_RGHT
KC_LCTL, MO(1), KC_LGUI, KC_LALT, KC_SPC, KC_SPC, KC_RALT, KC_RCTL, CK_NOREMAP, KC_LEFT, KC_DOWN, KC_RGHT
),
/* Layer 1, function layer
@@ -120,7 +246,7 @@ ________________________________________________________________________________
| x | | | | | | | | | | | | PGUP |
|________________|________|________|________|________|________|________|________|________|________|________|____________|________|_________
| | | | | | | | | | | | | |
| x | | x | x | | | x | x | | | HOME | PGDN | END |
| x | | x | x | | | x | x | NOREMAP | | HOME | PGDN | END |
|____________|________|_______|________|_________________|_________________|________|________|_____________| |________|________|________|
*/
@@ -130,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_PGUP,
KC_NO, KC_TRNS, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_NO, KC_TRNS, 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(
@@ -152,20 +278,17 @@ ________________________________________________________________________________
),
};
// Override layer resolution: any modifier forces layer 0 (ignores FN)
uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key) {
if (os_mode != OS_MODE_NONE && get_mods()) {
layer = 0;
}
return keycode_at_keymap_location(layer, key.row, key.col);
}
static bool super_tapped = false;
static bool no_remap = false;
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
// Safety: no rewrites in none mode
if (os_mode == OS_MODE_NONE) return true;
// Cycle OS mode (FN+Del)
if (keycode == CK_OSMODE) {
if (record->event.pressed) {
teardown_active_rewrite();
os_mode = (os_mode + 1) % 4;
os_mode_manual = true;
rgb_matrix_mode_noeeprom(RGB_MATRIX_SOLID_COLOR);
@@ -180,8 +303,6 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
}
// 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 (record->event.pressed) {
uint8_t mode = layer_rgb[0].mode + 1;
@@ -193,138 +314,53 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
return false;
}
// No rewrites in none mode
if (os_mode == OS_MODE_NONE) return true;
// Hold CK_NOREMAP to bypass all remaps
if (keycode == CK_NOREMAP) {
no_remap = record->event.pressed;
if (no_remap) teardown_active_rewrite();
return false;
}
// No rewrites when noremap held
if (no_remap) return true;
// Handle release events for active modifier rewrites
if (!process_rewrite_release(keycode, record)) return false;
// Any other key pressed while GUI held means it's being used as a modifier
if (record->event.pressed && keycode != KC_LGUI) {
super_tapped = false;
}
switch (keycode) {
// Mac mode: tap Super -> Cmd+Space (Spotlight)
// Linux/Windows: tap Super passes through normally (KDE app menu)
case KC_LGUI:
if (os_mode == OS_MODE_MAC) {
if (record->event.pressed) {
super_tapped = true;
register_mods(MOD_BIT(KC_LGUI));
} else {
if (super_tapped) {
tap_code(KC_SPC); // GUI still held, sends Super+Space
super_tapped = false;
}
unregister_mods(MOD_BIT(KC_LGUI));
}
return false;
// Mac: tap Super -> Cmd+Space (Spotlight)
if (keycode == KC_LGUI && os_mode == OS_MODE_MAC) {
if (record->event.pressed) {
super_tapped = true;
register_mods(MOD_BIT(KC_LGUI));
} else {
if (super_tapped) {
tap_code(KC_SPC);
super_tapped = false;
}
break;
// Mac mode rewrites:
// 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(NONE_TO_CTRL, KC_A)) return false;
}
if (rewrite_mods_and_key(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(NONE_TO_CTRL, KC_E)) return false;
}
if (rewrite_mods_and_key(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(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 (os_mode == OS_MODE_MAC) {
if (rewrite_mods(keycode, SUPER_TO_CTRL)) return false;
} else if (os_mode == OS_MODE_LINUX) {
if (rewrite_mods_and_key(SUPER_TO_SUPER, KC_W)) return false;
} else if (os_mode == OS_MODE_WINDOWS) {
if (rewrite_mods_and_key(SUPER_TO_SUPER, KC_TAB)) return false;
}
}
break;
unregister_mods(MOD_BIT(KC_LGUI));
}
return false;
}
// Apply remap rules on key press
if (record->event.pressed) {
// Cancel modifier-only rewrites (e.g. Ctrl->Cmd) and restore the
// original modifier so per-key rules see the correct mod state.
if (active_rw.trigger_keycode &&
(active_rw.flags & RULE_CANCEL_ON_KEYPRESS) &&
keycode != active_rw.trigger_keycode) {
uint16_t original_key = active_rw.trigger_keycode;
teardown_active_rewrite();
register_code(original_key);
}
if (apply_remap_rules(keycode)) return false;
}
return true;
}
@@ -332,4 +368,3 @@ void matrix_init_user(void) {
// Force dynamic keymap reset so PROGMEM keymaps always take effect
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`