first commit

This commit is contained in:
ioeu 2023-05-12 12:14:54 +02:00
commit 8b2ff2be33
18 changed files with 1740 additions and 0 deletions

53
.clang-format Normal file
View File

@ -0,0 +1,53 @@
BasedOnStyle: Google
Language: Cpp
# Lines
ColumnLimit: 80
AllowShortFunctionsOnASingleLine: All
AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: true
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLoopsOnASingleLine: true
AllowShortLambdasOnASingleLine: All
AlwaysBreakTemplateDeclarations: Yes
CompactNamespaces: true
# Wrapping / Packing
BinPackArguments: false
BinPackParameters: false
BreakConstructorInitializers: BeforeComma
BreakInheritanceList: BeforeComma
BreakStringLiterals: true
ConstructorInitializerAllOnOneLineOrOnePerLine: false
# Alignment
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: true
AlignConsecutiveMacros: true
AlignOperands: true
AlignTrailingComments: true
AlignEscapedNewlines: Right
PointerAlignment: Left
# Classes
AccessModifierOffset: -3
# Indentation
TabWidth: 3
IndentWidth: 3
ContinuationIndentWidth: 3
UseTab: ForIndentation
IndentCaseLabels: false
IndentPPDirectives: AfterHash
SpacesBeforeTrailingComments: 2
IndentCaseLabels: true
# Misc
FixNamespaceComments: true
# Includes
SortIncludes: true
IncludeBlocks: Preserve
# Penalties
PenaltyReturnTypeOnItsOwnLine: 1000

2
.clangd Normal file
View File

@ -0,0 +1,2 @@
CompileFlags:
CompilationDatabase: ./qmk_firmware/

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.DS_Store

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "qmk_firmware"]
path = qmk_firmware
url = https://github.com/qmk/qmk_firmware.git

17
Makefile Normal file
View File

@ -0,0 +1,17 @@
KEYMAP = ioeu
KEYBOARD = splitkb/aurora/sweep
init:
# Init submodule
git submodule update --init --recursive
# Symlink keymap
rm -rf qmk_firmware/keyboards/$(KEYBOARD)/keymaps/$(KEYMAP)
ln -s $(shell pwd)/keymap qmk_firmware/keyboards/$(KEYBOARD)/keymaps/$(KEYMAP)
flash:
cd qmk_firmware; qmk flash -kb $(KEYBOARD) -km $(KEYMAP)
clean:
rm -rf qmk_firmware/keyboards/$(KEYBOARD)/keymaps$(KEYMAP)
rm -rf qmk_firmware/

11
keymap/combos.def Normal file
View File

@ -0,0 +1,11 @@
COMB(TN_CW, CW_TOGG, HRM_T, HRM_N)
COMB(RE_ESC, KC_ESC, HRM_N, HRM_E)
COMBO_REF_LAYER(STE, DEF)
COMB(YKZQUOT_STENO, TG(STE), KC_Y, KC_K, KC_Z, KC_QUOT)
COMB(CB_ALTREP, ALTREP, HRM_T, TMB_RPT)
SUBS(O_OUGH, "ough", TMB_RPT, HRM_O)
SUBS(G_ING, "ing", TMB_RPT, KC_G)
SUBS(Q_QU, "qu", TMB_RPT, KC_Q)

44
keymap/config.h Normal file
View File

@ -0,0 +1,44 @@
/* Copyright 2022 splitkb.com <support@splitkb.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#define TAPPING_TERM 200
#define QUICK_TAP_TERM 0
#define IGNORE_MOD_TAP_INTERRUPT
#define PERMISSIVE_HOLD
#define COMBO_TERM 30
#define COMBO_SHOULD_TRIGGER
#define FORCE_NKRO
// Not yet available in `keymap.json` format
#ifdef RGB_MATRIX_ENABLE
# define RGB_DISABLE_WHEN_USB_SUSPENDED
# define RGB_MATRIX_KEYPRESSES
# define ENABLE_RGB_MATRIX_SOLID_REACTIVE_SIMPLE
# define ENABLE_RGB_MATRIX_SOLID_SPLASH
# define ENABLE_RGB_MATRIX_RAINBOW_BEACON
#endif
// Not yet available in `keymap.json` format
#ifdef MOUSEKEY_ENABLE
// The default is 100
# define MOUSEKEY_WHEEL_INTERVAL 50
// The default is 40
# define MOUSEKEY_WHEEL_TIME_TO_MAX 100
#endif

215
keymap/features/achordion.c Normal file
View File

@ -0,0 +1,215 @@
// Copyright 2022-2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @file achordion.c
* @brief Achordion implementation
*
* For full documentation, see
* <https://getreuer.info/posts/keyboards/achordion>
*/
#include "achordion.h"
// Copy of the `record` and `keycode` args for the current active tap-hold key.
static keyrecord_t tap_hold_record;
static uint16_t tap_hold_keycode = KC_NO;
// Timeout timer. When it expires, the key is considered held.
static uint16_t hold_timer = 0;
// Eagerly applied mods, if any.
static uint8_t eager_mods = 0;
// Achordion's current state.
enum {
// A tap-hold key is pressed, but hasn't yet been settled as tapped or held.
STATE_UNSETTLED,
// Achordion is inactive.
STATE_RELEASED,
// Active tap-hold key has been settled as tapped.
STATE_TAPPING,
// Active tap-hold key has been settled as held.
STATE_HOLDING,
// This state is set while calling `process_record()`, which will recursively
// call `process_achordion()`. This state is checked so that we don't process
// events generated by Achordion and potentially create an infinite loop.
STATE_RECURSING,
};
static uint8_t achordion_state = STATE_RELEASED;
// Calls `process_record()` with state set to RECURSING.
static void recursively_process_record(keyrecord_t* record, uint8_t state) {
achordion_state = STATE_RECURSING;
process_record(record);
achordion_state = state;
}
// Clears eagerly-applied mods.
static void clear_eager_mods(void) {
unregister_mods(eager_mods);
eager_mods = 0;
}
// Sends hold press event and settles the active tap-hold key as held.
static void settle_as_hold(void) {
clear_eager_mods();
// Create hold press event.
recursively_process_record(&tap_hold_record, STATE_HOLDING);
}
bool process_achordion(uint16_t keycode, keyrecord_t* record) {
// Don't process events that Achordion generated.
if (achordion_state == STATE_RECURSING) {
return true;
}
// Determine whether the current event is for a mod-tap or layer-tap key.
const bool is_mt = IS_QK_MOD_TAP(keycode);
const bool is_tap_hold = is_mt || IS_QK_LAYER_TAP(keycode);
// Check key position to avoid acting on combos.
const bool is_physical_pos = (record->event.key.row < KEYLOC_COMBO &&
record->event.key.col < KEYLOC_COMBO);
if (achordion_state == STATE_RELEASED) {
if (is_tap_hold && record->tap.count == 0 && record->event.pressed &&
is_physical_pos) {
// A tap-hold key is pressed and considered by QMK as "held".
const uint16_t timeout = achordion_timeout(keycode);
if (timeout > 0) {
achordion_state = STATE_UNSETTLED;
// Save info about this key.
tap_hold_keycode = keycode;
tap_hold_record = *record;
hold_timer = record->event.time + timeout;
if (is_mt) { // Apply mods immediately if they are "eager."
uint8_t mod = mod_config(QK_MOD_TAP_GET_MODS(tap_hold_keycode));
if (achordion_eager_mod(mod)) {
eager_mods = ((mod & 0x10) == 0) ? mod : (mod << 4);
register_mods(eager_mods);
}
}
dprintf("Achordion: Key 0x%04X pressed.%s\n", keycode,
eager_mods ? " Set eager mods." : "");
return false; // Skip default handling.
}
}
return true; // Otherwise, continue with default handling.
}
if (keycode == tap_hold_keycode && !record->event.pressed) {
// The active tap-hold key is being released.
if (achordion_state == STATE_HOLDING) {
dprintln("Achordion: Key released. Plumbing hold release.");
tap_hold_record.event.pressed = false;
// Plumb hold release event.
recursively_process_record(&tap_hold_record, STATE_RELEASED);
} else {
dprintf("Achordion: Key released.%s\n",
eager_mods ? " Clearing eager mods." : "");
if (is_mt) {
clear_eager_mods();
}
}
achordion_state = STATE_RELEASED;
return false;
}
if (achordion_state == STATE_UNSETTLED && record->event.pressed) {
// Press event occurred on a key other than the active tap-hold key.
// If the other key is *also* a tap-hold key and considered by QMK to be
// held, then we settle the active key as held. This way, things like
// chording multiple home row modifiers will work, but let's our logic
// consider simply a single tap-hold key as "active" at a time.
//
// Otherwise, we call `achordion_chord()` to determine whether to settle the
// tap-hold key as tapped vs. held. We implement the tap or hold by plumbing
// events back into the handling pipeline so that QMK features and other
// user code can see them. This is done by calling `process_record()`, which
// in turn calls most handlers including `process_record_user()`.
if (!is_physical_pos || (is_tap_hold && record->tap.count == 0) ||
achordion_chord(tap_hold_keycode, &tap_hold_record, keycode, record)) {
dprintln("Achordion: Plumbing hold press.");
settle_as_hold();
} else {
clear_eager_mods(); // Clear in case eager mods were set.
dprintln("Achordion: Plumbing tap press.");
tap_hold_record.tap.count = 1; // Revise event as a tap.
tap_hold_record.tap.interrupted = true;
// Plumb tap press event.
recursively_process_record(&tap_hold_record, STATE_TAPPING);
#if TAP_CODE_DELAY > 0
wait_ms(TAP_CODE_DELAY);
#endif // TAP_CODE_DELAY > 0
dprintln("Achordion: Plumbing tap release.");
tap_hold_record.event.pressed = false;
// Plumb tap release event.
recursively_process_record(&tap_hold_record, STATE_TAPPING);
}
recursively_process_record(record, achordion_state); // Re-process event.
return false; // Block the original event.
}
return true;
}
void achordion_task(void) {
if (achordion_state == STATE_UNSETTLED &&
timer_expired(timer_read(), hold_timer)) {
dprintln("Achordion: Timeout. Plumbing hold press.");
settle_as_hold(); // Timeout expired, settle the key as held.
}
}
// Returns true if `pos` on the left hand of the keyboard, false if right.
static bool on_left_hand(keypos_t pos) {
#ifdef SPLIT_KEYBOARD
return pos.row < MATRIX_ROWS / 2;
#else
return (MATRIX_COLS > MATRIX_ROWS) ? pos.col < MATRIX_COLS / 2
: pos.row < MATRIX_ROWS / 2;
#endif
}
bool achordion_opposite_hands(const keyrecord_t* tap_hold_record,
const keyrecord_t* other_record) {
return on_left_hand(tap_hold_record->event.key) !=
on_left_hand(other_record->event.key);
}
// By default, use the BILATERAL_COMBINATIONS rule to consider the tap-hold key
// "held" only when it and the other key are on opposite hands.
__attribute__((weak)) bool achordion_chord(uint16_t tap_hold_keycode,
keyrecord_t* tap_hold_record,
uint16_t other_keycode,
keyrecord_t* other_record) {
return achordion_opposite_hands(tap_hold_record, other_record);
}
// By default, the timeout is 1000 ms for all keys.
__attribute__((weak)) uint16_t achordion_timeout(uint16_t tap_hold_keycode) {
return 1000;
}
// By default, Shift and Ctrl mods are eager, and Alt and GUI are not.
__attribute__((weak)) bool achordion_eager_mod(uint8_t mod) {
return (mod & (MOD_LALT | MOD_LGUI)) == 0;
}

