@ -0,0 +1,367 @@ | |||
# QMK Breaking Changes - 2023 February 26 Changelog | |||
## Changes Requiring User Action :id=changes-requiring-user-action | |||
### `IGNORE_MOD_TAP_INTERRUPT` behaviour changes ([#15741](https://github.com/qmk/qmk_firmware/pull/15741)) :id=i-m-t-i | |||
`IGNORE_MOD_TAP_INTERRUPT_PER_KEY` has been removed and `IGNORE_MOD_TAP_INTERRUPT` deprecated as a stepping stone towards making `IGNORE_MOD_TAP_INTERRUPT` the new default behavior for mod-taps in the future. | |||
In place of the now removed `IGNORE_MOD_TAP_INTERRUPT_PER_KEY`, one must use the pre-existing `HOLD_ON_OTHER_KEY_PRESS` option. | |||
In most cases, updating `get_ignore_mod_tap_interrupt` to `get_hold_on_other_key_press` is simply a matter of renaming the function and swapping every `true` by `false` and vice versa. The one subtlety you may need to look out for is that the `get_ignore_mod_tap_interrupt` was only ever called with mod-taps passed in as the `keycode` argument, while the `keycode` argument of `get_hold_on_other_key_press` can be any dual-role key. This includes not only mod-taps, but also layer-taps, one shot keys, `TT(layer)` and more. This has an impact on the effect of the `default` case in a typical per-key configuration making use of a `switch(keycode)` statement. | |||
To illustrate, let's take the example of a configuration where we'd want all mod-taps to activate the modifier if another key is pressed while held with the exception of `LCTL_T(KC_A)`, which should ignore keys pressed while it is held and activate the modifier only if it has been held for longer than the tapping term. In addition, we would like to keep the default "ignore-interrupt" behavior of layer taps. | |||
An old way to do this would be via the following code: | |||
```c | |||
bool get_ignore_mod_tap_interrupt(uint16_t keycode, keyrecord_t *record) { | |||
switch(keycode) { | |||
case LCTL_T(KC_A): | |||
return true; | |||
default: | |||
return false; | |||
} | |||
} | |||
``` | |||
The correct way to update this code without accidentally changing how the layer-taps work would be the following: | |||
```c | |||
bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record) { | |||
switch(keycode) { | |||
// Capture all mod-tap keycodes. | |||
case QK_MOD_TAP ... QK_MOD_TAP_MAX: | |||
if (keycode == LCTL_T(KC_A)) { | |||
// Disable HOLD_ON_OTHER_KEY_PRESS for LCTL_T(KC_A) | |||
// aka enable IGNORE_MOD_TAP_INTERRUPT for LCTL_T(KC_A). | |||
return false; | |||
} else { | |||
// Enable HOLD_ON_OTHER_KEY_PRESS for every other mod-tap keycode. | |||
return true; | |||
} | |||
default: | |||
return false; | |||
} | |||
} | |||
``` | |||
For more information, you are invited to read the sections on [IGNORE_MOD_TAP_INTERRUPT](tap_hold.md#ignore-mod-tap-interrupt) and [HOLD_ON_OTHER_KEY_PRESS](tap_hold.md#hold-on-other-key-press) in the page on [Tap-Hold configuration options](tap_hold.md). | |||
### `TAPPING_FORCE_HOLD` => `QUICK_TAP_TERM` ([#17007](https://github.com/qmk/qmk_firmware/pull/17007)) :id=quick-tap-term | |||
`TAPPING_FORCE_HOLD` feature is now replaced by `QUICK_TAP_TERM`. Instead of turning off auto-repeat completely, user will have the option to configure a `QUICK_TAP_TERM` in milliseconds. When the user holds a tap-hold key after tapping it within `QUICK_TAP_TERM`, QMK will send the tap keycode to the host, enabling auto-repeat. | |||
Its value is set to `TAPPING_TERM` by default and it can be reduced to match typing habits to avoid false triggers. To disable auto-repeat completely, set `QUICK_TAP_TERM` to zero. | |||
`TAPPING_FORCE_HOLD_PER_KEY` is also deprecated and replaced by `QUICK_TAP_TERM_PER_KEY`. The old granular control function for tapping force hold is: | |||
```c | |||
bool get_tapping_force_hold(uint16_t keycode, keyrecord_t *record) { | |||
switch (keycode) { | |||
case LT(1, KC_BSPC): | |||
return true; | |||
default: | |||
return false; | |||
} | |||
} | |||
``` | |||
That function can be replaced with: | |||
```c | |||
uint16_t get_quick_tap_term(uint16_t keycode, keyrecord_t *record) { | |||
switch (keycode) { | |||
case SFT_T(KC_SPC): | |||
return 0; | |||
default: | |||
return QUICK_TAP_TERM; | |||
} | |||
} | |||
``` | |||
For more details, please read the updated documentation section on [Quick Tap Term](tap_hold.md#quick-tap-term). | |||
### Leader Key Rework :id=leader-key-rework ([#19632](https://github.com/qmk/qmk_firmware/pull/19632)) | |||
The Leader Key feature API has been significantly improved, along with some bugfixes and added tests. | |||
Instead of defining your leader sequences in `matrix_scan_user()`, they are now handled in the `leader_end_user()` callback, and the `LEADER_EXTERNS()`/`LEADER_DICTIONARY()` macros are no longer needed: | |||
```c | |||
void leader_end_user(void) { | |||
if (leader_sequence_one_key(KC_F)) { | |||
// Leader, f => Types the below string | |||
SEND_STRING("QMK is awesome."); | |||
} else if (leader_sequence_two_keys(KC_D, KC_D)) { | |||
// Leader, d, d => Ctrl+A, Ctrl+C | |||
SEND_STRING(SS_LCTL("a") SS_LCTL("c")); | |||
} else if (leader_sequence_three_keys(KC_D, KC_D, KC_S)) { | |||
// Leader, d, d, s => Types the below string | |||
SEND_STRING("https://start.duckduckgo.com\n"); | |||
} else if (leader_sequence_two_keys(KC_A, KC_S)) { | |||
// Leader, a, s => GUI+S | |||
tap_code16(LGUI(KC_S)); | |||
} | |||
} | |||
``` | |||
For more information please see the [Leader Key documentation](feature_leader_key.md). | |||
### Updated Keyboard Codebases :id=updated-keyboard-codebases | |||
The following keyboards have had their source moved within QMK: | |||
| Old Keyboard Name | New Keyboard Name | | |||
|-----------------------------|--------------------------| | |||
| ramonimbao/aelith | rmi_kb/aelith | | |||
| ramonimbao/herringbone/pro | rmi_kb/herringbone/pro | | |||
| ramonimbao/herringbone/v1 | rmi_kb/herringbone/v1 | | |||
| ramonimbao/mona/v1_1 | rmi_kb/mona/v1_1 | | |||
| ramonimbao/mona/v1 | rmi_kb/mona/v1 | | |||
| ramonimbao/mona/v32a | rmi_kb/mona/v32a | | |||
| ramonimbao/squishy65 | rmi_kb/squishy65 | | |||
| ramonimbao/squishytkl | rmi_kb/squishytkl | | |||
| ramonimbao/tkl_ff | rmi_kb/tkl_ff | | |||
| ramonimbao/tkl_ff/v1 | rmi_kb/tkl_ff/v1 | | |||
| ramonimbao/tkl_ff/v2 | rmi_kb/tkl_ff/v2 | | |||
| ramonimbao/wete/v1 | rmi_kb/wete/v1 | | |||
| ramonimbao/wete/v2 | rmi_kb/wete/v2 | | |||
| the_uni | stenothe_uni | | |||
| xelus/xs60 | xelus/xs60/soldered | | |||
## Notable core changes :id=notable-core | |||
As per last breaking changes cycle, there has been _a lot_ of emphasis on behind-the-scenes changes, mainly around consolidation of core subsystems and constant values, as well as addressing tech debt. Whilst not outwardly visible, this cleanup and refactoring should start paying dividends as it simplifies future development and maintenance. | |||
A handful of examples: | |||
* Standardised the lower/raise/adjust layer change pattern with explicit keycodes and configurable target layers | |||
* Cleaned up a lot of Makefile logic to simplify and speed up builds | |||
* Automated tooling to regenerate keycode values has been hooked into the PR pipeline and will trigger failures if they're incorrect | |||
* Many more configuration options have moved into `info.json`, such as backlight, encoders | |||
* Additional unit tests to ensure keycode behaviours don't accidentally change | |||
## Full changelist :id=full-changelist | |||
Core: | |||
* Remove IGNORE_MOD_TAP_INTERRUPT_PER_KEY in favour of HOLD_ON_OTHER_KEY_PRESS_PER_KEY ([#15741](https://github.com/qmk/qmk_firmware/pull/15741)) | |||
* Add combo hook to allow per layer combo reference layers. ([#16699](https://github.com/qmk/qmk_firmware/pull/16699)) | |||
* Replace Tapping Force Hold feature with Quick Tap Term ([#17007](https://github.com/qmk/qmk_firmware/pull/17007)) | |||
* [Test] Reset timer for every unit test and provide timestamps for log messages ([#17028](https://github.com/qmk/qmk_firmware/pull/17028)) | |||
* Bug17281 - Retain momentary layers until the end of tapping ([#17282](https://github.com/qmk/qmk_firmware/pull/17282)) | |||
* Detect host OS based on USB fingerprint ([#18463](https://github.com/qmk/qmk_firmware/pull/18463)) | |||
* allow locking the matrix state ([#18852](https://github.com/qmk/qmk_firmware/pull/18852)) | |||
* Initial DD keymap_extras migration ([#19031](https://github.com/qmk/qmk_firmware/pull/19031)) | |||
* Support inverted scan logic for optical switches ([#19053](https://github.com/qmk/qmk_firmware/pull/19053)) | |||
* Corrections to uart driver for Chibios platform ([#19075](https://github.com/qmk/qmk_firmware/pull/19075)) | |||
* Remaining DD keymap_extras migration ([#19110](https://github.com/qmk/qmk_firmware/pull/19110)) | |||
* Add udev rule for the WB32 DFU bootloader ([#19135](https://github.com/qmk/qmk_firmware/pull/19135)) | |||
* Add Michi MCU Converter support ([#19163](https://github.com/qmk/qmk_firmware/pull/19163)) | |||
* Add Split support for Haptic feedback ([#19203](https://github.com/qmk/qmk_firmware/pull/19203)) | |||
* Allow mod-tap hold action on one shot layer ([#19214](https://github.com/qmk/qmk_firmware/pull/19214)) | |||
* Remove RGBLIGHT_ANIMATIONS from core (+cleanup) ([#19216](https://github.com/qmk/qmk_firmware/pull/19216)) | |||
* Revert WB32 ISO workaround ([#19224](https://github.com/qmk/qmk_firmware/pull/19224)) | |||
* Prevent dynamic keymaps from processing layers that don't exist ([#19225](https://github.com/qmk/qmk_firmware/pull/19225)) | |||
* Add `*_RIGHT` configuration for PMW33XX driver ([#19243](https://github.com/qmk/qmk_firmware/pull/19243)) | |||
* Remove deprecated led_set_kb ([#19273](https://github.com/qmk/qmk_firmware/pull/19273)) | |||
* Tests that caps word stays active after use of OSL ([#19303](https://github.com/qmk/qmk_firmware/pull/19303)) | |||
* Allow overriding of keymap/encodermap layer count. ([#19325](https://github.com/qmk/qmk_firmware/pull/19325)) | |||
* guard action related debug messages ([#19348](https://github.com/qmk/qmk_firmware/pull/19348)) | |||
* use `IS_EVENT` macro instead of `!IS_NOEVENT` ([#19366](https://github.com/qmk/qmk_firmware/pull/19366)) | |||
* [Test] Introduce VERIFY_AND_CLEAR shorthand ([#19370](https://github.com/qmk/qmk_firmware/pull/19370)) | |||
* Add RGB565 and RGB888 color support to Quantum Painter ([#19382](https://github.com/qmk/qmk_firmware/pull/19382)) | |||
* Initial DD keycode regen workflow ([#19400](https://github.com/qmk/qmk_firmware/pull/19400)) | |||
* Update RGB matrix reactive gradient timer scale ([#19415](https://github.com/qmk/qmk_firmware/pull/19415)) | |||
* De-obfuscate random8 functions ([#19416](https://github.com/qmk/qmk_firmware/pull/19416)) | |||
* Use random8 for jellybean effect ([#19418](https://github.com/qmk/qmk_firmware/pull/19418)) | |||
* Align definition of unicode_map ([#19452](https://github.com/qmk/qmk_firmware/pull/19452)) | |||
* Add analog support for RP2040 ([#19453](https://github.com/qmk/qmk_firmware/pull/19453)) | |||
* [CI] Regenerate Files ([#19463](https://github.com/qmk/qmk_firmware/pull/19463)) | |||
* Build warning when not valid work-tree ([#19475](https://github.com/qmk/qmk_firmware/pull/19475)) | |||
* Migrate 'make git-submodule' to CLI command ([#19479](https://github.com/qmk/qmk_firmware/pull/19479)) | |||
* Remove cmp checks from Makefile ([#19480](https://github.com/qmk/qmk_firmware/pull/19480)) | |||
* Replace list_keyboards.sh with CLI calls ([#19485](https://github.com/qmk/qmk_firmware/pull/19485)) | |||
* Remove unused Makefile paths ([#19487](https://github.com/qmk/qmk_firmware/pull/19487)) | |||
* Migrate submodule dirty check to CLI ([#19488](https://github.com/qmk/qmk_firmware/pull/19488)) | |||
* Remove `make all-<platform>` build targets ([#19496](https://github.com/qmk/qmk_firmware/pull/19496)) | |||
* Relax converter validation within keymap schema ([#19544](https://github.com/qmk/qmk_firmware/pull/19544)) | |||
* De-duplicate platform detection ([#19545](https://github.com/qmk/qmk_firmware/pull/19545)) | |||
* Add alias support for converters ([#19563](https://github.com/qmk/qmk_firmware/pull/19563)) | |||
* Revert "De-duplicate platform detection" ([#19564](https://github.com/qmk/qmk_firmware/pull/19564)) | |||
* Add mmoskal/uf2-stm32f103 bootloader support ([#19594](https://github.com/qmk/qmk_firmware/pull/19594)) | |||
* usb_main.c: remove `CH_KERNEL_MAJOR` check ([#19597](https://github.com/qmk/qmk_firmware/pull/19597)) | |||
* Use the correct keycode when updating WPM ([#19599](https://github.com/qmk/qmk_firmware/pull/19599)) | |||
* De-duplicate platform detection ([#19603](https://github.com/qmk/qmk_firmware/pull/19603)) | |||
* Refactor rain pixel function ([#19606](https://github.com/qmk/qmk_firmware/pull/19606)) | |||
* ChibiOS: Consolidate report sending code ([#19607](https://github.com/qmk/qmk_firmware/pull/19607)) | |||
* Add f303 to tinyuf2 bootloader support ([#19620](https://github.com/qmk/qmk_firmware/pull/19620)) | |||
* Refactor Leader key feature ([#19632](https://github.com/qmk/qmk_firmware/pull/19632)) | |||
* Split out mcu_selection to platform ([#19701](https://github.com/qmk/qmk_firmware/pull/19701)) | |||
* Move MIDI code out of tmk_core ([#19704](https://github.com/qmk/qmk_firmware/pull/19704)) | |||
* Remove deprecated Quantum keycodes ([#19712](https://github.com/qmk/qmk_firmware/pull/19712)) | |||
* QP: Correct rotation and offset when using LVGL ([#19713](https://github.com/qmk/qmk_firmware/pull/19713)) | |||
* Remove usages of config_common.h from config.h files. ([#19714](https://github.com/qmk/qmk_firmware/pull/19714)) | |||
* Relocate diode direction definitions ([#19715](https://github.com/qmk/qmk_firmware/pull/19715)) | |||
* Normalise Swap Hands keycodes ([#19720](https://github.com/qmk/qmk_firmware/pull/19720)) | |||
* Strip out more of config_common ([#19722](https://github.com/qmk/qmk_firmware/pull/19722)) | |||
* Remove `IS_HOST_LED_ON` and migrate usages ([#19753](https://github.com/qmk/qmk_firmware/pull/19753)) | |||
* Move more unicode ranges to DD ([#19755](https://github.com/qmk/qmk_firmware/pull/19755)) | |||
* Tidy up use of keycode range helpers ([#19756](https://github.com/qmk/qmk_firmware/pull/19756)) | |||
* Tri Layer Keys ([#19795](https://github.com/qmk/qmk_firmware/pull/19795)) | |||
* Remove matrix_init_quantum/matrix_scan_quantum ([#19806](https://github.com/qmk/qmk_firmware/pull/19806)) | |||
* Tidy up use of keycode range helpers ([#19813](https://github.com/qmk/qmk_firmware/pull/19813)) | |||
* Remove `config.h` include from quantum files ([#19817](https://github.com/qmk/qmk_firmware/pull/19817)) | |||
* Add rp2040_ce and add elite-pi and helios as alias ([#19830](https://github.com/qmk/qmk_firmware/pull/19830)) | |||
* Add swap hands status function ([#19831](https://github.com/qmk/qmk_firmware/pull/19831)) | |||
* Align sequencer keycodes ([#19875](https://github.com/qmk/qmk_firmware/pull/19875)) | |||
* Align magic keycodes ([#19877](https://github.com/qmk/qmk_firmware/pull/19877)) | |||
* Move `KC_MISSION_CONTROL`/`KC_LAUNCHPAD` keycodes to core ([#19884](https://github.com/qmk/qmk_firmware/pull/19884)) | |||
* Reallocate user/kb keycode ranges ([#19907](https://github.com/qmk/qmk_firmware/pull/19907)) | |||
* Reallocate SAFE_RANGE ([#19909](https://github.com/qmk/qmk_firmware/pull/19909)) | |||
* Hide hex output when building uf2 ([#19940](https://github.com/qmk/qmk_firmware/pull/19940)) | |||
CLI: | |||
* Automate "Data Driven" migrations? ([#17820](https://github.com/qmk/qmk_firmware/pull/17820)) | |||
* Generate encodermap output from keymap.json. ([#18915](https://github.com/qmk/qmk_firmware/pull/18915)) | |||
* Publish keymap.json to API ([#19167](https://github.com/qmk/qmk_firmware/pull/19167)) | |||
* Apply suggested workaround for #18371 ([#19226](https://github.com/qmk/qmk_firmware/pull/19226)) | |||
* Align new-keymap with new-keyboard ([#19229](https://github.com/qmk/qmk_firmware/pull/19229)) | |||
* Validate keyboard name before accepting further input ([#19394](https://github.com/qmk/qmk_firmware/pull/19394)) | |||
* Implement XAP style merge semantics for DD keycodes ([#19397](https://github.com/qmk/qmk_firmware/pull/19397)) | |||
* Allow CLI to flash .uf2 files ([#19462](https://github.com/qmk/qmk_firmware/pull/19462)) | |||
* Report submodule status when not valid work-tree ([#19474](https://github.com/qmk/qmk_firmware/pull/19474)) | |||
* `qmk compile`/`qmk flash` - Validate keymap argument ([#19530](https://github.com/qmk/qmk_firmware/pull/19530)) | |||
* Add commit info to `version.h` ([#19542](https://github.com/qmk/qmk_firmware/pull/19542)) | |||
* Remove CLI commands: `multibuild`, `cformat`, `fileformat`, `pyformat`. ([#19629](https://github.com/qmk/qmk_firmware/pull/19629)) | |||
* Print distro in doctor output ([#19633](https://github.com/qmk/qmk_firmware/pull/19633)) | |||
* Reduce false positives in layout name validation ([#19646](https://github.com/qmk/qmk_firmware/pull/19646)) | |||
* Add `mass-compile` ability to filter by key existence. ([#19885](https://github.com/qmk/qmk_firmware/pull/19885)) | |||
Submodule updates: | |||
* Update ChibiOS[-Contrib], SIO driver, configs ([#17915](https://github.com/qmk/qmk_firmware/pull/17915)) | |||
* Quantum Painter - LVGL Integration ([#18499](https://github.com/qmk/qmk_firmware/pull/18499)) | |||
* [RP2040] update i2c drivers to reflect peripheral number ([#19277](https://github.com/qmk/qmk_firmware/pull/19277)) | |||
* Update pico-sdk to 1.5.0 ([#19829](https://github.com/qmk/qmk_firmware/pull/19829)) | |||
Keyboards: | |||
* Refactor entire Handwired K552 keyboard ([#18066](https://github.com/qmk/qmk_firmware/pull/18066)) | |||
* Moonlander: Add RGB LED layout map macro ([#18745](https://github.com/qmk/qmk_firmware/pull/18745)) | |||
* Add the Ortho60 v2 Keyboard to QMK ([#18890](https://github.com/qmk/qmk_firmware/pull/18890)) | |||
* Refactor xs60 with soldered and hotswap version ([#19049](https://github.com/qmk/qmk_firmware/pull/19049)) | |||
* [GMMK Pro] Change DEBOUNCE_TYPE to sym_eager_pk to reduce latency ([#19153](https://github.com/qmk/qmk_firmware/pull/19153)) | |||
* Add KPrepublic BM16A v2 ([#19194](https://github.com/qmk/qmk_firmware/pull/19194)) | |||
* Add Rama Works M60-B ([#19248](https://github.com/qmk/qmk_firmware/pull/19248)) | |||
* Revert RESET-> QK_BOOT in Read Me files where applicable ([#19262](https://github.com/qmk/qmk_firmware/pull/19262)) | |||
* Remove broken keymap/userspace ([#19271](https://github.com/qmk/qmk_firmware/pull/19271)) | |||
* The Uni change folder location ([#19326](https://github.com/qmk/qmk_firmware/pull/19326)) | |||
* New keymap for ID75 - paryz ([#19350](https://github.com/qmk/qmk_firmware/pull/19350)) | |||
* Remove useless line continuations ([#19399](https://github.com/qmk/qmk_firmware/pull/19399)) | |||
* Add The Uni Utility Belt Keymap ([#19411](https://github.com/qmk/qmk_firmware/pull/19411)) | |||
* Migrate `MCU` and `BOOTLOADER` to data-driven ([#19529](https://github.com/qmk/qmk_firmware/pull/19529)) | |||
* Migrate `LAYOUTS` to data driven ([#19541](https://github.com/qmk/qmk_firmware/pull/19541)) | |||
* Tidy up use of CTPC ([#19570](https://github.com/qmk/qmk_firmware/pull/19570)) | |||
* Remove matrix size defines ([#19581](https://github.com/qmk/qmk_firmware/pull/19581)) | |||
* keebio/iris document LED matrix ([#19588](https://github.com/qmk/qmk_firmware/pull/19588)) | |||
* Add support for current/voltage measurement on Ghoul. ([#19630](https://github.com/qmk/qmk_firmware/pull/19630)) | |||
* Rename ramonimbao folder to rmi_kb ([#19699](https://github.com/qmk/qmk_firmware/pull/19699)) | |||
* Remove commented out backlight config & stray "backlight levels" ([#19703](https://github.com/qmk/qmk_firmware/pull/19703)) | |||
* Clean up Force NKRO in config.h ([#19718](https://github.com/qmk/qmk_firmware/pull/19718)) | |||
* Remove unused `MATRIX_HAS_GHOST` from config.h ([#19726](https://github.com/qmk/qmk_firmware/pull/19726)) | |||
* Debounce defines cleanup ([#19742](https://github.com/qmk/qmk_firmware/pull/19742)) | |||
* Remove unused `LOCKING_SUPPORT_ENABLE` from config.h ([#19748](https://github.com/qmk/qmk_firmware/pull/19748)) | |||
* Remove `DEBOUNCE` macro usage ([#19750](https://github.com/qmk/qmk_firmware/pull/19750)) | |||
* Remove unused `GRAVE_ESC_CTRL_OVERRIDE` from config.h ([#19752](https://github.com/qmk/qmk_firmware/pull/19752)) | |||
* Remove unused Bootmagic row/col defines from config.h ([#19761](https://github.com/qmk/qmk_firmware/pull/19761)) | |||
* Remove unused `SOFT_SERIAL_PIN` from config.h ([#19768](https://github.com/qmk/qmk_firmware/pull/19768)) | |||
* Remove `SOFT_SERIAL_PIN` for non-split boards ([#19774](https://github.com/qmk/qmk_firmware/pull/19774)) | |||
* implement missing layouts + DD migration for wilba_tech/wt60_d ([#19777](https://github.com/qmk/qmk_firmware/pull/19777)) | |||
* Move LED indicator config to data driven ([#19800](https://github.com/qmk/qmk_firmware/pull/19800)) | |||
* Migrate `DIRECT_PINS` to data driven ([#19826](https://github.com/qmk/qmk_firmware/pull/19826)) | |||
* Remove lingering `I2CD2` usages w/ RP2040 ([#19833](https://github.com/qmk/qmk_firmware/pull/19833)) | |||
* Brick ([#19851](https://github.com/qmk/qmk_firmware/pull/19851)) | |||
* Remove unused RGBLight defines from config.h ([#19859](https://github.com/qmk/qmk_firmware/pull/19859)) | |||
* Move Bootmagic config to data driven ([#19860](https://github.com/qmk/qmk_firmware/pull/19860)) | |||
* Move `SOFT_SERIAL_PIN` to data driven ([#19863](https://github.com/qmk/qmk_firmware/pull/19863)) | |||
* Move layouts for direct_pins boards to data driven ([#19872](https://github.com/qmk/qmk_firmware/pull/19872)) | |||
* Move QMK LUFA bootloader config to data driven ([#19879](https://github.com/qmk/qmk_firmware/pull/19879)) | |||
* Move backlight config to data driven, part 1 ([#19887](https://github.com/qmk/qmk_firmware/pull/19887)) | |||
* Add license headers to all default layout keymaps ([#19888](https://github.com/qmk/qmk_firmware/pull/19888)) | |||
* Migrate some more layouts to data driven ([#19889](https://github.com/qmk/qmk_firmware/pull/19889)) | |||
* Remove magic bodges from via keymaps ([#19890](https://github.com/qmk/qmk_firmware/pull/19890)) | |||
* Refactor more `KC_MISSION_CONTROL`/`KC_LAUNCHPAD` usages ([#19891](https://github.com/qmk/qmk_firmware/pull/19891)) | |||
* Remove default and unused `BACKLIGHT_LEVELS` ([#19898](https://github.com/qmk/qmk_firmware/pull/19898)) | |||
* Move backlight config to data driven ([#19910](https://github.com/qmk/qmk_firmware/pull/19910)) | |||
* Remove VIA specific use of `MACRO0*` ([#19918](https://github.com/qmk/qmk_firmware/pull/19918)) | |||
* Use standard magic keycodes in `yandrstudio` keymaps ([#19919](https://github.com/qmk/qmk_firmware/pull/19919)) | |||
* Move encoder config to data driven ([#19923](https://github.com/qmk/qmk_firmware/pull/19923)) | |||
Keyboard fixes: | |||
* Partially revert #18940 for Ploopy Thumb Trackball ([#18943](https://github.com/qmk/qmk_firmware/pull/18943)) | |||
* Fix up Info.Json files that weren't parsing correctly ([#19275](https://github.com/qmk/qmk_firmware/pull/19275)) | |||
* Fix DZTECH Tofu II v1 i2c config ([#19306](https://github.com/qmk/qmk_firmware/pull/19306)) | |||
* Fixup build failures. ([#19332](https://github.com/qmk/qmk_firmware/pull/19332)) | |||
* Fixup horrortroll/handwired_k552 ([#19447](https://github.com/qmk/qmk_firmware/pull/19447)) | |||
* Ignore defaults.hjson values if already set ([#19511](https://github.com/qmk/qmk_firmware/pull/19511)) | |||
* Fix mk0_avr_extra PIN_COMPATIBLE lint warning ([#19640](https://github.com/qmk/qmk_firmware/pull/19640)) | |||
* fix pegasushoof caps light, add via keymap ([#19649](https://github.com/qmk/qmk_firmware/pull/19649)) | |||
* Fixup handwired/jscotto/scotto40 ([#19675](https://github.com/qmk/qmk_firmware/pull/19675)) | |||
* Clean up remaining rules.mk `MCU`/`BOOTLOADER`s ([#19778](https://github.com/qmk/qmk_firmware/pull/19778)) | |||
* Fix errors flagged by generate-api ([#19784](https://github.com/qmk/qmk_firmware/pull/19784)) | |||
* Fix merge error with fave84 board ([#19808](https://github.com/qmk/qmk_firmware/pull/19808)) | |||
* Fixup ek65 -- add processor & bootloader in `info.json` ([#19815](https://github.com/qmk/qmk_firmware/pull/19815)) | |||
* Fixup durgod/dgk6x (scroll lock mis-defined as num lock) ([#19864](https://github.com/qmk/qmk_firmware/pull/19864)) | |||
* Fix API generation ([#19866](https://github.com/qmk/qmk_firmware/pull/19866)) | |||
* Fixup for_science ([#19867](https://github.com/qmk/qmk_firmware/pull/19867)) | |||
* Fix more build failures ([#19869](https://github.com/qmk/qmk_firmware/pull/19869)) | |||
* Fixup pegasushoof VIA keymap ([#19874](https://github.com/qmk/qmk_firmware/pull/19874)) | |||
* Fixup cannonkeys/satisfaction75 (readd `backlight.breathing_period`) ([#19901](https://github.com/qmk/qmk_firmware/pull/19901)) | |||
* Add some missing `#pragma once`s ([#19902](https://github.com/qmk/qmk_firmware/pull/19902)) | |||
* `keebio/kbo5000`: fix encoder config ([#19941](https://github.com/qmk/qmk_firmware/pull/19941)) | |||
Others: | |||
* KC_GESC -> QK_GESC for cn and ja Docs ([#19024](https://github.com/qmk/qmk_firmware/pull/19024)) | |||
* Update files changed action ([#19172](https://github.com/qmk/qmk_firmware/pull/19172)) | |||
* DD bootmagic config ([#19201](https://github.com/qmk/qmk_firmware/pull/19201)) | |||
* Rework input_pressed_state docs ([#19267](https://github.com/qmk/qmk_firmware/pull/19267)) | |||
* Change log for Quick Tap Term ([#19341](https://github.com/qmk/qmk_firmware/pull/19341)) | |||
* Promote CTPC warning to error ([#19565](https://github.com/qmk/qmk_firmware/pull/19565)) | |||
* Run format-text on keyboard PRs ([#19656](https://github.com/qmk/qmk_firmware/pull/19656)) | |||
* Change defines by enums ([#19793](https://github.com/qmk/qmk_firmware/pull/19793)) | |||
* [Doc]Remove depracted extension links in vscode guide ([#19842](https://github.com/qmk/qmk_firmware/pull/19842)) | |||
Bugs: | |||
* Make Magic handling more consistent in Action Keycode handling ([#9126](https://github.com/qmk/qmk_firmware/pull/9126)) | |||
* Fix functions when `NO_ACTION_TAPPING` is defined ([#11528](https://github.com/qmk/qmk_firmware/pull/11528)) | |||
* Return USB HID GET_REPORT requests ([#14814](https://github.com/qmk/qmk_firmware/pull/14814)) | |||
* Fixed NKRO issue caused by HID_SET_PROTOCOL on Chibios platform ([#17588](https://github.com/qmk/qmk_firmware/pull/17588)) | |||
* kint36: do not restart USB stack after wakeup ([#19077](https://github.com/qmk/qmk_firmware/pull/19077)) | |||
* Fixes to source generation [mostly typographic] ([#19160](https://github.com/qmk/qmk_firmware/pull/19160)) | |||
* Teensy 3.5: do not restart USB stack after wakeup ([#19269](https://github.com/qmk/qmk_firmware/pull/19269)) | |||
* Fixing PMW3389.c so it can compile ([#19301](https://github.com/qmk/qmk_firmware/pull/19301)) | |||
* UCIS: remove `qk_` prefix ([#19302](https://github.com/qmk/qmk_firmware/pull/19302)) | |||
* Leader: remove `qk_` prefix ([#19304](https://github.com/qmk/qmk_firmware/pull/19304)) | |||
* Tap Dance: remove `qk_` prefix ([#19313](https://github.com/qmk/qmk_firmware/pull/19313)) | |||
* Revert changes to keymap_steno.h ([#19412](https://github.com/qmk/qmk_firmware/pull/19412)) | |||
* Use unique name for regen PR branches ([#19464](https://github.com/qmk/qmk_firmware/pull/19464)) | |||
* Restore packing of midi note keycodes ([#19468](https://github.com/qmk/qmk_firmware/pull/19468)) | |||
* Fix 'Need at least one layout defined in info.json' check ([#19537](https://github.com/qmk/qmk_firmware/pull/19537)) | |||
* `qmk doctor` - Handle permission issues while checking udev ([#19548](https://github.com/qmk/qmk_firmware/pull/19548)) | |||
* `qmk doctor` - Handle timeouts while checking binaries ([#19549](https://github.com/qmk/qmk_firmware/pull/19549)) | |||
* Fix CLI community detection ([#19562](https://github.com/qmk/qmk_firmware/pull/19562)) | |||
* Fix joystick build for ChibiOS ([#19602](https://github.com/qmk/qmk_firmware/pull/19602)) | |||
* Fix converter alias after 19603 ([#19644](https://github.com/qmk/qmk_firmware/pull/19644)) | |||
* Fix functions with empty params ([#19647](https://github.com/qmk/qmk_firmware/pull/19647)) | |||
* rp2040: fix timer wrap deadlock in ws2812 vendor driver ([#19652](https://github.com/qmk/qmk_firmware/pull/19652)) | |||
* analog.c: Fix `pinToMux()` for STM32F0xx ([#19658](https://github.com/qmk/qmk_firmware/pull/19658)) | |||
* Fix quantum ring_buffer for ChibiOS ([#19683](https://github.com/qmk/qmk_firmware/pull/19683)) | |||
* Regen keycode_table for unit tests ([#19721](https://github.com/qmk/qmk_firmware/pull/19721)) | |||
* Fix midi after recent refactoring ([#19723](https://github.com/qmk/qmk_firmware/pull/19723)) | |||
* Fix build failures with `OPT = 0` due to inline functions ([#19767](https://github.com/qmk/qmk_firmware/pull/19767)) | |||
* Fix tri layer compiler issue if NO_ACTION_LAYER is defined ([#19821](https://github.com/qmk/qmk_firmware/pull/19821)) | |||
* Fixup `develop` compiles. ([#19828](https://github.com/qmk/qmk_firmware/pull/19828)) | |||
* Fix Layer Mod mishandling of right-handed mods, a mixup of 5-bit vs. 8-bit mods representation. ([#19845](https://github.com/qmk/qmk_firmware/pull/19845)) | |||
* Fix compilation issue for Key Overrides ([#19856](https://github.com/qmk/qmk_firmware/pull/19856)) | |||
* Fix regen script for macOS ([#19857](https://github.com/qmk/qmk_firmware/pull/19857)) | |||
* Fix compilation error when defining QUICK_TAP_TERM_PER_KEY ([#19893](https://github.com/qmk/qmk_firmware/pull/19893)) | |||
* VIA Protocol 12 + fixes ([#19916](https://github.com/qmk/qmk_firmware/pull/19916)) |
@ -1,165 +1,297 @@ | |||
# The Leader Key: A New Kind of Modifier | |||
# The Leader Key: A New Kind of Modifier :id=the-leader-key | |||
If you've ever used Vim, you know what a Leader key is. If not, you're about to discover a wonderful concept. :) Instead of hitting Alt+Shift+W for example (holding down three keys at the same time), what if you could hit a _sequence_ of keys instead? So you'd hit our special modifier (the Leader key), followed by W and then C (just a rapid succession of keys), and something would happen. | |||
If you're a Vim user, you probably know what a Leader key is. In contrast to [Combos](feature_combo.md), the Leader key allows you to hit a *sequence* of up to five keys instead, which triggers some custom functionality once complete. | |||
That's what `QK_LEAD` does. Here's an example: | |||
## Usage :id=usage | |||
1. Pick a key on your keyboard you want to use as the Leader key. Assign it the keycode `QK_LEAD`. This key would be dedicated just for this -- it's a single action key, can't be used for anything else. | |||
2. Include the line `#define LEADER_TIMEOUT 300` in your `config.h`. This sets the timeout for the `QK_LEAD` key. Specifically, when you press the `QK_LEAD` key, you only have a certain amount of time to complete the Leader Key sequence. The `300` here sets that to 300ms, and you can increase this value to give you more time to hit the sequence. But any keys pressed during this timeout are intercepted and not sent, so you may want to keep this value low. | |||
* By default, this timeout is how long after pressing `QK_LEAD` to complete your entire sequence. This may be very low for some people. So you may want to increase this timeout. Optionally, you may want to enable the `LEADER_PER_KEY_TIMING` option, which resets the timeout after each key is tapped. This allows you to maintain a low value here, but still be able to use the longer sequences. To enable this option, add `#define LEADER_PER_KEY_TIMING` to your `config.h`. | |||
3. Within your `matrix_scan_user` function, add something like this: | |||
Add the following to your `rules.mk`: | |||
```c | |||
LEADER_EXTERNS(); | |||
void matrix_scan_user(void) { | |||
LEADER_DICTIONARY() { | |||
leading = false; | |||
leader_end(); | |||
SEQ_ONE_KEY(KC_F) { | |||
// Anything you can do in a macro. | |||
SEND_STRING("QMK is awesome."); | |||
} | |||
SEQ_TWO_KEYS(KC_D, KC_D) { | |||
SEND_STRING(SS_LCTL("a") SS_LCTL("c")); | |||
} | |||
SEQ_THREE_KEYS(KC_D, KC_D, KC_S) { | |||
SEND_STRING("https://start.duckduckgo.com\n"); | |||
} | |||
SEQ_TWO_KEYS(KC_A, KC_S) { | |||
register_code(KC_LGUI); | |||
register_code(KC_S); | |||
unregister_code(KC_S); | |||
unregister_code(KC_LGUI); | |||
} | |||
} | |||
} | |||
```make | |||
LEADER_ENABLE = yes | |||
``` | |||
As you can see, you have a few functions. You can use `SEQ_ONE_KEY` for single-key sequences (Leader followed by just one key), and `SEQ_TWO_KEYS`, `SEQ_THREE_KEYS` up to `SEQ_FIVE_KEYS` for longer sequences. | |||
Then add the `QK_LEAD` keycode to your keymap. | |||
Each of these accepts one or more keycodes as arguments. This is an important point: You can use keycodes from **any layer on your keyboard**. That layer would need to be active for the leader macro to fire, obviously. | |||
## Callbacks :id=callbacks | |||
## Adding Leader Key Support | |||
These callbacks are invoked when the leader sequence begins and ends. In the latter you can implement your custom functionality based on the contents of the sequence buffer. | |||
To enable Leader Key, add the following line to your keymap's `rules.mk`: | |||
```c | |||
void leader_start_user(void) { | |||
// Do something when the leader key is pressed | |||
} | |||
```make | |||
LEADER_ENABLE = yes | |||
void leader_end_user(void) { | |||
if (leader_sequence_one_key(KC_F)) { | |||
// Leader, f => Types the below string | |||
SEND_STRING("QMK is awesome."); | |||
} else if (leader_sequence_two_keys(KC_D, KC_D)) { | |||
// Leader, d, d => Ctrl+A, Ctrl+C | |||
SEND_STRING(SS_LCTL("a") SS_LCTL("c")); | |||
} else if (leader_sequence_three_keys(KC_D, KC_D, KC_S)) { | |||
// Leader, d, d, s => Types the below string | |||
SEND_STRING("https://start.duckduckgo.com\n"); | |||
} else if (leader_sequence_two_keys(KC_A, KC_S)) { | |||
// Leader, a, s => GUI+S | |||
tap_code16(LGUI(KC_S)); | |||
} | |||
} | |||
``` | |||
Place the following macro in your `keymap.c` or user space source file, before any functional code. It handles declaration of external variables that will be referenced by Leader Key codes that follows: | |||
## Basic Configuration :id=basic-configuration | |||
### Timeout :id=timeout | |||
This is the amount of time you have to complete a sequence once the leader key has been pressed. The default value is 300 milliseconds, but you can change this by adding the following to your `config.h`: | |||
```c | |||
LEADER_EXTERNS(); | |||
#define LEADER_TIMEOUT 350 | |||
``` | |||
## Per Key Timing on Leader keys | |||
### Per-Key Timeout :id=per-key-timeout | |||
Rather than relying on an incredibly high timeout for long leader key strings or those of us without 200 wpm typing skills, you can enable per-key timing to ensure that each key pressed provides you with more time to finish the sequence. This is incredibly helpful with leader key emulation of tap dance (such as multiple taps of the same key like C, C, C). | |||
Rather than relying on an incredibly high timeout for long leader key strings or those of us without 200wpm typing skills, we can enable per key timing to ensure that each key pressed provides us with more time to finish our stroke. This is incredibly helpful with leader key emulation of tap dance (read: multiple taps of the same key like C, C, C). | |||
To enable this, add the following to your `config.h`: | |||
In order to enable this, place this in your `config.h`: | |||
```c | |||
#define LEADER_PER_KEY_TIMING | |||
``` | |||
After this, it's recommended that you lower your `LEADER_TIMEOUT` to something less that 300ms. | |||
After this, it's recommended that you lower your timeout below 300 ms: | |||
```c | |||
#define LEADER_TIMEOUT 250 | |||
``` | |||
Now, something like this won't seem impossible to do without a 1000MS leader key timeout: | |||
Now, something like this won't seem impossible to do without a 1000 millisecond timeout: | |||
```c | |||
SEQ_THREE_KEYS(KC_C, KC_C, KC_C) { | |||
SEND_STRING("Per key timing is great!!!"); | |||
if (leader_sequence_three_keys(KC_C, KC_C, KC_C)) { | |||
SEND_STRING("Per key timing is great!!!"); | |||
} | |||
``` | |||
## Infinite Leader key timeout | |||
### Disabling Initial Timeout :id=disabling-initial-timeout | |||
Sometimes your leader key is not on a comfortable place as the rest of keys on your sequence. Imagine that your leader key is one of your outer top right keys, you may need to reposition your hand just to reach your leader key. | |||
This can make typing the entire sequence on time hard even if you are able to type most of the sequence fast. For example, if your sequence is `Leader + asd` typing `asd` fast is very easy once you have your hands in your home row. However starting the sequence in time after moving your hand out of the home row to reach the leader key and back is not. | |||
To remove the stress this situation produces to your hands you can enable an infinite timeout just for the leader key. This means that after you hit the leader key you will have an infinite amount of time to start the rest of the sequence, allowing you to proper position your hands on the best position to type the rest of the sequence comfortably. | |||
This infinite timeout only affects the leader key, so in our previous example of `Leader + asd` you will have an infinite amount of time between `Leader` and `a`, but once you start the sequence the timeout you have configured (global or per key) will work normally. | |||
This way you can configure a very short `LEADER_TIMEOUT` but still have plenty of time to position your hands. | |||
Sometimes your leader key may be too far away from the rest of the keys in the sequence. Imagine that your leader key is one of your outer top right keys - you may need to reposition your hand just to reach your leader key. This can make typing the entire sequence on time hard difficult if you are able to type most of the sequence fast. For example, if your sequence is `Leader + asd`, typing `asd` fast is very easy once you have your hands in your home row, but starting the sequence in time after moving your hand out of the home row to reach the leader key and back is not. | |||
To remove the stress this situation produces to your hands, you can disable the timeout just for the leader key. Add the following to your `config.h`: | |||
In order to enable this, place this in your `config.h`: | |||
```c | |||
#define LEADER_NO_TIMEOUT | |||
``` | |||
## Strict Key Processing | |||
By default, the Leader Key feature will filter the keycode out of [`Mod-Tap`](mod_tap.md) and [`Layer Tap`](feature_layers.md#switching-and-toggling-layers) functions when checking for the Leader sequences. That means if you're using `LT(3, KC_A)`, it will pick this up as `KC_A` for the sequence, rather than `LT(3, KC_A)`, giving a more expected behavior for newer users. | |||
While, this may be fine for most, if you want to specify the whole keycode (eg, `LT(3, KC_A)` from the example above) in the sequence, you can enable this by adding `#define LEADER_KEY_STRICT_KEY_PROCESSING` to your `config.h` file. This will then disable the filtering, and you'll need to specify the whole keycode. | |||
Now, after you hit the leader key, you will have an infinite amount of time to start the rest of the sequence, allowing you to properly position your hands to type the rest of the sequence comfortably. This way you can configure a very short `LEADER_TIMEOUT`, but still have plenty of time to position your hands. | |||
## Customization | |||
### Strict Key Processing :id=strict-key-processing | |||
The Leader Key feature has some additional customization to how the Leader Key feature works. It has two functions that can be called at certain parts of the process. Namely `leader_start()` and `leader_end()`. | |||
By default, only the "tap keycode" portions of [Mod-Taps](mod_tap.md) and [Layer Taps](feature_layers.md#switching-and-toggling-layers) are added to the sequence buffer. This means if you press eg. `LT(3, KC_A)` as part of a sequence, `KC_A` will be added to the buffer, rather than the entire `LT(3, KC_A)` keycode. | |||
The `leader_start()` function is called when you tap the `QK_LEAD` key, and the `leader_end()` function is called when either the leader sequence is completed, or the leader timeout is hit. | |||
This gives a more expected behaviour for most users, however you may want to change this. | |||
You can add these functions to your code (`keymap.c` usually) to add feedback to the Leader sequences (such as beeping or playing music). | |||
To enable this, add the following to your `config.h`: | |||
```c | |||
void leader_start(void) { | |||
// sequence started | |||
} | |||
void leader_end(void) { | |||
// sequence ended (no success/failure detection) | |||
} | |||
#define LEADER_KEY_STRICT_KEY_PROCESSING | |||
``` | |||
### Example | |||
## Example :id=example | |||
This example will play the Mario "One Up" sound when you hit `QK_LEAD` to start the Leader Sequence, and will play "All Star" if it completes successfully or "Rick Roll" you if it fails. | |||
This example will play the Mario "One Up" sound when you hit `QK_LEAD` to start the leader sequence. When the sequence ends, it will play "All Star" if it completes successfully or "Rick Roll" you if it fails (in other words, no sequence matched). | |||
```c | |||
bool did_leader_succeed; | |||
#ifdef AUDIO_ENABLE | |||
float leader_start[][2] = SONG(ONE_UP_SOUND ); | |||
float leader_succeed[][2] = SONG(ALL_STAR); | |||
float leader_fail[][2] = SONG(RICK_ROLL); | |||
float leader_start_song[][2] = SONG(ONE_UP_SOUND); | |||
float leader_succeed_song[][2] = SONG(ALL_STAR); | |||
float leader_fail_song[][2] = SONG(RICK_ROLL); | |||
#endif | |||
LEADER_EXTERNS(); | |||
void matrix_scan_user(void) { | |||
LEADER_DICTIONARY() { | |||
did_leader_succeed = leading = false; | |||
SEQ_ONE_KEY(KC_E) { | |||
// Anything you can do in a macro. | |||
SEND_STRING(SS_LCTL(SS_LSFT("t"))); | |||
did_leader_succeed = true; | |||
} else | |||
SEQ_TWO_KEYS(KC_E, KC_D) { | |||
SEND_STRING(SS_LGUI("r") "cmd\n" SS_LCTL("c")); | |||
did_leader_succeed = true; | |||
} | |||
leader_end(); | |||
} | |||
} | |||
void leader_start(void) { | |||
void leader_start_user(void) { | |||
#ifdef AUDIO_ENABLE | |||
PLAY_SONG(leader_start); | |||
PLAY_SONG(leader_start_song); | |||
#endif | |||
} | |||
void leader_end(void) { | |||
if (did_leader_succeed) { | |||
#ifdef AUDIO_ENABLE | |||
PLAY_SONG(leader_succeed); | |||
#endif | |||
} else { | |||
void leader_end_user(void) { | |||
bool did_leader_succeed = false; | |||
if (leader_sequence_one_key(KC_E)) { | |||
SEND_STRING(SS_LCTL(SS_LSFT("t"))); | |||
did_leader_succeed = true; | |||
} else if (leader_sequence_two_keys(KC_E, KC_D)) { | |||
SEND_STRING(SS_LGUI("r") "cmd\n" SS_LCTL("c")); | |||
did_leader_succeed = true; | |||
} | |||
#ifdef AUDIO_ENABLE | |||
PLAY_SONG(leader_fail); | |||
if (did_leader_succeed) { | |||
PLAY_SONG(leader_succeed_song); | |||
} else { | |||
PLAY_SONG(leader_fail_song); | |||
} | |||
#endif | |||
} | |||
} | |||
``` | |||
## Keycodes :id=keycodes | |||
|Key |Aliases |Description | | |||
|-----------------------|---------|-------------------------| | |||
|`QK_LEADER` |`QK_LEAD`|Begin the leader sequence| | |||
## API :id=api | |||
### `void leader_start_user(void)` :id=api-leader-start-user | |||
User callback, invoked when the leader sequence begins. | |||
--- | |||
### `void leader_end_user(void)` :id=api-leader-end-user | |||
User callback, invoked when the leader sequence ends. | |||
--- | |||
### `void leader_start(void)` :id=api-leader-start | |||
Begin the leader sequence, resetting the buffer and timer. | |||
--- | |||
### `void leader_end(void)` :id=api-leader-end | |||
End the leader sequence. | |||
--- | |||
### `bool leader_sequence_active(void)` :id=api-leader-sequence-active | |||
Whether the leader sequence is active. | |||
--- | |||
### `bool leader_sequence_add(uint16_t keycode)` :id=api-leader-sequence-add | |||
Add the given keycode to the sequence buffer. | |||
If `LEADER_NO_TIMEOUT` is defined, the timer is reset if the buffer is empty. | |||
#### Arguments :id=api-leader-sequence-add-arguments | |||
- `uint16_t keycode` | |||
The keycode to add. | |||
#### Return Value :id=api-leader-sequence-add-return | |||
`true` if the keycode was added, `false` if the buffer is full. | |||
--- | |||
### `bool leader_sequence_timed_out(void)` :id=api-leader-sequence-timed-out | |||
Whether the leader sequence has reached the timeout. | |||
If `LEADER_NO_TIMEOUT` is defined, the buffer must also contain at least one key. | |||
--- | |||
### `bool leader_reset_timer(void)` :id=api-leader-reset-timer | |||
Reset the leader sequence timer. | |||
--- | |||
### `bool leader_sequence_one_key(uint16_t kc)` :id=api-leader-sequence-one-key | |||
Check the sequence buffer for the given keycode. | |||
#### Arguments :id=api-leader-sequence-one-key-arguments | |||
- `uint16_t kc` | |||
The keycode to check. | |||
#### Return Value :id=api-leader-sequence-one-key-return | |||
`true` if the sequence buffer matches. | |||
--- | |||
### `bool leader_sequence_two_keys(uint16_t kc1, uint16_t kc2)` :id=api-leader-sequence-two-keys | |||
Check the sequence buffer for the given keycodes. | |||
#### Arguments :id=api-leader-sequence-two-keys-arguments | |||
- `uint16_t kc1` | |||
The first keycode to check. | |||
- `uint16_t kc2` | |||
The second keycode to check. | |||
#### Return Value :id=api-leader-sequence-two-keys-return | |||
`true` if the sequence buffer matches. | |||
--- | |||
### `bool leader_sequence_three_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3)` :id=api-leader-sequence-three-keys | |||
Check the sequence buffer for the given keycodes. | |||
#### Arguments :id=api-leader-sequence-three-keys-arguments | |||
- `uint16_t kc1` | |||
The first keycode to check. | |||
- `uint16_t kc2` | |||
The second keycode to check. | |||
- `uint16_t kc3` | |||
The third keycode to check. | |||
#### Return Value :id=api-leader-sequence-three-keys-return | |||
`true` if the sequence buffer matches. | |||
--- | |||
### `bool leader_sequence_four_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3, uint16_t kc4)` :id=api-leader-sequence-four-keys | |||
Check the sequence buffer for the given keycodes. | |||
#### Arguments :id=api-leader-sequence-four-keys-arguments | |||
- `uint16_t kc1` | |||
The first keycode to check. | |||
- `uint16_t kc2` | |||
The second keycode to check. | |||
- `uint16_t kc3` | |||
The third keycode to check. | |||
- `uint16_t kc4` | |||
The fourth keycode to check. | |||
#### Return Value :id=api-leader-sequence-four-keys-return | |||
`true` if the sequence buffer matches. | |||
--- | |||
### `bool leader_sequence_five_keys(uint16_t kc1, uint16_t kc2, uint16_t kc3, uint16_t kc4, uint16_t kc5)` :id=api-leader-sequence-five-keys | |||
Check the sequence buffer for the given keycodes. | |||
#### Arguments :id=api-leader-sequence-five-keys-arguments | |||
- `uint16_t kc1` | |||
The first keycode to check. | |||
- `uint16_t kc2` | |||
The second keycode to check. | |||
- `uint16_t kc3` | |||
The third keycode to check. | |||
- `uint16_t kc4` | |||
The fourth keycode to check. | |||
- `uint16_t kc5` | |||
The fifth keycode to check. | |||
#### Return Value :id=api-leader-sequence-five-keys-return | |||
`true` if the sequence buffer matches. |
@ -0,0 +1,77 @@ | |||
# OS Detection | |||
This feature makes a best guess at the host OS based on OS specific behavior during USB setup. It may not always get the correct OS, and shouldn't be relied on as for critical functionality. | |||
Using it you can have OS specific key mappings or combos which work differently on different devices. | |||
It is available for keyboards which use ChibiOS, LUFA and V-USB. | |||
## Usage | |||
In your `rules.mk` add: | |||
```make | |||
OS_DETECTION_ENABLE = yes | |||
``` | |||
Include `"os_detection.h"` in your `keymap.c`. | |||
It declares `os_variant_t detected_host_os(void);` which you can call to get detected OS. | |||
It returns one of the following values: | |||
```c | |||
enum { | |||
OS_UNSURE, | |||
OS_LINUX, | |||
OS_WINDOWS, | |||
OS_MACOS, | |||
OS_IOS, | |||
} os_variant_t; | |||
``` | |||
?> Note that it takes some time after firmware is booted to detect the OS. | |||
This time is quite short, probably hundreds of milliseconds, but this data may be not ready in keyboard and layout setup functions which run very early during firmware startup. | |||
## Debug | |||
If OS is guessed incorrectly, you may want to collect data about USB setup packets to refine the detection logic. | |||
To do so in your `rules.mk` add: | |||
```make | |||
OS_DETECTION_DEBUG_ENABLE = yes | |||
CONSOLE_ENABLE = yes | |||
``` | |||
And also include `"os_detection.h"` in your `keymap.c`. | |||
Then you can define custom keycodes to store data about USB setup packets in EEPROM (persistent memory) and to print it later on host where you can run `qmk console`: | |||
```c | |||
enum custom_keycodes { | |||
STORE_SETUPS = SAFE_RANGE, | |||
PRINT_SETUPS, | |||
}; | |||
bool process_record_user(uint16_t keycode, keyrecord_t *record) { | |||
switch (keycode) { | |||
case STORE_SETUPS: | |||
if (record->event.pressed) { | |||
store_setups_in_eeprom(); | |||
} | |||
return false; | |||
case PRINT_SETUPS: | |||
if (record->event.pressed) { | |||
print_stored_setups(); | |||
} | |||
return false; | |||
} | |||
} | |||
``` | |||
Then please open an issue on Github with this information and tell what OS was not detected correctly and if you have any intermediate devices between keyboard and your computer. | |||
## Credits | |||
Original idea is coming from [FingerprintUSBHost](https://github.com/keyboardio/FingerprintUSBHost) project. |
@ -0,0 +1,48 @@ | |||
# Tri Layers :id=tri-layers | |||
This enables support for the OLKB style "Tri Layer" keycodes. These function similar to the `MO` (momentary) function key, but if both the "Lower" and "Upper" keys are pressed, it activates a third "Adjust" layer. To enable this functionality, add this line to your `rules.mk`: | |||
```make | |||
TRI_LAYER_ENABLE = yes | |||
``` | |||
Note that the "upper", "lower" and "adjust" names don't have a particular significance, they are just used to identify and clarify the behavior. Layers are processed from highest numeric value to lowest, however the values are not required to be consecutive. | |||
For a detailed explanation of how the layer stack works, check out [Keymap Overview](keymap.md#keymap-and-layers). | |||
## Keycodes :id=keycodes | |||
| Keycode | Alias | Description | | |||
|----------------------|-----------|---------------------------------------------------------------------------------------------------------| | |||
| `QK_TRI_LAYER_LOWER` | `TL_LOWR` | Momentarily enables the "lower" layer. Enables the "adjust" layer if the "upper" layer is also enabled" | | |||
| `QK_TRI_LAYER_UPPER` | `TL_UPPR` | Momentarily enables the "upper" layer. Enables the "adjust" layer if the "lower" layer is also enabled" | | |||
## Configuration | |||
To change the default values for the layers, you can change these defines, in your `config.h` | |||
| Config name | Default | Description | | |||
|--------------------------|---------|------------------------------------------| | |||
| `TRI_LAYER_LOWER_LAYER` | `1` | Sets the default for the "lower" layer. | | |||
| `TRI_LAYER_UPPER_LAYER` | `2` | Sets the default for the "upper" layer. | | |||
| `TRI_LAYER_ADJUST_LAYER` | `3` | Sets the default for the "adjust" layer. | | |||
Eg, if you wanted to set the "Adjust" layer to be layer 5, you'd add this to your `config.h`: | |||
```c | |||
#define TRI_LAYER_ADJUST_LAYER 5 | |||
``` | |||
## Functions | |||
| Function name | Description | | |||
|----------------------------------------------|-------------------------------------------------| | |||
| `set_tri_layer_lower_layer(layer)` | Changes the "lower" layer*. | | |||
| `set_tri_layer_upper_layer(layer)` | Changes the "upper" layer*. | | |||
| `set_tri_layer_adjust_layer(layer)` | Changes the "adjust" layer*. | | |||
| `set_tri_layer_layers(lower, upper, adjust)` | Stes the "lower", "upper" and "adjust" layers*. | | |||
| `get_tri_layer_lower_layer()` | Gets the current "lower" layer. | | |||
| `get_tri_layer_upper_layer()` | Gets the current "upper" layer. | | |||
| `get_tri_layer_adjust_layer()` | Gets the current "adjust" layer. | | |||
!> Note: these settings are not persisent, and will be reset to the default on power loss or power cycling of the controller. |
@ -0,0 +1,139 @@ | |||
# group `leader` | |||
Leader Key | |||
## Summary | |||
Members | Descriptions | |||
--------------------------------|--------------------------------------------- | |||
`public void `[`leader_start_user`](#group__leader_1gae7b6746b0c6bd83cada1e770a7d8315d)`(void)` | User callback, invoked when the leader sequence begins. | |||
`public void `[`leader_end_user`](#group__leader_1ga20986d88ffc5f21f00eab4b5e9a03188)`(void)` | User callback, invoked when the leader sequence ends. | |||
`public void `[`leader_start`](#group__leader_1ga1f5dad3a2df12d29023e48852ff638ba)`(void)` | Begin the leader sequence, resetting the buffer and timer. | |||
`public void `[`leader_end`](#group__leader_1ga7ecc035ad29aed6d5ee3848725234b6c)`(void)` | End the leader sequence. | |||
`public void `[`leader_task`](#group__leader_1gaa451b483f86840a1a3a773a074b73019)`(void)` | | |||
`public bool `[`leader_sequence_active`](#group__leader_1gab52594aae07ec2f59cc6cc00dce9cc0b)`(void)` | Whether the leader sequence is active. | |||
`public bool `[`leader_sequence_add`](#group__leader_1ga0b9825ee3240ffac41837664d10c36ff)`(uint16_t keycode)` | Add the given keycode to the sequence buffer. | |||
`public bool `[`leader_sequence_timed_out`](#group__leader_1gadb5905415a54ffab4a47a8d3227b3942)`(void)` | Whether the leader sequence has reached the timeout. | |||
`public void `[`leader_reset_timer`](#group__leader_1ga5a9246dc3f952a5522c188f747ce0722)`(void)` | Reset the leader sequence timer. | |||
`public bool `[`leader_sequence_one_key`](#group__leader_1ga6bcdd330c94f7074361c21ad8ee901fd)`(uint16_t kc)` | Check the sequence buffer for the given keycode. | |||
`public bool `[`leader_sequence_two_keys`](#group__leader_1ga6a09c0ae645990e9d219c62244c30e42)`(uint16_t kc1,uint16_t kc2)` | Check the sequence buffer for the given keycodes. | |||
`public bool `[`leader_sequence_three_keys`](#group__leader_1ga0dd4e91b428947bfb5ddb6fd09b29c11)`(uint16_t kc1,uint16_t kc2,uint16_t kc3)` | Check the sequence buffer for the given keycodes. | |||
`public bool `[`leader_sequence_four_keys`](#group__leader_1ga5f71174f1afa31112108336e0d782ff2)`(uint16_t kc1,uint16_t kc2,uint16_t kc3,uint16_t kc4)` | Check the sequence buffer for the given keycodes. | |||
`public bool `[`leader_sequence_five_keys`](#group__leader_1ga2c565e3c2d21eac4b23db75a15a9fa2d)`(uint16_t kc1,uint16_t kc2,uint16_t kc3,uint16_t kc4,uint16_t kc5)` | Check the sequence buffer for the given keycodes. | |||
## Members | |||
#### `public void `[`leader_start_user`](#group__leader_1gae7b6746b0c6bd83cada1e770a7d8315d)`(void)` | |||
User callback, invoked when the leader sequence begins. | |||
#### `public void `[`leader_end_user`](#group__leader_1ga20986d88ffc5f21f00eab4b5e9a03188)`(void)` | |||
User callback, invoked when the leader sequence ends. | |||
#### `public void `[`leader_start`](#group__leader_1ga1f5dad3a2df12d29023e48852ff638ba)`(void)` | |||
Begin the leader sequence, resetting the buffer and timer. | |||
#### `public void `[`leader_end`](#group__leader_1ga7ecc035ad29aed6d5ee3848725234b6c)`(void)` | |||
End the leader sequence. | |||
#### `public void `[`leader_task`](#group__leader_1gaa451b483f86840a1a3a773a074b73019)`(void)` | |||
#### `public bool `[`leader_sequence_active`](#group__leader_1gab52594aae07ec2f59cc6cc00dce9cc0b)`(void)` | |||
Whether the leader sequence is active. | |||
#### `public bool `[`leader_sequence_add`](#group__leader_1ga0b9825ee3240ffac41837664d10c36ff)`(uint16_t keycode)` | |||
Add the given keycode to the sequence buffer. | |||
If `LEADER_NO_TIMEOUT` is defined, the timer is reset if the buffer is empty. | |||
#### Parameters | |||
* `keycode` The keycode to add. | |||
#### Returns | |||
`true` if the keycode was added, `false` if the buffer is full. | |||
#### `public bool `[`leader_sequence_timed_out`](#group__leader_1gadb5905415a54ffab4a47a8d3227b3942)`(void)` | |||
Whether the leader sequence has reached the timeout. | |||
If `LEADER_NO_TIMEOUT` is defined, the buffer must also contain at least one key. | |||
#### `public void `[`leader_reset_timer`](#group__leader_1ga5a9246dc3f952a5522c188f747ce0722)`(void)` | |||
Reset the leader sequence timer. | |||
#### `public bool `[`leader_sequence_one_key`](#group__leader_1ga6bcdd330c94f7074361c21ad8ee901fd)`(uint16_t kc)` | |||
Check the sequence buffer for the given keycode. | |||
#### Parameters | |||
* `kc` The keycode to check. | |||
#### Returns | |||
`true` if the sequence buffer matches. | |||
#### `public bool `[`leader_sequence_two_keys`](#group__leader_1ga6a09c0ae645990e9d219c62244c30e42)`(uint16_t kc1,uint16_t kc2)` | |||
Check the sequence buffer for the given keycodes. | |||
#### Parameters | |||
* `kc1` The first keycode to check. | |||
* `kc2` The second keycode to check. | |||
#### Returns | |||
`true` if the sequence buffer matches. | |||
#### `public bool `[`leader_sequence_three_keys`](#group__leader_1ga0dd4e91b428947bfb5ddb6fd09b29c11)`(uint16_t kc1,uint16_t kc2,uint16_t kc3)` | |||
Check the sequence buffer for the given keycodes. | |||
#### Parameters | |||
* `kc1` The first keycode to check. | |||
* `kc2` The second keycode to check. | |||
* `kc3` The third keycode to check. | |||
#### Returns | |||
`true` if the sequence buffer matches. | |||
#### `public bool `[`leader_sequence_four_keys`](#group__leader_1ga5f71174f1afa31112108336e0d782ff2)`(uint16_t kc1,uint16_t kc2,uint16_t kc3,uint16_t kc4)` | |||
Check the sequence buffer for the given keycodes. | |||
#### Parameters | |||
* `kc1` The first keycode to check. | |||
* `kc2` The second keycode to check. | |||
* `kc3` The third keycode to check. | |||
* `kc4` The fourth keycode to check. | |||
#### Returns | |||
`true` if the sequence buffer matches. | |||
#### `public bool `[`leader_sequence_five_keys`](#group__leader_1ga2c565e3c2d21eac4b23db75a15a9fa2d)`(uint16_t kc1,uint16_t kc2,uint16_t kc3,uint16_t kc4,uint16_t kc5)` | |||
Check the sequence buffer for the given keycodes. | |||
#### Parameters | |||
* `kc1` The first keycode to check. | |||
* `kc2` The second keycode to check. | |||
* `kc3` The third keycode to check. | |||
* `kc4` The fourth keycode to check. | |||
* `kc5` The fifth keycode to check. | |||
#### Returns | |||
`true` if the sequence buffer matches. | |||
@ -0,0 +1,55 @@ | |||
# Quantum Painter LVGL Integration :id=lvgl | |||
LVGL (Light and Versatile Graphics Library) is an open-source graphics library providing everything you need to create an embedded GUI for your board with easy-to-use graphical elements. | |||
LVGL integrates with [Quantum Painter's](quantum_painter.md) API and drivers to render to the display, the hardware supported by Quantum Painter is also supported by LVGL. | |||
?> Keep in mind that enabling the LVGL integration has a big impact in firmware size, it is recommeded to use a supported MCU with >256 kB of flash space. | |||
To learn more about LVGL and how to use it please take a look at their [official documentation](https://docs.lvgl.io/8.2/intro/) | |||
## Enabling LVGL :id=lvgl-enabling | |||
To enable LVGL to be built into your firmware, add the following to `rules.mk`: | |||
```make | |||
QUANTUM_PAINTER_ENABLE = yes | |||
QUANTUM_PAINTER_DRIVERS = ...... | |||
QUANTUM_PAINTER_LVGL_INTEGRATION = yes | |||
``` | |||
To configure the Quantum Painter Display Drivers please read the [Quantum Painter Display Drivers](quantum_painter.md#quantum-painter-drivers) section. | |||
## Quantum Painter LVGL API :id=lvgl-api | |||
### Quantum Painter LVGL Attach :id=lvgl-api-init | |||
```c | |||
bool qp_lvgl_attach(painter_device_t device); | |||
``` | |||
The `qp_lvgl_attach` function is used to set up LVGL with the supplied display, and requires an already configured display. | |||
```c | |||
static painter_device_t display; | |||
void keyboard_post_init_kb(void) { | |||
display = qp_make_.......; // Create the display | |||
qp_init(display, QP_ROTATION_0); // Initialise the display | |||
if (qp_lvgl_attach(display)) { // Attach LVGL to the display | |||
...Your code to draw // Run LVGL specific code to draw | |||
} | |||
} | |||
``` | |||
To init. the display please read the [Display Initialisation](quantum_painter.md#quantum-painter-api-init) section. | |||
!> Attaching LVGL to a display means LVGL subsequently "owns" the display. Using standard Quantum Painter drawing operations with the display after LVGL attachment will likely result in display artifacts. | |||
### Quantum Painter LVGL Detach :id=lvgl-api-init | |||
```c | |||
void qp_lvgl_detach(void) | |||
``` | |||
The `qp_lvgl_detach` function stops the internal LVGL ticks and releases resources related to it. | |||
## Enabling/Disabling LVGL features :id=lvgl-configuring | |||
You can overwrite LVGL specific features in your `lv_conf.h` file. |