164
keymap/features/achordion.h Normal file
View File

@ -0,0 +1,164 @@
// Copyright 2022-2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @file achordion.h
* @brief Achordion: Customizing the tap-hold decision.
*
* Overview
* --------
*
* This library customizes when tap-hold keys are considered held vs. tapped
* based on the next pressed key, like Manna Harbour's Bilateral Combinations or
* ZMK's positional hold. The library works on top of QMK's existing tap-hold
* implementation. You define mod-tap and layer-tap keys as usual and use
* Achordion to fine-tune the behavior.
*
* When QMK settles a tap-hold key as held, Achordion intercepts the event.
* Achordion then revises the event as a tap or passes it along as a hold:
*
* * Chord condition: On the next key press, a customizable `achordion_chord()`
* function is called, which takes the tap-hold key and the next key pressed
* as args. When the function returns true, the tap-hold key is settled as
* held, and otherwise as tapped.
*
* * Timeout: If no other key press occurs within a timeout, the tap-hold key
* is settled as held. This is customizable with `achordion_timeout()`.
*
* Achordion only changes the behavior when QMK considered the key held. It
* changes some would-be holds to taps, but no taps to holds.
*
* @note Some QMK features handle events before the point where Achordion can
* intercept them, particularly: Combos, Key Lock, and Dynamic Macros. It's
* still possible to use these features and Achordion in your keymap, but beware
* they might behave poorly when used simultaneously with tap-hold keys.
*
*
* For full documentation, see
* <https://getreuer.info/posts/keyboards/achordion>
*/
#pragma once
#include "quantum.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Handler function for Achordion.
*
* Call this function from `process_record_user()` as
*
* #include "features/achordion.h"
*
* bool process_record_user(uint16_t keycode, keyrecord_t* record) {
* if (!process_achordion(keycode, record)) { return false; }
* // Your macros...
* return true;
* }
*/
bool process_achordion(uint16_t keycode, keyrecord_t* record);
/**
* Matrix task function for Achordion.
*
* Call this function from `matrix_scan_user()` as
*
* void matrix_scan_user(void) {
* achordion_task();
* }
*/
void achordion_task(void);
/**
* Optional callback to customize which key chords are considered "held".
*
* In your keymap.c, define the callback
*
* bool achordion_chord(uint16_t tap_hold_keycode,
* keyrecord_t* tap_hold_record,
* uint16_t other_keycode,
* keyrecord_t* other_record) {
* // Conditions...
* }
*
* This callback is called if while `tap_hold_keycode` is pressed,
* `other_keycode` is pressed. Return true if the tap-hold key should be
* considered held, or false to consider it tapped.
*
* @param tap_hold_keycode Keycode of the tap-hold key.
* @param tap_hold_record keyrecord_t from the tap-hold press event.
* @param other_keycode Keycode of the other key.
* @param other_record keyrecord_t from the other key's press event.
* @return True if the tap-hold key should be considered held.
*/
bool achordion_chord(uint16_t tap_hold_keycode, keyrecord_t* tap_hold_record,
uint16_t other_keycode, keyrecord_t* other_record);
/**
* Optional callback to define a timeout duration per keycode.
*
* In your keymap.c, define the callback
*
* uint16_t achordion_timeout(uint16_t tap_hold_keycode) {
* // ...
* }
*
* The callback determines Achordion's timeout duration for `tap_hold_keycode`
* in units of milliseconds. The timeout be in the range 0 to 32767 ms (upper
* bound is due to 16-bit timer limitations). Use a timeout of 0 to bypass
* Achordion.
*
* @param tap_hold_keycode Keycode of the tap-hold key.
* @return Timeout duration in milliseconds in the range 0 to 32767.
*/
uint16_t achordion_timeout(uint16_t tap_hold_keycode);
/**
* Optional callback defining which mods are "eagerly" applied.
*
* This callback defines which mods are "eagerly" applied while a mod-tap
* key is still being settled. This is helpful to reduce delay particularly when
* using mod-tap keys with an external mouse.
*
* Define this callback in your keymap.c. The default callback is eager for
* Shift and Ctrl, and not for Alt and GUI:
*
* bool achordion_eager_mod(uint8_t mod) {
* return (mod & (MOD_LALT | MOD_LGUI)) == 0;
* }
*
* @note `mod` should be compared with `MOD_` prefixed codes, not `KC_` codes,
* described at <https://docs.qmk.fm/#/mod_tap>.
*
* @param mod Modifier `MOD_` code.
* @return True if the modifier should be eagerly applied.
*/
bool achordion_eager_mod(uint8_t mod);
/**
* Returns true if the args come from keys on opposite hands.
*
* @param tap_hold_record keyrecord_t from the tap-hold key's event.
* @param other_record keyrecord_t from the other key's event.
* @return True if the keys are on opposite hands.
*/
bool achordion_opposite_hands(const keyrecord_t* tap_hold_record,
const keyrecord_t* other_record);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,73 @@
// Copyright 2021-2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @file custom_shift_keys.c
* @brief Custom Shift Keys implementation
*
* For full documentation, see
* <https://getreuer.info/posts/keyboards/custom-shift-keys>
*/
#include "custom_shift_keys.h"
bool process_custom_shift_keys(uint16_t keycode, keyrecord_t *record) {
static uint16_t registered_keycode = KC_NO;
// If a custom shift key is registered, then this event is either releasing
// it or manipulating another key at the same time. Either way, we release
// the currently registered key.
if (registered_keycode != KC_NO) {
unregister_code16(registered_keycode);
registered_keycode = KC_NO;
}
if (record->event.pressed) { // Press event.
const uint8_t mods = get_mods();
#ifndef NO_ACTION_ONESHOT
if ((mods | get_weak_mods() | get_oneshot_mods()) & MOD_MASK_SHIFT) {
#else
if ((mods | get_weak_mods()) & MOD_MASK_SHIFT) { // Shift is held.
#endif // NO_ACTION_ONESHOT
// Continue default handling if this is a tap-hold key being held.
if ((IS_QK_MOD_TAP(keycode) || IS_QK_LAYER_TAP(keycode)) &&
record->tap.count == 0) {
return true;
}
// Search for a custom shift key whose keycode is `keycode`.
for (int i = 0; i < NUM_CUSTOM_SHIFT_KEYS; ++i) {
if (keycode == custom_shift_keys[i].keycode) {
registered_keycode = custom_shift_keys[i].shifted_keycode;
if (IS_QK_MODS(registered_keycode) && // Should keycode be shifted?
(QK_MODS_GET_MODS(registered_keycode) & MOD_LSFT) != 0) {
register_code16(registered_keycode); // If so, press it directly.
} else {
// Otherwise cancel shift mods, press the key, and restore mods.
del_weak_mods(MOD_MASK_SHIFT);
#ifndef NO_ACTION_ONESHOT
del_oneshot_mods(MOD_MASK_SHIFT);
#endif // NO_ACTION_ONESHOT
unregister_mods(MOD_MASK_SHIFT);
register_code16(registered_keycode);
set_mods(mods);
}
return false;
}
}
}
}
return true; // Continue with default handling.
}

View File

@ -0,0 +1,99 @@
// Copyright 2021-2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @file custom_shift_keys.h
* @brief Custom shift keys: customize what keycode is produced when shifted.
*
* Overview
* --------
*
* This library implements custom shift keys, keys where you can customize
* what keycode is produced when shifted.
*
* Step 1: In your keymap.c, define a table of custom shift keys like
*
* #include "features/custom_shift_keys.h"
*
* const custom_shift_key_t custom_shift_keys[] = {
* {KC_DOT , KC_QUES}, // Shift . is ?
* {KC_COMM, KC_EXLM}, // Shift , is !
* {KC_MINS, KC_EQL }, // Shift - is =
* {KC_COLN, KC_SCLN}, // Shift : is ;
* };
*
* Each row defines one key. The first field is the keycode as it appears in
* your layout and determines what is typed normally. The second entry is what
* you want the key to type when shifted.
*
* Step 2: Handle custom shift keys from your `process_record_user` function as
*
* bool process_record_user(uint16_t keycode, keyrecord_t* record) {
* if (!process_custom_shift_keys(keycode, record)) { return false; }
* // Your macros ...
*
* return true;
* }
*
* Step 3: add `features/custom_shift_keys.c` to your rules.mk as
*
* SRC += features/custom_shift_keys.c
*
*
* For full documentation, see
* <https://getreuer.info/posts/keyboards/custom-shift-keys>
*/
#pragma once
#include "quantum.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Custom shift key entry. The `keycode` field is the keycode as it appears in
* your layout and determines what is typed normally. The `shifted_keycode` is
* what you want the key to type when shifted.
*/
typedef struct {
uint16_t keycode;
uint16_t shifted_keycode;
} custom_shift_key_t;
/** Table of custom shift keys. */
extern const custom_shift_key_t custom_shift_keys[];
/** Number of entries in the `custom_shift_keys` table. */
extern uint8_t NUM_CUSTOM_SHIFT_KEYS;
/**
* Handler function for custom shift keys.
*
* In keymap.c, call this function from your `process_record_user` function as
*
* #include "features/custom_shift_keys.h"
*
* bool process_record_user(uint16_t keycode, keyrecord_t* record) {
* if (!process_custom_shift_keys(keycode, record)) { return false; }
* // Your macros ...
*
* return true;
* }
*/
bool process_custom_shift_keys(uint16_t keycode, keyrecord_t *record);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,412 @@
// Copyright 2022-2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @file repeat_key.c
* @brief Repeat Key implementation
*/
#include "repeat_key.h"
// This library makes use of keyrecord_t's `.keycode` field. This field is only
// present when Combos are enabled, which we check here. Enable Combos in your
// rules.mk by setting:
// COMBO_ENABLE = yes
#ifndef COMBO_ENABLE
#error "repeat_key: Please set `COMBO_ENABLE = yes` in rules.mk."
#else
// Variables saving the state of the last key press.
static keyrecord_t last_record = {0};
static uint8_t last_mods = 0;
// Signed count of the number of times the last key has been repeated or alternate
// repeated: it is 0 when a key is pressed normally, positive when repeated,
// and negative when alternate repeated.
static int8_t last_repeat_count = 0;
// The repeat_count, but set to 0 outside of repeat_key_invoke() so that it is
// nonzero only while a repeated key is being processed.
static int8_t processing_repeat_count = 0;
/** @brief Updates `last_repeat_count` in direction `dir`. */
static void update_last_repeat_count(int8_t dir) {
if (dir * last_repeat_count < 0) {
last_repeat_count = dir;
} else if (dir * last_repeat_count < 127) {
last_repeat_count += dir;
}
}
static void set_repeat_key_record(uint16_t keycode, keyrecord_t* record) {
last_record = *record;
last_record.keycode = keycode;
last_repeat_count = 0;
}
static void repeat_key_invoke(const keyevent_t* event) {
// It is possible (e.g. in rolled presses) that the last key changes while the
// Repeat Key is pressed. To prevent stuck keys, it is important to remember
// separately what key record was processed on press so that the the
// corresponding record is generated on release.
static keyrecord_t registered_record = {0};
static int8_t registered_repeat_count = 0;
// Since this function calls process_record(), it may recursively call itself.
// We return early if `processing_repeat_count` is nonzero to prevent infinite
// recursion.
if (processing_repeat_count || !last_record.keycode) {
return;
}
if (event->pressed) {
update_last_repeat_count(1);
// On press, apply the last mods state, stacking on top of current mods.
register_weak_mods(last_mods);
registered_record = last_record;
registered_repeat_count = last_repeat_count;
}
// Generate a keyrecord and plumb it into the event pipeline.
registered_record.event = *event;
processing_repeat_count = registered_repeat_count;
process_record(&registered_record);
processing_repeat_count = 0;
// On release, restore the mods state.
if (!event->pressed) {
unregister_weak_mods(last_mods);
}
}
/**
* @brief Find alternate keycode from a table of opposing keycode pairs.
* @param table Array of pairs of basic keycodes, declared as PROGMEM.
* @param table_size_bytes The size of the table in bytes.
* @param target The basic keycode to find.
* @return The alternate basic keycode, or KC_NO if none was found.
*
* @note The table keycodes and target must be basic keycodes.
*
* This helper is used several times below to define alternate keys. Given a table
* of pairs of basic keycodes, the function finds the pair containing `target`
* and returns the other keycode in the pair.
*/
static uint8_t find_alt_keycode(const uint8_t (*table)[2],
uint8_t table_size_bytes, uint8_t target) {
const uint8_t* keycodes = (const uint8_t*)table;
for (uint8_t i = 0; i < table_size_bytes; ++i) {
if (target == pgm_read_byte(keycodes + i)) {
// Xor (i ^ 1) the index to get the other element in the pair.
return pgm_read_byte(keycodes + (i ^ 1));
}
}
return KC_NO;
}
static void alt_repeat_key_invoke(const keyevent_t* event) {
static keyrecord_t registered_record = {0};
static int8_t registered_repeat_count = 0;
// Since this function calls process_record(), it may recursively call itself.
// We return early if `processing_repeat_count` is nonzero to prevent infinite
// recursion.
if (processing_repeat_count) {
return;
}
if (event->pressed) {
registered_record = (keyrecord_t){
#ifndef NO_ACTION_TAPPING
.tap.interrupted = false,
.tap.count = 0,
#endif
.keycode = get_alt_repeat_key_keycode(),
};
}
// Early return if there is no alternate key defined.
if (!registered_record.keycode) {
return;
}
if (event->pressed) {
update_last_repeat_count(-1);
registered_repeat_count = last_repeat_count;
}
// Generate a keyrecord and plumb it into the event pipeline.
registered_record.event = *event;
processing_repeat_count = registered_repeat_count;
process_record(&registered_record);
processing_repeat_count = 0;
}
bool process_repeat_key(uint16_t keycode, keyrecord_t* record,
uint16_t repeat_keycode) {
if (get_repeat_key_count()) {
return true;
}
if (keycode == repeat_keycode) {
repeat_key_invoke(&record->event);
return false;
} else if (record->event.pressed &&
get_repeat_key_eligible(keycode, record)) {
set_repeat_key_record(keycode, record);
set_repeat_key_mods(get_mods() | get_weak_mods()
#ifndef NO_ACTION_ONESHOT
| get_oneshot_mods()
#endif // NO_ACTION_ONESHOT
);
}
return true;
}
bool process_repeat_key_with_alt(uint16_t keycode, keyrecord_t* record,
uint16_t repeat_keycode,
uint16_t alt_repeat_keycode) {
if (keycode == alt_repeat_keycode) {
alt_repeat_key_invoke(&record->event);
return false;
}
return process_repeat_key(keycode, record, repeat_keycode);
}
int8_t get_repeat_key_count(void) { return processing_repeat_count; }
uint16_t get_repeat_key_keycode(void) { return last_record.keycode; }
uint8_t get_repeat_key_mods(void) { return last_mods; }
void set_repeat_key_keycode(uint16_t keycode) {
set_repeat_key_record(keycode, &(keyrecord_t){
#ifndef NO_ACTION_TAPPING
.tap.interrupted = false,
.tap.count = 1,
#endif
});
}
void set_repeat_key_mods(uint8_t mods) { last_mods = mods; }
uint16_t get_alt_repeat_key_keycode(void) {
uint16_t keycode = last_record.keycode;
uint8_t mods = last_mods;
// Call the user callback first to give it a chance to override the default
// alternate key definitions that follow.
uint16_t alt_keycode = get_alt_repeat_key_keycode_user(keycode, mods);
if (alt_keycode) {
return alt_keycode;
}
// Convert 8-bit mods to the 5-bit format used in keycodes. This is lossy: if
// left and right handed mods were mixed, they all become right handed.
mods = ((mods & 0xf0) ? /* set right hand bit */ 0x10 : 0)
// Combine right and left hand mods.
| (((mods >> 4) | mods) & 0xf);
switch (keycode) {
case QK_MODS ... QK_MODS_MAX: // Unpack modifier + basic key.
mods |= QK_MODS_GET_MODS(keycode);
keycode = QK_MODS_GET_BASIC_KEYCODE(keycode);
break;
#ifndef NO_ACTION_TAPPING
case QK_MOD_TAP ... QK_MOD_TAP_MAX:
keycode = QK_MOD_TAP_GET_TAP_KEYCODE(keycode);
break;
#ifndef NO_ACTION_LAYER
case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
keycode = QK_LAYER_TAP_GET_TAP_KEYCODE(keycode);
break;
#endif // NO_ACTION_LAYER
#endif // NO_ACTION_TAPPING
#ifdef SWAP_HANDS_ENABLE
case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:
if (IS_SWAP_HANDS_KEYCODE(keycode)) {
return KC_NO;
}
keycode = QK_SWAP_HANDS_GET_TAP_KEYCODE(keycode);
break;
#endif // SWAP_HANDS_ENABLE
}
if (IS_QK_BASIC(keycode)) {
if ((mods & (MOD_LCTL | MOD_LALT | MOD_LGUI))) {
// The last key was pressed with a modifier other than Shift. The
// following maps
// mod + F <-> mod + B
// and a few others, supporting several core hotkeys used in Emacs, Vim,
// less, and other programs.
// clang-format off
static const uint8_t pairs[][2] PROGMEM = {
{KC_F , KC_B }, // Forward / Backward.
{KC_D , KC_U }, // Down / Up.
{KC_N , KC_P }, // Next / Previous.
{KC_A , KC_E }, // Home / End.
};
// clang-format on
alt_keycode = find_alt_keycode(pairs, sizeof(pairs), keycode);
} else {
// The last key was pressed with no mods or only Shift. The following map
// a few more Vim hotkeys.
// clang-format off
static const uint8_t pairs[][2] PROGMEM = {
{KC_J , KC_K }, // Down / Up.
{KC_H , KC_L }, // Left / Right.
// These two lines map W and E to B, and B to W.
{KC_W , KC_B }, // Forward / Backward by word.
{KC_E , KC_B }, // Forward / Backward by word.
};
// clang-format on
alt_keycode = find_alt_keycode(pairs, sizeof(pairs), keycode);
}
if (!alt_keycode) {
// The following key pairs are considered with any mods.
// clang-format off
static const uint8_t pairs[][2] PROGMEM = {
{KC_LEFT, KC_RGHT}, // Left / Right Arrow.
{KC_UP , KC_DOWN}, // Up / Down Arrow.
{KC_HOME, KC_END }, // Home / End.
{KC_PGUP, KC_PGDN}, // Page Up / Page Down.
{KC_BSPC, KC_DEL }, // Backspace / Delete.
{KC_LBRC, KC_RBRC}, // Brackets [ ] and { }.
#ifdef EXTRAKEY_ENABLE
{KC_WBAK, KC_WFWD}, // Browser Back / Forward.
{KC_MNXT, KC_MPRV}, // Next / Previous Media Track.
{KC_MFFD, KC_MRWD}, // Fast Forward / Rewind Media.
{KC_VOLU, KC_VOLD}, // Volume Up / Down.
{KC_BRIU, KC_BRID}, // Brightness Up / Down.
#endif // EXTRAKEY_ENABLE
#ifdef MOUSEKEY_ENABLE
{KC_MS_L, KC_MS_R}, // Mouse Cursor Left / Right.
{KC_MS_U, KC_MS_D}, // Mouse Cursor Up / Down.
{KC_WH_L, KC_WH_R}, // Mouse Wheel Left / Right.
{KC_WH_U, KC_WH_D}, // Mouse Wheel Up / Down.
#endif // MOUSEKEY_ENABLE
};
// clang-format on
alt_keycode = find_alt_keycode(pairs, sizeof(pairs), keycode);
}
if (alt_keycode) {
// Combine basic keycode with mods.
return (mods << 8) | alt_keycode;
}
}
return KC_NO; // No alternate key found.
}
void repeat_key_register(void) {
repeat_key_invoke(&MAKE_KEYEVENT(0, 0, true));
}
void repeat_key_unregister(void) {
repeat_key_invoke(&MAKE_KEYEVENT(0, 0, false));
}
void repeat_key_tap(void) {
repeat_key_register();
wait_ms(TAP_CODE_DELAY);
repeat_key_unregister();
}
bool alt_repeat_key_register(void) {
if (get_alt_repeat_key_keycode()) {
alt_repeat_key_invoke(&MAKE_KEYEVENT(0, 0, true));
return true;
}
return false;
}
bool alt_repeat_key_unregister(void) {
if (get_alt_repeat_key_keycode()) {
alt_repeat_key_invoke(&MAKE_KEYEVENT(0, 0, false));
return true;
}
return false;
}
bool alt_repeat_key_tap(void) {
if (get_alt_repeat_key_keycode()) {
alt_repeat_key_register();
wait_ms(TAP_CODE_DELAY);
alt_repeat_key_unregister();
return true;
}
return false;
}
// Default implementation of get_repeat_key_eligible().
__attribute__((weak)) bool get_repeat_key_eligible(uint16_t keycode,
keyrecord_t* record) {
switch (keycode) {
// Ignore MO, TO, TG, and TT layer switch keys.
case QK_MOMENTARY ... QK_MOMENTARY_MAX:
case QK_TO ... QK_TO_MAX:
case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX:
case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:
// Ignore mod keys.
case KC_LCTL ... KC_RGUI:
case KC_HYPR:
case KC_MEH:
// Ignore one-shot keys.
#ifndef NO_ACTION_ONESHOT
case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX:
case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX:
#endif // NO_ACTION_ONESHOT
return false;
// Ignore hold events on tap-hold keys.
#ifndef NO_ACTION_TAPPING
case QK_MOD_TAP ... QK_MOD_TAP_MAX:
#ifndef NO_ACTION_LAYER
case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
#endif // NO_ACTION_LAYER
if (record->tap.count == 0) {
return false;
}
break;
#endif // NO_ACTION_TAPPING
#ifdef SWAP_HANDS_ENABLE
case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:
if (IS_SWAP_HANDS_KEYCODE(keycode) || record->tap.count == 0) {
return false;
}
break;
#endif // SWAP_HANDS_ENABLE
}
return true;
}
// Default implementation of get_alt_repeat_key_keycode_user().
__attribute__((weak)) uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode,
uint8_t mods) {
return get_rev_repeat_key_keycode_user(keycode, mods);
}
// Default implementation of deprecated callback.
__attribute__((weak)) uint16_t get_rev_repeat_key_keycode_user(uint16_t keycode,
uint8_t mods) {
return KC_NO;
}
#endif

View File

@ -0,0 +1,261 @@
// Copyright 2022-2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @file repeat_key.h
* @brief Repeat Key - an extensible "repeat last key" implementation.
*
* Repeat Key performs the action of last pressed key. Tapping the Repeat Key
* after tapping the Z key types another "z." Repeat Key remembers the modifiers
* that were active with the last key press. These modifiers are combined with
* any additional active modifiers while pressing the Repeat Key. For instance,
* if the last press key was Ctrl + Z, then Shift + Repeat Key performs Ctrl +
* Shift + Z.
*
* Also included is an Alternate Repeat Key, performing the "alternate" if there
* is one for the last key. By default it is defined for navigation keys to act
* in the reverse direction. If Page Down was the last key, the Alternate Repeat
* performs Page Up.
*
* The implementation is a generic event-plumbing strategy that interoperates
* predictably with most QMK features, including tap-hold keys, Auto Shift,
* Combos, and userspace macros.
*
* For full documentation, see
* <https://getreuer.info/posts/keyboards/repeat-key>
*/
#pragma once
#include "quantum.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Handler function for Repeat Key. Call either this function or
* `process_repeat_key_with_rev()` (but not both) from `process_record_user()`
* to implement Repeat Key in your keymap.
*
* If your `process_record_user()` has other handlers or macros, Repeat Keys
* handler `process_repeat_key()` should preferably be called before anything
* else. (If you also use Achordion, then call Achordion's handler first, Repeat
* Key's handler second, and then other handlers.)
*/
bool process_repeat_key(uint16_t keycode, keyrecord_t* record,
uint16_t repeat_keycode);
/** Handler function for Repeat Key and Alternate Repeat Key. */
bool process_repeat_key_with_alt(uint16_t keycode, keyrecord_t* record,
uint16_t repeat_keycode,
uint16_t alt_repeat_keycode);
/**
* @brief Signed count of times the key has been repeated or alternate repeated.
*
* @note The count is nonzero only while a repeated or alternate-repeated key is
* being processed.
*
* When a key is pressed normally, the count is 0. When the Repeat Key is used
* to repeat a key, the count is 1 on the first repeat, 2 on the second repeat,
* and continuing up to 127.
*
* Negative counts are used similarly for alternate repeating. When the
* Alternate Repeat Key is used, the count is -1 on the first alternate repeat,
* -2 on the second, continuing down to -127.
*/
int8_t get_repeat_key_count(void);
/** @brief Keycode of the key to be repeated. */
uint16_t get_repeat_key_keycode(void);
/** @brief Mods to be applied when repeating. */
uint8_t get_repeat_key_mods(void);
/** @brief Sets the keycode to repeat. */
void set_repeat_key_keycode(uint16_t keycode);
/** @brief Sets the mods to repeat. */
void set_repeat_key_mods(uint8_t mods);
/**
* @brief Callback defining which keys are eligible for repeating.
*
* The callback is called on every key press. Returning true means the key may
* be repeated, and returning false means the key is ignored.
*
* Here is the default implementation, which ignores modifier and layer switch
* keys so that it is possible to set some mods and change layers between
* pressing a key and repeating it:
*
* bool get_repeat_key_eligible(uint16_t keycode, keyrecord_t* record) {
* switch (keycode) {
* // Ignore MO, TO, TG, and TT layer switch keys.
* case QK_MOMENTARY ... QK_MOMENTARY_MAX:
* case QK_TO ... QK_TO_MAX:
* case QK_TOGGLE_LAYER ... QK_TOGGLE_LAYER_MAX:
* case QK_LAYER_TAP_TOGGLE ... QK_LAYER_TAP_TOGGLE_MAX:
* // Ignore mod keys.
* case KC_LCTL ... KC_RGUI:
* case KC_HYPR:
* case KC_MEH:
* // Ignore one-shot keys.
* #ifndef NO_ACTION_ONESHOT
* case QK_ONE_SHOT_LAYER ... QK_ONE_SHOT_LAYER_MAX:
* case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX:
* #endif // NO_ACTION_ONESHOT
* return false;
*
* // Ignore hold events on tap-hold keys.
* #ifndef NO_ACTION_TAPPING
* case QK_MOD_TAP ... QK_MOD_TAP_MAX:
* #ifndef NO_ACTION_LAYER
* case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
* #endif // NO_ACTION_LAYER
* if (record->tap.count == 0) {
* return false;
* }
* break;
* #endif // NO_ACTION_TAPPING
*
* #ifdef SWAP_HANDS_ENABLE
* case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:
* if (IS_SWAP_HANDS_KEYCODE(keycode) || record->tap.count == 0) {
* return false;
* }
* break;
* #endif // SWAP_HANDS_ENABLE
* }
*
* return true;
* }
*
* To customize, copy the above function into your keymap and edit.
*/
bool get_repeat_key_eligible(uint16_t keycode, keyrecord_t* record);
/**
* @brief Keycode to be used for alternate repeating.
*
* Alternate Repeat performs this keycode based on the last eligible pressed key
* and mods, get_repeat_key_keycode() and get_repeat_key_mods(). For example,
* when the last key was KC_UP, this function returns KC_DOWN. The function
* returns KC_NO if the last key doesn't have a defined alternate.
*/
uint16_t get_alt_repeat_key_keycode(void);
/**
* @brief Optional user callback to define additional alternate keys.
*
* When `get_alt_repeat_key_keycode()` is called, it first calls this callback.
* It should return a keycode representing the "alternate" of the given keycode
* and mods. Returning KC_NO defers to the default definitions in
* `get_alt_repeat_key_keycode()`.
*
* This callback can be used to define additional pairs of keys that "reverse"
* each other. More generally, Alternate Repeat can be configured to perform an
* action that "complements" the last key---Alternate Repeat not limited to
* reverse repeating, and it need not be symmetric. For instance, you can use it
* to eliminate the worst same-finger bigrams in your layout.
*/
uint16_t get_alt_repeat_key_keycode_user(uint16_t keycode, uint8_t mods);
/**
* Registers (presses down) the Repeat Key. This is useful for invoking Repeat
* as part of a tap dance or other custom handler. Note that if doing so, you
* likely want to define `repeat_key_press_user()` to ignore the key associated
* with that handler so that the Repeat Key does not attempt to repeat itself.
*/
void repeat_key_register(void);
/** Unregisters (releases) the Repeat Key. */
void repeat_key_unregister(void);
/** Taps the Repeat Key with a delay of `TAP_CODE_DELAY`. */
void repeat_key_tap(void);
/**
* Registers (presses down) the Alternate Repeat Key, performing the alternate,
* if there is one, for the last pressed key. If no alternate is found, the
* function takes no action and returns false.
*
* @return True if an alternate key was found.
*/
bool alt_repeat_key_register(void);
/**
* Unregisters (releases) the Alternate Repeat Key.
*
* @return True if an alternate key was found.
*/
bool alt_repeat_key_unregister(void);
/**
* Taps the Alternate Repeat Key with a delay of TAP_CODE_DELAY.
*
* @return True if an alternate key was found.
*/
bool alt_repeat_key_tap(void);
// Deprecated APIs.
/** @deprecated Use `process_repeat_key_with_alt()` instead. */
static inline bool process_repeat_key_with_rev(
uint16_t keycode, keyrecord_t* record,
uint16_t repeat_keycode,
uint16_t rev_repeat_keycode) {
return process_repeat_key_with_alt(keycode, record,
repeat_keycode, rev_repeat_keycode);
}
/** @deprecated Use `get_repeat_key_count()` instead. */
static inline int8_t repeat_key_count(void) { return get_repeat_key_count(); }
/** @deprecated Use `get_repeat_key_keycode()` instead. */
static inline uint16_t repeat_key_keycode(void) {
return get_repeat_key_keycode();
}
/** @deprecated Use `get_repeat_key_mods()` instead. */
static inline uint8_t repeat_key_mods(void) { return get_repeat_key_mods(); }
/** @deprecated Use `get_alt_repeat_key_keycode()` instead. */
static inline uint16_t rev_repeat_key_keycode(void) {
return get_alt_repeat_key_keycode();
}
/** @deprecated Use `get_alt_repeat_key_keycode()` instead. */
static inline uint16_t get_rev_repeat_key_keycode(void) {
return get_alt_repeat_key_keycode();
}
/** @deprecated Use `get_alt_repeat_key_keycode_user()` instead. */
uint16_t get_rev_repeat_key_keycode_user(uint16_t keycode, uint8_t mods);
/** @deprecated Use `alt_repeat_key_register()` instead. */
static inline bool rev_repeat_key_register(void) {
return alt_repeat_key_register();
}
/** @deprecated Use `alt_repeat_key_unregister()` instead. */
static inline bool rev_repeat_key_unregister(void) {
return alt_repeat_key_unregister();
}
/** @deprecated Use `alt_repeat_key_tap()` instead. */
static inline bool rev_repeat_key_tap(void) {
return alt_repeat_key_tap();
}
#ifdef __cplusplus
}
#endif

228
keymap/keymap.c Normal file
View File

@ -0,0 +1,228 @@
#include QMK_KEYBOARD_H
#include "features/achordion.h"
#include "features/custom_shift_keys.h"
#include "features/repeat_key.h"
#include "quantum.h"
// #include "keymap_steno.h"
enum layers {
DEF,
SYM,
NUM,
NAV,
OPS,
MED,
STE,
};
enum custom_keycodes {
REPEAT = SAFE_RANGE,
ALTREP,
DIRUP,
NEQ,
COLNEQ,
};
// Home row mods
#define HRM_I LCTL_T(KC_I)
#define HRM_S LALT_T(KC_S)
#define HRM_R LT(NUM, KC_R)
#define HRM_T LSFT_T(KC_T)
#define HRM_N RSFT_T(KC_N)
#define HRM_E LT(OPS, KC_E)
#define HRM_A RALT_T(KC_A)
#define HRM_O RCTL_T(KC_O)
#define MY_D LT(MED, KC_D)
// Thumb cluster
#define TMB_TAB LGUI_T(KC_TAB)
#define TMB_ENT RGUI_T(KC_ENT)
#define TMB_SPC LT(SYM, KC_SPC)
#define TMB_RPT LT(NAV, REPEAT)
// Nav
#define TAB_NXT LCTL(KC_TAB)
#define TAB_PRV LCTL(LSFT(KC_TAB))
#define HST_BCK LGUI(KC_LBRC)
#define HST_FWD LGUI(KC_RBRC)
// clang-format off
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
/*
* y c l m k z f u , '
* i s r t g p n e a o
* q v w d j b h : . x
*/
[DEF] = LAYOUT(
KC_Y, KC_C, KC_L, KC_M, KC_K, KC_Z, KC_F, KC_U, KC_COMM, KC_QUOT,
HRM_I, HRM_S, HRM_R, HRM_T, KC_G, KC_P, HRM_N, HRM_E, HRM_A, HRM_O,
KC_Q, KC_V, KC_W, MY_D, KC_J, KC_B, KC_H, KC_COLN, KC_DOT, KC_X,
TMB_RPT, TMB_TAB, TMB_ENT, TMB_SPC
),
/*
* ` $ { }
* # _ ( )
* ~ @ [ ]
*/
[SYM] = LAYOUT(
KC_GRV, KC_DLR, KC_LCBR, KC_RCBR, _______, _______, _______, _______, _______, _______,
KC_HASH, KC_UNDS, KC_LPRN, KC_RPRN, _______, _______, _______, _______, _______, _______,
KC_TILD, KC_AT, KC_LBRC, KC_RBRC, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______
),
/*
* % < > != ^
* | - + = :=
* & / * \ ../
*/
[OPS] = LAYOUT(
KC_PERC, KC_LABK, KC_RABK, NEQ, KC_CIRC, _______, _______, _______, _______, _______,
KC_PIPE, KC_MINS, KC_PLUS, KC_EQL, COLNEQ, _______, _______, _______, _______, _______,
KC_AMPR, KC_SLSH, KC_ASTR, KC_BSLS, DIRUP, _______, _______, _______, _______, _______,
_______, _______, _______, _______
),
[NUM] = LAYOUT(
_______, _______, _______, _______, _______, _______, KC_7, KC_8, KC_9, _______,
_______, _______, _______, _______, _______, _______, KC_1, KC_2, KC_3, KC_DOT,
_______, _______, _______, _______, _______, _______, KC_4, KC_5, KC_6, _______,
_______, _______, _______, KC_0
),
[NAV] = LAYOUT(
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, _______, _______, _______, KC_LEFT, KC_DOWN, KC_UP, KC_RIGHT,
_______, _______, _______, _______, _______, _______, TAB_PRV, HST_BCK, HST_FWD, TAB_NXT,
_______, _______, _______, KC_BSPC
),
[MED] = LAYOUT(
_______, _______, _______, _______, _______, RGB_TOG, RGB_HUI, RGB_SAI, RGB_VAI, _______,
_______, _______, _______, _______, _______, _______, KC_MRWD, KC_VOLD, KC_VOLU, KC_MFFD,
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
_______, _______, _______, KC_MPLY
),
[STE] = LAYOUT(
_______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
KC_LSFT, KC_I, KC_A, KC_E, KC_BSPC, KC_BSPC, KC_E, KC_A, KC_I, KC_LSFT,
KC_DOT, KC_S, KC_T, KC_N, _______, _______, KC_N, KC_T, KC_S, KC_DOT,
KC_SPC, _______, _______, KC_SPC
),
// [STE] = LAYOUT(
// STN_N1, STN_N2, STN_N3, STN_N4, STN_N5, STN_N6, STN_N7, STN_N8, STN_N9, STN_NA,
// STN_S1, STN_TL, STN_PL, STN_HL, STN_ST1, STN_DR, STN_TR, STN_LR, STN_PR, STN_FR,
// STN_S2, STN_KL, STN_WL, STN_RL, STN_ST2, STN_ZR, STN_SR, STN_GR, STN_BR, STN_RR,
// STN_A, STN_O, STN_E, STN_U
// ),
// [] = LAYOUT(
// _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
// _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
// _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
// _______, _______, _______, _______
// ),
};
// clang-format on
// Custom Shift Keys {{{
const custom_shift_key_t custom_shift_keys[] = {
{KC_DOT, KC_QUES},
{KC_COMM, KC_EXLM},
{KC_COLN, KC_SCLN},
};
uint8_t NUM_CUSTOM_SHIFT_KEYS =
sizeof(custom_shift_keys) / sizeof(custom_shift_key_t);
// }}}
// Combos {{{
#include "g/keymap_combo.h"
bool combo_should_trigger(uint16_t combo_index,
combo_t *combo,
uint16_t keycode,
keyrecord_t *record) {
if (layer_state_is(STE) && combo_index != YKZQUOT_STENO) return false;
return true;
}
// }}}
// User functions {{{
void keyboard_pre_init_user(void) {
// Set our LED pin as output
setPinOutput(24);
// Turn the LED off
// (Due to technical reasons, high is off and low is on)
writePinHigh(24);
}
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
if (!process_achordion(keycode, record)) return false;
if (!process_custom_shift_keys(keycode, record)) return false;
if (keycode == TMB_RPT && !record->tap.count) { return true; }
if (!process_repeat_key_with_alt(keycode, record, TMB_RPT, ALTREP))
return false;
switch (keycode) {
case DIRUP:
if (record->event.pressed) {
SEND_STRING("../");
return false;
}
break;
case NEQ:
if (record->event.pressed) {
SEND_STRING("!=");
return false;
}
break;
case COLNEQ:
if (record->event.pressed) {
SEND_STRING(":=");
return false;
}
break;
}
return true;
}
void matrix_scan_user(void) { achordion_task(); }
// }}}
// Achordion {{{
bool achordion_chord(uint16_t tap_hold_keycode,
keyrecord_t *tap_hold_record,
uint16_t other_keycode,
keyrecord_t *other_record) {
switch (tap_hold_keycode) {
// Allow same hand shortcuts with thumbs
case TMB_TAB:
case TMB_ENT: return true;
// Allow ctrl + tab
case HRM_I:
if (other_keycode == TMB_TAB) return true;
}
return achordion_opposite_hands(tap_hold_record, other_record);
}
bool achordion_eager_mod(uint8_t mod) {
switch (mod) {
case MOD_LSFT:
case MOD_RSFT:
case MOD_LGUI:
case MOD_RGUI: return true;
default: return false;
}
}
// }}}
// vim: set foldmethod=marker :

144
keymap/readme.md Normal file
View File

@ -0,0 +1,144 @@
# Aurora Sweep's Default Keymap
_This keymap is a copy of the [Ferris default keymap](https://github.com/qmk/qmk_firmware/tree/master/keyboards/ferris/keymaps/default), with the addition of RGB modifier keys._
A usable default keymap for the Ferris keyboard
===============================================
Keymaps in general are quite personal, so it is difficult to come up with a default that will suit every user.
This keymap makes heavy use of keys behaving differently when tapped and held, so that all the keys one may need remain accessible despite the low number of thumb keys.
It comes with a number of layers to give access to most of the keys one may need on a keyboard. It is not meant to be the best possible keymap, but rather a good base on which to build a keymap that works for you.
This is not the only way to make 34 keys a comfortable typing experience, but it is one way to do so. If you don't already know of a better way, this may be as good a starting point as any :)
Note that this keymap was built from the perspective that it is OK to take a steep learning curve if it results in a keymap that is easier to use in the long run. This means that it may take more effort to learn this keymap than some alternatives. "Easy to use" was assessed against the workflow of the author, so your mileage may vary on some of the details.
What do all these layers do?
----------------------------
### Layer 0: Base layer
![Layer 0](https://raw.githubusercontent.com/splitkb/qmk_firmware/assets/aurora/sweep/keymaps/default/layer0.png)
On tapping the keys, our base layer is qwerty with space on the right homing thumb and backspace on the left homing thumb.
In this layer, the non-homing-thumb positions have 0 and 1. I recommend modifying this to some frequently accessed shortcut such as copy/paste, previous/next tab or anything that makes most sense in your own workflow. O and 1 are place-holders and make it easy to troubleshoot that all keys are working properly before soldering in the switches.
The reason I recommend convenience shortcuts instead of more commonly used keys like tab or meta is that unhoming of the thumbs was a frequent source of typos for me when I used more than one thumb key frequently in the context of typing.
Despite being missing on this layer, "meta", "tab", "esc" and such are accessible from any other layer: see Layer 7.
The behaviour of some keys differ when held:
* Both homing pinkies behave as shift.
* Both bottom-row ring fingers behave as ctrl.
* Both bottom-row middle fingers behave as alt.
* The homing left ring finger gives access to the Function keys layer
* The homing right ring finger gives access to the Numbers layer
* The homing left middle finger gives access to the Mouse layer
* The homing right middle finger gives access to the Navigation layer
* The homing left index finger gives access to the Right symbols layer
* The homing right index finger gives access to the Left symbols layer
* The homing right thumb gives access to the Always accessible layer
### Layer 1: Mouse
![Layer 1](https://raw.githubusercontent.com/splitkb/qmk_firmware/assets/aurora/sweep/keymaps/default/layer1.png)
Layer 1 is a mouse layer: it can be used one-handed or two-handed. The most common way to use it is two handed, with left and right click on the homerow of the left hand and directions on the homerow of the right hand.
Scrolling is available on the right hand with mid finger up and down for vertical scroll and index and ring finger down for horizontal scroll.
On the right hand, left click and right click are also available with index and ring finger up to allow one handed operation. This can be particularly handy when enabling the mouse layer permanently (no need to hold the left middle finger), which can be done from Layer 7.
**Addition**: The Aurora Sweep allows the RGB settings to be modified on this layer. This is an extra feature over the default Ferris / Sweep keymap.
### Layer 2: Navigation
![Layer 2](https://raw.githubusercontent.com/splitkb/qmk_firmware/assets/aurora/sweep/keymaps/default/layer2.png)
The navigation layer somewhat mirrors the mouse layer. It is accessed by holding the right middle finger and gives access to arrow keys on the left homerow. Page up and down, Home and End mirror the vertical scrolling and horizontal scrolling on the mouse layer.
On the right hand, in addition to ctrl and alt which are available through transparency, ctrl + alt, ctrl + alt + shift and meta are accessible on the homerow to enable common shortcuts in some window managers. This part is quite workflow dependent, so make sure to adapt it to your own workflow as appropriate.
### Layer 3: Right symbols
![Layer 3](https://raw.githubusercontent.com/splitkb/qmk_firmware/assets/aurora/sweep/keymaps/default/layer3.png)
When holding down the left index, one may access about half of the symbols. The pinkies store `^` and `$` symbols that represent begin and end in vim. The left homerow hosts `*` and `&`, symbols which are related in the way that they represent some form of indirection in programming languages such as rust. On the right hand, most symbols used when navigating the command line are stored together, organized by columns of related symbols.
### Layer 4: Left symbols
![Layer 4](https://raw.githubusercontent.com/splitkb/qmk_firmware/assets/aurora/sweep/keymaps/default/layer4.png)
When holding down the right index, one may access the other symbols. On the left hand, most of the different brackets are laid out. The most frequent ones (round brackets and curly brackets) get a spot on the homerow. The rest of the layer hosts the remaining symbols that are easier to access here than on any other layers.
### Layer 5: Function keys
![Layer 5](https://raw.githubusercontent.com/splitkb/qmk_firmware/assets/aurora/sweep/keymaps/default/layer5.png)
By holding down the left ring finger, one may access the function keys, roughly in a numpad layout.
This means that alt+F4 is easy to type, with F4 being on the homerow.
There is a shortcut for ctrl+alt on the left hand to enable convenient switching between virtual terminals on Linux.
### Layer 6: Numbers
![Layer 6](https://raw.githubusercontent.com/splitkb/qmk_firmware/assets/aurora/sweep/keymaps/default/layer6.png)
The number layer is accessed by holding the right ring finger. It hosts the numbers and some duplicated symbols that are commonly accessed next to numbers, such as mathematical operators.
The number are layed out similarly to a numpad, but with the middle row and the homerow swapped so that the most used numbers: 0, 1, 2 and 3 are all available in homing positions.
### Layer 7: Always accessible
![Layer 7](https://raw.githubusercontent.com/splitkb/qmk_firmware/assets/aurora/sweep/keymaps/default/layer7.png)
Layer 7 is accessed by holding the right homing thumb down. Because this position is left transparent from every other layer, this layer is always accessible.
It gives access to some essential keys that would typically be accessed on a thumb cluster or pinkies, such as meta, enter, tab, esc and delete.
As the layer hosting esc, we duplicated some symbols here to allow for fast navigation in vim. For instance, esc, :, w, q can be done in a single roll.
Where is the keymap.c?
----------------------
The keymap.c file is not published to the repository. It is generated from `keymap.json` by the build system.
This avoids duplicating information and allow users to edit their keymap from the QMK Configurator web interface.
How do I edit and update the keymap?
------------------------------------
The `keymap.json` file is generated from the QMK Configurator interface and formatted for better readability in the context of the Ferris keyboard.
To edit it, you may:
* Edit it directly from a text editor.
* Edit it from the QMK Configurator.
If you decide to use the latter workflow, here are the steps to follow:
* From the [QMK Configurator](https://config.qmk.fm/#/splitkb/aurora/sweep/rev1/LAYOUT_split_3x5_2), hit the "import QMK keymap json file" button (it has a drawing with an up arrow on it).
* Browse to the location of your keymap (for example, `<your qmk repo>/keyboards/splitkb/aurora/sweep/keymaps/default/keymap.json`)
* Perform any modification to the keymap in the web UI
* Export the keymap to your downloads folder, by hitting the "Export QMK keymap json file" button (it has a drawing with a down arrow on it)
* Replace your original keymap with the one you just downloaded
_**Note:** At the time of writing (the 24th of October 2022), not every feature used in the default keymap is supported by the QMK Configurator. You cannot yet upload the default `keymap.json` due to a file format mismatch - use the "Load Default" button to load the default keymap instead. Additionally, custom configuration options are still being worked on: if your keymap depends on them, please compile your firmware offline for now._
I want to do more than the JSON format supports!
-------------------------------------------------
While the `json` format is easy to use, it does lack certain functionality - most notably custom OLED or encoder behaviour.
To add this, you need to convert it to the `c` format. Do keep in mind that this is generally a one-way operation.
First, from the root of your qmk repo, move to your keymap folder
```bash
cd ./keymaps/splitkb/aurora/corne/my_personal_keymap
```
Next, convert your `keymap.json` to a `keymap.c`
```bash
qmk json2c -o keymap.c keymap.json
```
You can add custom C code to the newly generated `keymap.c` file. Do note that you have to use **either** a C file **or** a JSON file - you cannot do both!
**If a JSON file is present, the C file is ignored.**

11
keymap/rules.mk Normal file
View File

@ -0,0 +1,11 @@
SRC += features/achordion.c \
features/repeat_key.c \
features/custom_shift_keys.c
VPATH += keyboards/gboards
COMBO_ENABLE = yes
CAPS_WORD_ENABLE = yes
STENO_ENABLE = yes
STENO_PROTOCOL = geminipr
NKRO_ENABLE = yes
RGBLIGHT_ENABLE = yes
CONVERT_TO=promicro_rp2040

1
keymap/sweep Symbolic link
View File

@ -0,0 +1 @@
/Users/joel/Code/keymap/sweep

1
qmk_firmware Submodule

@ -0,0 +1 @@
Subproject commit f8d01e5ecab6488a17299384880506ff969200ba