`
- * [ ] `git tag ` # Prevent the breakpoint tag from confusing version incrementing
- * [ ] `git push upstream develop`
- * [ ] `git push --tags`
-
-## 4 Weeks Before Merge
+### 4 Weeks Before Merge
* `develop` is now closed to new PR's, only fixes for current PR's may be merged
-* Post call for testers
- * [ ] Discord
- * [ ] GitHub PR
- * [ ] https://reddit.com/r/olkb
+* Post call for testers: message `@Breaking Changes Updates` on `#qmk_firmware` in Discord:
+ * `@Breaking Changes Updates -- Hey folks, last day for functional PRs to be raised against qmk_firmware for this breaking changes cycle is today.`
-## 2 Weeks Before Merge
+### 2 Weeks Before Merge
* `develop` is now closed to existing PR merges, only bugfixes for previous merges may be included
-* Post call for testers
- * [ ] Discord
- * [ ] GitHub PR
- * [ ] https://reddit.com/r/olkb
+* Post call for testers: message `@Breaking Changes Updates` on `#qmk_firmware` in Discord.
+ * `@Breaking Changes Updates -- Hey folks, last day for functional PRs to be merged into qmk_firmware for this breaking changes cycle is today. After that, we're handling bugfixes only.`
-## 1 Week Before Merge
+### 1 Week Before Merge
-* Announce that master will be closed from <2 Days Before> to
- * [ ] Discord
- * [ ] GitHub PR
- * [ ] https://reddit.com/r/olkb
+* `develop` is now closed to PR merges, only critical bugfixes may be included
+* Announce that master will be closed from <2 Days Before> to -- message `@Breaking Changes Updates` on `#qmk_firmware` in Discord:
+ * `@Breaking Changes Updates -- Hey folks, last day for functional PRs to be merged into qmk_firmware for this breaking changes cycle is today. After that, we're handling bugfixes only.`
-## 2 Days Before Merge
+### 2 Days Before Merge
+* `master` is now closed to PR merges
* Announce that master is closed for 2 days
- * [ ] Discord
- * [ ] GitHub PR
- * [ ] https://reddit.com/r/olkb
+ * `@Breaking Changes Updates -- Hey folks, the master branch of qmk_firmware is now locked for the next couple of days while we prepare to merge the newest batch of changes from develop.`
-## Day Of Merge
+### Day Of Merge
* `qmk_firmware` git commands
- * [ ] `git checkout develop`
- * [ ] `git pull --ff-only`
- * [ ] Edit `readme.md`
- * [ ] Remove the notes about `develop`
- * [ ] Roll up the ChangeLog into one file.
- * [ ] `git commit -m 'Merge point for Breaking Change'`
- * [ ] `git push upstream develop`
+ * `git checkout develop`
+ * `git pull --ff-only`
+ * Edit `readme.md`
+ * Remove the notes about `develop`
+ * Roll up the ChangeLog into one file.
+ * `git commit -m 'Merge point for Breaking Change'`
+ * `git push upstream develop`
* GitHub Actions
- * [ ] Create a PR for `develop`
- * [ ] **Turn off 'Automatically delete head branches' for the repository** -- confirm with @qmk/directors that it is done before continuing
+ * Create a PR for `develop`
+ * **Turn off 'Automatically delete head branches' for the repository** -- confirm with @qmk/directors that it is done before continuing
* `qmk_firmware` git commands
- * [ ] `git checkout master`
- * [ ] `git pull --ff-only`
- * [ ] `git merge --no-ff develop`
- * [ ] `git push upstream master`
+ * `git checkout master`
+ * `git pull --ff-only`
+ * `git merge --no-ff develop`
+ * `git tag ` # Prevent the breakpoint tag from confusing version incrementing
+ * `git push upstream `
+ * `git push upstream master`
## Post-merge operations
+### Updating the `develop` branch
+
+This happens immediately after the previous `develop` branch is merged to `master`.
+
+* `qmk_firmware` git commands
+ * `git checkout master`
+ * `git pull --ff-only`
+ * `git checkout develop`
+ * `git pull --ff-only`
+ * `git merge --no-ff master`
+ * Edit `readme.md`
+ * Add a big notice at the top that this is a testing branch. See previous revisions of the `develop` branch.
+ * Include a link to this document
+ * `git commit -m 'Branch point for Breaking Change'`
+ * `git tag breakpoint___
`
+ * `git push upstream breakpoint___
`
+
+* All submodules under `lib` now need to be checked against their QMK-based forks:
+ * `git submodule foreach git log -n1`
+ * Validate each submodule SHA1 matches the qmk fork, e.g. for ChibiOS:
+ * Go to [qmk/ChibiOS](https://github.com/qmk/ChibiOS)
+ * Compare the commit hash in the above output to the commit hash in the repository
+ * If there's a mismatch, that repository needs to have its `master` branch updated to match (otherwise Configurator won't work):
+ * `cd lib/chibios`
+ * `git fetch --all`
+ * `git checkout master`
+ * `git reset --hard `
+ * `git push origin master --force-with-lease`
+
+* Announce that both `master` and `develop` are now unlocked -- message `@Breaking Changes Updates` on `#qmk_firmware` in Discord:
+ * `@Breaking Changes Updates -- Hey folks, develop has now been merged into master -- newest batch of changes are now available for everyone to use!`
+
* (Optional) [update ChibiOS + ChibiOS-Contrib on `develop`](chibios_upgrade_instructions.md)
+
+
+### Set up Discord events for the next cycle
+
+* Update this file with the new dates: `docs/breaking_changes.md`
+* Create Events on the QMK Discord - "Somewhere Else" => "GitHub":
+ * Event #1:
+ | Field | Value |
+ |-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+ | Topic | Last `develop` functionality PRs to be raised |
+ | Start Date | ((5 weeks before merge)), 12:00am |
+ | End Date | ((4 weeks before merge)), 12:00am |
+ | Description | This is the last window for functional PRs to be raised against `develop` for the current breaking changes cycle. After ((4 weeks before merge)), any new PRs targeting `develop` will be deferred to the next cycle. |
+ * Event #2:
+ | Field | Value |
+ |-------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+ | Topic | Last `develop` functionality PRs to be merged |
+ | Start Date | ((4 weeks before merge)), 12:00am |
+ | End Date | ((2 weeks before merge)), 12:00am |
+ | Description | This is the last window for functional PRs to be merged into `develop` for the current breaking changes cycle. After ((2 weeks before merge)), only bugfix PRs targeting `develop` will be considered for merge. |
+ * Event #3:
+ | Field | Value |
+ |-------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+ | Topic | `develop` closed for merges |
+ | Start Date | ((2 weeks before merge)), 12:00am |
+ | End Date | ((day of merge)), 12:00am |
+ | Description | This is the deadline for functionality bugfix PRs to be merged into `develop` for the current breaking changes cycle. After ((1 week before merge)), only critical bugfix PRs targeting `develop` will be considered for merge. |
+ * Event #4:
+ | Field | Value |
+ |-------------|----------------------------------------------------------------------------------------------------------------------|
+ | Topic | `master` closed for merges |
+ | Start Date | ((2 days before merge)), 12:00am |
+ | End Date | ((day of merge)), 12:00am |
+ | Description | This is the period that no PRs are to be merged to `master`, so that the merge of `develop` into `master` is stable. |
+ * Event #5:
+ | Field | Value |
+ |-------------|--------------------------------------------------------------------------------------------------------------------------------------------|
+ | Topic | `develop` merges to `master` |
+ | Start Date | ((day of merge)), 12:00am |
+ | End Date | ((day of merge)), 11:45pm |
+ | Description | At some point, QMK will merge `develop` into `master` and everyone will be able to reap the benefits of the newest batch of functionality. |
diff --git a/docs/breaking_changes_history.md b/docs/breaking_changes_history.md
index c40989fb5ee4..df94b81e04be 100644
--- a/docs/breaking_changes_history.md
+++ b/docs/breaking_changes_history.md
@@ -2,6 +2,9 @@
This page links to all previous changelogs from the QMK Breaking Changes process.
+* [2022 Nov 26](ChangeLog/20221126.md) - version 0.19.0
+* [2022 Aug 27](ChangeLog/20220827.md) - version 0.18.0
+* [2022 May 28](ChangeLog/20220528.md) - version 0.17.0
* [2022 Feb 26](ChangeLog/20220226.md) - version 0.16.0
* [2021 Nov 27](ChangeLog/20211127.md) - version 0.15.0
* [2021 Aug 28](ChangeLog/20210828.md) - version 0.14.0
diff --git a/docs/chibios_upgrade_instructions.md b/docs/chibios_upgrade_instructions.md
index b0a71142a39b..14afe2c74343 100644
--- a/docs/chibios_upgrade_instructions.md
+++ b/docs/chibios_upgrade_instructions.md
@@ -51,7 +51,7 @@ ChibiOS and ChibiOS-Contrib need to be updated in tandem -- the latter has a bra
* `./util/chibios_conf_updater.sh`
* Build everything
* `cd $QMK_FIRMWARE`
- * `qmk multibuild -j4`
+ * `qmk mass-compile -j 4`
* Make sure there are no errors
* Push to the repo
* `git commit -am 'Update ChibiOS to 99.9.9'`
diff --git a/docs/cli_commands.md b/docs/cli_commands.md
index 463abcef12f2..019447075b42 100644
--- a/docs/cli_commands.md
+++ b/docs/cli_commands.md
@@ -90,6 +90,8 @@ This command is similar to `qmk compile`, but can also target a bootloader. The
This command is directory aware. It will automatically fill in KEYBOARD and/or KEYMAP if you are in a keyboard or keymap directory.
+This command can also flash binary firmware files (hex or bin) such as the ones produced by [Configurator](https://config.qmk.fm).
+
**Usage for Configurator Exports**:
```
@@ -102,6 +104,21 @@ qmk flash [-bl ] [-c] [-e =] [-j ] -km [-bl ] [-c] [-e =] [-j ]
```
+**Usage for pre-compiled firmwares**:
+
+**Note**: The microcontroller needs to be specified (`-m` argument) for keyboards with the following bootloaders:
+* HalfKay
+* QMK HID
+* USBaspLoader
+
+ISP flashing is also supported with the following flashers and require the microcontroller to be specified:
+* USBasp
+* USBtinyISP
+
+```
+qmk flash [-m ]
+```
+
**Listing the Bootloaders**
```
@@ -206,7 +223,7 @@ Check your environment and report problems only:
## `qmk format-json`
-Formats a JSON file in a (mostly) human-friendly way. Will usually correctly detect the format of the JSON (info.json or keymap.json) but you can override this with `--format` if neccesary.
+Formats a JSON file in a (mostly) human-friendly way. Will usually correctly detect the format of the JSON (info.json or keymap.json) but you can override this with `--format` if necessary.
**Usage**:
@@ -352,6 +369,73 @@ $ qmk via2json -kb ai03/polaris -o polaris_keymap.json polaris_via_backup.json
Ψ Wrote keymap to /home/you/qmk_firmware/polaris_keymap.json
```
+## `qmk import-keyboard`
+
+This command imports a data-driven `info.json` keyboard into the repo.
+
+**Usage**:
+
+```
+usage: qmk import-keyboard [-h] filename
+```
+
+**Example:**
+
+```
+$ qmk import-keyboard ~/Downloads/forever60.json
+Ψ Importing forever60.json.
+
+Ψ Imported a new keyboard named forever60.
+Ψ To start working on things, `cd` into keyboards/forever60,
+Ψ or open the directory in your preferred text editor.
+Ψ And build with qmk compile -kb forever60 -km default.
+```
+
+## `qmk import-keymap`
+
+This command imports a data-driven `keymap.json` keymap into the repo.
+
+**Usage**:
+
+```
+usage: qmk import-keymap [-h] filename
+```
+
+**Example:**
+
+```
+qmk import-keymap ~/Downloads/asdf2.json
+Ψ Importing asdf2.json.
+
+Ψ Imported a new keymap named asdf2.
+Ψ To start working on things, `cd` into keyboards/takashicompany/dogtag/keymaps/asdf2,
+Ψ or open the directory in your preferred text editor.
+Ψ And build with qmk compile -kb takashicompany/dogtag -km asdf2.
+```
+
+## `qmk import-kbfirmware`
+
+This command creates a new keyboard based on a [Keyboard Firmware Builder](https://kbfirmware.com/) export.
+
+**Usage**:
+
+```
+usage: qmk import-kbfirmware [-h] filename
+```
+
+**Example:**
+
+```
+$ qmk import-kbfirmware ~/Downloads/gh62.json
+Ψ Importing gh62.json.
+
+âš Support here is basic - Consider using 'qmk new-keyboard' instead
+Ψ Imported a new keyboard named gh62.
+Ψ To start working on things, `cd` into keyboards/gh62,
+Ψ or open the directory in your preferred text editor.
+Ψ And build with qmk compile -kb gh62 -km default.
+```
+
---
# Developer Commands
@@ -515,3 +599,16 @@ Run single test:
qmk pytest -t qmk.tests.test_cli_commands.test_c2json
qmk pytest -t qmk.tests.test_qmk_path
+
+## `qmk painter-convert-graphics`
+
+This command converts images to a format usable by QMK, i.e. the QGF File Format. See the [Quantum Painter](quantum_painter.md?id=quantum-painter-cli) documentation for more information on this command.
+
+## `qmk painter-make-font-image`
+
+This command converts a TTF font to an intermediate format for editing, before converting to the QFF File Format. See the [Quantum Painter](quantum_painter.md?id=quantum-painter-cli) documentation for more information on this command.
+
+## `qmk painter-convert-font-image`
+
+This command converts an intermediate font image to the QFF File Format. See the [Quantum Painter](quantum_painter.md?id=quantum-painter-cli) documentation for more information on this command.
+
diff --git a/docs/cli_development.md b/docs/cli_development.md
index 17370a732a25..d878deff17c4 100644
--- a/docs/cli_development.md
+++ b/docs/cli_development.md
@@ -162,7 +162,7 @@ del(cli.config..)
## Writing The Configuration File
-The configuration is not written out when it is changed. Most commands do not need to do this. We prefer to have the user change their configuration deliberitely using `qmk config`.
+The configuration is not written out when it is changed. Most commands do not need to do this. We prefer to have the user change their configuration deliberately using `qmk config`.
You can use `cli.save_config()` to write out the configuration.
diff --git a/docs/cli_tab_complete.md b/docs/cli_tab_complete.md
index 200477624ed9..90950b82da15 100644
--- a/docs/cli_tab_complete.md
+++ b/docs/cli_tab_complete.md
@@ -14,6 +14,10 @@ Add this to the end of your `.profile` or `.bashrc`:
If you put `qmk_firmware` into another location you will need to adjust this path.
+Zsh users will need to load `bashcompinit`. The following can be added to `~/.zshrc` file:
+
+ autoload -Uz bashcompinit && bashcompinit
+
### System Wide Symlink
If you want the tab completion available to all users of the system you can add a symlink to the `qmk_tab_complete.sh` script:
diff --git a/docs/coding_conventions_c.md b/docs/coding_conventions_c.md
index c4bace66cc01..3f44da713d48 100644
--- a/docs/coding_conventions_c.md
+++ b/docs/coding_conventions_c.md
@@ -24,7 +24,7 @@ Most of our style is pretty easy to pick up on, but right now it's not entirely
* Readability is more important than consistency.
* Follow the file's existing style. If the file is mixed, follow the style that makes sense for the section you are modifying.
* When indenting, keep the hash at the start of the line and add whitespace between `#` and `if`, starting with 4 spaces after the `#`.
- * You can follow the indention level of the surrounding C code, or preprocessor directives can have their own indentation levels. Choose the style that best communicates the intent of your code.
+ * You can follow the indentation level of the surrounding C code, or preprocessor directives can have their own indentation levels. Choose the style that best communicates the intent of your code.
Here is an example for easy reference:
diff --git a/docs/coding_conventions_python.md b/docs/coding_conventions_python.md
index 960b9cb49e95..2b6870344873 100644
--- a/docs/coding_conventions_python.md
+++ b/docs/coding_conventions_python.md
@@ -2,7 +2,7 @@
Most of our style follows PEP8 with some local modifications to make things less nit-picky.
-* We target Python 3.7 for compatability with all supported platforms.
+* We target Python 3.7 for compatibility with all supported platforms.
* We indent using four (4) spaces (soft tabs)
* We encourage liberal use of comments
* Think of them as a story describing the feature
@@ -21,7 +21,7 @@ You can use [yapf](https://github.com/google/yapf) to style your code. We provid
We don't have a hard and fast rule for when to use `import ...` vs `from ... import ...`. Understandability and maintainability is our ultimate goal.
-Generally we prefer to import specific function and class names from a module to keep code shorter and easier to understand. Sometimes this results in a name that is ambiguous, and in such cases we prefer to import the module instead. You should avoid using the "as" keyword when importing, unless you are importing a compatability module.
+Generally we prefer to import specific function and class names from a module to keep code shorter and easier to understand. Sometimes this results in a name that is ambiguous, and in such cases we prefer to import the module instead. You should avoid using the "as" keyword when importing, unless you are importing a compatibility module.
Imports should be one line per module. We group import statements together using the standard python rules- system, 3rd party, local.
diff --git a/docs/compatible_microcontrollers.md b/docs/compatible_microcontrollers.md
index 1c5e37f72408..cc9c0b7f92a2 100644
--- a/docs/compatible_microcontrollers.md
+++ b/docs/compatible_microcontrollers.md
@@ -2,6 +2,8 @@
QMK runs on any USB-capable AVR or ARM microcontroller with enough flash space - generally 32kB+ for AVR, and 64kB+ for ARM. With significant disabling of features, QMK may *just* squeeze into 16kB AVR MCUs.
+Features within QMK may or may not be compatible with every microcontroller.
+
## Atmel AVR
The following use [LUFA](https://www.fourwalledcubicle.com/LUFA.php) as the USB stack:
@@ -51,6 +53,7 @@ You can also use any ARM chip with USB that [ChibiOS](https://www.chibios.org) s
### WestBerryTech (WB32)
* [WB32F3G71xx](http://www.westberrytech.com)
+ * [WB32FQ95xx](http://www.westberrytech.com)
### NXP (Kinetis)
@@ -59,9 +62,17 @@ You can also use any ARM chip with USB that [ChibiOS](https://www.chibios.org) s
* [MK20DX128](https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/k-series-cortex-m4/k2x-usb/kinetis-k20-50-mhz-full-speed-usb-mixed-signal-integration-microcontrollers-based-on-arm-cortex-m4-core:K20_50)
* [MK20DX256](https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/k-series-cortex-m4/k2x-usb/kinetis-k20-72-mhz-full-speed-usb-mixed-signal-integration-microcontrollers-mcus-based-on-arm-cortex-m4-core:K20_72)
* PJRC Teensy 3.2
+ * [MK64FX512](https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/k-series-cortex-m4/k6x-ethernet/kinetis-k64-120-mhz-256-kb-sram-microcontrollers-mcus-based-on-arm-cortex-m4-core:K64_120)
+ * PJRC Teensy 3.5
* [MK66FX1M0](https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/k-series-cortex-m4/k6x-ethernet/kinetis-k66-180-mhz-dual-high-speed-full-speed-usbs-2mb-flash-microcontrollers-mcus-based-on-arm-cortex-m4-core:K66_180)
* PJRC Teensy 3.6
+### Raspberry Pi
+
+* [RP2040](https://www.raspberrypi.com/documentation/microcontrollers/rp2040.html)
+
+For a detailed overview about the RP2040 support by QMK see the [dedicated RP2040 page](platformdev_rp2040.md).
+
## Atmel ATSAM
There is limited support for one of Atmel's ATSAM microcontrollers, that being the [ATSAMD51J18A](https://www.microchip.com/wwwproducts/en/ATSAMD51J18A) used by the [Massdrop keyboards](https://github.com/qmk/qmk_firmware/tree/master/keyboards/massdrop). However, it is not recommended to design a board with this microcontroller as the support is quite specialized to Massdrop hardware.
diff --git a/docs/config_options.md b/docs/config_options.md
index 838c4d86fdb0..6b1f83214c7b 100644
--- a/docs/config_options.md
+++ b/docs/config_options.md
@@ -2,7 +2,17 @@
QMK is nearly infinitely configurable. Wherever possible we err on the side of allowing users to customize their keyboard, even at the expense of code size. That level of flexibility makes for a daunting configuration experience, however.
-There are two main types of configuration files in QMK- `config.h` and `rules.mk`. These files exist at various levels in QMK and all files of the same type are combined to build the final configuration. The levels, from lowest priority to highest priority, are:
+There are three main types of configuration files in QMK:
+
+* `config.h`, which contains various preprocessor directives (`#define`, `#ifdef`)
+* `rules.mk`, which contains additional variables
+* `info.json`, which is utilized for [data-driven configuration](https://docs.qmk.fm/#/data_driven_config)
+
+This page will only discuss the first two types, `config.h` and `rules.mk`.
+
+?> While not all settings have data-driven equivalents yet, keyboard makers are encouraged to utilize the `info.json` file to set the metadata for their boards when possible. See the [`info.json` Format](https://docs.qmk.fm/#/reference_info_json) page for more details.
+
+These files exist at various levels in QMK and all files of the same type are combined to build the final configuration. The levels, from lowest priority to highest priority, are:
* QMK Default
* Keyboard
@@ -39,11 +49,11 @@ This is a C header file that is one of the first things included, and will persi
* defines your VID, and for most DIY projects, can be whatever you want
* `#define PRODUCT_ID 0x5678`
* defines your PID, and for most DIY projects, can be whatever you want
-* `#define DEVICE_VER 0`
+* `#define DEVICE_VER 0x0100`
* defines the device version (often used for revisions)
-* `#define MANUFACTURER Me`
+* `#define MANUFACTURER "Me"`
* generally who/whatever brand produced the board
-* `#define PRODUCT Board`
+* `#define PRODUCT "Board"`
* the name of the keyboard
* `#define MATRIX_ROWS 5`
* the number of rows in your keyboard's matrix
@@ -57,8 +67,6 @@ This is a C header file that is one of the first things included, and will persi
* may be omitted by the keyboard designer if matrix reads are handled in an alternate manner. See [low-level matrix overrides](custom_quantum_functions.md?id=low-level-matrix-overrides) for more information.
* `#define MATRIX_IO_DELAY 30`
* the delay in microseconds when between changing matrix pin state and reading values
-* `#define UNUSED_PINS { D1, D2, D3, B1, B2, B3 }`
- * pins unused by the keyboard for reference
* `#define MATRIX_HAS_GHOST`
* define is matrix has ghost (unlikely)
* `#define MATRIX_UNSELECT_DRIVE_HIGH`
@@ -107,8 +115,10 @@ This is a C header file that is one of the first things included, and will persi
* sets the maximum power (in mA) over USB for the device (default: 500)
* `#define USB_POLLING_INTERVAL_MS 10`
* sets the USB polling rate in milliseconds for the keyboard, mouse, and shared (NKRO/media keys) interfaces
-* `#define USB_SUSPEND_WAKEUP_DELAY 200`
- * set the number of milliseconde to pause after sending a wakeup packet
+* `#define USB_SUSPEND_WAKEUP_DELAY 0`
+ * sets the number of milliseconds to pause after sending a wakeup packet.
+ Disabled by default, you might want to set this to 200 (or higher) if the
+ keyboard does not wake up properly after suspending.
* `#define F_SCL 100000L`
* sets the I2C clock rate speed for keyboards using I2C. The default is `400000L`, except for keyboards using `split_common`, where the default is `100000L`.
@@ -131,6 +141,8 @@ If you define these options you will disable the associated feature, which can s
If you define these options you will enable the associated feature, which may increase your code size.
+* `#define ENABLE_COMPILE_KEYCODE`
+ * Enables the `QK_MAKE` keycode
* `#define FORCE_NKRO`
* NKRO by default requires to be turned on, this forces it on during keyboard startup regardless of EEPROM setting. NKRO can still be turned off but will be turned on again if the keyboard reboots.
* `#define STRICT_LAYER_RELEASE`
@@ -139,7 +151,7 @@ If you define these options you will enable the associated feature, which may in
## Behaviors That Can Be Configured
* `#define TAPPING_TERM 200`
- * how long before a tap becomes a hold, if set above 500, a key tapped during the tapping term will turn it into a hold too
+ * how long before a key press becomes a hold
* `#define TAPPING_TERM_PER_KEY`
* enables handling for per key `TAPPING_TERM` settings
* `#define RETRO_TAPPING`
@@ -172,27 +184,20 @@ If you define these options you will enable the associated feature, which may in
* sets the timer for leader key chords to run on each key press rather than overall
* `#define LEADER_KEY_STRICT_KEY_PROCESSING`
* Disables keycode filtering for Mod-Tap and Layer-Tap keycodes. Eg, if you enable this, you would need to specify `MT(MOD_CTL, KC_A)` if you want to use `KC_A`.
+* `#define MOUSE_EXTENDED_REPORT`
+ * Enables support for extended reports (-32767 to 32767, instead of -127 to 127), which may allow for smoother reporting, and prevent maxing out of the reports. Applies to both Pointing Device and Mousekeys.
* `#define ONESHOT_TIMEOUT 300`
* how long before oneshot times out
* `#define ONESHOT_TAP_TOGGLE 2`
* how many taps before oneshot toggle is triggered
-* `#define QMK_KEYS_PER_SCAN 4`
- * Allows sending more than one key per scan. By default, only one key event gets
- sent via `process_record()` per scan. This has little impact on most typing, but
- if you're doing a lot of chords, or your scan rate is slow to begin with, you can
- have some delay in processing key events. Each press and release is a separate
- event. For a keyboard with 1ms or so scan times, even a very fast typist isn't
- going to produce the 500 keystrokes a second needed to actually get more than a
- few ms of delay from this. But if you're doing chording on something with 3-4ms
- scan times? You probably want this.
* `#define COMBO_COUNT 2`
* Set this to the number of combos that you're using in the [Combo](feature_combo.md) feature. Or leave it undefined and programmatically set the count.
* `#define COMBO_TERM 200`
* how long for the Combo keys to be detected. Defaults to `TAPPING_TERM` if not defined.
* `#define COMBO_MUST_HOLD_MODS`
- * Flag for enabling extending timeout on Combos containing modifers
+ * Flag for enabling extending timeout on Combos containing modifiers
* `#define COMBO_MOD_TERM 200`
- * Allows for extending COMBO_TERM for mod keys while mid-combo.
+ * Allows for extending COMBO_TERM for mod keys while mid-combo.
* `#define COMBO_MUST_HOLD_PER_COMBO`
* Flag to enable per-combo COMBO_TERM extension and `get_combo_must_hold()` function
* `#define COMBO_TERM_PER_COMBO`
@@ -202,7 +207,7 @@ If you define these options you will enable the associated feature, which may in
* `#define COMBO_NO_TIMER`
* Disable the combo timer completely for relaxed combos.
* `#define TAP_CODE_DELAY 100`
- * Sets the delay between `register_code` and `unregister_code`, if you're having issues with it registering properly (common on VUSB boards). The value is in milliseconds.
+ * Sets the delay between `register_code` and `unregister_code`, if you're having issues with it registering properly (common on VUSB boards). The value is in milliseconds and defaults to `0`.
* `#define TAP_HOLD_CAPS_DELAY 80`
* Sets the delay for Tap Hold keys (`LT`, `MT`) when using `KC_CAPS_LOCK` keycode, as this has some special handling on MacOS. The value is in milliseconds, and defaults to 80 ms if not defined. For macOS, you may want to set this to 200 or higher.
* `#define KEY_OVERRIDE_REPEAT_DELAY 500`
@@ -212,14 +217,12 @@ If you define these options you will enable the associated feature, which may in
* `#define RGB_DI_PIN D7`
* pin the DI on the WS2812 is hooked-up to
-* `#define RGBLIGHT_ANIMATIONS`
- * run RGB animations
* `#define RGBLIGHT_LAYERS`
* Lets you define [lighting layers](feature_rgblight.md?id=lighting-layers) that can be toggled on or off. Great for showing the current keyboard layer or caps lock state.
* `#define RGBLIGHT_MAX_LAYERS`
* Defaults to 8. Can be expanded up to 32 if more [lighting layers](feature_rgblight.md?id=lighting-layers) are needed.
* Note: Increasing the maximum will increase the firmware size and slow sync on split keyboards.
-* `#define RGBLIGHT_LAYER_BLINK`
+* `#define RGBLIGHT_LAYER_BLINK`
* Adds ability to [blink](feature_rgblight.md?id=lighting-layer-blink) a lighting layer for a specified number of milliseconds (e.g. to acknowledge an action).
* `#define RGBLIGHT_LAYERS_OVERRIDE_RGB_OFF`
* If defined, then [lighting layers](feature_rgblight?id=overriding-rgb-lighting-onoff-status) will be shown even if RGB Light is off.
@@ -322,6 +325,13 @@ There are a few different ways to set handedness for split keyboards (listed in
* `#define SPLIT_USB_TIMEOUT_POLL 10`
* Poll frequency when detecting master/slave when using `SPLIT_USB_DETECT`
+
+* `#define SPLIT_WATCHDOG_ENABLE`
+ * Reboot slave if no communication from master within timeout.
+ * Helps resolve issue where both sides detect as slave using `SPLIT_USB_DETECT`
+
+* `#define SPLIT_WATCHDOG_TIMEOUT 3000`
+ * Maximum slave timeout when waiting for communication from master when using `SPLIT_WATCHDOG_ENABLE`
* `#define FORCED_SYNC_THROTTLE_MS 100`
* Deadline for synchronizing data from master to slave when using the QMK-provided split transport.
@@ -364,8 +374,8 @@ This is a [make](https://www.gnu.org/software/make/manual/make.html) file that i
* `SRC`
* Used to add files to the compilation/linking list.
* `LIB_SRC`
- * Used to add files as a library to the compilation/linking list.
- The files specified by `LIB_SRC` is linked after the files specified by `SRC`.
+ * Used to add files as a library to the compilation/linking list.
+ The files specified by `LIB_SRC` is linked after the files specified by `SRC`.
For example, if you specify:
```
SRC += a.c
@@ -418,7 +428,7 @@ Use these to enable or disable building certain features. The more you have enab
* `NKRO_ENABLE`
* USB N-Key Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
* `RING_BUFFERED_6KRO_REPORT_ENABLE`
- * USB 6-Key Rollover - Instead of stopping any new input once 6 keys are pressed, the oldest key is released and the new key is pressed.
+ * USB 6-Key Rollover - Instead of stopping any new input once 6 keys are pressed, the oldest key is released and the new key is pressed.
* `AUDIO_ENABLE`
* Enable the audio subsystem.
* `KEY_OVERRIDE_ENABLE`
diff --git a/docs/configurator_default_keymaps.md b/docs/configurator_default_keymaps.md
index d2b14ec41101..d08ec2953967 100644
--- a/docs/configurator_default_keymaps.md
+++ b/docs/configurator_default_keymaps.md
@@ -56,7 +56,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[1] = LAYOUT_all(
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, RGB_TOG, RGB_MOD, RGB_HUD, RGB_HUI, RGB_SAD, RGB_SAI, RGB_VAD, RGB_VAI, BL_TOGG, BL_DEC, BL_INC,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_VOLU,
- KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, RESET, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_MPLY, KC_MNXT, KC_VOLD,
+ KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, QK_BOOT, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_MPLY, KC_MNXT, KC_VOLD,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS),
@@ -84,7 +84,7 @@ The default keymap uses the `LAYOUT_all` macro, so that will be the value of the
[
"KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "RGB_TOG", "RGB_MOD", "RGB_HUD", "RGB_HUI", "RGB_SAD", "RGB_SAI", "RGB_VAD", "RGB_VAI", "BL_TOGG", "BL_DEC", "BL_INC",
"KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_VOLU",
- "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "RESET", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_MPLY", "KC_MNXT", "KC_VOLD",
+ "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "QK_BOOT", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_MPLY", "KC_MNXT", "KC_VOLD",
"KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS",
"KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS",
"KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS", "KC_TRNS"
@@ -122,26 +122,26 @@ There is a way to support custom keycodes: if the logic for a custom keycode is
```c
enum custom_keycodes {
- MACRO_1 = SAFE_RANGE,
- MACRO_2,
- MACRO_3
+ CUSTOM_1 = SAFE_RANGE,
+ CUSTOM_2,
+ CUSTOM_3
};
...
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
switch(keycode) {
- case MACRO_1:
+ case CUSTOM_1:
if (record->event.pressed) {
- SEND_STRING("This is macro #1.");
+ SEND_STRING("This is custom keycode #1.");
}
return false;
- case MACRO_2:
+ case CUSTOM_2:
if (record->event.pressed) {
- SEND_STRING("This is macro #2.");
+ SEND_STRING("This is custom keycode #2.");
}
return false;
- case MACRO_3:
+ case CUSTOM_3:
if (record->event.pressed) {
- SEND_STRING("This is macro #3.");
+ SEND_STRING("This is custom keycode #3.");
}
return false;
}
@@ -153,9 +153,9 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
```c
enum keyboard_keycodes {
- MACRO_1 = SAFE_RANGE,
- MACRO_2,
- MACRO_3,
+ CUSTOM_1 = SAFE_RANGE,
+ CUSTOM_2,
+ CUSTOM_3,
NEW_SAFE_RANGE // Important!
};
```
@@ -165,19 +165,19 @@ enum keyboard_keycodes {
```c
bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
switch(keycode) {
- case MACRO_1:
+ case CUSTOM_1:
if (record->event.pressed) {
- SEND_STRING("This is macro #1.");
+ SEND_STRING("This is custom keycode #1.");
}
return false;
- case MACRO_2:
+ case CUSTOM_2:
if (record->event.pressed) {
- SEND_STRING("This is macro #2.");
+ SEND_STRING("This is custom keycode #2.");
}
return false;
- case MACRO_3:
+ case CUSTOM_3:
if (record->event.pressed) {
- SEND_STRING("This is macro #3.");
+ SEND_STRING("This is custom keycode #3.");
}
return false;
}
diff --git a/docs/custom_matrix.md b/docs/custom_matrix.md
index 8f6878f94a6c..6d6ae5e97268 100644
--- a/docs/custom_matrix.md
+++ b/docs/custom_matrix.md
@@ -81,17 +81,17 @@ void matrix_init(void) {
}
uint8_t matrix_scan(void) {
- bool matrix_has_changed = false;
+ bool changed = false;
// TODO: add matrix scanning routine here
// Unless hardware debouncing - use the configured debounce routine
- debounce(raw_matrix, matrix, MATRIX_ROWS, changed);
+ changed = debounce(raw_matrix, matrix, MATRIX_ROWS, changed);
// This *must* be called for correct keyboard behavior
matrix_scan_quantum();
- return matrix_has_changed;
+ return changed;
}
```
diff --git a/docs/custom_quantum_functions.md b/docs/custom_quantum_functions.md
index f9a6e1bcc8db..2917fbad2685 100644
--- a/docs/custom_quantum_functions.md
+++ b/docs/custom_quantum_functions.md
@@ -102,11 +102,11 @@ These are the three main initialization functions, listed in the order that they
## Keyboard Pre Initialization code
-This runs very early during startup, even before the USB has been started.
+This runs very early during startup, even before the USB has been started.
Shortly after this, the matrix is initialized.
-For most users, this shouldn't be used, as it's primarily for hardware oriented initialization.
+For most users, this shouldn't be used, as it's primarily for hardware oriented initialization.
However, if you have hardware stuff that you need initialized, this is the best place for it (such as initializing LED pins).
@@ -134,9 +134,9 @@ void keyboard_pre_init_user(void) {
## Matrix Initialization Code
-This is called when the matrix is initialized, and after some of the hardware has been set up, but before many of the features have been initialized.
+This is called when the matrix is initialized, and after some of the hardware has been set up, but before many of the features have been initialized.
-This is useful for setting up stuff that you may need elsewhere, but isn't hardware related nor is dependant on where it's started.
+This is useful for setting up stuff that you may need elsewhere, but isn't hardware related nor is dependant on where it's started.
### `matrix_init_*` Function Documentation
@@ -227,190 +227,11 @@ void suspend_wakeup_init_user(void) {
* Keyboard/Revision: `void suspend_power_down_kb(void)` and `void suspend_wakeup_init_user(void)`
* Keymap: `void suspend_power_down_kb(void)` and `void suspend_wakeup_init_user(void)`
-# Layer Change Code :id=layer-change-code
-
-This runs code every time that the layers get changed. This can be useful for layer indication, or custom layer handling.
-
-### Example `layer_state_set_*` Implementation
-
-This example shows how to set the [RGB Underglow](feature_rgblight.md) lights based on the layer, using the Planck as an example.
-
-```c
-layer_state_t layer_state_set_user(layer_state_t state) {
- switch (get_highest_layer(state)) {
- case _RAISE:
- rgblight_setrgb (0x00, 0x00, 0xFF);
- break;
- case _LOWER:
- rgblight_setrgb (0xFF, 0x00, 0x00);
- break;
- case _PLOVER:
- rgblight_setrgb (0x00, 0xFF, 0x00);
- break;
- case _ADJUST:
- rgblight_setrgb (0x7A, 0x00, 0xFF);
- break;
- default: // for any other layers, or the default layer
- rgblight_setrgb (0x00, 0xFF, 0xFF);
- break;
- }
- return state;
-}
-```
-
-Use the `IS_LAYER_ON_STATE(state, layer)` and `IS_LAYER_OFF_STATE(state, layer)` macros to check the status of a particular layer.
-
-Outside of `layer_state_set_*` functions, you can use the `IS_LAYER_ON(layer)` and `IS_LAYER_OFF(layer)` macros to check global layer state.
-
-### `layer_state_set_*` Function Documentation
-
-* Keyboard/Revision: `layer_state_t layer_state_set_kb(layer_state_t state)`
-* Keymap: `layer_state_t layer_state_set_user(layer_state_t state)`
-
-
-The `state` is the bitmask of the active layers, as explained in the [Keymap Overview](keymap.md#keymap-layer-status)
-
-
-# Persistent Configuration (EEPROM)
-
-This allows you to configure persistent settings for your keyboard. These settings are stored in the EEPROM of your controller, and are retained even after power loss. The settings can be read with `eeconfig_read_kb` and `eeconfig_read_user`, and can be written to using `eeconfig_update_kb` and `eeconfig_update_user`. This is useful for features that you want to be able to toggle (like toggling rgb layer indication). Additionally, you can use `eeconfig_init_kb` and `eeconfig_init_user` to set the default values for the EEPROM.
-
-The complicated part here, is that there are a bunch of ways that you can store and access data via EEPROM, and there is no "correct" way to do this. However, you only have a DWORD (4 bytes) for each function.
-
-Keep in mind that EEPROM has a limited number of writes. While this is very high, it's not the only thing writing to the EEPROM, and if you write too often, you can potentially drastically shorten the life of your MCU.
-
-* If you don't understand the example, then you may want to avoid using this feature, as it is rather complicated.
-
-### Example Implementation
-
-This is an example of how to add settings, and read and write it. We're using the user keymap for the example here. This is a complex function, and has a lot going on. In fact, it uses a lot of the above functions to work!
-
-
-In your keymap.c file, add this to the top:
-```c
-typedef union {
- uint32_t raw;
- struct {
- bool rgb_layer_change :1;
- };
-} user_config_t;
-
-user_config_t user_config;
-```
-
-This sets up a 32 bit structure that we can store settings with in memory, and write to the EEPROM. Using this removes the need to define variables, since they're defined in this structure. Remember that `bool` (boolean) values use 1 bit, `uint8_t` uses 8 bits, `uint16_t` uses up 16 bits. You can mix and match, but changing the order can cause issues, as it will change the values that are read and written.
-
-We're using `rgb_layer_change`, for the `layer_state_set_*` function, and use `keyboard_post_init_user` and `process_record_user` to configure everything.
-
-Now, using the `keyboard_post_init_user` code above, you want to add `eeconfig_read_user()` to it, to populate the structure you've just created. And you can then immediately use this structure to control functionality in your keymap. And It should look like:
-```c
-void keyboard_post_init_user(void) {
- // Call the keymap level matrix init.
-
- // Read the user config from EEPROM
- user_config.raw = eeconfig_read_user();
-
- // Set default layer, if enabled
- if (user_config.rgb_layer_change) {
- rgblight_enable_noeeprom();
- rgblight_sethsv_noeeprom_cyan();
- rgblight_mode_noeeprom(1);
- }
-}
-```
-The above function will use the EEPROM config immediately after reading it, to set the default layer's RGB color. The "raw" value of it is converted in a usable structure based on the "union" that you created above.
-
-```c
-layer_state_t layer_state_set_user(layer_state_t state) {
- switch (get_highest_layer(state)) {
- case _RAISE:
- if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom_magenta(); rgblight_mode_noeeprom(1); }
- break;
- case _LOWER:
- if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom_red(); rgblight_mode_noeeprom(1); }
- break;
- case _PLOVER:
- if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom_green(); rgblight_mode_noeeprom(1); }
- break;
- case _ADJUST:
- if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom_white(); rgblight_mode_noeeprom(1); }
- break;
- default: // for any other layers, or the default layer
- if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom_cyan(); rgblight_mode_noeeprom(1); }
- break;
- }
- return state;
-}
-```
-This will cause the RGB underglow to be changed ONLY if the value was enabled. Now to configure this value, create a new keycode for `process_record_user` called `RGB_LYR`. Additionally, we want to make sure that if you use the normal RGB codes, that it turns off Using the example above, make it look this:
-```c
-
-bool process_record_user(uint16_t keycode, keyrecord_t *record) {
- switch (keycode) {
- case FOO:
- if (record->event.pressed) {
- // Do something when pressed
- } else {
- // Do something else when release
- }
- return false; // Skip all further processing of this key
- case KC_ENTER:
- // Play a tone when enter is pressed
- if (record->event.pressed) {
- PLAY_SONG(tone_qwerty);
- }
- return true; // Let QMK send the enter press/release events
- case RGB_LYR: // This allows me to use underglow as layer indication, or as normal
- if (record->event.pressed) {
- user_config.rgb_layer_change ^= 1; // Toggles the status
- eeconfig_update_user(user_config.raw); // Writes the new status to EEPROM
- if (user_config.rgb_layer_change) { // if layer state indication is enabled,
- layer_state_set(layer_state); // then immediately update the layer color
- }
- }
- return false;
- case RGB_MODE_FORWARD ... RGB_MODE_GRADIENT: // For any of the RGB codes (see quantum_keycodes.h, L400 for reference)
- if (record->event.pressed) { //This disables layer indication, as it's assumed that if you're changing this ... you want that disabled
- if (user_config.rgb_layer_change) { // only if this is enabled
- user_config.rgb_layer_change = false; // disable it, and
- eeconfig_update_user(user_config.raw); // write the setings to EEPROM
- }
- }
- return true; break;
- default:
- return true; // Process all other keycodes normally
- }
-}
-```
-And lastly, you want to add the `eeconfig_init_user` function, so that when the EEPROM is reset, you can specify default values, and even custom actions. To force an EEPROM reset, use the `EEP_RST` keycode or [Bootmagic Lite](feature_bootmagic.md) functionallity. For example, if you want to set rgb layer indication by default, and save the default valued.
-
-```c
-void eeconfig_init_user(void) { // EEPROM is getting reset!
- user_config.raw = 0;
- user_config.rgb_layer_change = true; // We want this enabled by default
- eeconfig_update_user(user_config.raw); // Write default value to EEPROM now
-
- // use the non noeeprom versions, to write these values to EEPROM too
- rgblight_enable(); // Enable RGB by default
- rgblight_sethsv_cyan(); // Set it to CYAN by default
- rgblight_mode(1); // set to solid by default
-}
-```
-
-And you're done. The RGB layer indication will only work if you want it to. And it will be saved, even after unplugging the board. And if you use any of the RGB codes, it will disable the layer indication, so that it stays on the mode and color that you set it to.
-
-### 'EECONFIG' Function Documentation
-
-* Keyboard/Revision: `void eeconfig_init_kb(void)`, `uint32_t eeconfig_read_kb(void)` and `void eeconfig_update_kb(uint32_t val)`
-* Keymap: `void eeconfig_init_user(void)`, `uint32_t eeconfig_read_user(void)` and `void eeconfig_update_user(uint32_t val)`
-
-The `val` is the value of the data that you want to write to EEPROM. And the `eeconfig_read_*` function return a 32 bit (DWORD) value from the EEPROM.
-
-### Deferred Execution :id=deferred-execution
+# Deferred Execution :id=deferred-execution
QMK has the ability to execute a callback after a specified period of time, rather than having to manually manage timers. To enable this functionality, set `DEFERRED_EXEC_ENABLE = yes` in rules.mk.
-#### Deferred executor callbacks
+## Deferred executor callbacks
All _deferred executor callbacks_ have a common function signature and look like:
@@ -430,7 +251,7 @@ The return value is the number of milliseconds to use if the function should be
?> Note that the returned delay will be applied to the intended trigger time, not the time of callback invocation. This allows for generally consistent timing even in the face of occasional late execution.
-#### Deferred executor registration
+## Deferred executor registration
Once a callback has been defined, it can be scheduled using the following API:
@@ -444,7 +265,7 @@ The third parameter is the `cb_arg` that gets passed to the callback at the poin
The return value is a `deferred_token` that can consequently be used to cancel the deferred executor callback before it's invoked. If a failure occurs, the returned value will be `INVALID_DEFERRED_TOKEN`. Usually this will be as a result of supplying `0` to the delay, or a `NULL` for the callback. The other failure case is if there are too many deferred executions "in flight" -- this can be increased by changing the limit, described below.
-#### Extending a deferred execution
+## Extending a deferred execution
The `deferred_token` returned by `defer_exec()` can be used to extend a the duration a pending execution waits before it gets invoked:
```c
@@ -452,7 +273,7 @@ The `deferred_token` returned by `defer_exec()` can be used to extend a the dura
extend_deferred_exec(my_token, 800);
```
-#### Cancelling a deferred execution
+## Cancelling a deferred execution
The `deferred_token` returned by `defer_exec()` can be used to cancel a pending execution before it gets invoked:
```c
@@ -462,7 +283,7 @@ cancel_deferred_exec(my_token);
Once a token has been canceled, it should be considered invalid. Reusing the same token is not supported.
-#### Deferred callback limits
+## Deferred callback limits
There are a maximum number of deferred callbacks that can be scheduled, controlled by the value of the define `MAX_DEFERRED_EXECUTORS`.
@@ -471,3 +292,15 @@ If registrations fail, then you can increase this value in your keyboard or keym
```c
#define MAX_DEFERRED_EXECUTORS 16
```
+
+# Advanced topics :id=advanced-topics
+
+This page used to encompass a large set of features. We have moved many sections that used to be part of this page to their own pages. Everything below this point is simply a redirect so that people following old links on the web find what they're looking for.
+
+## Layer Change Code :id=layer-change-code
+
+[Layer change code](feature_layers.md#layer-change-code)
+
+## Persistent Configuration (EEPROM) :id=persistent-configuration-eeprom
+
+[Persistent Configuration (EEPROM)](feature_eeprom.md)
diff --git a/docs/data_driven_config.md b/docs/data_driven_config.md
index 38fb5dbf1445..ba287f5688e9 100644
--- a/docs/data_driven_config.md
+++ b/docs/data_driven_config.md
@@ -22,7 +22,7 @@ You will then need to add support for your new configuration to `info.json`. The
1. Add it to the schema in `data/schemas/keyboards.jsonschema`
1. Add a mapping in `data/maps`
-1. (optional and discoraged) Add code to extract/generate it to:
+1. (optional and discouraged) Add code to extract/generate it to:
* `lib/python/qmk/info.py`
* `lib/python/qmk/cli/generate/config_h.py`
* `lib/python/qmk/cli/generate/rules_mk.py`
@@ -41,10 +41,10 @@ In other cases you should group like options together in an `object`. This is pa
### Add a mapping
-In most cases you can add a simple mapping. These are maintained as JSON files in `data/mappings/info_config.json` and `data/mappings/info_rules.json`, and control mapping for `config.h` and `rules.mk`, respectively. Each mapping is keyed by the `config.h` or `rules.mk` variable, and the value is a hash with the following keys:
+In most cases you can add a simple mapping. These are maintained as JSON files in `data/mappings/info_config.hjson` and `data/mappings/info_rules.hjson`, and control mapping for `config.h` and `rules.mk`, respectively. Each mapping is keyed by the `config.h` or `rules.mk` variable, and the value is a hash with the following keys:
* `info_key`: (required) The location within `info.json` for this value. See below.
-* `value_type`: (optional) Default `str`. The format for this variable's value. See below.
+* `value_type`: (optional) Default `raw`. The format for this variable's value. See below.
* `to_json`: (optional) Default `true`. Set to `false` to exclude this mapping from info.json
* `to_c`: (optional) Default `true`. Set to `false` to exclude this mapping from config.h
* `warn_duplicate`: (optional) Default `true`. Set to `false` to turn off warning when a value exists in both places
@@ -57,7 +57,7 @@ Under the hood we use [Dotty Dict](https://dotty-dict.readthedocs.io/en/latest/)
#### Value Types
-By default we treat all values as simple strings. If your value is more complex you can use one of these types to intelligently parse the data:
+By default we treat all values as unquoted "raw" data. If your value is more complex you can use one of these types to intelligently parse the data:
* `array`: A comma separated array of strings
* `array.int`: A comma separated array of integers
@@ -65,6 +65,7 @@ By default we treat all values as simple strings. If your value is more complex
* `hex`: A number formatted as hex
* `list`: A space separate array of strings
* `mapping`: A hash of key/value pairs
+* `str`: A quoted string literal
### Add code to extract it
diff --git a/docs/documentation_templates.md b/docs/documentation_templates.md
index e22dbf23874c..91ad80166246 100644
--- a/docs/documentation_templates.md
+++ b/docs/documentation_templates.md
@@ -4,7 +4,7 @@ This page documents the templates you should use when submitting new Keymaps and
## Keymap `readme.md` Template :id=keyboard-readmemd-template
-Most keymaps have an image depicting the layout. You can use [Keyboard Layout Editor](https://keyboard-layout-editor.com) to create an image. Upload it to [Imgur](https://imgur.com) or another hosting service, please do not include images in your Pull Request.
+Most keymaps have an image depicting the layout. You can use [Keyboard Layout Editor](http://keyboard-layout-editor.com) to create an image. Upload it to [Imgur](https://imgur.com) or another hosting service, please do not include images in your Pull Request.
Below the image you should write a short description to help people understand your keymap.
diff --git a/docs/driver_installation_zadig.md b/docs/driver_installation_zadig.md
index 003629ba9a00..3b2c0b74dc41 100644
--- a/docs/driver_installation_zadig.md
+++ b/docs/driver_installation_zadig.md
@@ -8,7 +8,7 @@ We recommend the use of the [Zadig](https://zadig.akeo.ie/) utility. If you have
## Installation
-Put your keyboard into bootloader mode, either by hitting the `RESET` keycode (which may be on a different layer), or by pressing the reset switch that's usually located on the underside of the board. If your keyboard has neither, try holding Escape or Space+`B` as you plug it in (see the [Bootmagic Lite](feature_bootmagic.md) docs for more details). Some boards use [Command](feature_command.md) instead of Bootmagic; in this case, you can enter bootloader mode by hitting Left Shift+Right Shift+`B` or Left Shift+Right Shift+Escape at any point while the keyboard is plugged in.
+Put your keyboard into bootloader mode, either by hitting the `QK_BOOT` keycode (which may be on a different layer), or by pressing the reset switch that's usually located on the underside of the board. If your keyboard has neither, try holding Escape or Space+`B` as you plug it in (see the [Bootmagic Lite](feature_bootmagic.md) docs for more details). Some boards use [Command](feature_command.md) instead of Bootmagic; in this case, you can enter bootloader mode by hitting Left Shift+Right Shift+`B` or Left Shift+Right Shift+Escape at any point while the keyboard is plugged in.
Some keyboards may have specific instructions for entering the bootloader. For example, the [Bootmagic Lite](feature_bootmagic.md) key (default: Escape) might be on a different key, e.g. Left Control; or the magic combination for Command (default: Left Shift+Right Shift) might require you to hold something else, e.g. Left Control+Right Control. Refer to the board's README file if you are unsure.
To put a device in bootloader mode with USBaspLoader, tap the `RESET` button while holding down the `BOOT` button.
diff --git a/docs/eeprom_driver.md b/docs/eeprom_driver.md
index 6dcf10c04d49..50d8bcb7b328 100644
--- a/docs/eeprom_driver.md
+++ b/docs/eeprom_driver.md
@@ -2,12 +2,15 @@
The EEPROM driver can be swapped out depending on the needs of the keyboard, or whether extra hardware is present.
+Selecting the EEPROM driver is done in your keyboard's `rules.mk`:
+
Driver | Description
-----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
`EEPROM_DRIVER = vendor` (default) | Uses the on-chip driver provided by the chip manufacturer. For AVR, this is provided by avr-libc. This is supported on ARM for a subset of chips -- STM32F3xx, STM32F1xx, and STM32F072xB will be emulated by writing to flash. STM32L0xx and STM32L1xx will use the onboard dedicated true EEPROM. Other chips will generally act as "transient" below.
`EEPROM_DRIVER = i2c` | Supports writing to I2C-based 24xx EEPROM chips. See the driver section below.
`EEPROM_DRIVER = spi` | Supports writing to SPI-based 25xx EEPROM chips. See the driver section below.
`EEPROM_DRIVER = transient` | Fake EEPROM driver -- supports reading/writing to RAM, and will be discarded when power is lost.
+`EEPROM_DRIVER = wear_leveling` | Frontend driver for the wear_leveling system, allowing for EEPROM emulation on top of flash -- both in-MCU and external SPI NOR flash.
## Vendor Driver Configuration :id=vendor-eeprom-driver-configuration
@@ -43,8 +46,9 @@ Module | Equivalent `#define` | Source
-----------------|---------------------------------|------------------------------------------
CAT24C512 EEPROM | `#define EEPROM_I2C_CAT24C512` |
RM24C512C EEPROM | `#define EEPROM_I2C_RM24C512C` |
-24LC64 EEPROM | `#define EEPROM_I2C_24LC64` |
-24LC128 EEPROM | `#define EEPROM_I2C_24LC128` |
+24LC32A EEPROM | `#define EEPROM_I2C_24LC32A` |
+24LC64 EEPROM | `#define EEPROM_I2C_24LC64` |
+24LC128 EEPROM | `#define EEPROM_I2C_24LC128` |
24LC256 EEPROM | `#define EEPROM_I2C_24LC256` |
MB85RC256V FRAM | `#define EEPROM_I2C_MB85RC256V` |
@@ -54,13 +58,13 @@ MB85RC256V FRAM | `#define EEPROM_I2C_MB85RC256V` | There's no way to determine if there is an SPI EEPROM actually responding. Generally, this will result in reads of nothing but zero.
@@ -73,3 +77,84 @@ The only configurable item for the transient EEPROM driver is its size:
`#define TRANSIENT_EEPROM_SIZE` | Total size of the EEPROM storage in bytes | 64
Default values and extended descriptions can be found in `drivers/eeprom/eeprom_transient.h`.
+
+## Wear-leveling Driver Configuration :id=wear_leveling-eeprom-driver-configuration
+
+The wear-leveling driver uses an algorithm to minimise the number of erase cycles on the underlying MCU flash memory.
+
+There is no specific configuration for this driver, but the wear-leveling system used by this driver may need configuration. See the [wear-leveling configuration](#wear_leveling-configuration) section for more information.
+
+# Wear-leveling Configuration :id=wear_leveling-configuration
+
+The wear-leveling driver has a few possible _backing stores_ that may be used by adding to your keyboard's `rules.mk` file:
+
+Driver | Description
+----------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+`WEAR_LEVELING_DRIVER = embedded_flash` | This driver is used for emulating EEPROM by writing to embedded flash on the MCU.
+`WEAR_LEVELING_DRIVER = spi_flash` | This driver is used to address external SPI NOR Flash peripherals.
+`WEAR_LEVELING_DRIVER = rp2040_flash` | This driver is used to write to the same storage the RP2040 executes code from.
+`WEAR_LEVELING_DRIVER = legacy` | This driver is the "legacy" emulated EEPROM provided in historical revisions of QMK. Currently used for STM32F0xx and STM32F4x1, but slated for deprecation and removal once `embedded_flash` support for those MCU families is complete.
+
+!> All wear-leveling drivers require an amount of RAM equivalent to the selected logical EEPROM size. Increasing the size to 32kB of EEPROM requires 32kB of RAM, which a significant number of MCUs simply do not have.
+
+## Wear-leveling Embedded Flash Driver Configuration :id=wear_leveling-efl-driver-configuration
+
+This driver performs writes to the embedded flash storage embedded in the MCU. In most circumstances, the last few of sectors of flash are used in order to minimise the likelihood of collision with program code.
+
+Configurable options in your keyboard's `config.h`:
+
+`config.h` override | Default | Description
+-----------------------------------------|-------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+`#define WEAR_LEVELING_EFL_FIRST_SECTOR` | _unset_ | The first sector on the MCU to use. By default this is not defined and calculated at runtime based on the MCU. However, different flash sizes on MCUs may require custom configuration.
+`#define WEAR_LEVELING_EFL_FLASH_SIZE` | _unset_ | Allows overriding the flash size available for use for wear-leveling. Under normal circumstances this is automatically calculated and should not need to be overridden. Specifying a size larger than the amount actually available in flash will usually prevent the MCU from booting.
+`#define WEAR_LEVELING_LOGICAL_SIZE` | `1024` | Number of bytes "exposed" to the rest of QMK and denotes the size of the usable EEPROM.
+`#define WEAR_LEVELING_BACKING_SIZE` | `2048` | Number of bytes used by the wear-leveling algorithm for its underlying storage, and needs to be a multiple of the logical size.
+`#define BACKING_STORE_WRITE_SIZE` | _automatic_ | The byte width of the underlying write used on the MCU, and is usually automatically determined from the selected MCU family. If an error occurs in the auto-detection, you'll need to consult the MCU's datasheet and determine this value, specifying it directly.
+
+!> If your MCU does not boot after swapping to the EFL wear-leveling driver, it's likely that the flash size is incorrectly detected, usually as an MCU with larger flash and may require overriding.
+
+## Wear-leveling SPI Flash Driver Configuration :id=wear_leveling-flash_spi-driver-configuration
+
+This driver performs writes to an external SPI NOR Flash peripheral. It also requires a working configuration for the SPI NOR Flash peripheral -- see the [flash driver](flash_driver.md) documentation for more information.
+
+Configurable options in your keyboard's `config.h`:
+
+`config.h` override | Default | Description
+----------------------------------------------------|--------------------------------|--------------------------------------------------------------------------------------------------------------------------------
+`#define WEAR_LEVELING_EXTERNAL_FLASH_BLOCK_COUNT` | `1` | Number of blocks in the external flash used by the wear-leveling algorithm.
+`#define WEAR_LEVELING_EXTERNAL_FLASH_BLOCK_OFFSET` | `0` | The index first block in the external flash used by the wear-leveling algorithm.
+`#define WEAR_LEVELING_LOGICAL_SIZE` | `((block_count*block_size)/2)` | Number of bytes "exposed" to the rest of QMK and denotes the size of the usable EEPROM. Result must be <= 64kB.
+`#define WEAR_LEVELING_BACKING_SIZE` | `(block_count*block_size)` | Number of bytes used by the wear-leveling algorithm for its underlying storage, and needs to be a multiple of the logical size.
+`#define BACKING_STORE_WRITE_SIZE` | `8` | The write width used whenever a write is performed on the external flash peripheral.
+
+!> There is currently a limit of 64kB for the EEPROM subsystem within QMK, so using a larger flash is not going to be beneficial as the logical size cannot be increased beyond 65536. The backing size may be increased to a larger value, but erase timing may suffer as a result.
+
+## Wear-leveling RP2040 Driver Configuration :id=wear_leveling-rp2040-driver-configuration
+
+This driver performs writes to the same underlying storage that the RP2040 executes its code.
+
+Configurable options in your keyboard's `config.h`:
+
+`config.h` override | Default | Description
+------------------------------------------|----------------------------|--------------------------------------------------------------------------------------------------------------------------------
+`#define WEAR_LEVELING_RP2040_FLASH_SIZE` | `PICO_FLASH_SIZE_BYTES` | Number of bytes of flash on the board.
+`#define WEAR_LEVELING_RP2040_FLASH_BASE` | `(flash_size-sector_size)` | The byte-wise location that the backing storage should be located.
+`#define WEAR_LEVELING_LOGICAL_SIZE` | `4096` | Number of bytes "exposed" to the rest of QMK and denotes the size of the usable EEPROM.
+`#define WEAR_LEVELING_BACKING_SIZE` | `8192` | Number of bytes used by the wear-leveling algorithm for its underlying storage, and needs to be a multiple of the logical size as well as the sector size.
+`#define BACKING_STORE_WRITE_SIZE` | `2` | The write width used whenever a write is performed on the external flash peripheral.
+
+## Wear-leveling Legacy EEPROM Emulation Driver Configuration :id=wear_leveling-legacy-driver-configuration
+
+This driver performs writes to the embedded flash storage embedded in the MCU much like the normal Embedded Flash Driver, and is only for use with STM32F0xx and STM32F4x1 devices. This flash implementation is still currently provided as the EFL driver is currently non-functional for the previously mentioned families.
+
+By default, `1024` bytes of emulated EEPROM is provided:
+
+MCU | EEPROM Provided | Flash Used
+----------|-----------------|--------------
+STM32F042 | `1024` bytes | `2048` bytes
+STM32F070 | `1024` bytes | `2048` bytes
+STM32F072 | `1024` bytes | `2048` bytes
+STM32F401 | `1024` bytes | `16384` bytes
+STM32F411 | `1024` bytes | `16384` bytes
+
+Under normal circumstances configuration of this driver requires intimate knowledge of the MCU's flash structure -- reconfiguration is at your own risk and will require referring to the code.
diff --git a/docs/faq_debug.md b/docs/faq_debug.md
index fba27c5f6838..cad98bc33171 100644
--- a/docs/faq_debug.md
+++ b/docs/faq_debug.md
@@ -4,7 +4,7 @@ This page details various common questions people have about troubleshooting the
## Debugging :id=debugging
-Your keyboard will output debug information if you have `CONSOLE_ENABLE = yes` in your `rules.mk`. By default the output is very limited, but you can turn on debug mode to increase the amount of debug output. Use the `DEBUG` keycode in your keymap, use the [Command](feature_command.md) feature to enable debug mode, or add the following code to your keymap.
+Your keyboard will output debug information if you have `CONSOLE_ENABLE = yes` in your `rules.mk`. By default the output is very limited, but you can turn on debug mode to increase the amount of debug output. Use the `DB_TOGG` keycode in your keymap, use the [Command](feature_command.md) feature to enable debug mode, or add the following code to your keymap.
```c
void keyboard_post_init_user(void) {
@@ -59,7 +59,7 @@ When porting, or when attempting to diagnose pcb issues, it can be useful to kno
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
// If console is enabled, it will print the matrix position and status of each key pressed
#ifdef CONSOLE_ENABLE
- uprintf("KL: kc: 0x%04X, col: %u, row: %u, pressed: %b, time: %u, interrupt: %b, count: %u\n", keycode, record->event.key.col, record->event.key.row, record->event.pressed, record->event.time, record->tap.interrupted, record->tap.count);
+ uprintf("KL: kc: 0x%04X, col: %2u, row: %2u, pressed: %u, time: %5u, int: %u, count: %u\n", keycode, record->event.key.col, record->event.key.row, record->event.pressed, record->event.time, record->tap.interrupted, record->tap.count);
#endif
return true;
}
@@ -69,12 +69,12 @@ Example output
```
Waiting for device:.......
Listening:
-KL: kc: 169, col: 0, row: 0, pressed: 1
-KL: kc: 169, col: 0, row: 0, pressed: 0
-KL: kc: 174, col: 1, row: 0, pressed: 1
-KL: kc: 174, col: 1, row: 0, pressed: 0
-KL: kc: 172, col: 2, row: 0, pressed: 1
-KL: kc: 172, col: 2, row: 0, pressed: 0
+KL: kc: 169, col: 0, row: 0, pressed: 1, time: 15505, int: 0, count: 0
+KL: kc: 169, col: 0, row: 0, pressed: 0, time: 15510, int: 0, count: 0
+KL: kc: 174, col: 1, row: 0, pressed: 1, time: 15703, int: 0, count: 0
+KL: kc: 174, col: 1, row: 0, pressed: 0, time: 15843, int: 0, count: 0
+KL: kc: 172, col: 2, row: 0, pressed: 1, time: 16303, int: 0, count: 0
+KL: kc: 172, col: 2, row: 0, pressed: 0, time: 16411, int: 0, count: 0
```
### How long did it take to scan for a keypress?
@@ -133,3 +133,4 @@ Check:
- Set `debug_enable=true`. See [Debugging](#debugging)
- Try using `print` function instead of debug print. See **common/print.h**.
- Disconnect other devices with console function. See [Issue #97](https://github.com/tmk/tmk_keyboard/issues/97).
+- Ensure all strings end with a newline character (`\n`). QMK Toolbox prints console output on a per-line basis.
diff --git a/docs/faq_keymap.md b/docs/faq_keymap.md
index 01ded4f23199..864128183508 100644
--- a/docs/faq_keymap.md
+++ b/docs/faq_keymap.md
@@ -3,6 +3,7 @@
This page covers questions people often have about keymaps. If you haven't you should read [Keymap Overview](keymap.md) first.
## What Keycodes Can I Use?
+
See [Keycodes](keycodes.md) for an index of keycodes available to you. These link to more extensive documentation when available.
Keycodes are actually defined in [quantum/keycode.h](https://github.com/qmk/qmk_firmware/blob/master/quantum/keycode.h).
@@ -25,25 +26,30 @@ Sometimes, for readability's sake, it's useful to define custom names for some k
This will allow you to use `FN_CAPS` and `ALT_TAB` in your keymap, keeping it more readable.
-## Some Of My Keys Are Swapped Or Not Working
+## My Keymap Doesn't Update When I Flash It
-QMK has two features, Bootmagic and Command, which allow you to change the behavior of your keyboard on the fly. This includes, but is not limited to, swapping Ctrl/Caps, disabling Gui, swapping Alt/Gui, swapping Backspace/Backslash, disabling all keys, and other behavioral modifications.
+This is usually due to VIA, and has to do with how it deals with keymaps.
-As a quick fix try holding down `Space`+`Backspace` while you plug in your keyboard. This will reset the stored settings on your keyboard, returning those keys to normal operation. If that doesn't work look here:
+On first run, the VIA code in the firmware will copy the keymap from flash memory into EEPROM so that it can be rewritten at runtime by the VIA app. From this point QMK will use the keymap stored in EEPROM instead of flash, and so updates to your `keymap.c` will not be reflected.
-* [Bootmagic Lite](feature_bootmagic.md)
-* [Command](feature_command.md)
+The simple fix for this is to clear the EEPROM. You can do this in several ways:
-## The Menu Key Isn't Working
+* Hold the Bootmagic Lite key (usually top left/Escape) while plugging the board in, which will also place the board into bootloader mode; then unplug and replug the board.
+* Press the `QK_CLEAR_EEPROM`/`EE_CLR` keycode if it is accessible on your keymap.
+* Place the board into bootloader mode and hit the "Clear EEPROM" button. This may not be available for all bootloaders, and you may need to reflash the board afterwards.
+
+## Some Of My Keys Are Swapped Or Not Working
+
+QMK has a couple of features which allow you to change the behavior of your keyboard on the fly. This includes, but is not limited to, swapping Ctrl/Caps, disabling GUI, swapping Alt/GUI, swapping Backspace/Backslash, disabling all keys, and other behavioral modifications.
-The key found on most modern keyboards that is located between `KC_RGUI` and `KC_RCTL` is actually called `KC_APP`. This is because when that key was invented there was already a key named `MENU` in the relevant standards, so MS chose to call that the `APP` key.
+Refer to the EEPROM clearing methods above, which should return those keys to normal operation. If that doesn't work, look here:
-## `KC_SYSTEM_REQUEST` Isn't Working
-Use keycode for Print Screen (`KC_PRINT_SCREEN`/`KC_PSCR`) instead of `KC_SYSTEM_REQUEST`. Key combination of 'Alt + Print Screen' is recognized as 'System request'.
+* [Magic Keycodes](keycodes_magic.md)
+* [Command](feature_command.md)
-See [issue #168](https://github.com/tmk/tmk_keyboard/issues/168) and
-* https://en.wikipedia.org/wiki/Magic_SysRq_key
-* https://en.wikipedia.org/wiki/System_request
+## The Menu Key Isn't Working
+
+The key found on most modern keyboards that is located between `KC_RGUI` and `KC_RCTL` is actually called `KC_APP`. This is because when the key was invented, there was already a key named "Menu" in the HID specification, so for whatever reason, Microsoft chose to create a new key and call it "Application".
## Power Keys Aren't Working
@@ -52,10 +58,12 @@ Somewhat confusingly, there are two "Power" keycodes in QMK: `KC_KB_POWER` in th
The former is only recognized on macOS, while the latter, `KC_SLEP` and `KC_WAKE` are supported by all three major operating systems, so it is recommended to use those instead. Under Windows, these keys take effect immediately, however on macOS they must be held down until a dialog appears.
## One Shot Modifier
+
Solves my personal 'the' problem. I often got 'the' or 'THe' wrongly instead of 'The'. One Shot Shift mitigates this for me.
https://github.com/tmk/tmk_keyboard/issues/67
## Modifier/Layer Stuck
+
Modifier keys or layers can be stuck unless layer switching is configured properly.
For Modifier keys and layer actions you have to place `KC_TRNS` on same position of destination layer to unregister the modifier key or return to previous layer on release event.
@@ -63,12 +71,11 @@ For Modifier keys and layer actions you have to place `KC_TRNS` on same position
* https://geekhack.org/index.php?topic=57008.msg1492604#msg1492604
* https://github.com/tmk/tmk_keyboard/issues/248
-
## Mechanical Lock Switch Support
This feature is for *mechanical lock switch* like [this Alps one](https://deskthority.net/wiki/Alps_SKCL_Lock). You can enable it by adding this to your `config.h`:
-```
+```c
#define LOCKING_SUPPORT_ENABLE
#define LOCKING_RESYNC_ENABLE
```
@@ -91,6 +98,7 @@ Even worse, it is not recognized unless the keyboard's VID and PID match that of
See [this issue](https://github.com/qmk/qmk_firmware/issues/2179) for detailed information.
## Keys Supported in Mac OSX?
+
You can know which keycodes are supported in OSX from this source code.
`usb_2_adb_keymap` array maps Keyboard/Keypad Page usages to ADB scancodes(OSX internal keycodes).
@@ -101,8 +109,8 @@ And `IOHIDConsumer::dispatchConsumerEvent` handles Consumer page usages.
https://opensource.apple.com/source/IOHIDFamily/IOHIDFamily-606.1.7/IOHIDFamily/IOHIDConsumer.cpp
-
## JIS Keys in Mac OSX
+
Japanese JIS keyboard specific keys like `無変æ›(Muhenkan)`, `変æ›(Henkan)`, `ã²ã‚‰ãŒãª(hiragana)` are not recognized on OSX. You can use **Seil** to enable those keys, try following options.
* Enable NFER Key on PC keyboard
@@ -111,8 +119,8 @@ Japanese JIS keyboard specific keys like `無変æ›(Muhenkan)`, `変æ›(Henkan)`
https://pqrs.org/osx/karabiner/seil.html
-
## RN-42 Bluetooth Doesn't Work with Karabiner
+
Karabiner - Keymapping tool on Mac OSX - ignores inputs from RN-42 module by default. You have to enable this option to make Karabiner working with your keyboard.
https://github.com/tekezo/Karabiner/issues/403#issuecomment-102559237
@@ -120,37 +128,24 @@ See these for the detail of this problem.
https://github.com/tmk/tmk_keyboard/issues/213
https://github.com/tekezo/Karabiner/issues/403
-
## Esc and ` on a Single Key
See the [Grave Escape](feature_grave_esc.md) feature.
## Eject on Mac OSX
+
`KC_EJCT` keycode works on OSX. https://github.com/tmk/tmk_keyboard/issues/250
It seems Windows 10 ignores the code and Linux/Xorg recognizes but has no mapping by default.
-Not sure what keycode Eject is on genuine Apple keyboard actually. HHKB uses `F20` for Eject key(`Fn+f`) on Mac mode but this is not same as Apple Eject keycode probably.
-
-
-## What's `weak_mods` and `real_mods` in `action_util.c`
-___TO BE IMPROVED___
-
-real_mods is intended to retains state of real/physical modifier key state, while
-weak_mods retains state of virtual or temporary modifiers which should not affect state real modifier key.
+Not sure what keycode Eject is on genuine Apple keyboard actually. HHKB uses `F20` for Eject key(`Fn+F`) on Mac mode but this is not same as Apple Eject keycode probably.
-Let's say you hold down physical left shift key and type ACTION_MODS_KEY(LSHIFT, KC_A),
+## What are "Real" and "Weak" modifiers?
-with weak_mods,
-* (1) hold down left shift: real_mods |= MOD_BIT(LSHIFT)
-* (2) press ACTION_MODS_KEY(LSHIFT, KC_A): weak_mods |= MOD_BIT(LSHIFT)
-* (3) release ACTION_MODS_KEY(LSHIFT, KC_A): weak_mods &= ~MOD_BIT(LSHIFT)
-real_mods still keeps modifier state.
+Real modifiers refer to the state of the real/physical modifier keys, while weak modifiers are the state of "virtual" or temporary modifiers which should not interfere with the internal state of the real modifier keys.
-without weak mods,
-* (1) hold down left shift: real_mods |= MOD_BIT(LSHIFT)
-* (2) press ACTION_MODS_KEY(LSHIFT, KC_A): real_mods |= MOD_BIT(LSHIFT)
-* (3) release ACTION_MODS_KEY(LSHIFT, KC_A): real_mods &= ~MOD_BIT(LSHIFT)
-here real_mods lost state for 'physical left shift'.
+The real and weak modifier states are ORed together when the keyboard report is sent, so if you release a weak modifier while the same real modifier is still held, the report does not change:
-weak_mods is ORed with real_mods when keyboard report is sent.
-https://github.com/tmk/tmk_core/blob/master/common/action_util.c#L57
+ 1. **Hold down physical Left Shift:** Real mods now contains Left Shift, final state is Left Shift
+ 2. **Add weak Left Shift:** Weak mods now contains Left Shift, final state is Left Shift
+ 3. **Remove weak Left Shift:** Weak mods now contains nothing, final state is Left Shift
+ 4. **Release physical Left Shift:** Real mods now contains nothing, final state is nothing
diff --git a/docs/faq_misc.md b/docs/faq_misc.md
index 9e34a048156c..287ca7711d13 100644
--- a/docs/faq_misc.md
+++ b/docs/faq_misc.md
@@ -10,7 +10,7 @@ You probably don't want to "brick" your keyboard, making it impossible
to rewrite firmware onto it. Here are some of the parameters to show
what things are (and likely aren't) too risky.
-- If your keyboard map does not include RESET, then, to get into DFU
+- If your keyboard map does not include QK_BOOT, then, to get into DFU
mode, you will need to press the reset button on the PCB, which
requires unscrewing the bottom.
- Messing with tmk_core / common files might make the keyboard
diff --git a/docs/feature_advanced_keycodes.md b/docs/feature_advanced_keycodes.md
index 36b81a4e8e0f..b04721b23a6d 100644
--- a/docs/feature_advanced_keycodes.md
+++ b/docs/feature_advanced_keycodes.md
@@ -39,7 +39,7 @@ In practice, this means that you can check whether a given modifier is active wi
To check that *only* a specific set of mods is active at a time, AND the modifier state and your desired mod mask as explained above and compare the result to the mod mask itself: `get_mods() & == `.
-For example, let's say you want to trigger a piece of custom code if one-shot left control and one-shot left shift are on but every other one-shot mods are off. To do so, you can compose the desired mod mask by combining the mod bits for left control and shift with `(MOD_BIT(KC_LCTL) | MOD_BIT(KC_LSFT))` and then plug it in: `get_oneshot_mods & (MOD_BIT(KC_LCTL) | MOD_BIT(KC_LSFT)) == (MOD_BIT(KC_LCTL) | MOD_BIT(KC_LSFT))`. Using `MOD_MASK_CS` instead for the mod bitmask would have forced you to press four modifier keys (both versions of control and shift) to fulfill the condition.
+For example, let's say you want to trigger a piece of custom code if one-shot left control and one-shot left shift are on but every other one-shot mods are off. To do so, you can compose the desired mod mask by combining the mod bits for left control and shift with `(MOD_BIT(KC_LCTL) | MOD_BIT(KC_LSFT))` and then plug it in: `get_oneshot_mods() & (MOD_BIT(KC_LCTL) | MOD_BIT(KC_LSFT)) == (MOD_BIT(KC_LCTL) | MOD_BIT(KC_LSFT))`. Using `MOD_MASK_CS` instead for the mod bitmask would have forced you to press four modifier keys (both versions of control and shift) to fulfill the condition.
The full list of mod masks is as follows:
@@ -161,7 +161,7 @@ bool process_record_user(uint16_t keycode, keyrecord_t *record) {
};
```
-# Legacy Content :id=legacy-content
+# Advanced topics :id=advanced-topics
This page used to encompass a large set of features. We have moved many sections that used to be part of this page to their own pages. Everything below this point is simply a redirect so that people following old links on the web find what they're looking for.
diff --git a/docs/feature_audio.md b/docs/feature_audio.md
index 5b84dc774732..5227d063c3a5 100644
--- a/docs/feature_audio.md
+++ b/docs/feature_audio.md
@@ -114,7 +114,7 @@ The audio core offers interface functions to get/set/change the tone multiplexin
There's a couple of different sounds that will automatically be enabled without any other configuration:
```
STARTUP_SONG // plays when the keyboard starts up (audio.c)
-GOODBYE_SONG // plays when you press the RESET key (quantum.c)
+GOODBYE_SONG // plays when you press the QK_BOOT key (quantum.c)
AG_NORM_SONG // plays when you press AG_NORM (quantum.c)
AG_SWAP_SONG // plays when you press AG_SWAP (quantum.c)
CG_NORM_SONG // plays when you press CG_NORM (quantum.c)
@@ -161,9 +161,11 @@ It's advised that you wrap all audio features in `#ifdef AUDIO_ENABLE` / `#endif
The available keycodes for audio are:
-* `AU_ON` - Turn Audio Feature on
-* `AU_OFF` - Turn Audio Feature off
-* `AU_TOG` - Toggle Audio Feature state
+|Key |Aliases |Description |
+|-------------------------|---------|-------------------------------------------|
+|`QK_AUDIO_ON` |`AU_ON` |Turns on Audio Feature |
+|`QK_AUDIO_OFF` |`AU_OFF` |Turns off Audio Feature |
+|`QK_AUDIO_TOGGLE` |`AU_TOGG`|Toggles Audio state |
!> These keycodes turn all of the audio functionality on and off. Turning it off means that audio feedback, audio clicky, music mode, etc. are disabled, completely.
@@ -177,7 +179,7 @@ The available keycodes for audio are:
|`AUDIO_INIT_DELAY` | *Not defined* |Enables delay during startup song to accomidate for USB startup issues. |
|`AUDIO_ENABLE_TONE_MULTIPLEXING` | *Not defined* |Enables time splicing/multiplexing to create multiple tones simutaneously. |
|`STARTUP_SONG` | `STARTUP_SOUND` |Plays when the keyboard starts up (audio.c) |
-|`GOODBYE_SONG` | `GOODBYE_SOUND` |Plays when you press the RESET key (quantum.c) |
+|`GOODBYE_SONG` | `GOODBYE_SOUND` |Plays when you press the QK_BOOT key (quantum.c) |
|`AG_NORM_SONG` | `AG_NORM_SOUND` |Plays when you press AG_NORM (process_magic.c) |
|`AG_SWAP_SONG` | `AG_SWAP_SOUND` |Plays when you press AG_SWAP (process_magic.c) |
|`CG_NORM_SONG` | `AG_NORM_SOUND` |Plays when you press CG_NORM (process_magic.c) |
@@ -219,6 +221,12 @@ Aka "audio effects", different ones can be enabled by setting in `config.h` thes
`#define AUDIO_VOICES` to enable the feature, and `#define AUDIO_VOICE_DEFAULT something` to select a specific effect
for details see quantum/audio/voices.h and .c
+Keycodes available:
+
+|Key |Aliases |Description |
+|-------------------------|---------|-------------------------------------------|
+|`QK_AUDIO_VOICE_NEXT` |`AU_NEXT`|Cycles through the audio voices |
+|`QK_AUDIO_VOICE_PREVIOUS`|`AU_PREV`|Cycles through the audio voices in reverse |
## Music Mode
@@ -228,10 +236,14 @@ Recording is experimental due to some memory issues - if you experience some wei
Keycodes available:
-* `MU_ON` - Turn music mode on
-* `MU_OFF` - Turn music mode off
-* `MU_TOG` - Toggle music mode
-* `MU_MOD` - Cycle through the music modes:
+|Key |Aliases |Description |
+|-------------------------|---------|-------------------------------------------|
+|`QK_MUSIC_ON` |`MU_ON` |Turns on Music Mode |
+|`QK_MUSIC_OFF` |`MU_OFF` |Turns off Music Mode |
+|`QK_MUSIC_TOGGLE` |`MU_TOGG`|Toggles Music Mode |
+|`QK_MUSIC_MODE_NEXT` |`MU_NEXT`|Cycles through the music modes |
+
+Available Modes:
* `CHROMATIC_MODE` - Chromatic scale, row changes the octave
* `GUITAR_MODE` - Chromatic scale, but the row changes the string (+5 st)
* `VIOLIN_MODE` - Chromatic scale, but the row changes the string (+7 st)
@@ -300,13 +312,16 @@ You can look at the [Planck Keyboard](https://github.com/qmk/qmk_firmware/blob/e
This adds a click sound each time you hit a button, to simulate click sounds from the keyboard. And the sounds are slightly different for each keypress, so it doesn't sound like a single long note, if you type rapidly.
-* `CK_TOGG` - Toggles the status (will play sound if enabled)
-* `CK_ON` - Turns on Audio Click (plays sound)
-* `CK_OFF` - Turns off Audio Click (doesn't play sound)
-* `CK_RST` - Resets the frequency to the default state (plays sound at default frequency)
-* `CK_UP` - Increases the frequency of the clicks (plays sound at new frequency)
-* `CK_DOWN` - Decreases the frequency of the clicks (plays sound at new frequency)
+Keycodes available:
+|Key |Aliases |Description |
+|-------------------------|---------|-------------------------------------------|
+|`QK_AUDIO_CLICKY_TOGGLE` |`CK_TOGG`|Toggles Audio clicky mode |
+|`QK_AUDIO_CLICKY_ON` |`CK_ON` |Turns on Audio clicky mode |
+|`QK_AUDIO_CLICKY_OFF` |`CK_OFF` |Turns on Audio clicky mode |
+|`QK_AUDIO_CLICKY_UP` |`CK_UP` |Increases frequency of the clicks |
+|`QK_AUDIO_CLICKY_DOWN` |`CK_DOWN`|Decreases frequency of the clicks |
+|`QK_AUDIO_CLICKY_RESET` |`CK_RST` |Resets frequency to default |
The feature is disabled by default, to save space. To enable it, add this to your `config.h`:
@@ -333,16 +348,20 @@ See [MIDI](feature_midi.md)
## Audio Keycodes
-|Key |Aliases |Description |
-|----------------|---------|----------------------------------|
-|`AU_ON` | |Audio mode on |
-|`AU_OFF` | |Audio mode off |
-|`AU_TOG` | |Toggles Audio mode |
-|`CLICKY_TOGGLE` |`CK_TOGG`|Toggles Audio clicky mode |
-|`CLICKY_UP` |`CK_UP` |Increases frequency of the clicks |
-|`CLICKY_DOWN` |`CK_DOWN`|Decreases frequency of the clicks |
-|`CLICKY_RESET` |`CK_RST` |Resets frequency to default |
-|`MU_ON` | |Turns on Music Mode |
-|`MU_OFF` | |Turns off Music Mode |
-|`MU_TOG` | |Toggles Music Mode |
-|`MU_MOD` | |Cycles through the music modes |
+|Key |Aliases |Description |
+|-------------------------|---------|-------------------------------------------|
+|`QK_AUDIO_ON` |`AU_ON` |Turns on Audio Feature |
+|`QK_AUDIO_OFF` |`AU_OFF` |Turns off Audio Feature |
+|`QK_AUDIO_TOGGLE` |`AU_TOGG`|Toggles Audio state |
+|`QK_AUDIO_CLICKY_TOGGLE` |`CK_TOGG`|Toggles Audio clicky mode |
+|`QK_AUDIO_CLICKY_ON` |`CK_ON` |Turns on Audio clicky mode |
+|`QK_AUDIO_CLICKY_OFF` |`CK_OFF` |Turns on Audio clicky mode |
+|`QK_AUDIO_CLICKY_UP` |`CK_UP` |Increases frequency of the clicks |
+|`QK_AUDIO_CLICKY_DOWN` |`CK_DOWN`|Decreases frequency of the clicks |
+|`QK_AUDIO_CLICKY_RESET` |`CK_RST` |Resets frequency to default |
+|`QK_MUSIC_ON` |`MU_ON` |Turns on Music Mode |
+|`QK_MUSIC_OFF` |`MU_OFF` |Turns off Music Mode |
+|`QK_MUSIC_TOGGLE` |`MU_TOGG`|Toggles Music Mode |
+|`QK_MUSIC_MODE_NEXT` |`MU_NEXT`|Cycles through the music modes |
+|`QK_AUDIO_VOICE_NEXT` |`AU_NEXT`|Cycles through the audio voices |
+|`QK_AUDIO_VOICE_PREVIOUS`|`AU_PREV`|Cycles through the audio voices in reverse |
diff --git a/docs/feature_auto_shift.md b/docs/feature_auto_shift.md
index 99b0ca3c8a3c..d3437a9c6070 100644
--- a/docs/feature_auto_shift.md
+++ b/docs/feature_auto_shift.md
@@ -300,14 +300,14 @@ This will enable you to define three keys temporarily to increase, decrease and
Map three keys temporarily in your keymap:
-| Key Name | Description |
-|----------|-----------------------------------------------------|
-| KC_ASDN | Lower the Auto Shift timeout variable (down) |
-| KC_ASUP | Raise the Auto Shift timeout variable (up) |
-| KC_ASRP | Report your current Auto Shift timeout value |
-| KC_ASON | Turns on the Auto Shift Function |
-| KC_ASOFF | Turns off the Auto Shift Function |
-| KC_ASTG | Toggles the state of the Auto Shift feature |
+|Keycode |Aliases |Description |
+|----------------------|---------|--------------------------------------------|
+|`QK_AUTO_SHIFT_DOWN` |`AS_DOWN`|Lower the Auto Shift timeout variable (down)|
+|`QK_AUTO_SHIFT_UP` |`AS_UP` |Raise the Auto Shift timeout variable (up) |
+|`QK_AUTO_SHIFT_REPORT`|`AS_RPT` |Report your current Auto Shift timeout value|
+|`QK_AUTO_SHIFT_ON` |`AS_ON` |Turns on the Auto Shift Function |
+|`QK_AUTO_SHIFT_OFF` |`AS_OFF` |Turns off the Auto Shift Function |
+|`QK_AUTO_SHIFT_TOGGLE`|`AS_TOGG`|Toggles the state of the Auto Shift feature |
Compile and upload your new firmware.
@@ -318,18 +318,18 @@ completely normal and with no intention of shifted keys.
1. Type multiple sentences of alphabetical letters.
2. Observe any upper case letters.
-3. If there are none, press the key you have mapped to `KC_ASDN` to decrease
+3. If there are none, press the key you have mapped to `AS_DOWN` to decrease
time Auto Shift timeout value and go back to step 1.
4. If there are some upper case letters, decide if you need to work on tapping
those keys with less down time, or if you need to increase the timeout.
5. If you decide to increase the timeout, press the key you have mapped to
- `KC_ASUP` and go back to step 1.
+ `AS_UP` and go back to step 1.
6. Once you are happy with your results, press the key you have mapped to
- `KC_ASRP`. The keyboard will type by itself the value of your
+ `AS_RPT`. The keyboard will type by itself the value of your
`AUTO_SHIFT_TIMEOUT`.
7. Update `AUTO_SHIFT_TIMEOUT` in your `config.h` with the value reported.
8. Add `AUTO_SHIFT_NO_SETUP` to your `config.h`.
-9. Remove the key bindings `KC_ASDN`, `KC_ASUP` and `KC_ASRP`.
+9. Remove the key bindings `AS_DOWN`, `AS_UP` and `AS_RPT`.
10. Compile and upload your new firmware.
#### An Example Run
@@ -337,17 +337,17 @@ completely normal and with no intention of shifted keys.
hello world. my name is john doe. i am a computer programmer playing with
keyboards right now.
- [PRESS KC_ASDN quite a few times]
+ [PRESS AS_DOWN quite a few times]
heLLo woRLd. mY nAMe is JOHn dOE. i AM A compUTeR proGRaMMER PlAYiNG witH
KEYboArDS RiGHT NOw.
- [PRESS KC_ASUP a few times]
+ [PRESS AS_UP a few times]
hello world. my name is john Doe. i am a computer programmer playing with
keyboarDs right now.
- [PRESS KC_ASRP]
+ [PRESS AS_RPT]
115
diff --git a/docs/feature_autocorrect.md b/docs/feature_autocorrect.md
new file mode 100644
index 000000000000..e042027c0faa
--- /dev/null
+++ b/docs/feature_autocorrect.md
@@ -0,0 +1,295 @@
+# Autocorrect
+
+There are a lot of words that are prone to being typed incorrectly, due to habit, sequence or just user error. This feature leverages your firmware to automatically correct these errors, to help reduce typos.
+
+## How does it work? :id=how-does-it-work
+
+The feature maintains a small buffer of recent key presses. On each key press, it checks whether the buffer ends in a recognized typo, and if so, automatically sends keystrokes to correct it.
+
+The tricky part is how to efficiently check the buffer for typos. We don’t want to spend too much memory or time on storing or searching the typos. A good solution is to represent the typos with a trie data structure. A trie is a tree data structure where each node is a letter, and words are formed by following a path to one of the leaves.
+
+![An example trie](https://i.imgur.com/HL5DP8H.png)
+
+Since we search whether the buffer ends in a typo, we store the trie writing in reverse. The trie is queried starting from the last letter, then second to last letter, and so on, until either a letter doesn’t match or we reach a leaf, meaning a typo was found.
+
+## How do I enable Autocorrection :id=how-do-i-enable-autocorrection
+
+In your `rules.mk`, add this:
+
+```make
+AUTOCORRECT_ENABLE = yes
+```
+
+Additionally, you will need a library for autocorrection. A small sample library is included by default, so that you can get up and running right away, but you can provide a customized library.
+
+By default, autocorrect is disabled. To enable it, you need to use the `AC_TOGG` keycode to enable it. The status is stored in persistent memory, so you shouldn't need to enabled it again.
+
+## Customizing autocorrect library :id=customizing-autocorrect-library
+
+To provide a custom library, you need to create a text file with the corrections. For instance:
+
+```text
+:thier -> their
+fitler -> filter
+lenght -> length
+ouput -> output
+widht -> width
+```
+
+The syntax is `typo -> correction`. Typos and corrections are case insensitive, and any whitespace before or after the typo and correction is ignored. The typo must be only the letters a–z, or the special character : representing a word break. The correction may have any non-unicode characters.
+
+Then, run:
+
+```sh
+qmk generate-autocorrect-data autocorrect_dictionary.txt
+```
+
+This will process the file and produce an `autocorrect_data.h` file with the trie library, in the folder that you are at. You can specify the keyboard and keymap (eg `-kb planck/rev6 -km jackhumbert`), and it will place the file in that folder instead. But as long as the file is located in your keymap folder, or user folder, it should be picked up automatically.
+
+This file will look like this:
+
+```c
+// :thier -> their
+// fitler -> filter
+// lenght -> length
+// ouput -> output
+// widht -> width
+
+#define AUTOCORRECT_MIN_LENGTH 5 // "ouput"
+#define AUTOCORRECT_MAX_LENGTH 6 // ":thier"
+
+#define DICTIONARY_SIZE 74
+
+static const uint8_t autocorrect_data[DICTIONARY_SIZE] PROGMEM = {85, 7, 0, 23, 35, 0, 0, 8, 0, 76, 16, 0, 15, 25, 0, 0,
+ 11, 23, 44, 0, 130, 101, 105, 114, 0, 23, 12, 9, 0, 131, 108, 116, 101, 114, 0, 75, 42, 0, 24, 64, 0, 0, 71, 49, 0,
+ 10, 56, 0, 0, 12, 26, 0, 129, 116, 104, 0, 17, 8, 15, 0, 129, 116, 104, 0, 19, 24, 18, 0, 130, 116, 112, 117, 116,
+ 0};
+```
+
+### Avoiding false triggers :id=avoiding-false-triggers
+
+By default, typos are searched within words, to find typos within longer identifiers like maxFitlerOuput. While this is useful, a consequence is that autocorrection will falsely trigger when a typo happens to be a substring of a correctly-spelled word. For instance, if we had thier -> their as an entry, it would falsely trigger on (correct, though relatively uncommon) words like “wealthier†and “filthier.â€
+
+The solution is to set a word break : before and/or after the typo to constrain matching. : matches space, period, comma, underscore, digits, and most other non-alpha characters.
+
+|Text |thier |:thier |thier: |:thier: |
+|-----------------|:------:|:------:|:------:|:------:|
+|see `thier` typo |matches |matches |matches |matches |
+|it’s `thiers` |matches |matches |no |no |
+|wealthier words |matches |no |matches |no |
+
+:thier: is most restrictive, matching only when thier is a whole word.
+
+The `qmk generate-autocorrect-data` commands can make an effort to check for entries that would false trigger as substrings of correct words. It searches each typo against a dictionary of 25K English words from the english_words Python package, provided it’s installed. (run `python3 -m pip install english_words` to install it.)
+
+?> Unfortunately, this is limited to just english words, at this point.
+
+## Overriding Autocorrect
+
+Occasionally you might actually want to type a typo (for instance, while editing autocorrection_dict.txt) without being autocorrected. There are a couple of ways to do this:
+
+1. Begin typing the typo.
+2. Before typing the last letter, press and release the Ctrl or Alt key.
+3. Type the remaining letters.
+
+This works because the autocorrection implementation doesn’t understand hotkeys, so it resets itself whenever a modifier other than shift is held.
+
+Additionally, you can use the `AC_TOGG` keycode to toggle the on/off status for Autocorrect.
+
+### Keycodes :id=keycodes
+
+|Keycode |Aliases |Description |
+|-----------------------|---------|----------------------------------------------|
+|`QK_AUTOCORRECT_ON` |`AC_ON` |Turns on the Autocorrect feature. |
+|`QK_AUTOCORRECT_OFF` |`AC_OFF` |Turns off the Autocorrect feature. |
+|`QK_AUTOCORRECT_TOGGLE`|`AC_TOGG`|Toggles the status of the Autocorrect feature.|
+
+## User Callback Functions
+
+### Process Autocorrect
+
+Callback function `bool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, uint8_t *mods)` is available to customise incoming keycodes and handle exceptions. You can use this function to sanitise input before they are passed onto the autocorrect engine
+
+?> Sanitisation of input is required because autocorrect will only match 8-bit [basic keycodes](keycodes_basic.md) for typos. If valid modifier keys or 16-bit keycodes that are part of a user's word input (such as Shift + A) is passed through, they will fail typo letter detection. For example a [Mod-Tap](mod_tap.md) key such as `LCTL_T(KC_A)` is 16-bit and should be masked for the 8-bit `KC_A`.
+
+The default user callback function is found inside `quantum/process_keycode/process_autocorrect.c`. It covers most use-cases for QMK special functions and quantum keycodes, including [overriding autocorrect](#overriding-autocorrect) with a modifier other than shift. The `process_autocorrect_user` function is `weak` defined to allow user's copy inside `keymap.c` (or code files) to overwrite it.
+
+#### Process Autocorrect Example
+
+If you have a custom keycode `QMKBEST` that should be ignored as part of a word, and another custom keycode `QMKLAYER` that should override autocorrect, both can be added to the bottom of the `process_autocorrect_user` `switch` statement in your source code:
+
+```c
+bool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, uint8_t *mods) {
+ // See quantum_keycodes.h for reference on these matched ranges.
+ switch (*keycode) {
+ // Exclude these keycodes from processing.
+ case KC_LSFT:
+ case KC_RSFT:
+ case KC_CAPS:
+ case QK_TO ... QK_ONE_SHOT_LAYER_MAX:
+ case QK_LAYER_TAP_TOGGLE ... QK_LAYER_MOD_MAX:
+ case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX:
+ return false;
+
+ // Mask for base keycode from shifted keys.
+ case QK_LSFT ... QK_LSFT + 255:
+ case QK_RSFT ... QK_RSFT + 255:
+ if (*keycode >= QK_LSFT && *keycode <= (QK_LSFT + 255)) {
+ *mods |= MOD_LSFT;
+ } else {
+ *mods |= MOD_RSFT;
+ }
+ *keycode &= 0xFF; // Get the basic keycode.
+ return true;
+#ifndef NO_ACTION_TAPPING
+ // Exclude tap-hold keys when they are held down
+ // and mask for base keycode when they are tapped.
+ case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
+# ifdef NO_ACTION_LAYER
+ // Exclude Layer Tap, if layers are disabled
+ // but action tapping is still enabled.
+ return false;
+# endif
+ case QK_MOD_TAP ... QK_MOD_TAP_MAX:
+ // Exclude hold if mods other than Shift is not active
+ if (!record->tap.count) {
+ return false;
+ }
+ *keycode &= 0xFF;
+ break;
+#else
+ case QK_MOD_TAP ... QK_MOD_TAP_MAX:
+ case QK_LAYER_TAP ... QK_LAYER_TAP_MAX:
+ // Exclude if disabled
+ return false;
+#endif
+ // Exclude swap hands keys when they are held down
+ // and mask for base keycode when they are tapped.
+ case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX:
+#ifdef SWAP_HANDS_ENABLE
+ if (*keycode >= 0x56F0 || !record->tap.count) {
+ return false;
+ }
+ *keycode &= 0xFF;
+ break;
+#else
+ // Exclude if disabled
+ return false;
+#endif
+ // Handle custom keycodes
+ case QMKBEST:
+ return false;
+ case QMKLAYER:
+ *typo_buffer_size = 0;
+ return false;
+ }
+
+ // Disable autocorrect while a mod other than shift is active.
+ if ((*mods & ~MOD_MASK_SHIFT) != 0) {
+ *typo_buffer_size = 0;
+ return false;
+ }
+
+ return true;
+}
+```
+
+?> In this callback function, `return false` will skip processing of that keycode for autocorrect. Adding `*typo_buffer_size = 0` will also reset the autocorrect buffer at the same time, cancelling any current letters already stored in the buffer.
+
+### Apply Autocorrect
+
+Additionally, `apply_autocorrect(uint8_t backspaces, const char *str)` allows for users to add additional handling to the autocorrection, or replace the functionality entirely. This passes on the number of backspaces needed to replace the words, as well as the replacement string (partial word, not the full word).
+
+#### Apply Autocorrect Example
+
+This following example will play a sound when a typo is autocorrected and execute the autocorrection itself:
+
+```c
+#ifdef AUDIO_ENABLE
+float autocorrect_song[][2] = SONG(TERMINAL_SOUND);
+#endif
+
+bool apply_autocorrect(uint8_t backspaces, const char *str) {
+#ifdef AUDIO_ENABLE
+ PLAY_SONG(autocorrect_song);
+#endif
+ for (uint8_t i = 0; i < backspaces; ++i) {
+ tap_code(KC_BSPC);
+ }
+ send_string_P(str);
+ return false;
+}
+```
+
+?> In this callback function, `return false` will stop the normal processing of autocorrect, which requires manually handling of removing the "bad" characters and typing the new characters.
+
+!> ***IMPORTANT***: `str` is a pointer to `PROGMEM` data for the autocorrection. If you return false, and want to send the string, this needs to use `send_string_P` and not `send_string` or `SEND_STRING`.
+
+You can also use `apply_autocorrect` to detect and display the event but allow internal code to execute the autocorrection with `return true`:
+
+```c
+bool apply_autocorrect(uint8_t backspaces, const char *str) {
+#ifdef OLED_ENABLE
+ oled_write_P(PSTR("Auto-corrected"), false);
+#endif
+ return true;
+}
+```
+
+## Appendix: Trie binary data format :id=appendix
+
+This section details how the trie is serialized to byte data in autocorrection_data. You don’t need to care about this to use this autocorrection implementation. But it is documented for the record in case anyone is interested in modifying the implementation, or just curious how it works.
+
+What I did here is fairly arbitrary, but it is simple to decode and gets the job done.
+
+### Encoding :id=encoding
+
+All autocorrection data is stored in a single flat array autocorrection_data. Each trie node is associated with a byte offset into this array, where data for that node is encoded, beginning with root at offset 0. There are three kinds of nodes. The highest two bits of the first byte of the node indicate what kind:
+
+* 00 ⇒ chain node: a trie node with a single child.
+* 01 ⇒ branching node: a trie node with multiple children.
+* 10 ⇒ leaf node: a leaf, corresponding to a typo and storing its correction.
+
+![An example trie](https://i.imgur.com/HL5DP8H.png)
+
+**Branching node**. Each branch is encoded with one byte for the keycode (KC_A–KC_Z) followed by a link to the child node. Links between nodes are 16-bit byte offsets relative to the beginning of the array, serialized in little endian order.
+
+All branches are serialized this way, one after another, and terminated with a zero byte. As described above, the node is identified as a branch by setting the two high bits of the first byte to 01, done by bitwise ORing the first keycode with 64. keycode. The root node for the above figure would be serialized like:
+
+```
++-------+-------+-------+-------+-------+-------+-------+
+| R|64 | node 2 | T | node 3 | 0 |
++-------+-------+-------+-------+-------+-------+-------+
+```
+
+**Chain node**. Tries tend to have long chains of single-child nodes, as seen in the example above with f-i-t-l in fitler. So to save space, we use a different format to encode chains than branching nodes. A chain is encoded as a string of keycodes, beginning with the node closest to the root, and terminated with a zero byte. The child of the last node in the chain is encoded immediately after. That child could be either a branching node or a leaf.
+
+In the figure above, the f-i-t-l chain is encoded as
+
+```
++-------+-------+-------+-------+-------+
+| L | T | I | F | 0 |
++-------+-------+-------+-------+-------+
+```
+
+If we were to encode this chain using the same format used for branching nodes, we would encode a 16-bit node link with every node, costing 8 more bytes in this example. Across the whole trie, this adds up. Conveniently, we can point to intermediate points in the chain and interpret the bytes in the same way as before. E.g. starting at the i instead of the l, and the subchain has the same format.
+
+**Leaf node**. A leaf node corresponds to a particular typo and stores data to correct the typo. The leaf begins with a byte for the number of backspaces to type, and is followed by a null-terminated ASCII string of the replacement text. The idea is, after tapping backspace the indicated number of times, we can simply pass this string to the `send_string_P` function. For fitler, we need to tap backspace 3 times (not 4, because we catch the typo as the final ‘r’ is pressed) and replace it with lter. To identify the node as a leaf, the two high bits are set to 10 by ORing the backspace count with 128:
+
+```
++-------+-------+-------+-------+-------+-------+
+| 3|128 | 'l' | 't' | 'e' | 'r' | 0 |
++-------+-------+-------+-------+-------+-------+
+```
+
+### Decoding :id=decoding
+
+This format is by design decodable with fairly simple logic. A 16-bit variable state represents our current position in the trie, initialized with 0 to start at the root node. Then, for each keycode, test the highest two bits in the byte at state to identify the kind of node.
+
+* 00 ⇒ **chain node**: If the node’s byte matches the keycode, increment state by one to go to the next byte. If the next byte is zero, increment again to go to the following node.
+* 01 ⇒ **branching node**: Search the branches for one that matches the keycode, and follow its node link.
+* 10 ⇒ **leaf node**: a typo has been found! We read its first byte for the number of backspaces to type, then pass its following bytes to send_string_P to type the correction.
+
+## Credits
+
+Credit goes to [getreuer](https://github.com/getreuer) for originally implementing this [here](https://getreuer.info/posts/keyboards/autocorrection/#how-does-it-work). As well as to [filterpaper](https://github.com/filterpaper) for converting the code to use PROGMEM, and additional improvements.
diff --git a/docs/feature_backlight.md b/docs/feature_backlight.md
index 79782cf56493..24057c608f8c 100644
--- a/docs/feature_backlight.md
+++ b/docs/feature_backlight.md
@@ -16,15 +16,15 @@ BACKLIGHT_ENABLE = yes
Once enabled, the following keycodes below can be used to change the backlight level.
-|Key |Description |
-|---------|-----------------------------------|
-|`BL_TOGG`|Turn the backlight on or off |
-|`BL_STEP`|Cycle through backlight levels |
-|`BL_ON` |Set the backlight to max brightness|
-|`BL_OFF` |Turn the backlight off |
-|`BL_INC` |Increase the backlight level |
-|`BL_DEC` |Decrease the backlight level |
-|`BL_BRTG`|Toggle backlight breathing |
+| Key | Aliases | Description |
+|---------------------------------|-----------|-------------------------------------|
+| `QK_BACKLIGHT_TOGGLE` | `BL_TOGG` | Turn the backlight on or off |
+| `QK_BACKLIGHT_STEP` | `BL_STEP` | Cycle through backlight levels |
+| `QK_BACKLIGHT_ON` | `BL_ON` | Set the backlight to max brightness |
+| `QK_BACKLIGHT_OFF` | `BL_OFF` | Turn the backlight off |
+| `QK_BACKLIGHT_UP` | `BL_UP` | Increase the backlight level |
+| `QK_BACKLIGHT_DOWN` | `BL_DOWN` | Decrease the backlight level |
+| `QK_BACKLIGHT_TOGGLE_BREATHING` | `BL_BRTG` | Toggle backlight breathing |
## Functions :id=functions
diff --git a/docs/feature_bluetooth.md b/docs/feature_bluetooth.md
index d4ed494053b1..f7ded84d8679 100644
--- a/docs/feature_bluetooth.md
+++ b/docs/feature_bluetooth.md
@@ -39,8 +39,8 @@ BLUETOOTH_DRIVER = BluefruitLE # or RN42
This is used when multiple keyboard outputs can be selected. Currently this only allows for switching between USB and Bluetooth on keyboards that support both.
-|Name |Description |
-|----------|----------------------------------------------|
-|`OUT_AUTO`|Automatically switch between USB and Bluetooth|
-|`OUT_USB` |USB only |
-|`OUT_BT` |Bluetooth only |
+|Key |Aliases |Description |
+|---------------------|---------|----------------------------------------------|
+|`QK_OUTPUT_AUTO` |`OU_AUTO`|Automatically switch between USB and Bluetooth|
+|`QK_OUTPUT_USB` |`OU_USB` |USB only |
+|`QK_OUTPUT_BLUETOOTH`|`OU_BT` |Bluetooth only |
diff --git a/docs/feature_caps_word.md b/docs/feature_caps_word.md
new file mode 100644
index 000000000000..c58d1a56e2ff
--- /dev/null
+++ b/docs/feature_caps_word.md
@@ -0,0 +1,169 @@
+# Caps Word
+
+It is often useful to type a single word in all capitals, for instance
+abbreviations like "QMK", or in code, identifiers like `KC_SPC`. "Caps Word" is
+a modern alternative to Caps Lock:
+
+* While active, letters are capitalized and `-` becomes `_`. The `_` makes it easier
+ to type constant names (eg 'PROGRAM\_CONSTANTS').
+
+* Caps Word automatically disables
+ itself at the end of the word. That is, it stops by default once a space or
+ any key other than `KC_A`--`KC_Z`, `KC_0`--`KC_9`, `KC_MINS`, `KC_UNDS`,
+ `KC_DELETE`, or `KC_BACKSPACE` is pressed. Caps Word also disables itself if
+ the keyboard is idle for 5 seconds. This is configurable, see below.
+
+* To avoid requiring a dedicated key for Caps Word, there is an option
+ (`BOTH_SHIFTS_TURNS_ON_CAPS_WORD`) to activate Caps Word by simultaneously
+ pressing both shift keys. See below for other options.
+
+* The implementation does not use the Caps Lock (`KC_CAPS`) keycode. Caps Word
+ works even if you're remapping Caps Lock at the OS level to Ctrl or something
+ else, as Emacs and Vim users often do. As a consequence, Caps Word does not
+ follow the typical Caps Lock behaviour and may thus act in potentially
+ unexpected ways, especially when using an *OS* keyboard layout other than US
+ or UK. For example, Dvorak's , < key (`DV_COMM` aka `KC_W`) will
+ get shifted because Caps Word interprets that keycode as the letter 'W' by
+ default, the Spanish Ñ key (`ES_NTIL` aka `KC_SCLN`) will not get
+ capitalized because Caps Word interprets it as the semicolon ';' punctuation
+ character, and the US hyphen key (`KC_MINS`), while unaffected by Caps Lock,
+ is shifted by Caps Word. However, this is not really a problem because you can
+ [configure which keys should Caps Word
+ shift](#configure-which-keys-are-word-breaking).
+
+
+## How do I enable Caps Word :id=how-do-i-enable-caps-word
+
+In your `rules.mk`, add:
+
+```make
+CAPS_WORD_ENABLE = yes
+```
+
+Next, use one the following methods to activate Caps Word:
+
+* **Activate by pressing a key**: Use the `QK_CAPS_WORD_TOGGLE` keycode (short
+ alias `CW_TOGG`) in your keymap.
+
+* **Activate by pressing Left Shift + Right Shift**: Add `#define
+ BOTH_SHIFTS_TURNS_ON_CAPS_WORD` to config.h. You may also need to disable or
+ reconfigure Command, details below. Then, simultaneously pressing both left
+ and right shifts turns on Caps Word. This method works with the plain
+ `KC_LSFT` and `KC_RSFT` keycodes as well as one-shot shifts and Space Cadet
+ shifts. If your shift keys are mod-taps, hold both shift mod-tap keys until
+ the tapping term, then release them.
+
+* **Activate by double tapping Left Shift**: Add `#define
+ DOUBLE_TAP_SHIFT_TURNS_ON_CAPS_WORD` config.h. Then, double tapping Left Shift
+ turns on Caps Word. This method works with `KC_LSFT` or one-shot Left Shift
+ `OSM(MOD_LSFT)`. To count as a double tap, the maximum time in milliseconds
+ between taps is `TAPPING_TERM`, or if using `TAPPING_TERM_PER_KEY`, the time
+ returned by `get_tapping_term()` for the shift keycode being tapped.
+
+* **Custom activation**: You can activate Caps Word from code by calling
+ `caps_word_on()`. This may be used to activate Caps Word through [a
+ combo](feature_combo.md) or [tap dance](feature_tap_dance.md) or any means
+ you like.
+
+### Troubleshooting: Command :id=troubleshooting-command
+
+When using `BOTH_SHIFTS_TURNS_ON_CAPS_WORD`, you might see a compile message
+**"BOTH_SHIFTS_TURNS_ON_CAPS_WORD and Command should not be enabled at the same
+time, since both use the Left Shift + Right Shift key combination."**
+
+Many keyboards enable the [Command feature](feature_command.md), which by
+default is also activated using the Left Shift + Right Shift key combination. To
+fix this conflict, please disable Command by adding in rules.mk:
+
+```make
+COMMAND_ENABLE = no
+```
+
+Or configure Command to use another key combination like Left Ctrl + Right Ctrl
+by defining `IS_COMMAND()` in config.h:
+
+```c
+// Activate Command with Left Ctrl + Right Ctrl.
+#define IS_COMMAND() (get_mods() == MOD_MASK_CTRL)
+```
+
+
+## Customizing Caps Word :id=customizing-caps-word
+
+### Idle timeout :id=idle-timeout
+
+Caps Word turns off automatically if no keys are pressed for
+`CAPS_WORD_IDLE_TIMEOUT` milliseconds. The default is 5000 (5 seconds).
+Configure the timeout duration in config.h, for instance
+
+```c
+#define CAPS_WORD_IDLE_TIMEOUT 3000 // 3 seconds.
+```
+
+Setting `CAPS_WORD_IDLE_TIMEOUT` to 0 configures Caps Word to never time out.
+Caps Word then remains active indefinitely until a word breaking key is pressed.
+
+
+### Functions :id=functions
+
+Functions to manipulate Caps Word:
+
+| Function | Description |
+|-------------------------|------------------------------------------------|
+| `caps_word_on()` | Turns Caps Word on. |
+| `caps_word_off()` | Turns Caps Word off. |
+| `caps_word_toggle()` | Toggles Caps Word. |
+| `is_caps_word_on()` | Returns true if Caps Word is currently on. |
+
+
+### Configure which keys are "word breaking" :id=configure-which-keys-are-word-breaking
+
+You can define the `caps_word_press_user(uint16_t keycode)` callback to
+configure which keys should be shifted and which keys are considered "word
+breaking" and stop Caps Word.
+
+The callback is called on every key press while Caps Word is active. When the
+key should be shifted (that is, a letter key), the callback should call
+`add_weak_mods(MOD_BIT(KC_LSFT))` to shift the key. Returning true continues the
+current "word," while returning false is "word breaking" and deactivates Caps
+Word. The default callback is
+
+```c
+bool caps_word_press_user(uint16_t keycode) {
+ switch (keycode) {
+ // Keycodes that continue Caps Word, with shift applied.
+ case KC_A ... KC_Z:
+ case KC_MINS:
+ add_weak_mods(MOD_BIT(KC_LSFT)); // Apply shift to next key.
+ return true;
+
+ // Keycodes that continue Caps Word, without shifting.
+ case KC_1 ... KC_0:
+ case KC_BSPC:
+ case KC_DEL:
+ case KC_UNDS:
+ return true;
+
+ default:
+ return false; // Deactivate Caps Word.
+ }
+}
+```
+
+
+### Representing Caps Word state :id=representing-caps-word-state
+
+Define `caps_word_set_user(bool active)` to get callbacks when Caps Word turns
+on or off. This is useful to represent the current Caps Word state, e.g. by
+setting an LED or playing a sound. In your keymap, define
+
+```c
+void caps_word_set_user(bool active) {
+ if (active) {
+ // Do something when Caps Word activates.
+ } else {
+ // Do something when Caps Word deactivates.
+ }
+}
+```
+
diff --git a/docs/feature_combo.md b/docs/feature_combo.md
index c0e10f09d5ae..3af8b83643ba 100644
--- a/docs/feature_combo.md
+++ b/docs/feature_combo.md
@@ -55,7 +55,7 @@ const uint16_t PROGMEM sd_combo[] = {KC_S, KC_D, COMBO_END};
combo_t key_combos[COMBO_COUNT] = {
[AB_ESC] = COMBO(ab_combo, KC_ESC),
[JK_TAB] = COMBO(jk_combo, KC_TAB),
- [QW_SFT] = COMBO(qw_combo, KC_LSFT)
+ [QW_SFT] = COMBO(qw_combo, KC_LSFT),
[SD_LAYER] = COMBO(sd_combo, MO(_LAYER)),
};
```
@@ -105,11 +105,11 @@ It is worth noting that `COMBO_ACTION`s are not needed anymore. As of [PR#8591](
## Keycodes
You can enable, disable and toggle the Combo feature on the fly. This is useful if you need to disable them temporarily, such as for a game. The following keycodes are available for use in your `keymap.c`
-|Keycode |Description |
-|----------|---------------------------------|
-|`CMB_ON` |Turns on Combo feature |
-|`CMB_OFF` |Turns off Combo feature |
-|`CMB_TOG` |Toggles Combo feature on and off |
+|Keycode |Aliases |Description |
+|-----------------|---------|--------------------------------|
+|`QK_COMBO_ON` |`CM_ON` |Turns on Combo feature |
+|`QK_COMBO_OFF` |`CM_OFF` |Turns off Combo feature |
+|`QK_COMBO_TOGGLE`|`CM_TOGG`|Toggles Combo feature on and off|
# Advanced Configuration
These configuration settings can be set in your `config.h` file.
@@ -255,7 +255,7 @@ bool combo_should_trigger(uint16_t combo_index, combo_t *combo, uint16_t keycode
```
## Variable Length Combos
-If you leave `COMBO_COUNT` undefined in `config.h`, it allows you to programmatically declare the size of the Combo data structure and avoid updating `COMBO_COUNT`. Instead a variable called `COMBO_LEN` has to be set. It can be set with something similar to the following in `keymap.c`: `uint16_t COMBO_LEN = sizeof(key_combos) / sizeof(key_combos[0]);` or by adding `COMBO_LENGTH` as the *last* entry in the combo enum and then `uint16_t COMBO_LEN = COMBO_LENGTH;` as such:
+If you leave `COMBO_COUNT` undefined in `config.h`, it allows you to programmatically declare the size of the Combo data structure and avoid updating `COMBO_COUNT`. Instead a variable called `COMBO_LEN` has to be set. It can be set with something similar to the following in `keymap.c`: `uint16_t COMBO_LEN = ARRAY_SIZE(key_combos);` or by adding `COMBO_LENGTH` as the *last* entry in the combo enum and then `uint16_t COMBO_LEN = COMBO_LENGTH;` as such:
```c
enum myCombos {
...,
@@ -326,7 +326,7 @@ bool process_combo_key_release(uint16_t combo_index, combo_t *combo, uint8_t key
If you, for example, use multiple base layers for different key layouts, one for QWERTY, and another one for Colemak, you might want your combos to work from the same key positions on all layers. Defining the same combos again for another layout is redundant and takes more memory. The solution is to just check the keycodes from one layer.
-With `#define COMBO_ONLY_FROM_LAYER _LAYER_A` the combos' keys are always checked from layer `_LAYER_A` even though the active layer would be `_LAYER_B`.
+With `#define COMBO_ONLY_FROM_LAYER 0` in config.h, the combos' keys are always checked from layer `0`, even if other layers are active.
## User callbacks
diff --git a/docs/feature_converters.md b/docs/feature_converters.md
new file mode 100644
index 000000000000..0fa677f87326
--- /dev/null
+++ b/docs/feature_converters.md
@@ -0,0 +1,182 @@
+# Converters
+
+Since many drop-in replacement controllers now exist, we've done our best to make them easy to use in existing designs.
+
+This page documents the handy automated process for converting keyboards.
+
+### Supported Converters
+
+Currently the following converters are available:
+
+| From | To |
+|------------|-------------------|
+| `promicro` | `proton_c` |
+| `promicro` | `kb2040` |
+| `promicro` | `promicro_rp2040` |
+| `promicro` | `blok` |
+| `promicro` | `bit_c_pro` |
+| `promicro` | `stemcell` |
+| `promicro` | `bonsai_c4` |
+| `promicro` | `elite_pi` |
+| `elite_c` | `stemcell` |
+| `elite_c` | `elite_pi` |
+
+See below for more in depth information on each converter.
+
+## Overview
+
+Each converter category is broken down by its declared `pin compatibility`.
+This ensures that only valid combinations are attempted.
+
+You can generate the firmware by appending `-e CONVERT_TO=` to your compile/flash command. For example:
+
+```sh
+qmk flash -c -kb keebio/bdn9/rev1 -km default -e CONVERT_TO=proton_c
+```
+
+You can also add the same `CONVERT_TO=` to your keymap's `rules.mk`, which will accomplish the same thing.
+
+?> If you get errors about `PORTB/DDRB`, etc not being defined, you'll need to convert the keyboard's code to use the [GPIO Controls](gpio_control.md) that will work for both ARM and AVR. This shouldn't affect the AVR builds at all.
+
+### Conditional Configuration
+
+Once a converter is enabled, it exposes the `CONVERT_TO_` flag that you can use in your code with `#ifdef`s, For example:
+
+```c
+#ifdef CONVERT_TO_PROTON_C
+ // Proton C code
+#else
+ // Pro Micro code
+#endif
+```
+
+### Pin Compatibility
+
+To ensure compatibility, provide validation, and power future workflows, a keyboard should declare its `pin compatibility`. For legacy reasons, this is currently assumed to be `promicro`.
+
+Currently the following pin compatibility interfaces are defined:
+
+| Pinout | Notes |
+|------------|-----------------------------------|
+| `promicro` | Includes RX/TX LEDs |
+| `elite_c` | Includes bottom row pins, no LEDs |
+
+To declare the base for conversions, add this line to your keyboard's `rules.mk`:
+
+```makefile
+PIN_COMPATIBLE = elite_c
+```
+
+## Pro Micro
+
+If a board currently supported in QMK uses a [Pro Micro](https://www.sparkfun.com/products/12640) (or compatible board), the supported alternative controllers are:
+
+| Device | Target |
+|------------------------------------------------------------------------------------------|-------------------|
+| [Proton C](https://qmk.fm/proton-c/) | `proton_c` |
+| [Adafruit KB2040](https://learn.adafruit.com/adafruit-kb2040) | `kb2040` |
+| [SparkFun Pro Micro - RP2040](https://www.sparkfun.com/products/18288) | `promicro_rp2040` |
+| [Blok](https://boardsource.xyz/store/628b95b494dfa308a6581622) | `blok` |
+| [Bit-C PRO](https://nullbits.co/bit-c-pro) | `bit_c_pro` |
+| [STeMCell](https://github.com/megamind4089/STeMCell) | `stemcell` |
+| [customMK Bonsai C4](https://shop.custommk.com/products/bonsai-c4-microcontroller-board) | `bonsai_c4` |
+| [Elite-Pi](https://keeb.io/products/elite-pi-usb-c-pro-micro-replacement-rp2040) | `elite_pi` |
+
+Converter summary:
+
+| Target | Argument | `rules.mk` | Condition |
+|-------------------|---------------------------------|------------------------------|-------------------------------------|
+| `proton_c` | `-e CONVERT_TO=proton_c` | `CONVERT_TO=proton_c` | `#ifdef CONVERT_TO_PROTON_C` |
+| `kb2040` | `-e CONVERT_TO=kb2040` | `CONVERT_TO=kb2040` | `#ifdef CONVERT_TO_KB2040` |
+| `promicro_rp2040` | `-e CONVERT_TO=promicro_rp2040` | `CONVERT_TO=promicro_rp2040` | `#ifdef CONVERT_TO_PROMICRO_RP2040` |
+| `blok` | `-e CONVERT_TO=blok` | `CONVERT_TO=blok` | `#ifdef CONVERT_TO_BLOK` |
+| `bit_c_pro` | `-e CONVERT_TO=bit_c_pro` | `CONVERT_TO=bit_c_pro` | `#ifdef CONVERT_TO_BIT_C_PRO` |
+| `stemcell` | `-e CONVERT_TO=stemcell` | `CONVERT_TO=stemcell` | `#ifdef CONVERT_TO_STEMCELL` |
+| `bonsai_c4` | `-e CONVERT_TO=bonsai_c4` | `CONVERT_TO=bonsai_c4` | `#ifdef CONVERT_TO_BONSAI_C4` |
+| `elite_pi` | `-e CONVERT_TO=elite_pi` | `CONVERT_TO=elite_pi` | `#ifdef CONVERT_TO_ELITE_PI` |
+
+### Proton C :id=proton_c
+
+The Proton C only has one on-board LED (C13), and by default, the TXLED (D5) is mapped to it. If you want the RXLED (B0) mapped to it instead, add this line to your `config.h`:
+
+```c
+#define CONVERT_TO_PROTON_C_RXLED
+```
+
+The following defaults are based on what has been implemented for STM32 boards.
+
+| Feature | Notes |
+|----------------------------------------------|------------------------------------------------------------------------------------------------------------------|
+| [Audio](feature_audio.md) | Enabled |
+| [RGB Lighting](feature_rgblight.md) | Disabled |
+| [Backlight](feature_backlight.md) | Forces [task driven PWM](feature_backlight.md#software-pwm-driver) until ARM can provide automatic configuration |
+| USB Host (e.g. USB-USB converter) | Not supported (USB host code is AVR specific and is not currently supported on ARM) |
+| [Split keyboards](feature_split_keyboard.md) | Partial - heavily dependent on enabled features |
+
+### Adafruit KB2040 :id=kb2040
+
+The following defaults are based on what has been implemented for [RP2040](platformdev_rp2040.md) boards.
+
+| Feature | Notes |
+|----------------------------------------------|------------------------------------------------------------------------------------------------------------------|
+| [RGB Lighting](feature_rgblight.md) | Enabled via `PIO` vendor driver |
+| [Backlight](feature_backlight.md) | Forces [task driven PWM](feature_backlight.md#software-pwm-driver) until ARM can provide automatic configuration |
+| USB Host (e.g. USB-USB converter) | Not supported (USB host code is AVR specific and is not currently supported on ARM) |
+| [Split keyboards](feature_split_keyboard.md) | Partial via `PIO` vendor driver - heavily dependent on enabled features |
+
+### SparkFun Pro Micro - RP2040, Blok, Bit-C PRO, and Elite-Pi :id=promicro_rp2040
+
+Currently identical to [Adafruit KB2040](#kb2040).
+
+### STeMCell :id=stemcell
+
+Feature set currently identical to [Proton C](#proton_c).
+There are two versions of STeMCell available, with different pinouts:
+ - v1.0.0
+ - v2.0.0 (pre-release v1.0.1, v1.0.2)
+Default official firmware only supports v2.0.0 STeMCell.
+
+STeMCell has support to swap UART and I2C pins, to enable single-wire uart communication in STM chips.
+
+The following additional flags has to be used while compiling, based on the pin used for split communication.
+
+| Split Pin | Compile flags |
+|-----------|---------------|
+| D3 | -e STMC_US=yes|
+| D2 | Not needed |
+| D1 | -e STMC_IS=yes|
+| D0 | Not needed |
+
+### Bonsai C4 :id=bonsai_c4
+
+The Bonsai C4 only has one on-board LED (B2), and by default, both the Pro Micro TXLED (D5) and RXLED (B0) are mapped to it. If you want only one of them mapped, you can undefine one and redefine it to another pin by adding these line to your `config.h`:
+
+```c
+#undef B0
+// If Vbus detection is unused, we can send RXLED to the Vbus detect pin instead
+#define B0 PAL_LINE(GPIOA, 9)
+```
+
+## Elite-C
+
+If a board currently supported in QMK uses an [Elite-C](https://keeb.io/products/elite-c-low-profile-version-usb-c-pro-micro-replacement-atmega32u4), the supported alternative controllers are:
+
+| Device | Target |
+|----------------------------------------------------------------------------------|-------------------|
+| [STeMCell](https://github.com/megamind4089/STeMCell) | `stemcell` |
+| [Elite-Pi](https://keeb.io/products/elite-pi-usb-c-pro-micro-replacement-rp2040) | `elite_pi` |
+
+Converter summary:
+
+| Target | Argument | `rules.mk` | Condition |
+|-------------------|---------------------------------|------------------------------|-------------------------------------|
+| `stemcell` | `-e CONVERT_TO=stemcell` | `CONVERT_TO=stemcell` | `#ifdef CONVERT_TO_STEMCELL` |
+| `elite_pi` | `-e CONVERT_TO=elite_pi` | `CONVERT_TO=elite_pi` | `#ifdef CONVERT_TO_ELITE_PI` |
+
+### STeMCell :id=stemcell_elite
+
+Currently identical to [STeMCell](#stemcell) with support for the additional bottom row of pins.
+
+### Elite-Pi :id=elite_pi
+
+Currently identical to [Adafruit KB2040](#kb2040), with support for the additional bottom row of pins.
diff --git a/docs/feature_digitizer.md b/docs/feature_digitizer.md
index ac2d64f9777c..2e9e37cd5f24 100644
--- a/docs/feature_digitizer.md
+++ b/docs/feature_digitizer.md
@@ -1,35 +1,117 @@
-## Digitizer
+# Digitizer :id=digitizer
-The digitizer HID interface allows setting the mouse cursor position at absolute coordinates, unlike the Pointing Device feature that applies relative displacements.
+Digitizers allow the mouse cursor to be placed at absolute coordinates, unlike the [Pointing Device](feature_pointing_device.md) feature which applies relative displacements.
-To enable the digitizer interface, add the following line to your rules.mk:
+This feature implements a stylus device with a tip switch and barrel switch (generally equivalent to the primary and secondary mouse buttons respectively). Tip pressure is not currently implemented.
+
+## Usage :id=usage
+
+Add the following to your `rules.mk`:
```make
DIGITIZER_ENABLE = yes
```
-In order to change the mouse cursor position from your keymap.c file, include the digitizer header :
+## Positioning :id=positioning
+
+The X and Y coordinates are normalized, meaning their value must be set between 0 and 1. For the X component, the value `0` is the leftmost position, whereas the value `1` is the rightmost position. Similarly for the Y component, `0` is at the top and `1` at the bottom.
+
+?> Since there is no display attached, the OS will likely map these coordinates to the virtual desktop. This may be important to know if you have multiple monitors.
+
+## Examples :id=examples
+
+This example simply places the cursor in the middle of the screen:
```c
-#include "digitizer.h"
+digitizer_in_range_on();
+digitizer_set_position(0.5, 0.5);
```
-This gives you access to the `digitizer` structure which members allow you to change the cursor position.
-
-The coordinates are normalized, meaning there value must be set between 0 and 1. For the `x` coordinate, the value `0` is the leftmost position, whereas the value `1` is the rightmost position.
-For the `y` coordinate, `0` is at the top and `1` at the bottom.
+The "in range" indicator is required to be on for the change in coordinates to be taken. It can then be turned off again to signal the end of the digitizer interaction, but it is not strictly required.
-Here is an example setting the cursor in the middle of the screen:
+You can also modify the digitizer state directly, if you need to change multiple fields in a single report:
```c
-digitizer_t digitizer;
-digitizer.x = 0.5;
-digitizer.y = 0.5;
-digitizer.tipswitch = 0;
-digitizer.inrange = 1;
-digitizer_set_report(digitizer);
+digitizer_state.in_range = true;
+digitizer_state.dirty = true;
+digitizer_flush();
```
-The `tipswitch` member triggers what equates to a click when set to `1`. The `inrange` member is required for the change in coordinates to be taken. It can then be set to `0` in a new report to signal the end of the digitizer interaction, but it is not strictly required.
+`digitizer_state` is a struct of type `digitizer_t`.
+
+
+## API :id=api
+
+### `struct digitizer_t` :id=api-digitizer-t
+
+Contains the state of the digitizer.
+
+#### Members :id=api-digitizer-t-members
+
+ - `bool in_range`
+ Indicates to the host that the contact is within range (ie. close to or in contact with the digitizer surface).
+ - `bool tip`
+ The state of the tip switch.
+ - `bool barrel`
+ The state of the barrel switch.
+ - `float x`
+ The X coordinate of the digitizer contact.
+ - `float y`
+ The Y coordinate of the digitizer contact.
+ - `bool dirty`
+ Whether the current state needs to be sent to the host.
+
+---
+
+### `void digitizer_flush(void)` :id=api-digitizer-flush
+
+Send the digitizer report to the host if it is marked as dirty.
+
+---
+
+### `void digitizer_in_range_on(void)` :api-digitizer-in-range-on
+
+Assert the "in range" indicator, and flush the report.
+
+---
+
+### `void digitizer_in_range_off(void)` :api-digitizer-in-range-off
+
+Deassert the "in range" indicator, and flush the report.
+
+---
+
+### `void digitizer_tip_switch_on(void)` :api-digitizer-tip-switch-on
+
+Assert the tip switch, and flush the report.
+
+---
+
+### `void digitizer_tip_switch_off(void)` :api-digitizer-tip-switch-off
+
+Deassert the tip switch, and flush the report.
+
+---
+
+### `void digitizer_barrel_switch_on(void)` :api-digitizer-barrel-switch-on
+
+Assert the barrel switch, and flush the report.
+
+---
+
+### `void digitizer_barrel_switch_off(void)` :api-digitizer-barrel-switch-off
+
+Deassert the barrel switch, and flush the report.
+
+---
+
+### `void digitizer_set_position(float x, float y)` :api-digitizer-set-position
+
+Set the absolute X and Y position of the digitizer contact, and flush the report.
+
+#### Arguments :id=api-digitizer-set-position-arguments
-Once all members are set to the desired value, the `status` member needs its bitmask `DZ_UPDATED` to be set so the report is sent during the next main loop iteration.
+ - `float x`
+ The X value of the contact position, from 0 to 1.
+ - `float y`
+ The Y value of the contact position, from 0 to 1.
diff --git a/docs/feature_dynamic_macros.md b/docs/feature_dynamic_macros.md
index 01f2a0ca407b..f5a6952b6b5d 100644
--- a/docs/feature_dynamic_macros.md
+++ b/docs/feature_dynamic_macros.md
@@ -6,21 +6,21 @@ You can store one or two macros and they may have a combined total of 128 keypre
To enable them, first include `DYNAMIC_MACRO_ENABLE = yes` in your `rules.mk`. Then, add the following keys to your keymap:
-|Key |Alias |Description |
-|------------------|----------|---------------------------------------------------|
-|`DYN_REC_START1` |`DM_REC1` |Start recording Macro 1 |
-|`DYN_REC_START2` |`DM_REC2` |Start recording Macro 2 |
-|`DYN_MACRO_PLAY1` |`DM_PLY1` |Replay Macro 1 |
-|`DYN_MACRO_PLAY2` |`DM_PLY2` |Replay Macro 2 |
-|`DYN_REC_STOP` |`DM_RSTP` |Finish the macro that is currently being recorded. |
+|Key |Alias |Description |
+|---------------------------------|---------|--------------------------------------------------|
+|`QK_DYNAMIC_MACRO_RECORD_START_1`|`DM_REC1`|Start recording Macro 1 |
+|`QK_DYNAMIC_MACRO_RECORD_START_2`|`DM_REC2`|Start recording Macro 2 |
+|`QK_DYNAMIC_MACRO_PLAY_1` |`DM_PLY1`|Replay Macro 1 |
+|`QK_DYNAMIC_MACRO_PLAY_2` |`DM_PLY2`|Replay Macro 2 |
+|`QK_DYNAMIC_MACRO_RECORD_STOP` |`DM_RSTP`|Finish the macro that is currently being recorded.|
That should be everything necessary.
-To start recording the macro, press either `DYN_REC_START1` or `DYN_REC_START2`.
+To start recording the macro, press either `DM_REC1` or `DM_REC2`.
-To finish the recording, press the `DYN_REC_STOP` layer button. You can also press `DYN_REC_START1` or `DYN_REC_START2` again to stop the recording.
+To finish the recording, press the `DM_RSTP` layer button. You can also press `DM_REC1` or `DM_REC2` again to stop the recording.
-To replay the macro, press either `DYN_MACRO_PLAY1` or `DYN_MACRO_PLAY2`.
+To replay the macro, press either `DM_PLY1` or `DM_PLY2`.
It is possible to replay a macro as part of a macro. It's ok to replay macro 2 while recording macro 1 and vice versa but never create recursive macros i.e. macro 1 that replays macro 1. If you do so and the keyboard will get unresponsive, unplug the keyboard and plug it again. You can disable this completely by defining `DYNAMIC_MACRO_NO_NESTING` in your `config.h` file.
@@ -35,6 +35,7 @@ There are a number of options added that should allow some additional degree of
|`DYNAMIC_MACRO_SIZE` |128 |Sets the amount of memory that Dynamic Macros can use. This is a limited resource, dependent on the controller. |
|`DYNAMIC_MACRO_USER_CALL` |*Not defined* |Defining this falls back to using the user `keymap.c` file to trigger the macro behavior. |
|`DYNAMIC_MACRO_NO_NESTING` |*Not Defined* |Defining this disables the ability to call a macro from another macro (nested macros). |
+|`DYNAMIC_MACRO_DELAY` |*Not Defined* |Sets the waiting time (ms unit) when sending each key. |
If the LEDs start blinking during the recording with each keypress, it means there is no more space for the macro in the macro buffer. To fit the macro in, either make the other macro shorter (they share the same buffer) or increase the buffer size by adding the `DYNAMIC_MACRO_SIZE` define in your `config.h` (default value: 128; please read the comments for it in the header).
@@ -42,10 +43,10 @@ If the LEDs start blinking during the recording with each keypress, it means the
### DYNAMIC_MACRO_USER_CALL
-For users of the earlier versions of dynamic macros: It is still possible to finish the macro recording using just the layer modifier used to access the dynamic macro keys, without a dedicated `DYN_REC_STOP` key. If you want this behavior back, add `#define DYNAMIC_MACRO_USER_CALL` to your `config.h` and insert the following snippet at the beginning of your `process_record_user()` function:
+For users of the earlier versions of dynamic macros: It is still possible to finish the macro recording using just the layer modifier used to access the dynamic macro keys, without a dedicated `DM_RSTP` key. If you want this behavior back, add `#define DYNAMIC_MACRO_USER_CALL` to your `config.h` and insert the following snippet at the beginning of your `process_record_user()` function:
```c
- uint16_t macro_kc = (keycode == MO(_DYN) ? DYN_REC_STOP : keycode);
+ uint16_t macro_kc = (keycode == MO(_DYN) ? DM_RSTP : keycode);
if (!process_record_dynamic_macro(macro_kc, record)) {
return false;
diff --git a/docs/feature_eeprom.md b/docs/feature_eeprom.md
new file mode 100644
index 000000000000..088f4f36fff0
--- /dev/null
+++ b/docs/feature_eeprom.md
@@ -0,0 +1,134 @@
+# Persistent Configuration (EEPROM)
+
+This allows you to configure persistent settings for your keyboard. These settings are stored in the EEPROM of your controller, and are retained even after power loss. The settings can be read with `eeconfig_read_kb` and `eeconfig_read_user`, and can be written to using `eeconfig_update_kb` and `eeconfig_update_user`. This is useful for features that you want to be able to toggle (like toggling rgb layer indication). Additionally, you can use `eeconfig_init_kb` and `eeconfig_init_user` to set the default values for the EEPROM.
+
+The complicated part here, is that there are a bunch of ways that you can store and access data via EEPROM, and there is no "correct" way to do this. However, you only have a DWORD (4 bytes) for each function.
+
+Keep in mind that EEPROM has a limited number of writes. While this is very high, it's not the only thing writing to the EEPROM, and if you write too often, you can potentially drastically shorten the life of your MCU.
+
+* If you don't understand the example, then you may want to avoid using this feature, as it is rather complicated.
+
+## Example Implementation
+
+This is an example of how to add settings, and read and write it. We're using the user keymap for the example here. This is a complex function, and has a lot going on. In fact, it uses a lot of the above functions to work!
+
+
+In your keymap.c file, add this to the top:
+```c
+typedef union {
+ uint32_t raw;
+ struct {
+ bool rgb_layer_change :1;
+ };
+} user_config_t;
+
+user_config_t user_config;
+```
+
+This sets up a 32 bit structure that we can store settings with in memory, and write to the EEPROM. Using this removes the need to define variables, since they're defined in this structure. Remember that `bool` (boolean) values use 1 bit, `uint8_t` uses 8 bits, `uint16_t` uses up 16 bits. You can mix and match, but changing the order can cause issues, as it will change the values that are read and written.
+
+We're using `rgb_layer_change`, for the `layer_state_set_*` function, and use `keyboard_post_init_user` and `process_record_user` to configure everything.
+
+Now, using the `keyboard_post_init_user` code above, you want to add `eeconfig_read_user()` to it, to populate the structure you've just created. And you can then immediately use this structure to control functionality in your keymap. And It should look like:
+```c
+void keyboard_post_init_user(void) {
+ // Call the keymap level matrix init.
+
+ // Read the user config from EEPROM
+ user_config.raw = eeconfig_read_user();
+
+ // Set default layer, if enabled
+ if (user_config.rgb_layer_change) {
+ rgblight_enable_noeeprom();
+ rgblight_sethsv_noeeprom(HSV_CYAN);
+ rgblight_mode_noeeprom(1);
+ }
+}
+```
+The above function will use the EEPROM config immediately after reading it, to set the default layer's RGB color. The "raw" value of it is converted in a usable structure based on the "union" that you created above.
+
+```c
+layer_state_t layer_state_set_user(layer_state_t state) {
+ switch (get_highest_layer(state)) {
+ case _RAISE:
+ if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom(HSV_MAGENTA); rgblight_mode_noeeprom(1); }
+ break;
+ case _LOWER:
+ if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom(HSV_RED); rgblight_mode_noeeprom(1); }
+ break;
+ case _PLOVER:
+ if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom(HSV_GREEN); rgblight_mode_noeeprom(1); }
+ break;
+ case _ADJUST:
+ if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom(HSV_WHITE); rgblight_mode_noeeprom(1); }
+ break;
+ default: // for any other layers, or the default layer
+ if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom(HSV_CYAN); rgblight_mode_noeeprom(1); }
+ break;
+ }
+ return state;
+}
+```
+This will cause the RGB underglow to be changed ONLY if the value was enabled. Now to configure this value, create a new keycode for `process_record_user` called `RGB_LYR`. Additionally, we want to make sure that if you use the normal RGB codes, that it turns off Using the example above, make it look this:
+```c
+
+bool process_record_user(uint16_t keycode, keyrecord_t *record) {
+ switch (keycode) {
+ case FOO:
+ if (record->event.pressed) {
+ // Do something when pressed
+ } else {
+ // Do something else when release
+ }
+ return false; // Skip all further processing of this key
+ case KC_ENTER:
+ // Play a tone when enter is pressed
+ if (record->event.pressed) {
+ PLAY_SONG(tone_qwerty);
+ }
+ return true; // Let QMK send the enter press/release events
+ case RGB_LYR: // This allows me to use underglow as layer indication, or as normal
+ if (record->event.pressed) {
+ user_config.rgb_layer_change ^= 1; // Toggles the status
+ eeconfig_update_user(user_config.raw); // Writes the new status to EEPROM
+ if (user_config.rgb_layer_change) { // if layer state indication is enabled,
+ layer_state_set(layer_state); // then immediately update the layer color
+ }
+ }
+ return false;
+ case RGB_MODE_FORWARD ... RGB_MODE_GRADIENT: // For any of the RGB codes (see quantum_keycodes.h, L400 for reference)
+ if (record->event.pressed) { //This disables layer indication, as it's assumed that if you're changing this ... you want that disabled
+ if (user_config.rgb_layer_change) { // only if this is enabled
+ user_config.rgb_layer_change = false; // disable it, and
+ eeconfig_update_user(user_config.raw); // write the setings to EEPROM
+ }
+ }
+ return true; break;
+ default:
+ return true; // Process all other keycodes normally
+ }
+}
+```
+And lastly, you want to add the `eeconfig_init_user` function, so that when the EEPROM is reset, you can specify default values, and even custom actions. To force an EEPROM reset, use the `EE_CLR` keycode or [Bootmagic Lite](feature_bootmagic.md) functionallity. For example, if you want to set rgb layer indication by default, and save the default valued.
+
+```c
+void eeconfig_init_user(void) { // EEPROM is getting reset!
+ user_config.raw = 0;
+ user_config.rgb_layer_change = true; // We want this enabled by default
+ eeconfig_update_user(user_config.raw); // Write default value to EEPROM now
+
+ // use the non noeeprom versions, to write these values to EEPROM too
+ rgblight_enable(); // Enable RGB by default
+ rgblight_sethsv(HSV_CYAN); // Set it to CYAN by default
+ rgblight_mode(1); // set to solid by default
+}
+```
+
+And you're done. The RGB layer indication will only work if you want it to. And it will be saved, even after unplugging the board. And if you use any of the RGB codes, it will disable the layer indication, so that it stays on the mode and color that you set it to.
+
+## 'EECONFIG' Function Documentation
+
+* Keyboard/Revision: `void eeconfig_init_kb(void)`, `uint32_t eeconfig_read_kb(void)` and `void eeconfig_update_kb(uint32_t val)`
+* Keymap: `void eeconfig_init_user(void)`, `uint32_t eeconfig_read_user(void)` and `void eeconfig_update_user(uint32_t val)`
+
+The `val` is the value of the data that you want to write to EEPROM. And the `eeconfig_read_*` function return a 32 bit (DWORD) value from the EEPROM.
diff --git a/docs/feature_encoders.md b/docs/feature_encoders.md
index 6a1a3750a6cb..cccdcbe4b31c 100644
--- a/docs/feature_encoders.md
+++ b/docs/feature_encoders.md
@@ -54,9 +54,55 @@ If you are using different pinouts for the encoders on each half of a split keyb
#define ENCODER_RESOLUTIONS_RIGHT { 2, 4 }
```
+If the `_RIGHT` definitions aren't specified in your `config.h`, then the non-`_RIGHT` versions will be applied to both sides of the split.
+
+Additionally, if one side does not have an encoder, you can specify `{}` for the pins/resolution -- for example, a split keyboard with only a right-side encoder:
+
+```c
+#define ENCODERS_PAD_A { }
+#define ENCODERS_PAD_B { }
+#define ENCODER_RESOLUTIONS { }
+#define ENCODERS_PAD_A_RIGHT { B12 }
+#define ENCODERS_PAD_B_RIGHT { B13 }
+#define ENCODER_RESOLUTIONS_RIGHT { 4 }
+```
+
+!> Keep in mind that whenver you change the encoder resolution, you will need to reflash the half that has the encoder affected by the change.
+
+## Encoder map :id=encoder-map
+
+Encoder mapping may be added to your `keymap.c`, which replicates the normal keyswitch layer handling functionality, but with encoders. Add this to your keymap's `rules.mk`:
+
+```make
+ENCODER_MAP_ENABLE = yes
+```
+
+Your `keymap.c` will then need an encoder mapping defined (for four layers and two encoders):
+
+```c
+#if defined(ENCODER_MAP_ENABLE)
+const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][2] = {
+ [_BASE] = { ENCODER_CCW_CW(KC_MS_WH_UP, KC_MS_WH_DOWN), ENCODER_CCW_CW(KC_VOLD, KC_VOLU) },
+ [_LOWER] = { ENCODER_CCW_CW(RGB_HUD, RGB_HUI), ENCODER_CCW_CW(RGB_SAD, RGB_SAI) },
+ [_RAISE] = { ENCODER_CCW_CW(RGB_VAD, RGB_VAI), ENCODER_CCW_CW(RGB_SPD, RGB_SPI) },
+ [_ADJUST] = { ENCODER_CCW_CW(RGB_RMOD, RGB_MOD), ENCODER_CCW_CW(KC_RIGHT, KC_LEFT) },
+};
+#endif
+```
+
+?> This should only be enabled at the keymap level.
+
+Using encoder mapping pumps events through the normal QMK keycode processing pipeline, resulting in a _keydown/keyup_ combination pushed through `process_record_xxxxx()`. To configure the amount of time between the encoder "keyup" and "keydown", you can add the following to your `config.h`:
+
+```c
+#define ENCODER_MAP_KEY_DELAY 10
+```
+
+?> By default, the encoder map delay matches the value of `TAP_CODE_DELAY`.
+
## Callbacks
-The callback functions can be inserted into your `.c`:
+When not using `ENCODER_MAP_ENABLE = yes`, the callback functions can be inserted into your `.c`:
```c
bool encoder_update_kb(uint8_t index, bool clockwise) {
@@ -85,40 +131,43 @@ bool encoder_update_user(uint8_t index, bool clockwise) {
}
```
-!> If you return `true`, it will allow the keyboard level code to run as well. Returning `false` will override the keyboard level code, depending on how the keyboard function is set up.
+!> If you return `true`, it will allow the keyboard level code to run as well. Returning `false` will override the keyboard level code, depending on how the keyboard function is set up.
Layer conditions can also be used with the callback function like the following:
```c
bool encoder_update_user(uint8_t index, bool clockwise) {
- if (get_highest_layer(layer_state|default_layer_state) > 0) {
- if (index == 0) {
- if (clockwise) {
- tap_code(KC_WH_D);
- } else {
- tap_code(KC_WH_U);
- }
- } else if (index == 1) {
- if (clockwise) {
- tap_code_delay(KC_VOLU, 10);
- } else {
- tap_code_delay(KC_VOLD, 10);
+ switch(get_highest_layer(layer_state|default_layer_state)) {
+ case 0:
+ if (index == 0) {
+ if (clockwise) {
+ tap_code(KC_PGDN);
+ } else {
+ tap_code(KC_PGUP);
+ }
+ } else if (index == 1) {
+ if (clockwise) {
+ rgb_matrix_increase_speed();
+ } else {
+ rgb_matrix_decrease_speed();
+ }
}
- }
- } else { /* Layer 0 */
- if (index == 0) {
- if (clockwise) {
- tap_code(KC_PGDN);
- } else {
- tap_code(KC_PGUP);
- }
- } else if (index == 1) {
- if (clockwise) {
- rgb_matrix_increase_speed();
- } else {
- rgb_matrix_decrease_speed();
+ break;
+ case 1:
+ if (index == 0) {
+ if (clockwise) {
+ tap_code(KC_WH_D);
+ } else {
+ tap_code(KC_WH_U);
+ }
+ } else if (index == 1) {
+ if (clockwise) {
+ tap_code_delay(KC_VOLU, 10);
+ } else {
+ tap_code_delay(KC_VOLD, 10);
+ }
}
- }
+ break;
}
return false;
}
@@ -135,7 +184,7 @@ The A an B lines of the encoders should be wired directly to the MCU, and the C/
Multiple encoders may share pins so long as each encoder has a distinct pair of pins when the following conditions are met:
- using detent encoders
- pads must be high at the detent stability point which is called 'default position' in QMK
-- no more than two encoders sharing a pin can be turned at the same time
+- no more than two encoders sharing a pin can be turned at the same time
For example you can support two encoders using only 3 pins like this
```
@@ -148,4 +197,4 @@ You could even support three encoders using only three pins (one per encoder) ho
#define ENCODERS_PAD_A { B1, B1, B2 }
#define ENCODERS_PAD_B { B2, B3, B3 }
```
-Here rotating Encoder 0 `B1 B2` and Encoder 1 `B1 B3` could be interpreted as rotating Encoder 2 `B2 B3` or `B3 B2` depending on the timing. This may still be a useful configuration depending on your use case
+Here rotating Encoder 0 `B1 B2` and Encoder 1 `B1 B3` could be interpreted as rotating Encoder 2 `B2 B3` or `B3 B2` depending on the timing. This may still be a useful configuration depending on your use case
diff --git a/docs/feature_haptic_feedback.md b/docs/feature_haptic_feedback.md
index 63ac4305ff20..b456bad736f8 100644
--- a/docs/feature_haptic_feedback.md
+++ b/docs/feature_haptic_feedback.md
@@ -32,40 +32,46 @@ The following `config.h` settings are available for all types of haptic feedback
Not all keycodes below will work depending on which haptic mechanism you have chosen.
-| Name | Description |
-|-----------|-------------------------------------------------------|
-|`HPT_ON` | Turn haptic feedback on |
-|`HPT_OFF` | Turn haptic feedback off |
-|`HPT_TOG` | Toggle haptic feedback on/off |
-|`HPT_RST` | Reset haptic feedback config to default |
-|`HPT_FBK` | Toggle feedback to occur on keypress, release or both |
-|`HPT_BUZ` | Toggle solenoid buzz on/off |
-|`HPT_MODI` | Go to next DRV2605L waveform |
-|`HPT_MODD` | Go to previous DRV2605L waveform |
-|`HPT_CONT` | Toggle continuous haptic mode on/off |
-|`HPT_CONI` | Increase DRV2605L continous haptic strength |
-|`HPT_COND` | Decrease DRV2605L continous haptic strength |
-|`HPT_DWLI` | Increase Solenoid dwell time |
-|`HPT_DWLD` | Decrease Solenoid dwell time |
+| Key | Aliases | Description |
+|-----------------------------|---------|-------------------------------------------------------|
+|`QK_HAPTIC_ON` |`HF_ON` | Turn haptic feedback on |
+|`QK_HAPTIC_OFF` |`HF_OFF` | Turn haptic feedback off |
+|`QK_HAPTIC_TOGGLE` |`HF_TOGG`| Toggle haptic feedback on/off |
+|`QK_HAPTIC_RESET` |`HF_RST` | Reset haptic feedback config to default |
+|`QK_HAPTIC_FEEDBACK_TOGGLE` |`HF_FDBK`| Toggle feedback to occur on keypress, release or both |
+|`QK_HAPTIC_BUZZ_TOGGLE` |`HF_BUZZ`| Toggle solenoid buzz on/off |
+|`QK_HAPTIC_MODE_NEXT` |`HF_NEXT`| Go to next DRV2605L waveform |
+|`QK_HAPTIC_MODE_PREVIOUS` |`HF_PREV`| Go to previous DRV2605L waveform |
+|`QK_HAPTIC_CONTINUOUS_TOGGLE`|`HF_CONT`| Toggle continuous haptic mode on/off |
+|`QK_HAPTIC_CONTINUOUS_UP` |`HF_CONU`| Increase DRV2605L continous haptic strength |
+|`QK_HAPTIC_CONTINUOUS_DOWN` |`HF_COND`| Decrease DRV2605L continous haptic strength |
+|`QK_HAPTIC_DWELL_UP` |`HF_DWLU`| Increase Solenoid dwell time |
+|`QK_HAPTIC_DWELL_DOWN` |`HF_DWLD`| Decrease Solenoid dwell time |
### Solenoids
-First you will need a build a circuit to drive the solenoid through a mosfet as most MCU will not be able to provide the current needed to drive the coil in the solenoid.
+The solenoid code supports relay switches, and similar hardware, as well as solenoids.
-[Wiring diagram provided by Adafruit](https://cdn-shop.adafruit.com/product-files/412/solenoid_driver.pdf)
+For a regular solenoid, you will need a build a circuit to drive the solenoid through a mosfet as most MCU will not be able to provide the current needed to drive the coil in the solenoid.
+[Wiring diagram provided by Adafruit](https://cdn-shop.adafruit.com/product-files/412/solenoid_driver.pdf)
-| Settings | Default | Description |
-|----------------------------|----------------------|-------------------------------------------------------|
-|`SOLENOID_PIN` | *Not defined* |Configures the pin that the Solenoid is connected to. |
-|`SOLENOID_PIN_ACTIVE_LOW` | *Not defined* |If defined then the solenoid trigger pin is active low.|
-|`SOLENOID_DEFAULT_DWELL` | `12` ms |Configures the default dwell time for the solenoid. |
-|`SOLENOID_MIN_DWELL` | `4` ms |Sets the lower limit for the dwell. |
-|`SOLENOID_MAX_DWELL` | `100` ms |Sets the upper limit for the dwell. |
-|`SOLENOID_DWELL_STEP_SIZE` | `1` ms |The step size to use when `HPT_DWL*` keycodes are sent |
-|`SOLENOID_DEFAULT_BUZZ` | `0` (disabled) |On HPT_RST buzz is set "on" if this is "1" |
-|`SOLENOID_BUZZ_ACTUATED` | `SOLENOID_MIN_DWELL` |Actuated-time when the solenoid is in buzz mode |
-|`SOLENOID_BUZZ_NONACTUATED` | `SOLENOID_MIN_DWELL` |Non-Actuated-time when the solenoid is in buzz mode |
+For relay switches, the hardware may already contain all of that ciruitry, and just require VCC, GND and a data pin.
+
+| Settings | Default | Description |
+|----------------------------|----------------------|--------------------------------------------------------------|
+|`SOLENOID_PIN` | *Not defined* |Configures the pin that the switch is connected to. |
+|`SOLENOID_PIN_ACTIVE_LOW` | *Not defined* |If defined then the switch trigger pin is active low. |
+|`SOLENOID_PINS` | *Not defined* |Configures an array of pins to be used for switch activation. |
+|`SOLENOID_PINS_ACTIVE_LOW` | *Not defined* |Allows you to specify how each pin is pulled for activation. |
+|`SOLENOID_RANDOM_FIRE` | *Not defined* |When there are multiple solenoids, will select a random one to fire.|
+|`SOLENOID_DEFAULT_DWELL` | `12` ms |Configures the default dwell time for the switch. |
+|`SOLENOID_MIN_DWELL` | `4` ms |Sets the lower limit for the dwell. |
+|`SOLENOID_MAX_DWELL` | `100` ms |Sets the upper limit for the dwell. |
+|`SOLENOID_DWELL_STEP_SIZE` | `1` ms |The step size to use when `HF_DWL*` keycodes are sent. |
+|`SOLENOID_DEFAULT_BUZZ` | `0` (disabled) |On `HF_RST` buzz is set "on" if this is "1" |
+|`SOLENOID_BUZZ_ACTUATED` | `SOLENOID_MIN_DWELL` |Actuated-time when the switch is in buzz mode. |
+|`SOLENOID_BUZZ_NONACTUATED` | `SOLENOID_MIN_DWELL` |Non-Actuated-time when the switch is in buzz mode. |
* If solenoid buzz is off, then dwell time is how long the "plunger" stays activated. The dwell time changes how the solenoid sounds.
* If solenoid buzz is on, then dwell time sets the length of the buzz, while `SOLENOID_BUZZ_ACTUATED` and `SOLENOID_BUZZ_NONACTUATED` set the (non-)actuation times withing the buzz period.
@@ -172,7 +178,7 @@ If haptic feedback is enabled, the keyboard will vibrate to a specific sequence
```
#define DRV_MODE_DEFAULT *sequence name or number*
```
-This will set what sequence HPT_RST will set as the active mode. If not defined, mode will be set to 1 when HPT_RST is pressed.
+This will set what sequence `HF_RST` will set as the active mode. If not defined, mode will be set to 1 when `HF_RST` is pressed.
### DRV2605L Continuous Haptic Mode
diff --git a/docs/feature_hd44780.md b/docs/feature_hd44780.md
index dc476c734f8d..4ade640baae7 100644
--- a/docs/feature_hd44780.md
+++ b/docs/feature_hd44780.md
@@ -1,57 +1,298 @@
-# HD44780 LCD Displays
-
-This is an integration of Peter Fleury's LCD library. This page will explain the basics. [For in depth documentation visit his page.](http://www.peterfleury.epizy.com/doxygen/avr-gcc-libraries/group__pfleury__lcd.html)
-
-You can enable support for HD44780 Displays by setting the `HD44780_ENABLE` flag in your keyboards `rules.mk` to yes.
-
-## Configuration
-
-You will need to configure the pins used by your display, and its number of lines and columns in your keyboard's `config.h`.
-
-
-Uncomment the section labled HD44780 and change the parameters as needed.
-````
-/*
- * HD44780 LCD Display Configuration
- */
-
-#define LCD_LINES 2 //< number of visible lines of the display
-#define LCD_DISP_LENGTH 16 //< visibles characters per line of the display
-#define LCD_IO_MODE 1 //< 0: memory mapped mode, 1: IO port mode
-#if LCD_IO_MODE
-#define LCD_PORT PORTB //< port for the LCD lines
-#define LCD_DATA0_PORT LCD_PORT //< port for 4bit data bit 0
-#define LCD_DATA1_PORT LCD_PORT //< port for 4bit data bit 1
-#define LCD_DATA2_PORT LCD_PORT //< port for 4bit data bit 2
-#define LCD_DATA3_PORT LCD_PORT //< port for 4bit data bit 3
-#define LCD_DATA0_PIN 4 //< pin for 4bit data bit 0
-#define LCD_DATA1_PIN 5 //< pin for 4bit data bit 1
-#define LCD_DATA2_PIN 6 //< pin for 4bit data bit 2
-#define LCD_DATA3_PIN 7 //< pin for 4bit data bit 3
-#define LCD_RS_PORT LCD_PORT //< port for RS line
-#define LCD_RS_PIN 3 //< pin for RS line
-#define LCD_RW_PORT LCD_PORT //< port for RW line
-#define LCD_RW_PIN 2 //< pin for RW line
-#define LCD_E_PORT LCD_PORT //< port for Enable line
-#define LCD_E_PIN 1 //< pin for Enable line
-#endif
-````
-
-Should you need to configure other properties you can copy them from `quantum/hd44780.h` and set them in your `config.h`
+# HD44780 LCD Driver
+
+## Supported Hardware
+
+LCD modules using [HD44780U](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) IC or equivalent, communicating in 4-bit mode.
+
+|Module|Size |Notes |
+|------|--------------|---------------------------------|
+|1602A |16x2, 5x8 dots| |
+|2004A |20x4, 5x8 dots|Untested, not currently supported|
+
+To run these modules at 3.3V, an additional MAX660 voltage converter IC must be soldered on, along with two 10µF capacitors. See [this page](https://www.codrey.com/electronic-circuits/hack-your-16x2-lcd/) for more details.
## Usage
-To initialize your display, call `lcd_init()` with one of these parameters:
-````
-LCD_DISP_OFF : display off
-LCD_DISP_ON : display on, cursor off
-LCD_DISP_ON_CURSOR : display on, cursor on
-LCD_DISP_ON_CURSOR_BLINK : display on, cursor on flashing
-````
-This is best done in your keyboards `matrix_init_kb` or your keymaps `matrix_init_user`.
-It is advised to clear the display before use.
-To do so call `lcd_clrscr()`.
+Add the following to your `rules.mk`:
+
+```make
+HD44780_ENABLE = yes
+```
+
+## Basic Configuration
+
+Add the following to your `config.h`:
+
+|Define |Default |Description |
+|-----------------------|--------------|-----------------------------------------------------------------------------------------------------|
+|`HD44780_DATA_PINS` |*Not defined* |(Required) An array of four GPIO pins connected to the display's D4-D7 pins, eg. `{ B1, B3, B2, B6 }`|
+|`HD44780_RS_PIN` |*Not defined* |(Required) The GPIO connected to the display's RS pin |
+|`HD44780_RW_PIN` |*Not defined* |(Required) The GPIO connected to the display's RW pin |
+|`HD44780_E_PIN` |*Not defined* |(Required) The GPIO connected to the display's E pin |
+|`HD44780_DISPLAY_COLS` |`16` |The number of visible characters on a single line of the display |
+|`HD44780_DISPLAY_LINES`|`2` |The number of visible lines on the display |
+|`HD44780_WRAP_LINES` |*Not defined* |If defined, input characters will wrap to the next line |
+
+## Examples
+
+### Hello World
+
+Add the following to your `keymap.c`:
+
+```c
+void keyboard_post_init_user(void) {
+ hd44780_init(true, true); // Show blinking cursor
+ hd44780_puts_P(PSTR("Hello, world!\n"));
+}
+```
+
+### Custom Character Definition
+
+Up to eight custom characters can be defined. This data is stored in the Character Generator RAM (CGRAM), and is not persistent across power cycles.
+
+This example defines the QMK Psi as the first custom character. The first 16 positions in the character set are reserved for the eight custom characters duplicated.
+
+```
+Byte | 16 8 4 2 1
+ 1 | x x x â– â–¡ â– â–¡ â–
+ 2 | x x x â– â–¡ â– â–¡ â–
+ 3 | x x x â– â–¡ â– â–¡ â–
+ 4 | x x x â–¡ â– â– â– â–¡
+ 5 | x x x â–¡ â–¡ â– â–¡ â–¡
+ 6 | x x x â–¡ â–¡ â– â–¡ â–¡
+ 7 | x x x â–¡ â–¡ â– â–¡ â–¡
+ 8 | x x x â–¡ â–¡ â–¡ â–¡ â–¡
+```
+
+```c
+const uint8_t PROGMEM psi[8] = { 0x15, 0x15, 0x15, 0x0E, 0x04, 0x04, 0x04, 0x00 };
+
+void keyboard_post_init_user(void) {
+ hd44780_init(false, false);
+ hd44780_define_char_P(0, psi);
+ // Cursor is incremented while defining characters so must be reset
+ hd44780_home();
+ // 0x08 to avoid null terminator
+ hd44780_puts_P(PSTR("\x08 QMK Firmware"));
+}
+```
+
+## API
+
+### `void hd44780_init(bool cursor, bool blink)`
+
+Initialize the display.
+
+This function should be called only once, before any of the other functions can be called.
+
+#### Arguments
+
+ - `bool cursor`
+ Whether to show the cursor.
+ - `bool blink`
+ Whether to blink the cursor, if shown.
+
+---
+
+### `void hd44780_clear(void)`
+
+Clear the display.
+
+This function is called on init.
+
+---
+
+### `void hd44780_home(void)`
+
+Move the cursor to the home position.
+
+This function is called on init.
+
+---
+
+### `void hd44780_on(bool cursor, bool blink)`
+
+Turn the display on, and/or set the cursor properties.
+
+This function is called on init.
+
+#### Arguments
+
+ - `bool cursor`
+ Whether to show the cursor.
+ - `bool blink`
+ Whether to blink the cursor, if shown.
+
+---
+
+### `void hd44780_off(void)`
+
+Turn the display off.
+
+---
+
+### `void hd44780_set_cursor(uint8_t col, uint8_t line)`
+
+Move the cursor to the specified position on the display.
+
+#### Arguments
+
+ - `uint8_t col`
+ The column number to move to, from 0 to 15 on 16x2 displays.
+ - `bool line`
+ The line number to move to, either 0 or 1 on 16x2 displays.
+
+---
+
+### `void hd44780_putc(char c)`
+
+Print a character to the display. The newline character `\n` will move the cursor to the start of the next line.
+
+The exact character shown may depend on the ROM code of your particular display - refer to the datasheet for the full character set.
+
+#### Arguments
+
+ - `char c`
+ The character to print.
+
+---
+
+### `void hd44780_puts(const char *s)`
+
+Print a string of characters to the display.
+
+#### Arguments
+
+ - `const char *s`
+ The string to print.
+
+---
+
+### `void hd44780_puts_P(const char *s)`
+
+Print a string of characters from PROGMEM to the display.
+
+On ARM devices, this function is simply an alias of `hd44780_puts()`.
+
+#### Arguments
+
+ - `const char *s`
+ The PROGMEM string to print (ie. `PSTR("Hello")`).
+
+---
+
+### `void hd44780_define_char(uint8_t index, uint8_t *data)`
+
+Define a custom character.
+
+#### Arguments
+
+ - `uint8_t index`
+ The index of the custom character to define, from 0 to 7.
+ - `uint8_t *data`
+ An array of 8 bytes containing the 5-bit row data of the character, where the first byte is the topmost row, and the least significant bit of each byte is the rightmost column.
+
+---
+
+### `void hd44780_define_char_P(uint8_t index, const uint8_t *data)`
+
+Define a custom character from PROGMEM.
+
+On ARM devices, this function is simply an alias of `hd44780_define_char()`.
+
+#### Arguments
+
+ - `uint8_t index`
+ The index of the custom character to define, from 0 to 7.
+ - `const uint8_t *data`
+ A PROGMEM array of 8 bytes containing the 5-bit row data of the character, where the first byte is the topmost row, and the least significant bit of each byte is the rightmost column.
+
+---
+
+### `bool hd44780_busy(void)`
+
+Indicates whether the display is currently processing, and cannot accept instructions.
+
+#### Return Value
+
+`true` if the display is busy.
+
+---
+
+### `void hd44780_write(uint8_t data, bool isData)`
+
+Write a byte to the display.
+
+#### Arguments
+
+ - `uint8_t data`
+ The byte to send to the display.
+ - `bool isData`
+ Whether the byte is an instruction or character data.
+
+---
+
+### `uint8_t hd44780_read(bool isData)`
+
+Read a byte from the display.
+
+#### Arguments
+
+ - `bool isData`
+ Whether to read the current cursor position, or the character at the cursor.
+
+#### Return Value
+
+If `isData` is `true`, the returned byte will be the character at the current DDRAM address. Otherwise, it will be the current DDRAM address and the busy flag.
+
+---
+
+### `void hd44780_command(uint8_t command)`
+
+Send a command to the display. Refer to the datasheet and `hd44780.h` for the valid commands and defines.
+
+This function waits for the display to clear the busy flag before sending the command.
+
+#### Arguments
+
+ - `uint8_t command`
+ The command to send.
+
+---
+
+### `void hd44780_data(uint8_t data)`
+
+Send a byte of data to the display.
+
+This function waits for the display to clear the busy flag before sending the data.
+
+#### Arguments
+
+ - `uint8_t data`
+ The byte of data to send.
+
+---
+
+### `void hd44780_set_cgram_address(uint8_t address)`
+
+Set the CGRAM address.
+
+This function is used when defining custom characters.
+
+#### Arguments
+
+ - `uint8_t address`
+ The CGRAM address to move to, from `0x00` to `0x3F`.
+
+---
+
+### `void hd44780_set_ddram_address(uint8_t address)`
+
+Set the DDRAM address.
+
+This function is used when printing characters to the display, and setting the cursor.
-To now print something to your Display you first call `lcd_gotoxy(column, line)`. To go to the start of the first line you would call `lcd_gotoxy(0, 0)` and then print a string with `lcd_puts("example string")`.
+#### Arguments
-There are more methods available to control the display. [For in depth documentation please visit the linked page.](http://www.peterfleury.epizy.com/doxygen/avr-gcc-libraries/group__pfleury__lcd.html)
+ - `uint8_t address`
+ The DDRAM address to move to, from `0x00` to `0x7F`.
diff --git a/docs/feature_joystick.md b/docs/feature_joystick.md
index fe33517a1619..dc7b49a600c1 100644
--- a/docs/feature_joystick.md
+++ b/docs/feature_joystick.md
@@ -1,152 +1,228 @@
-## Joystick
+# Joystick :id=joystick
-The keyboard can be made to be recognized as a joystick HID device by the operating system.
+This feature provides game controller input as a joystick device supporting up to 6 axes and 32 buttons. Axes can be read either from an [ADC-capable input pin](adc_driver.md), or can be virtual, so that its value is provided by your code.
-!> Joystick support is not currently available on V-USB devices.
+An analog device such as a [potentiometer](https://en.wikipedia.org/wiki/Potentiometer) found on an analog joystick's axes is based on a voltage divider, where adjusting the movable wiper controls the output voltage which can then be read by the microcontroller's ADC.
-The joystick feature provides two services:
- * reading analog input devices (eg. potentiometers)
- * sending gamepad HID reports
+## Usage :id=usage
-Both services can be used without the other, depending on whether you just want to read a device but not send gamepad reports (for volume control for instance)
-or send gamepad reports based on values computed by the keyboard.
-
-### Analog Input
-
-To use analog input you must first enable it in `rules.mk`:
+Add the following to your `rules.mk`:
```make
JOYSTICK_ENABLE = yes
-JOYSTICK_DRIVER = analog # or 'digital'
```
-An analog device such as a potentiometer found on a gamepad's analog axes is based on a [voltage divider](https://en.wikipedia.org/wiki/Voltage_divider).
-It is composed of three connectors linked to the ground, the power input and power output (usually the middle one). The power output holds the voltage that varies based on the position of the cursor,
-which value will be read using your MCU's [ADC](https://en.wikipedia.org/wiki/Analog-to-digital_converter).
-Depending on which pins are already used by your keyboard's matrix, the rest of the circuit can get a little bit more complicated,
-feeding the power input and ground connection through pins and using diodes to avoid bad interactions with the matrix scanning procedures.
+By default the joystick driver is `analog`, but you can change this with:
+
+```make
+JOYSTICK_DRIVER = digital
+```
-### Configuring the Joystick
+## Configuration :id=configuration
-By default, two axes and eight buttons are defined. This can be changed in your `config.h`:
+By default, two axes and eight buttons are defined, with a reported resolution of 8 bits (-127 to +127). This can be changed in your `config.h`:
```c
-// Max 32
+// Min 0, max 32
#define JOYSTICK_BUTTON_COUNT 16
-// Max 6: X, Y, Z, Rx, Ry, Rz
-#define JOYSTICK_AXES_COUNT 3
+// Min 0, max 6: X, Y, Z, Rx, Ry, Rz
+#define JOYSTICK_AXIS_COUNT 3
+// Min 8, max 16
+#define JOYSTICK_AXIS_RESOLUTION 10
```
-When defining axes for your joystick, you have to provide a definition array. You can do this from your keymap.c file.
-A joystick will either be read from an input pin that allows the use of the ADC, or can be virtual, so that its value is provided by your code.
-You have to define an array of type ''joystick_config_t'' and of proper size.
-
-There are three ways for your circuit to work with the ADC, that relies on the use of 1, 2 or 3 pins of the MCU:
- * 1 pin: your analog device is directly connected to your device GND and VCC. The only pin used is the ADC pin of your choice.
- * 2 pins: your analog device is powered through a pin that allows toggling it on or off. The other pin is used to read the input value through the ADC.
- * 3 pins: both the power input and ground are connected to pins that must be set to a proper state before reading and restored afterwards.
+?> You must define at least one button or axis. Also note that the maximum ADC resolution of the supported AVR MCUs is 10-bit, and 12-bit for most STM32 MCUs.
-The configuration of each axis is performed using one of four macros:
- * `JOYSTICK_AXIS_VIRTUAL`: no ADC reading must be performed, that value will be provided by keyboard/keymap-level code
- * `JOYSTICK_AXIS_IN(INPUT_PIN, LOW, REST, HIGH)`: a voltage will be read on the provided pin, which must be an ADC-capable pin.
- * `JOYSTICK_AXIS_IN_OUT(INPUT_PIN, OUTPUT_PIN, LOW, REST, HIGH)`: the provided `OUTPUT_PIN` will be set high before `INPUT_PIN` is read.
- * `JOYSTICK_AXIS_IN_OUT_GROUND(INPUT_PIN, OUTPUT_PIN, GROUND_PIN, LOW, REST, HIGH)`: the `OUTPUT_PIN` will be set high and `GROUND_PIN` will be set low before reading from `INPUT_PIN`.
+### Axes :id=axes
-In any case where an ADC reading takes place (when `INPUT_PIN` is provided), additional `LOW`, `REST` and `HIGH` parameters are used.
-These implement the calibration of the analog device by defining the range of read values that will be mapped to the lowest, resting position and highest possible value for the axis (-127 to 127).
-In practice, you have to provide the lowest/highest raw ADC reading, and the raw reading at resting position, when no deflection is applied. You can provide inverted `LOW` and `HIGH` to invert the axis.
+When defining axes for your joystick, you must provide a definition array typically in your `keymap.c`.
-For instance, an axes configuration can be defined in the following way:
+For instance, the below example configures two axes. The X axis is read from the `A4` pin. With the default axis resolution of 8 bits, the range of values between 900 and 575 are scaled to -127 through 0, and values 575 to 285 are scaled to 0 through 127. The Y axis is configured as a virtual axis, and its value is not read from any pin. Instead, the user must update the axis value programmatically.
```c
-//joystick config
joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = {
- [0] = JOYSTICK_AXIS_IN_OUT_GROUND(A4, B0, A7, 900, 575, 285),
- [1] = JOYSTICK_AXIS_VIRTUAL
+ JOYSTICK_AXIS_IN(A4, 900, 575, 285),
+ JOYSTICK_AXIS_VIRTUAL
};
```
-When the ADC reads 900 or higher, the returned axis value will be -127, whereas it will be 127 when the ADC reads 285 or lower. Zero is returned when 575 is read.
+Axes can be configured using one of the following macros:
-In this example, the first axis will be read from the `A4` pin while `B0` is set high and `A7` is set low, using `analogReadPin()`, whereas the second axis will not be read.
+ * `JOYSTICK_AXIS_IN(input_pin, low, rest, high)`
+ The ADC samples the provided pin. `low`, `high` and `rest` correspond to the minimum, maximum, and resting (or centered) analog values of the axis, respectively.
+ * `JOYSTICK_AXIS_IN_OUT(input_pin, output_pin, low, rest, high)`
+ Same as `JOYSTICK_AXIS_IN()`, but the provided `output_pin` will be pulled high before `input_pin` is read.
+ * `JOYSTICK_AXIS_IN_OUT_GROUND(input_pin, output_pin, ground_pin, low, rest, high)`
+ Same as `JOYSTICK_AXIS_IN_OUT()`, but the provided `ground_pin` will be pulled low before reading from `input_pin`.
+ * `JOYSTICK_AXIS_VIRTUAL`
+ No ADC reading is performed. The value should be provided by user code.
-In order to give a value to the second axis, you can do so in any customizable entry point: as an action, in `process_record_user()` or in `matrix_scan_user()`, or even in `joystick_task()` which is called even when no key has been pressed.
-You assign a value by writing to `joystick_status.axes[axis_index]` a signed 8-bit value (ranging from -127 to 127). Then it is necessary to assign the flag `JS_UPDATED` to `joystick_status.status` in order for an updated HID report to be sent.
+The `low` and `high` values can be swapped to effectively invert the axis.
-The following example writes two axes based on keypad presses, with `KC_P5` as a precision modifier:
+#### Virtual Axes :id=virtual-axes
+
+The following example adjusts two virtual axes (X and Y) based on keypad presses, with `KC_P0` as a precision modifier:
```c
-#ifdef ANALOG_JOYSTICK_ENABLE
-static uint8_t precision_val = 70;
-static uint8_t axesFlags = 0;
-enum axes {
- Precision = 1,
- Axis1High = 2,
- Axis1Low = 4,
- Axis2High = 8,
- Axis2Low = 16
+joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = {
+ JOYSTICK_AXIS_VIRTUAL, // x
+ JOYSTICK_AXIS_VIRTUAL // y
};
-#endif
+
+static bool precision = false;
+static uint16_t precision_mod = 64;
+static uint16_t axis_val = 127;
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
- switch(keycode) {
-#ifdef ANALOG_JOYSTICK_ENABLE
- // virtual joystick
-# if JOYSTICK_AXES_COUNT > 1
+ int16_t precision_val = axis_val;
+ if (precision) {
+ precision_val -= precision_mod;
+ }
+
+ switch (keycode) {
case KC_P8:
- if (record->event.pressed) {
- axesFlags |= Axis2Low;
- } else {
- axesFlags &= ~Axis2Low;
- }
- joystick_status.status |= JS_UPDATED;
- break;
+ joystick_set_axis(1, record->event.pressed ? -precision_val : 0);
+ return false;
case KC_P2:
- if (record->event.pressed) {
- axesFlags |= Axis2High;
- } else {
- axesFlags &= ~Axis2High;
- }
- joystick_status.status |= JS_UPDATED;
- break;
-# endif
+ joystick_set_axis(1, record->event.pressed ? precision_val : 0);
+ return false;
case KC_P4:
- if (record->event.pressed) {
- axesFlags |= Axis1Low;
- } else {
- axesFlags &= ~Axis1Low;
- }
- joystick_status.status |= JS_UPDATED;
- break;
+ joystick_set_axis(0, record->event.pressed ? -precision_val : 0);
+ return false;
case KC_P6:
- if (record->event.pressed) {
- axesFlags |= Axis1High;
- } else {
- axesFlags &= ~Axis1High;
- }
- joystick_status.status |= JS_UPDATED;
- break;
- case KC_P5:
- if (record->event.pressed) {
- axesFlags |= Precision;
- } else {
- axesFlags &= ~Precision;
- }
- joystick_status.status |= JS_UPDATED;
- break;
-#endif
+ joystick_set_axis(0, record->event.pressed ? precision_val : 0);
+ return false;
+ case KC_P0:
+ precision = record->event.pressed;
+ return false;
}
return true;
}
```
-### Axis Resolution
+## Keycodes :id=keycodes
+
+|Key |Aliases|Description|
+|-----------------------|-------|-----------|
+|`QK_JOYSTICK_BUTTON_0` |`JS_0` |Button 0 |
+|`QK_JOYSTICK_BUTTON_1` |`JS_1` |Button 1 |
+|`QK_JOYSTICK_BUTTON_2` |`JS_2` |Button 2 |
+|`QK_JOYSTICK_BUTTON_3` |`JS_3` |Button 3 |
+|`QK_JOYSTICK_BUTTON_4` |`JS_4` |Button 4 |
+|`QK_JOYSTICK_BUTTON_5` |`JS_5` |Button 5 |
+|`QK_JOYSTICK_BUTTON_6` |`JS_6` |Button 6 |
+|`QK_JOYSTICK_BUTTON_7` |`JS_7` |Button 7 |
+|`QK_JOYSTICK_BUTTON_8` |`JS_8` |Button 8 |
+|`QK_JOYSTICK_BUTTON_9` |`JS_9` |Button 9 |
+|`QK_JOYSTICK_BUTTON_10`|`JS_10`|Button 10 |
+|`QK_JOYSTICK_BUTTON_11`|`JS_11`|Button 11 |
+|`QK_JOYSTICK_BUTTON_12`|`JS_12`|Button 12 |
+|`QK_JOYSTICK_BUTTON_13`|`JS_13`|Button 13 |
+|`QK_JOYSTICK_BUTTON_14`|`JS_14`|Button 14 |
+|`QK_JOYSTICK_BUTTON_15`|`JS_15`|Button 15 |
+|`QK_JOYSTICK_BUTTON_16`|`JS_16`|Button 16 |
+|`QK_JOYSTICK_BUTTON_17`|`JS_17`|Button 17 |
+|`QK_JOYSTICK_BUTTON_18`|`JS_18`|Button 18 |
+|`QK_JOYSTICK_BUTTON_19`|`JS_19`|Button 19 |
+|`QK_JOYSTICK_BUTTON_20`|`JS_20`|Button 20 |
+|`QK_JOYSTICK_BUTTON_21`|`JS_21`|Button 21 |
+|`QK_JOYSTICK_BUTTON_22`|`JS_22`|Button 22 |
+|`QK_JOYSTICK_BUTTON_23`|`JS_23`|Button 23 |
+|`QK_JOYSTICK_BUTTON_24`|`JS_24`|Button 24 |
+|`QK_JOYSTICK_BUTTON_25`|`JS_25`|Button 25 |
+|`QK_JOYSTICK_BUTTON_26`|`JS_26`|Button 26 |
+|`QK_JOYSTICK_BUTTON_27`|`JS_27`|Button 27 |
+|`QK_JOYSTICK_BUTTON_28`|`JS_28`|Button 28 |
+|`QK_JOYSTICK_BUTTON_29`|`JS_29`|Button 29 |
+|`QK_JOYSTICK_BUTTON_30`|`JS_30`|Button 30 |
+|`QK_JOYSTICK_BUTTON_31`|`JS_31`|Button 31 |
+
+## API :id=api
+
+### `struct joystick_t` :id=api-joystick-t
+
+Contains the state of the joystick.
+
+#### Members :id=api-joystick-t-members
+
+ - `uint8_t buttons[]`
+ A bit-packed array containing the joystick button states. The size is calculated as `(JOYSTICK_BUTTON_COUNT - 1) / 8 + 1`.
+ - `int16_t axes[]`
+ An array of analog values for each defined axis.
+ - `bool dirty`
+ Whether the current state needs to be sent to the host.
+
+---
+
+### `struct joystick_config_t` :id=api-joystick-config-t
+
+Describes a single axis.
+
+#### Members :id=api-joystick-config-t-members
+
+ - `pin_t output_pin`
+ A pin to set as output high when reading the analog value, or `JS_VIRTUAL_AXIS`.
+ - `pin_t input_pin`
+ The pin to read the analog value from, or `JS_VIRTUAL_AXIS`.
+ - `pin_t ground_pin`
+ A pin to set as output low when reading the analog value, or `JS_VIRTUAL_AXIS`.
+ - `uint16_t min_digit`
+ The minimum analog value.
+ - `uint16_t mid_digit`
+ The resting or midpoint analog value.
+ - `uint16_t max_digit`
+ The maximum analog value.
+
+---
+
+### `void joystick_flush(void)` :id=api-joystick-flush
+
+Send the joystick report to the host, if it has been marked as dirty.
+
+---
+
+### `void register_joystick_button(uint8_t button)` :id=api-register-joystick-button
+
+Set the state of a button, and flush the report.
+
+#### Arguments :id=api-register-joystick-button-arguments
+
+ - `uint8_t button`
+ The index of the button to press, from 0 to 31.
+
+---
+
+### `void unregister_joystick_button(uint8_t button)` :id=api-unregister-joystick-button
+
+Reset the state of a button, and flush the report.
+
+#### Arguments :id=api-unregister-joystick-button-arguments
+
+ - `uint8_t button`
+ The index of the button to release, from 0 to 31.
+
+---
+
+### `int16_t joystick_read_axis(uint8_t axis)` :id=api-joystick-read-axis
+
+Sample and process the analog value of the given axis.
+
+#### Arguments :id=api-joystick-read-axis-arguments
+
+ - `uint8_t axis`
+ The axis to read.
+
+#### Return Value :id=api-joystick-read-axis-return
+
+A signed 16-bit integer, where 0 is the resting or mid point.
-By default, the resolution of each axis is 8 bit, giving a range of -127 to +127. If you need higher precision, you can increase it by defining eg. `JOYSTICK_AXES_RESOLUTION 12` in your `config.h`. The resolution must be between 8 and 16.
+### `void joystick_set_axis(uint8_t axis, int16_t value)` :id=api-joystick-set-axis
-Note that the supported AVR MCUs have a 10-bit ADC, and 12-bit for most STM32 MCUs.
+Set the value of the given axis.
-### Triggering Joystick Buttons
+#### Arguments :id=api-joystick-set-axis-arguments
-Joystick buttons are normal Quantum keycodes, defined as `JS_BUTTON0` to `JS_BUTTON31`, depending on the number of buttons you have configured.
-To trigger a joystick button, just add the corresponding keycode to your keymap.
+ - `uint8_t axis`
+ The axis to set the value of.
+ - `int16_t value`
+ The value to set.
diff --git a/docs/feature_key_lock.md b/docs/feature_key_lock.md
index 76813942298c..1acee524dad6 100644
--- a/docs/feature_key_lock.md
+++ b/docs/feature_key_lock.md
@@ -2,21 +2,21 @@
Sometimes you may find yourself needing to hold down a specific key for a long period of time. Key Lock holds down the next key you press for you. Press it again, and it will be released.
-Let's say you need to type in ALL CAPS for a few sentences. Hit `KC_LOCK`, and then Shift. Now, Shift will be considered held until you tap it again. You can think of Key Lock as Caps Lock, but supercharged.
+Let's say you need to type in ALL CAPS for a few sentences. Hit `QK_LOCK`, and then Shift. Now, Shift will be considered held until you tap it again. You can think of Key Lock as Caps Lock, but supercharged.
## Usage
-First, enable Key Lock by setting `KEY_LOCK_ENABLE = yes` in your `rules.mk`. Then pick a key in your keymap and assign it the keycode `KC_LOCK`.
+First, enable Key Lock by setting `KEY_LOCK_ENABLE = yes` in your `rules.mk`. Then pick a key in your keymap and assign it the keycode `QK_LOCK`.
## Keycodes
|Keycode |Description |
|---------|--------------------------------------------------------------|
-|`KC_LOCK`|Hold down the next key pressed, until the key is pressed again|
+|`QK_LOCK`|Hold down the next key pressed, until the key is pressed again|
## Caveats
-Key Lock is only able to hold standard action keys and [One Shot modifier](one_shot_keys.md) keys (for example, if you have your Shift defined as `OSM(KC_LSFT)`).
+Key Lock is only able to hold standard action keys and [One Shot modifier](one_shot_keys.md) keys (for example, if you have your Shift defined as `OSM(MOD_LSFT)`).
This does not include any of the QMK special functions (except One Shot modifiers), or shifted versions of keys such as `KC_LPRN`. If it's in the [Basic Keycodes](keycodes_basic.md) list, it can be held.
Switching layers will not cancel the Key Lock. The Key Lock can be cancelled by calling the `cancel_key_lock()` function.
diff --git a/docs/feature_key_overrides.md b/docs/feature_key_overrides.md
index 2417fcf5942b..36fd383cd455 100644
--- a/docs/feature_key_overrides.md
+++ b/docs/feature_key_overrides.md
@@ -150,15 +150,13 @@ const key_override_t fn_override = {.trigger_mods = MOD_BIT(KC_RGUI) |
.enabled = NULL};
```
-## Keycodes
+## Keycodes
-You can enable, disable and toggle all key overrides on the fly.
-
-|Keycode |Description |Function Equivalent|
-|----------|---------------------------------|--------|
-|`KEY_OVERRIDE_ON` |Turns on Key Override feature | `key_override_on(void)`|
-|`KEY_OVERRIDE_OFF` |Turns off Key Override feature |`key_override_off(void)`|
-|`KEY_OVERRIDE_TOGGLE` |Toggles Key Override feature on and off |`key_override_toggle(void)`|
+|Keycode |Aliases |Description |
+|------------------------|---------|----------------------|
+|`QK_KEY_OVERRIDE_TOGGLE`|`KO_TOGG`|Toggle key overrides |
+|`QK_KEY_OVERRIDE_ON` |`KO_ON` |Turn on key overrides |
+|`QK_KEY_OVERRIDE_OFF` |`KO_OFF` |Turn off key overrides|
## Reference for `key_override_t`
diff --git a/docs/feature_layers.md b/docs/feature_layers.md
index e30c540a79e6..5ad4005f5d92 100644
--- a/docs/feature_layers.md
+++ b/docs/feature_layers.md
@@ -1,6 +1,6 @@
# Layers :id=layers
-One of the most powerful and well used features of QMK Firmware is the ability to use layers. For most people, this amounts to a function key that allows for different keys, much like what you would see on a laptop or tablet keyboard.
+One of the most powerful and well used features of QMK Firmware is the ability to use layers. For most people, this amounts to a function key that allows for different keys, much like what you would see on a laptop or tablet keyboard.
For a detailed explanation of how the layer stack works, checkout [Keymap Overview](keymap.md#keymap-and-layers).
@@ -9,7 +9,7 @@ For a detailed explanation of how the layer stack works, checkout [Keymap Overvi
These functions allow you to activate layers in various ways. Note that layers are not generally independent layouts -- multiple layers can be activated at once, and it's typical for layers to use `KC_TRNS` to allow keypresses to pass through to lower layers. When using momentary layer switching with MO(), LM(), TT(), or LT(), make sure to leave the key on the above layers transparent or it may not work as intended.
* `DF(layer)` - switches the default layer. The default layer is the always-active base layer that other layers stack on top of. See below for more about the default layer. This might be used to switch from QWERTY to Dvorak layout. (Note that this is a temporary switch that only persists until the keyboard loses power. To modify the default layer in a persistent way requires deeper customization, such as calling the `set_single_persistent_default_layer` function inside of [process_record_user](custom_quantum_functions.md#programming-the-behavior-of-any-keycode).)
-* `MO(layer)` - momentarily activates *layer*. As soon as you let go of the key, the layer is deactivated.
+* `MO(layer)` - momentarily activates *layer*. As soon as you let go of the key, the layer is deactivated.
* `LM(layer, mod)` - Momentarily activates *layer* (like `MO`), but with modifier(s) *mod* active. Only supports layers 0-15 and the left modifiers: `MOD_LCTL`, `MOD_LSFT`, `MOD_LALT`, `MOD_LGUI` (note the use of `MOD_` constants instead of `KC_`). These modifiers can be combined using bitwise OR, e.g. `LM(_RAISE, MOD_LCTL | MOD_LALT)`.
* `LT(layer, kc)` - momentarily activates *layer* when held, and sends *kc* when tapped. Only supports layers 0-15.
* `OSL(layer)` - momentarily activates *layer* until the next key is pressed. See [One Shot Keys](one_shot_keys.md) for details and additional functionality.
@@ -31,7 +31,7 @@ Care must be taken when switching layers, it's possible to lock yourself into a
If you are just getting started with QMK you will want to keep everything simple. Follow these guidelines when setting up your layers:
-* Setup layer 0 as your default, "base" layer. This is your normal typing layer, and could be whatever layout you want (qwerty, dvorak, colemak, etc.). It's important to set this as the lowest layer since it will typically have most or all of the keyboard's keys defined, so would block other layers from having any effect if it were above them (i.e., had a higher layer number).
+* Setup layer 0 as your default, "base" layer. This is your normal typing layer, and could be whatever layout you want (qwerty, dvorak, colemak, etc.). It's important to set this as the lowest layer since it will typically have most or all of the keyboard's keys defined, so would block other layers from having any effect if it were above them (i.e., had a higher layer number).
* Arrange your layers in a "tree" layout, with layer 0 as the root. Do not try to enter the same layer from more than one other layer.
* In a layer's keymap, only reference higher-numbered layers. Because layers are processed from the highest-numbered (topmost) active layer down, modifying the state of lower layers can be tricky and error-prone.
@@ -89,3 +89,46 @@ It is also possible to check the state of a particular layer using the following
|---------------------------------|-------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------|
| `layer_state_is(layer)` | Checks if the specified `layer` is enabled globally. | `IS_LAYER_ON(layer)`, `IS_LAYER_OFF(layer)` |
| `layer_state_cmp(state, layer)` | Checks `state` to see if the specified `layer` is enabled. Intended for use in layer callbacks. | `IS_LAYER_ON_STATE(state, layer)`, `IS_LAYER_OFF_STATE(state, layer)` |
+
+## Layer Change Code :id=layer-change-code
+
+This runs code every time that the layers get changed. This can be useful for layer indication, or custom layer handling.
+
+### Example `layer_state_set_*` Implementation
+
+This example shows how to set the [RGB Underglow](feature_rgblight.md) lights based on the layer, using the Planck as an example.
+
+```c
+layer_state_t layer_state_set_user(layer_state_t state) {
+ switch (get_highest_layer(state)) {
+ case _RAISE:
+ rgblight_setrgb (0x00, 0x00, 0xFF);
+ break;
+ case _LOWER:
+ rgblight_setrgb (0xFF, 0x00, 0x00);
+ break;
+ case _PLOVER:
+ rgblight_setrgb (0x00, 0xFF, 0x00);
+ break;
+ case _ADJUST:
+ rgblight_setrgb (0x7A, 0x00, 0xFF);
+ break;
+ default: // for any other layers, or the default layer
+ rgblight_setrgb (0x00, 0xFF, 0xFF);
+ break;
+ }
+ return state;
+}
+```
+
+Use the `IS_LAYER_ON_STATE(state, layer)` and `IS_LAYER_OFF_STATE(state, layer)` macros to check the status of a particular layer.
+
+Outside of `layer_state_set_*` functions, you can use the `IS_LAYER_ON(layer)` and `IS_LAYER_OFF(layer)` macros to check global layer state.
+
+### `layer_state_set_*` Function Documentation
+
+* Keyboard/Revision: `layer_state_t layer_state_set_kb(layer_state_t state)`
+* Keymap: `layer_state_t layer_state_set_user(layer_state_t state)`
+
+
+The `state` is the bitmask of the active layers, as explained in the [Keymap Overview](keymap.md#keymap-layer-status)
diff --git a/docs/feature_leader_key.md b/docs/feature_leader_key.md
index e5b1e7a4d9ed..e285d1059775 100644
--- a/docs/feature_leader_key.md
+++ b/docs/feature_leader_key.md
@@ -2,11 +2,11 @@
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.
-That's what `KC_LEAD` does. Here's an example:
+That's what `QK_LEAD` does. Here's an example:
-1. Pick a key on your keyboard you want to use as the Leader key. Assign it the keycode `KC_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 `KC_LEAD` key. Specifically, when you press the `KC_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 `KC_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`.
+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:
```c
@@ -95,7 +95,7 @@ While, this may be fine for most, if you want to specify the whole keycode (eg,
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()`.
-The `leader_start()` function is called when you tap the `KC_LEAD` key, and the `leader_end()` function is called when either the leader sequence is completed, or the leader timeout is hit.
+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.
You can add these functions to your code (`keymap.c` usually) to add feedback to the Leader sequences (such as beeping or playing music).
@@ -111,7 +111,7 @@ void leader_end(void) {
### Example
-This example will play the Mario "One Up" sound when you hit `KC_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, and will play "All Star" if it completes successfully or "Rick Roll" you if it fails.
```c
bool did_leader_succeed;
diff --git a/docs/feature_led_indicators.md b/docs/feature_led_indicators.md
index a2a2e17c6f1d..d89562a377c2 100644
--- a/docs/feature_led_indicators.md
+++ b/docs/feature_led_indicators.md
@@ -101,6 +101,13 @@ The `host_keyboard_led_state()` function will report the LED state returned from
bool caps = host_keyboard_led_state().caps_lock;
```
+## `led_update_ports()`
+
+This function writes the LED state to the actual hardware. Call it manually
+from your `led_update_*()` callbacks to modify the handling of the standard
+keyboard LEDs.
+For example when repurposing a standard LED indicator as layer indicator.
+
## Setting Physical LED State
Some keyboard implementations provide convenient methods for setting the state of the physical LEDs.
diff --git a/docs/feature_led_matrix.md b/docs/feature_led_matrix.md
index 37c74843aa62..b91a47ae43e6 100644
--- a/docs/feature_led_matrix.md
+++ b/docs/feature_led_matrix.md
@@ -22,7 +22,7 @@ You can use between 1 and 4 IS31FL3731 IC's. Do not specify `LED_DRIVER_ADDR_
| `ISSI_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 |
| `ISSI_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 |
| `LED_DRIVER_COUNT` | (Required) How many LED driver IC's are present | |
-| `DRIVER_LED_TOTAL` | (Required) How many LED lights are present across all drivers | |
+| `LED_MATRIX_LED_COUNT` | (Required) How many LED lights are present across all drivers | |
| `LED_DRIVER_ADDR_1` | (Required) Address for the first LED driver | |
| `LED_DRIVER_ADDR_2` | (Optional) Address for the second LED driver | |
| `LED_DRIVER_ADDR_3` | (Optional) Address for the third LED driver | |
@@ -44,17 +44,17 @@ Here is an example using 2 drivers.
#define LED_DRIVER_COUNT 2
#define LED_DRIVER_1_LED_TOTAL 25
#define LED_DRIVER_2_LED_TOTAL 24
-#define DRIVER_LED_TOTAL (LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL)
+#define LED_MATRIX_LED_COUNT (LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL)
```
-!> Note the parentheses, this is so when `LED_DRIVER_LED_TOTAL` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL)` will give very different results than `rand() % LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL`.
+!> Note the parentheses, this is so when `LED_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL)` will give very different results than `rand() % LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL`.
For split keyboards using `LED_MATRIX_SPLIT` with an LED driver, you can either have the same driver address or different driver addresses. If using different addresses, use `DRIVER_ADDR_1` for one and `DRIVER_ADDR_2` for the other one. Then, in `g_is31_leds`, fill out the correct driver index (0 or 1). If using one address, use `DRIVER_ADDR_1` for both, and use index 0 for `g_is31_leds`.
Define these arrays listing all the LEDs in your `.c`:
```c
-const is31_led PROGMEM g_is31_leds[DRIVER_LED_TOTAL] = {
+const is31_led PROGMEM g_is31_leds[LED_MATRIX_LED_COUNT] = {
/* Refer to IS31 manual for these locations
* driver
* | LED address
@@ -95,7 +95,7 @@ Configure the hardware via your `config.h`:
| `ISSI_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 |
| `ISSI_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 |
| `DRIVER_COUNT` | (Required) How many LED driver IC's are present | |
-| `DRIVER_LED_TOTAL` | (Required) How many LED lights are present across all drivers | |
+| `LED_MATRIX_LED_COUNT` | (Required) How many LED lights are present across all drivers | |
| `DRIVER_ADDR_1` | (Optional) Address for the first LED driver | |
| `DRIVER_ADDR_` | (Required) Address for the additional LED drivers | |
| `ISSI_SSR_` | (Optional) Configuration for the Spread Spectrum Register | |
@@ -130,16 +130,16 @@ Here is an example using 2 drivers.
#define DRIVER_COUNT 2
#define DRIVER_1_LED_TOTAL 66
#define DRIVER_2_LED_TOTAL 42
-#define DRIVER_LED_TOTAL (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
+#define LED_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
```
-!> Note the parentheses, this is so when `DRIVER_LED_TOTAL` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`.
+!> Note the parentheses, this is so when `LED_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`.
Currently only 4 drivers are supported, but it would be trivial to support for more. Note that using a combination of different drivers is not supported. All drivers must be of the same model.
Define these arrays listing all the LEDs in your `.c`:
```c
-const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = {
+const is31_led __flash g_is31_leds[LED_MATRIX_LED_COUNT] = {
/* Refer to IS31 manual for these locations
* driver
* | LED address
@@ -199,7 +199,7 @@ x = 224 / (NUMBER_OF_COLS - 1) * COL_POSITION
y = 64 / (NUMBER_OF_ROWS - 1) * ROW_POSITION
```
-Where NUMBER_OF_COLS, NUMBER_OF_ROWS, COL_POSITION, & ROW_POSITION are all based on the physical layout of your keyboard, not the electrical layout.
+Where NUMBER_OF_COLS, NUMBER_OF_ROWS, COL_POSITION, & ROW_POSITION are all based on the physical layout of your keyboard, not the electrical layout.
As mentioned earlier, the center of the keyboard by default is expected to be `{ 112, 32 }`, but this can be changed if you want to more accurately calculate the LED's physical `{ x, y }` positions. Keyboard designers can implement `#define LED_MATRIX_CENTER { 112, 32 }` in their config.h file with the new center point of the keyboard, or where they want it to be allowing more possibilities for the `{ x, y }` values. Do note that the maximum value for x or y is 255, and the recommended maximum is 224 as this gives animations runoff room before they reset.
@@ -221,14 +221,14 @@ As mentioned earlier, the center of the keyboard by default is expected to be `{
All LED matrix keycodes are currently shared with the [Backlight feature](feature_backlight.md).
-|Key |Description |
-|---------|-----------------------------|
-|`BL_TOGG`|Toggle LED Matrix on or off |
-|`BL_STEP`|Cycle through modes |
-|`BL_ON` |Turn on LED Matrix |
-|`BL_OFF` |Turn off LED Matrix |
-|`BL_INC` |Increase the brightness level|
-|`BL_DEC` |Decrease the brightness level|
+| Key | Aliases | Description |
+|-------------------------|-----------|-------------------------------|
+| `QK_BACKLIGHT_TOGGLE` | `BL_TOGG` | Toggle LED Matrix on or off |
+| `QK_BACKLIGHT_STEP` | `BL_STEP` | Cycle through modes |
+| `QK_BACKLIGHT_ON` | `BL_ON` | Turn on LED Matrix |
+| `QK_BACKLIGHT_OFF` | `BL_OFF` | Turn off LED Matrix |
+| `QK_BACKLIGHT_UP` | `BL_UP` | Increase the brightness level |
+| `QK_BACKLIGHT_DOWN` | `BL_DOWN` | Decrease the brightness level |
## LED Matrix Effects :id=led-matrix-effects
@@ -264,11 +264,11 @@ enum led_matrix_effects {
};
```
-You can disable a single effect by defining `DISABLE_[EFFECT_NAME]` in your `config.h`:
+You can enable a single effect by defining `ENABLE_[EFFECT_NAME]` in your `config.h`:
-|Define |Description |
-|-------------------------------------------------------|-----------------------------------------------|
+|Define |Description |
+|-------------------------------------------------------|----------------------------------------------|
|`#define ENABLE_LED_MATRIX_ALPHAS_MODS` |Enables `LED_MATRIX_ALPHAS_MODS` |
|`#define ENABLE_LED_MATRIX_BREATHING` |Enables `LED_MATRIX_BREATHING` |
|`#define ENABLE_LED_MATRIX_BAND` |Enables `LED_MATRIX_BAND` |
@@ -278,6 +278,13 @@ You can disable a single effect by defining `DISABLE_[EFFECT_NAME]` in your `con
|`#define ENABLE_LED_MATRIX_CYCLE_UP_DOWN` |Enables `LED_MATRIX_CYCLE_UP_DOWN` |
|`#define ENABLE_LED_MATRIX_CYCLE_OUT_IN` |Enables `LED_MATRIX_CYCLE_OUT_IN` |
|`#define ENABLE_LED_MATRIX_DUAL_BEACON` |Enables `LED_MATRIX_DUAL_BEACON` |
+|`#define ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT` |Enables `LED_MATRIX_WAVE_LEFT_RIGHT` |
+|`#define ENABLE_LED_MATRIX_WAVE_UP_DOWN` |Enables `LED_MATRIX_WAVE_UP_DOWN` |
+
+?> These modes don't require any additional defines.
+
+|Reactive Defines |Description |
+|-------------------------------------------------------|----------------------------------------------|
|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_SIMPLE` |Enables `LED_MATRIX_SOLID_REACTIVE_SIMPLE` |
|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_WIDE` |Enables `LED_MATRIX_SOLID_REACTIVE_WIDE` |
|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTIWIDE` |Enables `LED_MATRIX_SOLID_REACTIVE_MULTIWIDE` |
@@ -287,8 +294,8 @@ You can disable a single effect by defining `DISABLE_[EFFECT_NAME]` in your `con
|`#define ENABLE_LED_MATRIX_SOLID_REACTIVE_MULTINEXUS` |Enables `LED_MATRIX_SOLID_REACTIVE_MULTINEXUS`|
|`#define ENABLE_LED_MATRIX_SOLID_SPLASH` |Enables `LED_MATRIX_SOLID_SPLASH` |
|`#define ENABLE_LED_MATRIX_SOLID_MULTISPLASH` |Enables `LED_MATRIX_SOLID_MULTISPLASH` |
-|`#define ENABLE_LED_MATRIX_WAVE_LEFT_RIGHT` |Enables `LED_MATRIX_WAVE_LEFT_RIGHT` |
-|`#define ENABLE_LED_MATRIX_WAVE_UP_DOWN` |Enables `LED_MATRIX_WAVE_UP_DOWN` |
+
+?> These modes also require the `LED_MATRIX_KEYPRESSES` or `LED_MATRIX_KEYRELEASES` define to be available.
## Custom LED Matrix Effects :id=custom-led-matrix-effects
@@ -357,15 +364,14 @@ For inspiration and examples, check out the built-in effects under `quantum/led_
#define LED_MATRIX_KEYPRESSES // reacts to keypresses
#define LED_MATRIX_KEYRELEASES // reacts to keyreleases (instead of keypresses)
#define LED_MATRIX_FRAMEBUFFER_EFFECTS // enable framebuffer effects
-#define LED_DISABLE_TIMEOUT 0 // number of milliseconds to wait until led automatically turns off
-#define LED_DISABLE_AFTER_TIMEOUT 0 // OBSOLETE: number of ticks to wait until disabling effects
+#define LED_MATRIX_TIMEOUT 0 // number of milliseconds to wait until led automatically turns off
#define LED_DISABLE_WHEN_USB_SUSPENDED // turn off effects when suspended
-#define LED_MATRIX_LED_PROCESS_LIMIT (DRIVER_LED_TOTAL + 4) / 5 // limits the number of LEDs to process in an animation per task run (increases keyboard responsiveness)
+#define LED_MATRIX_LED_PROCESS_LIMIT (LED_MATRIX_LED_COUNT + 4) / 5 // limits the number of LEDs to process in an animation per task run (increases keyboard responsiveness)
#define LED_MATRIX_LED_FLUSH_LIMIT 16 // limits in milliseconds how frequently an animation will update the LEDs. 16 (16ms) is equivalent to limiting to 60fps (increases keyboard responsiveness)
#define LED_MATRIX_MAXIMUM_BRIGHTNESS 255 // limits maximum brightness of LEDs
-#define LED_MATRIX_STARTUP_MODE LED_MATRIX_SOLID // Sets the default mode, if none has been set
-#define LED_MATRIX_STARTUP_VAL LED_MATRIX_MAXIMUM_BRIGHTNESS // Sets the default brightness value, if none has been set
-#define LED_MATRIX_STARTUP_SPD 127 // Sets the default animation speed, if none has been set
+#define LED_MATRIX_DEFAULT_MODE LED_MATRIX_SOLID // Sets the default mode, if none has been set
+#define LED_MATRIX_DEFAULT_VAL LED_MATRIX_MAXIMUM_BRIGHTNESS // Sets the default brightness value, if none has been set
+#define LED_MATRIX_DEFAULT_SPD 127 // Sets the default animation speed, if none has been set
#define LED_MATRIX_SPLIT { X, Y } // (Optional) For split keyboards, the number of LEDs connected on each half. X = left, Y = Right.
// If LED_MATRIX_KEYPRESSES or LED_MATRIX_KEYRELEASES is enabled, you also will want to enable SPLIT_TRANSPORT_MIRROR
```
@@ -384,7 +390,7 @@ Where `28` is an unused index from `eeconfig.h`.
|Function |Description |
|--------------------------------------------|-------------|
|`led_matrix_set_value_all(v)` |Set all of the LEDs to the given value, where `v` is between 0 and 255 (not written to EEPROM) |
-|`led_matrix_set_value(index, v)` |Set a single LED to the given value, where `v` is between 0 and 255, and `index` is between 0 and `DRIVER_LED_TOTAL` (not written to EEPROM) |
+|`led_matrix_set_value(index, v)` |Set a single LED to the given value, where `v` is between 0 and 255, and `index` is between 0 and `LED_MATRIX_LED_COUNT` (not written to EEPROM) |
### Disable/Enable Effects :id=disable-enable-effects
|Function |Description |
@@ -433,10 +439,14 @@ Where `28` is an unused index from `eeconfig.h`.
### Indicators :id=indicators
-If you want to set custom indicators, such as an LED for Caps Lock, or layer indication, you can use the `led_matrix_indicators_kb` or `led_matrix_indicators_user` function for that:
+If you want to set custom indicators, such as an LED for Caps Lock, or layer indication, you can use the `led_matrix_indicators_kb` or `led_matrix_indicators_user` function for that:
```c
-void led_matrix_indicators_kb(void) {
+bool led_matrix_indicators_kb(void) {
+ if (!led_matrix_indicators_user()) {
+ return false;
+ }
led_matrix_set_value(index, value);
+ return true;
}
```
@@ -445,5 +455,6 @@ In addition, there are the advanced indicator functions. These are aimed at tho
```c
void led_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {
LED_MATRIX_INDICATOR_SET_VALUE(index, value);
+ return false;
}
```
diff --git a/docs/feature_macros.md b/docs/feature_macros.md
index 78bc4ba0a507..08310555fb77 100644
--- a/docs/feature_macros.md
+++ b/docs/feature_macros.md
@@ -33,7 +33,7 @@ You can define up to 32 macros in a `keymap.json` file, as used by [Configurator
],
"layout": "LAYOUT_all",
"layers": [
- ["MACRO_0", "MACRO_1", "MACRO_2", "MACRO_3"]
+ ["QK_MACRO_0", "QK_MACRO_1", "QK_MACRO_2", "QK_MACRO_3"]
]
}
```
@@ -52,7 +52,7 @@ If you type in a language other than English, or use a non-QWERTY layout like Co
],
"layout": "LAYOUT_all",
"layers": [
- ["MACRO_0"]
+ ["QK_MACRO_0"]
]
}
```
@@ -106,6 +106,8 @@ Only basic keycodes (prefixed by `KC_`) are supported. Do not include the `KC_`
### `SEND_STRING()` & `process_record_user`
+See also: [Send String](feature_send_string.md)
+
Sometimes you want a key to type out words or phrases. For the most common situations, we've provided `SEND_STRING()`, which will type out a string (i.e. a sequence of characters) for you. All ASCII characters that are easily translatable to a keycode are supported (e.g. `qmk 123\n\t`).
Here is an example `keymap.c` for a two-key keyboard:
@@ -197,7 +199,7 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
#### Advanced Macros
-In addition to the `process_record_user()` function, is the `post_process_record_user()` function. This runs after `process_record` and can be used to do things after a keystroke has been sent. This is useful if you want to have a key pressed before and released after a normal key, for instance.
+In addition to the `process_record_user()` function, is the `post_process_record_user()` function. This runs after `process_record` and can be used to do things after a keystroke has been sent. This is useful if you want to have a key pressed before and released after a normal key, for instance.
In this example, we modify most normal keypresses so that `F22` is pressed before the keystroke is normally sent, and release it __only after__ it's been released.
@@ -347,7 +349,7 @@ If the keycode is `KC_CAPS`, it waits `TAP_HOLD_CAPS_DELAY` milliseconds instead
Like `tap_code()`, but with a `delay` parameter for specifying arbitrary intervals before sending the unregister event.
-#### `register_code16();`, `unregister_code16();` and `tap_code16();`
+#### `register_code16();`, `unregister_code16();`, `tap_code16();` and `tap_code16_delay(, );`
These functions work similar to their regular counterparts, but allow you to use modded keycodes (with Shift, Alt, Control, and/or GUI applied to them).
@@ -372,7 +374,7 @@ This will clear all keys besides the mods currently pressed.
This macro will register `KC_LALT` and tap `KC_TAB`, then wait for 1000ms. If the key is tapped again, it will send another `KC_TAB`; if there is no tap, `KC_LALT` will be unregistered, thus allowing you to cycle through windows.
```c
-bool is_alt_tab_active = false; // ADD this near the begining of keymap.c
+bool is_alt_tab_active = false; // ADD this near the beginning of keymap.c
uint16_t alt_tab_timer = 0; // we will be using them soon.
enum custom_keycodes { // Make sure have the awesome keycode ready
diff --git a/docs/feature_midi.md b/docs/feature_midi.md
index 3da5c4940a36..1f0809ef1443 100644
--- a/docs/feature_midi.md
+++ b/docs/feature_midi.md
@@ -10,6 +10,10 @@ MIDI_ENABLE = yes
There are two MIDI systems in QMK: basic and advanced. With basic MIDI you will only be able to send Note On and Note Off messages using the note keycodes, meaning that keycodes like `MI_OCTU` and `MI_OCTD` will not work. Advanced MIDI allows you to do things like octave shifts, channel changes, velocity changes, modulation, and more.
+### Caveats
+
+MIDI requires 2 USB endpoints and as such may not work on some hardware such as V-USB controllers.
+
### Basic MIDI
To enable basic MIDI, add the following to your `config.h`:
@@ -76,164 +80,164 @@ const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
### Keycodes
-|Keycode |Aliases |Description |
-|------------|---------|---------------------------------|
-|`MI_ON` | |Turn MIDI on |
-|`MI_OFF` | |Turn MIDI off |
-|`MI_TOG` | |Toggle MIDI enabled |
-|`MI_C` | |C octave 0 |
-|`MI_Cs` |`MI_Db` |C♯/D♠octave 0 |
-|`MI_D` | |D octave 0 |
-|`MI_Ds` |`MI_Eb` |D♯/E♠octave 0 |
-|`MI_E` | |E octave 0 |
-|`MI_F` | |F octave 0 |
-|`MI_Fs` |`MI_Gb` |F♯/G♠octave 0 |
-|`MI_G` | |G octave 0 |
-|`MI_Gs` |`MI_Gs` |G♯/A♠octave 0 |
-|`MI_A` | |A octave 0 |
-|`MI_As` |`MI_Bb` |A♯/B♠octave 0 |
-|`MI_B` | |B octave 0 |
-|`MI_C_1` | |C octave 1 |
-|`MI_Cs_1` |`MI_Db_1`|C♯/D♠octave 1 |
-|`MI_D_1` | |D octave 1 |
-|`MI_Ds_1` |`MI_Eb_1`|D♯/E♠octave 1 |
-|`MI_E_1` | |E octave 1 |
-|`MI_F_1` | |F octave 1 |
-|`MI_Fs_1` |`MI_Gb_1`|F♯/G♠octave 1 |
-|`MI_G_1` | |G octave 1 |
-|`MI_Gs_1` |`MI_Ab_1`|G♯/A♠octave 1 |
-|`MI_A_1` | |A octave 1 |
-|`MI_As_1` |`MI_Bb_1`|A♯/B♠octave 1 |
-|`MI_B_1` | |B octave 1 |
-|`MI_C_2` | |C octave 2 |
-|`MI_Cs_2` |`MI_Db_2`|C♯/D♠octave 2 |
-|`MI_D_2` | |D octave 2 |
-|`MI_Ds_2` |`MI_Eb_2`|D♯/E♠octave 2 |
-|`MI_E_2` | |E octave 2 |
-|`MI_F_2` | |F octave 2 |
-|`MI_Fs_2` |`MI_Gb_2`|F♯/G♠octave 2 |
-|`MI_G_2` | |G octave 2 |
-|`MI_Gs_2` |`MI_Ab_2`|G♯/A♠octave 2 |
-|`MI_A_2` | |A octave 2 |
-|`MI_As_2` |`MI_Bb_2`|A♯/B♠octave 2 |
-|`MI_B_2` | |B octave 2 |
-|`MI_C_3` | |C octave 3 |
-|`MI_Cs_3` |`MI_Db_3`|C♯/D♠octave 3 |
-|`MI_D_3` | |D octave 3 |
-|`MI_Ds_3` |`MI_Eb_3`|D♯/E♠octave 3 |
-|`MI_E_3` | |E octave 3 |
-|`MI_F_3` | |F octave 3 |
-|`MI_Fs_3` |`MI_Gb_3`|F♯/G♠octave 3 |
-|`MI_G_3` | |G octave 3 |
-|`MI_Gs_3` |`MI_Ab_3`|G♯/A♠octave 3 |
-|`MI_A_3` | |A octave 3 |
-|`MI_As_3` |`MI_Bb_3`|A♯/B♠octave 3 |
-|`MI_B_3` | |B octave 3 |
-|`MI_C_4` | |C octave 4 |
-|`MI_Cs_4` |`MI_Db_4`|C♯/D♠octave 4 |
-|`MI_D_4` | |D octave 4 |
-|`MI_Ds_4` |`MI_Eb_4`|D♯/E♠octave 4 |
-|`MI_E_4` | |E octave 4 |
-|`MI_F_4` | |F octave 4 |
-|`MI_Fs_4` |`MI_Gb_4`|F♯/G♠octave 4 |
-|`MI_G_4` | |G octave 4 |
-|`MI_Gs_4` |`MI_Ab_4`|G♯/A♠octave 4 |
-|`MI_A_4` | |A octave 4 |
-|`MI_As_4` |`MI_Bb_4`|A♯/B♠octave 4 |
-|`MI_B_4` | |B octave 4 |
-|`MI_C_5` | |C octave 5 |
-|`MI_Cs_5` |`MI_Db_5`|C♯/D♠octave 5 |
-|`MI_D_5` | |D octave 5 |
-|`MI_Ds_5` |`MI_Eb_5`|D♯/E♠octave 5 |
-|`MI_E_5` | |E octave 5 |
-|`MI_F_5` | |F octave 5 |
-|`MI_Fs_5` |`MI_Gb_5`|F♯/G♠octave 5 |
-|`MI_G_5` | |G octave 5 |
-|`MI_Gs_5` |`MI_Ab_5`|G♯/A♠octave 5 |
-|`MI_A_5` | |A octave 5 |
-|`MI_As_5` |`MI_Bb_5`|A♯/B♠octave 5 |
-|`MI_B_5` | |B octave 5 |
-|`MI_OCT_N2` | |Set octave to -2 |
-|`MI_OCT_N1` | |Set octave to -1 |
-|`MI_OCT_0` | |Set octave to 0 |
-|`MI_OCT_1` | |Set octave to 1 |
-|`MI_OCT_2` | |Set octave to 2 |
-|`MI_OCT_3` | |Set octave to 3 |
-|`MI_OCT_4` | |Set octave to 4 |
-|`MI_OCT_5` | |Set octave to 5 |
-|`MI_OCT_6` | |Set octave to 6 |
-|`MI_OCT_7` | |Set octave to 7 |
-|`MI_OCTD` | |Move down an octave |
-|`MI_OCTU` | |Move up an octave |
-|`MI_TRNS_N6`| |Set transposition to -6 semitones|
-|`MI_TRNS_N5`| |Set transposition to -5 semitones|
-|`MI_TRNS_N4`| |Set transposition to -4 semitones|
-|`MI_TRNS_N3`| |Set transposition to -3 semitones|
-|`MI_TRNS_N2`| |Set transposition to -2 semitones|
-|`MI_TRNS_N1`| |Set transposition to -1 semitone |
-|`MI_TRNS_0` | |No transposition |
-|`MI_TRNS_1` | |Set transposition to +1 semitone |
-|`MI_TRNS_2` | |Set transposition to +2 semitones|
-|`MI_TRNS_3` | |Set transposition to +3 semitones|
-|`MI_TRNS_4` | |Set transposition to +4 semitones|
-|`MI_TRNS_5` | |Set transposition to +5 semitones|
-|`MI_TRNS_6` | |Set transposition to +6 semitones|
-|`MI_TRNSD` | |Decrease transposition |
-|`MI_TRNSU` | |Increase transposition |
-|`MI_VEL_0` | |Set velocity to 0 |
-|`MI_VEL_1` | |Set velocity to 12 |
-|`MI_VEL_2` | |Set velocity to 25 |
-|`MI_VEL_3` | |Set velocity to 38 |
-|`MI_VEL_4` | |Set velocity to 51 |
-|`MI_VEL_5` | |Set velocity to 64 |
-|`MI_VEL_6` | |Set velocity to 76 |
-|`MI_VEL_7` | |Set velocity to 89 |
-|`MI_VEL_8` | |Set velocity to 102 |
-|`MI_VEL_9` | |Set velocity to 114 |
-|`MI_VEL_10` | |Set velocity to 127 |
-|`MI_VELD` | |Decrease velocity |
-|`MI_VELU` | |Increase velocity |
-|`MI_CH1` | |Set channel to 1 |
-|`MI_CH2` | |Set channel to 2 |
-|`MI_CH3` | |Set channel to 3 |
-|`MI_CH4` | |Set channel to 4 |
-|`MI_CH5` | |Set channel to 5 |
-|`MI_CH6` | |Set channel to 6 |
-|`MI_CH7` | |Set channel to 7 |
-|`MI_CH8` | |Set channel to 8 |
-|`MI_CH9` | |Set channel to 9 |
-|`MI_CH10` | |Set channel to 10 |
-|`MI_CH11` | |Set channel to 11 |
-|`MI_CH12` | |Set channel to 12 |
-|`MI_CH13` | |Set channel to 13 |
-|`MI_CH14` | |Set channel to 14 |
-|`MI_CH15` | |Set channel to 15 |
-|`MI_CH16` | |Set channel to 16 |
-|`MI_CHD` | |Decrease channel |
-|`MI_CHU` | |Increase channel |
-|`MI_ALLOFF` | |Stop all notes |
-|`MI_SUS` | |Sustain |
-|`MI_PORT` | |Portmento |
-|`MI_SOST` | |Sostenuto |
-|`MI_SOFT` | |Soft Pedal |
-|`MI_LEG` | |Legato |
-|`MI_MOD` | |Modulation |
-|`MI_MODSD` | |Decrease modulation speed |
-|`MI_MODSU` | |Increase modulation speed |
-|`MI_BENDD` | |Bend pitch down |
-|`MI_BENDU` | |Bend pitch up |
+|Keycode |Aliases |Description |
+|-------------------------------|------------------|---------------------------------|
+|`QK_MIDI_ON` |`MI_ON` |Turn MIDI on |
+|`QK_MIDI_OFF` |`MI_OFF` |Turn MIDI off |
+|`QK_MIDI_TOGGLE` |`MI_TOGG` |Toggle MIDI enabled |
+|`QK_MIDI_NOTE_C_0` |`MI_C` |C octave 0 |
+|`QK_MIDI_NOTE_C_SHARP_0` |`MI_Cs`, `MI_Db` |C♯/D♠octave 0 |
+|`QK_MIDI_NOTE_D_0` |`MI_D` |D octave 0 |
+|`QK_MIDI_NOTE_D_SHARP_0` |`MI_Ds`, `MI_Eb` |D♯/E♠octave 0 |
+|`QK_MIDI_NOTE_E_0` |`MI_E` |E octave 0 |
+|`QK_MIDI_NOTE_F_0` |`MI_F` |F octave 0 |
+|`QK_MIDI_NOTE_F_SHARP_0` |`MI_Fs`, `MI_Gb` |F♯/G♠octave 0 |
+|`QK_MIDI_NOTE_G_0` |`MI_G` |G octave 0 |
+|`QK_MIDI_NOTE_G_SHARP_0` |`MI_Gs`, `MI_Ab` |G♯/A♠octave 0 |
+|`QK_MIDI_NOTE_A_0` |`MI_A` |A octave 0 |
+|`QK_MIDI_NOTE_A_SHARP_0` |`MI_As`, `MI_Bb` |A♯/B♠octave 0 |
+|`QK_MIDI_NOTE_B_0` |`MI_B` |B octave 0 |
+|`QK_MIDI_NOTE_C_1` |`MI_C1` |C octave 1 |
+|`QK_MIDI_NOTE_C_SHARP_1` |`MI_Cs1`, `MI_Db1`|C♯/D♠octave 1 |
+|`QK_MIDI_NOTE_D_1` |`MI_D1` |D octave 1 |
+|`QK_MIDI_NOTE_D_SHARP_1` |`MI_Ds1`, `MI_Eb1`|D♯/E♠octave 1 |
+|`QK_MIDI_NOTE_E_1` |`MI_E1` |E octave 1 |
+|`QK_MIDI_NOTE_F_1` |`MI_F1` |F octave 1 |
+|`QK_MIDI_NOTE_F_SHARP_1` |`MI_Fs1`, `MI_Gb1`|F♯/G♠octave 1 |
+|`QK_MIDI_NOTE_G_1` |`MI_G1` |G octave 1 |
+|`QK_MIDI_NOTE_G_SHARP_1` |`MI_Gs1`, `MI_Ab1`|G♯/A♠octave 1 |
+|`QK_MIDI_NOTE_A_1` |`MI_A1` |A octave 1 |
+|`QK_MIDI_NOTE_A_SHARP_1` |`MI_As1`, `MI_Bb1`|A♯/B♠octave 1 |
+|`QK_MIDI_NOTE_B_1` |`MI_B1` |B octave 1 |
+|`QK_MIDI_NOTE_C_2` |`MI_C2` |C octave 2 |
+|`QK_MIDI_NOTE_C_SHARP_2` |`MI_Cs2`, `MI_Db2`|C♯/D♠octave 2 |
+|`QK_MIDI_NOTE_D_2` |`MI_D2` |D octave 2 |
+|`QK_MIDI_NOTE_D_SHARP_2` |`MI_Ds2`, `MI_Eb2`|D♯/E♠octave 2 |
+|`QK_MIDI_NOTE_E_2` |`MI_E2` |E octave 2 |
+|`QK_MIDI_NOTE_F_2` |`MI_F2` |F octave 2 |
+|`QK_MIDI_NOTE_F_SHARP_2` |`MI_Fs2`, `MI_Gb2`|F♯/G♠octave 2 |
+|`QK_MIDI_NOTE_G_2` |`MI_G2` |G octave 2 |
+|`QK_MIDI_NOTE_G_SHARP_2` |`MI_Gs2`, `MI_Ab2`|G♯/A♠octave 2 |
+|`QK_MIDI_NOTE_A_2` |`MI_A2` |A octave 2 |
+|`QK_MIDI_NOTE_A_SHARP_2` |`MI_As2`, `MI_Bb2`|A♯/B♠octave 2 |
+|`QK_MIDI_NOTE_B_2` |`MI_B2` |B octave 2 |
+|`QK_MIDI_NOTE_C_3` |`MI_C3` |C octave 3 |
+|`QK_MIDI_NOTE_C_SHARP_3` |`MI_Cs3`, `MI_Db3`|C♯/D♠octave 3 |
+|`QK_MIDI_NOTE_D_3` |`MI_D3` |D octave 3 |
+|`QK_MIDI_NOTE_D_SHARP_3` |`MI_Ds3`, `MI_Eb3`|D♯/E♠octave 3 |
+|`QK_MIDI_NOTE_E_3` |`MI_E3` |E octave 3 |
+|`QK_MIDI_NOTE_F_3` |`MI_F3` |F octave 3 |
+|`QK_MIDI_NOTE_F_SHARP_3` |`MI_Fs3`, `MI_Gb3`|F♯/G♠octave 3 |
+|`QK_MIDI_NOTE_G_3` |`MI_G3` |G octave 3 |
+|`QK_MIDI_NOTE_G_SHARP_3` |`MI_Gs3`, `MI_Ab3`|G♯/A♠octave 3 |
+|`QK_MIDI_NOTE_A_3` |`MI_A3` |A octave 3 |
+|`QK_MIDI_NOTE_A_SHARP_3` |`MI_As3`, `MI_Bb3`|A♯/B♠octave 3 |
+|`QK_MIDI_NOTE_B_3` |`MI_B3` |B octave 3 |
+|`QK_MIDI_NOTE_C_4` |`MI_C4` |C octave 4 |
+|`QK_MIDI_NOTE_C_SHARP_4` |`MI_Cs4`, `MI_Db4`|C♯/D♠octave 4 |
+|`QK_MIDI_NOTE_D_4` |`MI_D4` |D octave 4 |
+|`QK_MIDI_NOTE_D_SHARP_4` |`MI_Ds4`, `MI_Eb4`|D♯/E♠octave 4 |
+|`QK_MIDI_NOTE_E_4` |`MI_E4` |E octave 4 |
+|`QK_MIDI_NOTE_F_4` |`MI_F4` |F octave 4 |
+|`QK_MIDI_NOTE_F_SHARP_4` |`MI_Fs4`, `MI_Gb4`|F♯/G♠octave 4 |
+|`QK_MIDI_NOTE_G_4` |`MI_G4` |G octave 4 |
+|`QK_MIDI_NOTE_G_SHARP_4` |`MI_Gs4`, `MI_Ab4`|G♯/A♠octave 4 |
+|`QK_MIDI_NOTE_A_4` |`MI_A4` |A octave 4 |
+|`QK_MIDI_NOTE_A_SHARP_4` |`MI_As4`, `MI_Bb4`|A♯/B♠octave 4 |
+|`QK_MIDI_NOTE_B_4` |`MI_B4` |B octave 4 |
+|`QK_MIDI_NOTE_C_5` |`MI_C5` |C octave 5 |
+|`QK_MIDI_NOTE_C_SHARP_5` |`MI_Cs5`, `MI_Db5`|C♯/D♠octave 5 |
+|`QK_MIDI_NOTE_D_5` |`MI_D5` |D octave 5 |
+|`QK_MIDI_NOTE_D_SHARP_5` |`MI_Ds5`, `MI_Eb5`|D♯/E♠octave 5 |
+|`QK_MIDI_NOTE_E_5` |`MI_E5` |E octave 5 |
+|`QK_MIDI_NOTE_F_5` |`MI_F5` |F octave 5 |
+|`QK_MIDI_NOTE_F_SHARP_5` |`MI_Fs5`, `MI_Gb5`|F♯/G♠octave 5 |
+|`QK_MIDI_NOTE_G_5` |`MI_G5` |G octave 5 |
+|`QK_MIDI_NOTE_G_SHARP_5` |`MI_Gs5`, `MI_Ab5`|G♯/A♠octave 5 |
+|`QK_MIDI_NOTE_A_5` |`MI_A5` |A octave 5 |
+|`QK_MIDI_NOTE_A_SHARP_5` |`MI_As5`, `MI_Bb5`|A♯/B♠octave 5 |
+|`QK_MIDI_NOTE_B_5` |`MI_B5` |B octave 5 |
+|`QK_MIDI_OCTAVE_N2` |`MI_OCN2` |Set octave to -2 |
+|`QK_MIDI_OCTAVE_N1` |`MI_OCN1` |Set octave to -1 |
+|`QK_MIDI_OCTAVE_0` |`MI_OC0` |Set octave to 0 |
+|`QK_MIDI_OCTAVE_1` |`MI_OC1` |Set octave to 1 |
+|`QK_MIDI_OCTAVE_2` |`MI_OC2` |Set octave to 2 |
+|`QK_MIDI_OCTAVE_3` |`MI_OC3` |Set octave to 3 |
+|`QK_MIDI_OCTAVE_4` |`MI_OC4` |Set octave to 4 |
+|`QK_MIDI_OCTAVE_5` |`MI_OC5` |Set octave to 5 |
+|`QK_MIDI_OCTAVE_6` |`MI_OC6` |Set octave to 6 |
+|`QK_MIDI_OCTAVE_7` |`MI_OC7` |Set octave to 7 |
+|`QK_MIDI_OCTAVE_DOWN` |`MI_OCTD` |Move down an octave |
+|`QK_MIDI_OCTAVE_UP` |`MI_OCTU` |Move up an octave |
+|`QK_MIDI_TRANSPOSE_N6` |`MI_TRN6` |Set transposition to -6 semitones|
+|`QK_MIDI_TRANSPOSE_N5` |`MI_TRN5` |Set transposition to -5 semitones|
+|`QK_MIDI_TRANSPOSE_N4` |`MI_TRN4` |Set transposition to -4 semitones|
+|`QK_MIDI_TRANSPOSE_N3` |`MI_TRN3` |Set transposition to -3 semitones|
+|`QK_MIDI_TRANSPOSE_N2` |`MI_TRN2` |Set transposition to -2 semitones|
+|`QK_MIDI_TRANSPOSE_N1` |`MI_TRN1` |Set transposition to -1 semitone |
+|`QK_MIDI_TRANSPOSE_0` |`MI_TR0` |No transposition |
+|`QK_MIDI_TRANSPOSE_1` |`MI_TR1` |Set transposition to +1 semitone |
+|`QK_MIDI_TRANSPOSE_2` |`MI_TR2` |Set transposition to +2 semitones|
+|`QK_MIDI_TRANSPOSE_3` |`MI_TR3` |Set transposition to +3 semitones|
+|`QK_MIDI_TRANSPOSE_4` |`MI_TR4` |Set transposition to +4 semitones|
+|`QK_MIDI_TRANSPOSE_5` |`MI_TR5` |Set transposition to +5 semitones|
+|`QK_MIDI_TRANSPOSE_6` |`MI_TR6` |Set transposition to +6 semitones|
+|`QK_MIDI_TRANSPOSE_DOWN` |`MI_TRSD` |Decrease transposition |
+|`QK_MIDI_TRANSPOSE_UP` |`MI_TRSU` |Increase transposition |
+|`QK_MIDI_VELOCITY_0` |`MI_VL0` |Set velocity to 0 |
+|`QK_MIDI_VELOCITY_1` |`MI_VL1` |Set velocity to 12 |
+|`QK_MIDI_VELOCITY_2` |`MI_VL2` |Set velocity to 25 |
+|`QK_MIDI_VELOCITY_3` |`MI_VL3` |Set velocity to 38 |
+|`QK_MIDI_VELOCITY_4` |`MI_VL4` |Set velocity to 51 |
+|`QK_MIDI_VELOCITY_5` |`MI_VL5` |Set velocity to 64 |
+|`QK_MIDI_VELOCITY_6` |`MI_VL6` |Set velocity to 76 |
+|`QK_MIDI_VELOCITY_7` |`MI_VL7` |Set velocity to 89 |
+|`QK_MIDI_VELOCITY_8` |`MI_VL8` |Set velocity to 102 |
+|`QK_MIDI_VELOCITY_9` |`MI_VL9` |Set velocity to 114 |
+|`QK_MIDI_VELOCITY_10` |`MI_VL10` |Set velocity to 127 |
+|`QK_MIDI_VELOCITY_DOWN` |`MI_VELD` |Decrease velocity |
+|`QK_MIDI_VELOCITY_UP` |`MI_VELU` |Increase velocity |
+|`QK_MIDI_CHANNEL_1` |`MI_CH1` |Set channel to 1 |
+|`QK_MIDI_CHANNEL_2` |`MI_CH2` |Set channel to 2 |
+|`QK_MIDI_CHANNEL_3` |`MI_CH3` |Set channel to 3 |
+|`QK_MIDI_CHANNEL_4` |`MI_CH4` |Set channel to 4 |
+|`QK_MIDI_CHANNEL_5` |`MI_CH5` |Set channel to 5 |
+|`QK_MIDI_CHANNEL_6` |`MI_CH6` |Set channel to 6 |
+|`QK_MIDI_CHANNEL_7` |`MI_CH7` |Set channel to 7 |
+|`QK_MIDI_CHANNEL_8` |`MI_CH8` |Set channel to 8 |
+|`QK_MIDI_CHANNEL_9` |`MI_CH9` |Set channel to 9 |
+|`QK_MIDI_CHANNEL_10` |`MI_CH10` |Set channel to 10 |
+|`QK_MIDI_CHANNEL_11` |`MI_CH11` |Set channel to 11 |
+|`QK_MIDI_CHANNEL_12` |`MI_CH12` |Set channel to 12 |
+|`QK_MIDI_CHANNEL_13` |`MI_CH13` |Set channel to 13 |
+|`QK_MIDI_CHANNEL_14` |`MI_CH14` |Set channel to 14 |
+|`QK_MIDI_CHANNEL_15` |`MI_CH15` |Set channel to 15 |
+|`QK_MIDI_CHANNEL_16` |`MI_CH16` |Set channel to 16 |
+|`QK_MIDI_CHANNEL_DOWN` |`MI_CHND` |Decrease channel |
+|`QK_MIDI_CHANNEL_UP` |`MI_CHNU` |Increase channel |
+|`QK_MIDI_ALL_NOTES_OFF` |`MI_AOFF` |Stop all notes |
+|`QK_MIDI_SUSTAIN` |`MI_SUST` |Sustain |
+|`QK_MIDI_PORTAMENTO` |`MI_PORT` |Portmento |
+|`QK_MIDI_SOSTENUTO` |`MI_SOST` |Sostenuto |
+|`QK_MIDI_SOFT` |`MI_SOFT` |Soft Pedal |
+|`QK_MIDI_LEGATO` |`MI_LEG` |Legato |
+|`QK_MIDI_MODULATION` |`MI_MOD` |Modulation |
+|`QK_MIDI_MODULATION_SPEED_DOWN`|`MI_MODD` |Decrease modulation speed |
+|`QK_MIDI_MODULATION_SPEED_UP` |`MI_MODU` |Increase modulation speed |
+|`QK_MIDI_PITCH_BEND_DOWN` |`MI_BNDD` |Bend pitch down |
+|`QK_MIDI_PITCH_BEND_UP` |`MI_BNDU` |Bend pitch up |
### Configuration
Certain values are stored in the `midi_config` struct. This configuration is not persisted to EEPROM. By default, these values are:
-|Configuration |Value|Comments |
-|-------------------|-----|-------------------------|
-|Octave |`4` |Corresponds to `MI_OCT_2`|
-|Transposition |`0` | |
-|Velocity |`127`| |
-|Channel |`0` | |
-|Modulation Interval|`8` | |
+|Configuration |Value|Comments |
+|-------------------|-----|-----------------------|
+|Octave |`4` |Corresponds to `MI_OC2`|
+|Transposition |`0` | |
+|Velocity |`127`| |
+|Channel |`0` | |
+|Modulation Interval|`8` | |
For the above, the `MI_C` keycode will produce a C3 (note number 48), and so on.
@@ -254,7 +258,7 @@ For the above, the `MI_C` keycode will produce a C3 (note number 48), and so on.
diff --git a/docs/feature_mouse_keys.md b/docs/feature_mouse_keys.md
index 905da36e430c..eed4f4f4aaaf 100644
--- a/docs/feature_mouse_keys.md
+++ b/docs/feature_mouse_keys.md
@@ -48,8 +48,9 @@ Mouse keys supports three different modes to move the cursor:
* **Kinetic:** Holding movement keys accelerates the cursor with its speed following a quadratic curve until it reaches its maximum speed.
* **Constant:** Holding movement keys moves the cursor at constant speeds.
* **Combined:** Holding movement keys accelerates the cursor until it reaches its maximum speed, but holding acceleration and movement keys simultaneously moves the cursor at constant speeds.
+* **Inertia:** Cursor accelerates when key held, and decelerates after key release. Tracks X and Y velocity separately for more nuanced movements. Applies to cursor only, not scrolling.
-The same principle applies to scrolling.
+The same principle applies to scrolling, in most modes.
Configuration options that are times, intervals or delays are given in milliseconds. Scroll speed is given as multiples of the default scroll step. For example, a scroll speed of 8 means that each scroll action covers 8 times the length of the default scroll step as defined by your operating system or application.
@@ -87,9 +88,9 @@ This is an extension of the accelerated mode. The kinetic mode uses a quadratic
|`MK_KINETIC_SPEED` |undefined|Enable kinetic mode |
|`MOUSEKEY_DELAY` |5 |Delay between pressing a movement key and cursor movement |
|`MOUSEKEY_INTERVAL` |10 |Time between cursor movements in milliseconds |
-|`MOUSEKEY_MOVE_DELTA` |5 |Step size for accelerating from initial to base speed |
+|`MOUSEKEY_MOVE_DELTA` |16 |Step size for accelerating from initial to base speed |
|`MOUSEKEY_INITIAL_SPEED` |100 |Initial speed of the cursor in pixel per second |
-|`MOUSEKEY_BASE_SPEED` |1000 |Maximum cursor speed at which acceleration stops |
+|`MOUSEKEY_BASE_SPEED` |5000 |Maximum cursor speed at which acceleration stops |
|`MOUSEKEY_DECELERATED_SPEED` |400 |Decelerated cursor speed |
|`MOUSEKEY_ACCELERATED_SPEED` |3000 |Accelerated cursor speed |
|`MOUSEKEY_WHEEL_INITIAL_MOVEMENTS` |16 |Initial number of movements of the mouse wheel |
@@ -100,7 +101,7 @@ This is an extension of the accelerated mode. The kinetic mode uses a quadratic
Tips:
* The smoothness of the cursor movement depends on the `MOUSEKEY_INTERVAL` setting. The shorter the interval is set the smoother the movement will be. Setting the value too low makes the cursor unresponsive. Lower settings are possible if the micro processor is fast enough. For example: At an interval of `8` milliseconds, `125` movements per second will be initiated. With a base speed of `1000` each movement will move the cursor by `8` pixels.
-* Mouse wheel movements are implemented differently from cursor movements. While it's okay for the cursor to move multiple pixels at once for the mouse wheel this would lead to jerky movements. Instead, the mouse wheel operates at step size `1`. Setting mouse wheel speed is done by adjusting the number of wheel movements per second.
+* Mouse wheel movements are implemented differently from cursor movements. While it's okay for the cursor to move multiple pixels at once for the mouse wheel this would lead to jerky movements. Instead, the mouse wheel operates at step size `2`. Setting mouse wheel speed is done by adjusting the number of wheel movements per second.
### Constant mode
@@ -170,6 +171,37 @@ To use combined speed mode, you must at least define `MK_COMBINED` in your keyma
#define MK_COMBINED
```
+### Inertia mode
+
+This mode provides smooth motion, like sliding on ice. The cursor accelerates
+along a quadratic curve while a key is held, then glides to a stop after the
+key is released. Vertical and horizontal movements are tracked independently,
+so the cursor can move in many directions and make curves.
+
+Cannot be used at the same time as Kinetic mode, Constant mode, or Combined mode.
+
+Recommended settings in your keymap’s `config.h` file:
+
+|Define |Default |Description |
+|----------------------------|---------|-----------------------------------------------------------|
+|`MOUSEKEY_INERTIA` |undefined|Enable Inertia mode |
+|`MOUSEKEY_DELAY` |150 |Delay between pressing a movement key and cursor movement |
+|`MOUSEKEY_INTERVAL` |16 |Time between cursor movements in milliseconds (16 = 60fps) |
+|`MOUSEKEY_MAX_SPEED` |32 |Maximum cursor speed at which acceleration stops |
+|`MOUSEKEY_TIME_TO_MAX` |32 |Number of frames until maximum cursor speed is reached |
+|`MOUSEKEY_FRICTION` |24 |How quickly the cursor stops after releasing a key |
+|`MOUSEKEY_MOVE_DELTA` |1 |How much to move on first frame (1 strongly recommended) |
+
+Tips:
+
+* Set `MOUSEKEY_DELAY` to roughly the same value as your host computer's key repeat delay, in ms. Recommended values are 100 to 300.
+* Set `MOUSEKEY_INTERVAL` to a value of 1000 / your monitor's FPS. For 60 FPS, 1000/60 = 16.
+* Set `MOUSEKEY_MAX_SPEED` based on your screen resolution and refresh rate, like Width / FPS. For example, 1920 pixels / 60 FPS = 32 pixels per frame.
+* Set `MOUSEKEY_TIME_TO_MAX` to a value of approximately FPS / 2, to make it reach full speed in half a second (or so).
+* Set `MOUSEKEY_FRICTION` to something between 1 and 255. Lower makes the cursor glide longer. Values from 8 to 40 are the most effective.
+* Keep `MOUSEKEY_MOVE_DELTA` at 1. This allows precise movements before the gliding effect starts.
+* Mouse wheel options are the same as the default accelerated mode, and do not use inertia.
+
## Use with PS/2 Mouse and Pointing Device
Mouse keys button state is shared with [PS/2 mouse](feature_ps2_mouse.md) and [pointing device](feature_pointing_device.md) so mouse keys button presses can be used for clicks and drags.
diff --git a/docs/feature_oled_driver.md b/docs/feature_oled_driver.md
index 0d04f007f8bc..dea9cb807495 100644
--- a/docs/feature_oled_driver.md
+++ b/docs/feature_oled_driver.md
@@ -14,8 +14,6 @@ Tested combinations:
Hardware configurations using Arm-based microcontrollers or different sizes of OLED modules may be compatible, but are untested.
-!> Warning: This OLED driver currently uses the new i2c_master driver from Split Common code. If your split keyboard uses I2C to communicate between sides, this driver could cause an address conflict (serial is fine). Please contact your keyboard vendor and ask them to migrate to the latest Split Common code to fix this. In addition, the display timeout system to reduce OLED burn-in also uses Split Common to detect keypresses, so you will need to implement custom timeout logic for non-Split Common keyboards.
-
## Usage
To enable the OLED feature, there are two steps. First, when compiling your keyboard, you'll need to add the following to your `rules.mk`:
@@ -82,6 +80,11 @@ static void render_logo(void) {
oled_write_P(qmk_logo, false);
}
+
+bool oled_task_user(void) {
+ render_logo();
+ return false;
+}
```
?> The default font file is located at `drivers/oled/glcdfont.c` and its location can be overwritten with the `OLED_FONT_H` configuration option. Font file content can be edited with external tools such as [Helix Font Editor](https://helixfonteditor.netlify.app/) and [Logo Editor](https://joric.github.io/qle/).
@@ -261,12 +264,12 @@ void oled_render(void);
void oled_set_cursor(uint8_t col, uint8_t line);
// Advances the cursor to the next page, writing ' ' if true
-// Wraps to the begining when out of bounds
+// Wraps to the beginning when out of bounds
void oled_advance_page(bool clearPageRemainder);
// Moves the cursor forward 1 character length
// Advance page if there is not enough room for the next character
-// Wraps to the begining when out of bounds
+// Wraps to the beginning when out of bounds
void oled_advance_char(void);
// Writes a single character to the buffer at current cursor position
diff --git a/docs/feature_pointing_device.md b/docs/feature_pointing_device.md
index 8c51865558e2..be984dd5a58c 100644
--- a/docs/feature_pointing_device.md
+++ b/docs/feature_pointing_device.md
@@ -10,7 +10,7 @@ POINTING_DEVICE_ENABLE = yes
## Sensor Drivers
-There are a number of sensors that are supported by default. Note that only one sensor can be enabled by `POINTING_DEVICE_DRIVER` at a time. If you need to enable more than one sensor, then you need to implement it manually.
+There are a number of sensors that are supported by default. Note that only one sensor can be enabled by `POINTING_DEVICE_DRIVER` at a time. If you need to enable more than one sensor, then you need to implement it manually, using the `custom` driver.
### ADNS 5050 Sensor
@@ -22,11 +22,13 @@ POINTING_DEVICE_DRIVER = adns5050
The ADNS 5050 sensor uses a serial type protocol for communication, and requires an additional light source.
-| Setting | Description |
-|--------------------|---------------------------------------------------------------------|
-|`ADNS5050_SCLK_PIN` | (Required) The pin connected to the clock pin of the sensor. |
-|`ADNS5050_SDIO_PIN` | (Required) The pin connected to the data pin of the sensor. |
-|`ADNS5050_CS_PIN` | (Required) The pin connected to the cable select pin of the sensor. |
+| Setting | Description | Default |
+| ------------------- | ------------------------------------------------------------------- | -------------------------- |
+| `ADNS5050_SCLK_PIN` | (Required) The pin connected to the clock pin of the sensor. | `POINTING_DEVICE_SCLK_PIN` |
+| `ADNS5050_SDIO_PIN` | (Required) The pin connected to the data pin of the sensor. | `POINTING_DEVICE_SDIO_PIN` |
+| `ADNS5050_CS_PIN` | (Required) The pin connected to the cable select pin of the sensor. | `POINTING_DEVICE_CS_PIN` |
+
+
The CPI range is 125-1375, in increments of 125. Defaults to 500 CPI.
@@ -40,13 +42,13 @@ POINTING_DEVICE_DRIVER = adns9800
The ADNS 9800 is an SPI driven optical sensor, that uses laser output for surface tracking.
-| Setting | Description | Default |
-|--------------------------------|------------------------------------------------------------------------|---------------|
-|`ADNS9800_CLOCK_SPEED` | (Optional) Sets the clock speed that the sensor runs at. | `2000000` |
-|`ADNS9800_SPI_LSBFIRST` | (Optional) Sets the Least/Most Significant Byte First setting for SPI. | `false` |
-|`ADNS9800_SPI_MODE` | (Optional) Sets the SPI Mode for the sensor. | `3` |
-|`ADNS9800_SPI_DIVISOR` | (Optional) Sets the SPI Divisor used for SPI communication. | _varies_ |
-|`ADNS9800_CS_PIN` | (Required) Sets the Cable Select pin connected to the sensor. | _not defined_ |
+| Setting | Description | Default |
+| ----------------------- | ---------------------------------------------------------------------- | ------------------------ |
+| `ADNS9800_CLOCK_SPEED` | (Optional) Sets the clock speed that the sensor runs at. | `2000000` |
+| `ADNS9800_SPI_LSBFIRST` | (Optional) Sets the Least/Most Significant Byte First setting for SPI. | `false` |
+| `ADNS9800_SPI_MODE` | (Optional) Sets the SPI Mode for the sensor. | `3` |
+| `ADNS9800_SPI_DIVISOR` | (Optional) Sets the SPI Divisor used for SPI communication. | _varies_ |
+| `ADNS9800_CS_PIN` | (Required) Sets the Cable Select pin connected to the sensor. | `POINTING_DEVICE_CS_PIN` |
The CPI range is 800-8200, in increments of 200. Defaults to 1800 CPI.
@@ -61,17 +63,16 @@ POINTING_DEVICE_DRIVER = analog_joystick
The Analog Joystick is an analog (ADC) driven sensor. There are a variety of joysticks that you can use for this.
-| Setting | Description | Default |
-|----------------------------------|----------------------------------------------------------------------------|---------------|
-|`ANALOG_JOYSTICK_X_AXIS_PIN` | (Required) The pin used for the vertical/X axis. | _not defined_ |
-|`ANALOG_JOYSTICK_Y_AXIS_PIN` | (Required) The pin used for the horizontal/Y axis. | _not defined_ |
-|`ANALOG_JOYSTICK_AXIS_MIN` | (Optional) Sets the lower range to be considered movement. | `0` |
-|`ANALOG_JOYSTICK_AXIS_MAX` | (Optional) Sets the upper range to be considered movement. | `1023` |
-|`ANALOG_JOYSTICK_SPEED_REGULATOR` | (Optional) The divisor used to slow down movement. (lower makes it faster) | `20` |
-|`ANALOG_JOYSTICK_READ_INTERVAL` | (Optional) The interval in milliseconds between reads. | `10` |
-|`ANALOG_JOYSTICK_SPEED_MAX` | (Optional) The maximum value used for motion. | `2` |
-|`ANALOG_JOYSTICK_CLICK_PIN` | (Optional) The pin wired up to the press switch of the analog stick. | _not defined_ |
-
+| Setting | Description | Default |
+| --------------------------------- | -------------------------------------------------------------------------- | ------------- |
+| `ANALOG_JOYSTICK_X_AXIS_PIN` | (Required) The pin used for the vertical/X axis. | _not defined_ |
+| `ANALOG_JOYSTICK_Y_AXIS_PIN` | (Required) The pin used for the horizontal/Y axis. | _not defined_ |
+| `ANALOG_JOYSTICK_AXIS_MIN` | (Optional) Sets the lower range to be considered movement. | `0` |
+| `ANALOG_JOYSTICK_AXIS_MAX` | (Optional) Sets the upper range to be considered movement. | `1023` |
+| `ANALOG_JOYSTICK_SPEED_REGULATOR` | (Optional) The divisor used to slow down movement. (lower makes it faster) | `20` |
+| `ANALOG_JOYSTICK_READ_INTERVAL` | (Optional) The interval in milliseconds between reads. | `10` |
+| `ANALOG_JOYSTICK_SPEED_MAX` | (Optional) The maximum value used for motion. | `2` |
+| `ANALOG_JOYSTICK_CLICK_PIN` | (Optional) The pin wired up to the press switch of the analog stick. | _not defined_ |
### Cirque Trackpad
@@ -90,29 +91,93 @@ POINTING_DEVICE_DRIVER = cirque_pinnacle_spi
This supports the Cirque Pinnacle 1CA027 Touch Controller, which is used in the TM040040, TM035035 and the TM023023 trackpads. These are I2C or SPI compatible, and both configurations are supported.
-| Setting | Description | Default |
-|---------------------------------|---------------------------------------------------------------------------------|-----------------------|
-|`CIRQUE_PINNACLE_X_LOWER` | (Optional) The minimum reachable X value on the sensor. | `127` |
-|`CIRQUE_PINNACLE_X_UPPER` | (Optional) The maximum reachable X value on the sensor. | `1919` |
-|`CIRQUE_PINNACLE_Y_LOWER` | (Optional) The minimum reachable Y value on the sensor. | `63` |
-|`CIRQUE_PINNACLE_Y_UPPER` | (Optional) The maximum reachable Y value on the sensor. | `1471` |
-|`CIRQUE_PINNACLE_TAPPING_TERM` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` |
-|`CIRQUE_PINNACLE_TOUCH_DEBOUNCE` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` |
-
-| I2C Setting | Description | Default |
-|--------------------------|---------------------------------------------------------------------------------|---------|
-|`CIRQUE_PINNACLE_ADDR` | (Required) Sets the I2C Address for the Cirque Trackpad | `0x2A` |
-|`CIRQUE_PINNACLE_TIMEOUT` | (Optional) The timeout for i2c communication with the trackpad in milliseconds. | `20` |
-
-| SPI Setting | Description | Default |
-|-------------------------------|------------------------------------------------------------------------|---------------|
-|`CIRQUE_PINNACLE_CLOCK_SPEED` | (Optional) Sets the clock speed that the sensor runs at. | `1000000` |
-|`CIRQUE_PINNACLE_SPI_LSBFIRST` | (Optional) Sets the Least/Most Significant Byte First setting for SPI. | `false` |
-|`CIRQUE_PINNACLE_SPI_MODE` | (Optional) Sets the SPI Mode for the sensor. | `1` |
-|`CIRQUE_PINNACLE_SPI_DIVISOR` | (Optional) Sets the SPI Divisor used for SPI communication. | _varies_ |
-|`CIRQUE_PINNACLE_SPI_CS_PIN` | (Required) Sets the Cable Select pin connected to the sensor. | _not defined_ |
-
-Default Scaling/CPI is 1024.
+#### Common settings
+
+| Setting | Description | Default |
+| -------------------------------- | ---------------------------------------------------------- | ------------------------------------------- |
+| `CIRQUE_PINNACLE_DIAMETER_MM` | (Optional) Diameter of the trackpad sensor in millimeters. | `40` |
+| `CIRQUE_PINNACLE_ATTENUATION` | (Optional) Sets the attenuation of the sensor data. | `EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_4X` |
+| `CIRQUE_PINNACLE_CURVED_OVERLAY` | (Optional) Applies settings tuned for curved overlay. | _not defined_ |
+| `CIRQUE_PINNACLE_POSITION_MODE` | (Optional) Mode of operation. | _not defined_ |
+
+**`CIRQUE_PINNACLE_ATTENUATION`** is a measure of how much data is suppressed in regards to sensitivity. The higher the attenuation, the less sensitive the touchpad will be.
+
+Default attenuation is set to 4X, although if you are using a thicker overlay (such as the curved overlay) you will want a lower attenuation such as 2X. The possible values are:
+* `EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_4X`: Least sensitive
+* `EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_3X`
+* `EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_2X`
+* `EXTREG__TRACK_ADCCONFIG__ADC_ATTENUATE_1X`: Most sensitive
+
+**`CIRQUE_PINNACLE_POSITION_MODE`** can be `CIRQUE_PINNACLE_ABSOLUTE_MODE` or `CIRQUE_PINNACLE_RELATIVE_MODE`. Modes differ in supported features/gestures.
+
+* `CIRQUE_PINNACLE_ABSOLUTE_MODE`: Reports absolute x, y, z (touch pressure) coordinates and up to 5 hw buttons connected to the trackpad
+* `CIRQUE_PINNACLE_RELATIVE_MODE`: Reports x/y deltas, scroll and up to 3 buttons (2 of them can be from taps, see gestures) connected to trackpad. Supports taps on secondary side of split. Saves about 2k of flash compared to absolute mode with all features.
+
+| I2C Setting | Description | Default |
+| ------------------------- | ------------------------------------------------------------------------------- | ------- |
+| `CIRQUE_PINNACLE_ADDR` | (Required) Sets the I2C Address for the Cirque Trackpad | `0x2A` |
+| `CIRQUE_PINNACLE_TIMEOUT` | (Optional) The timeout for i2c communication with the trackpad in milliseconds. | `20` |
+
+| SPI Setting | Description | Default |
+| ------------------------------ | ---------------------------------------------------------------------- | ------------------------ |
+| `CIRQUE_PINNACLE_CLOCK_SPEED` | (Optional) Sets the clock speed that the sensor runs at. | `1000000` |
+| `CIRQUE_PINNACLE_SPI_LSBFIRST` | (Optional) Sets the Least/Most Significant Byte First setting for SPI. | `false` |
+| `CIRQUE_PINNACLE_SPI_MODE` | (Optional) Sets the SPI Mode for the sensor. | `1` |
+| `CIRQUE_PINNACLE_SPI_DIVISOR` | (Optional) Sets the SPI Divisor used for SPI communication. | _varies_ |
+| `CIRQUE_PINNACLE_SPI_CS_PIN` | (Required) Sets the Cable Select pin connected to the sensor. | `POINTING_DEVICE_CS_PIN` |
+
+Default Scaling is 1024. Actual CPI depends on trackpad diameter.
+
+Also see the `POINTING_DEVICE_TASK_THROTTLE_MS`, which defaults to 10ms when using Cirque Pinnacle, which matches the internal update rate of the position registers (in standard configuration). Advanced configuration for pen/stylus usage might require lower values.
+
+#### Absolute mode settings
+
+| Setting | Description | Default |
+| -------------------------------- | ---------------------------------------------------------- | ------------------ |
+| `CIRQUE_PINNACLE_X_LOWER` | (Optional) The minimum reachable X value on the sensor. | `127` |
+| `CIRQUE_PINNACLE_X_UPPER` | (Optional) The maximum reachable X value on the sensor. | `1919` |
+| `CIRQUE_PINNACLE_Y_LOWER` | (Optional) The minimum reachable Y value on the sensor. | `63` |
+| `CIRQUE_PINNACLE_Y_UPPER` | (Optional) The maximum reachable Y value on the sensor. | `1471` |
+
+#### Absolute mode gestures
+
+| Gesture Setting | Description | Default |
+| ---------------------------------------------- | ------------------------------------------------------------------------------ | -------------------- |
+| `CIRQUE_PINNACLE_TAP_ENABLE` | (Optional) Enable tap to click. This currently only works on the master side. | _not defined_ |
+| `CIRQUE_PINNACLE_TAPPING_TERM` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` |
+| `CIRQUE_PINNACLE_TOUCH_DEBOUNCE` | (Optional) Length of time that a touch can be to be considered a tap. | `TAPPING_TERM`/`200` |
+
+`POINTING_DEVICE_GESTURES_SCROLL_ENABLE` in this mode enables circular scroll. Touch originating in outer ring can trigger scroll by moving along the perimeter. Near side triggers vertical scroll and far side triggers horizontal scroll.
+
+Additionally, `POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE` is supported in this mode.
+
+#### Relative mode gestures
+
+| Gesture Setting | Description | Default |
+| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
+| `CIRQUE_PINNACLE_TAP_ENABLE` | (Optional) Enable tap to "left click". Works on both sides of a split keyboard. | _not defined_ |
+| `CIRQUE_PINNACLE_SECONDARY_TAP_ENABLE` | (Optional) Tap in upper right corner (half of the finger needs to be outside of the trackpad) of the trackpad will result in "right click". `CIRQUE_PINNACLE_TAP_ENABLE` must be enabled. | _not defined_ |
+
+Tapping term and debounce are not configurable in this mode since it's handled by trackpad internally.
+
+`POINTING_DEVICE_GESTURES_SCROLL_ENABLE` in this mode enables side scroll. Touch originating on the right side can trigger vertical scroll (IntelliSense trackpad style).
+
+### PAW 3204 Sensor
+
+To use the paw 3204 sensor, add this to your `rules.mk`
+
+```make
+POINTING_DEVICE_DRIVER = paw3204
+```
+
+The paw 3204 sensor uses a serial type protocol for communication, and requires an additional light source.
+
+| Setting | Description | Default |
+| ------------------ |--------------------------------------------------------------- | -------------------------- |
+| `PAW3204_SCLK_PIN` | (Required) The pin connected to the clock pin of the sensor. | `POINTING_DEVICE_SCLK_PIN` |
+| `PAW3204_SDIO_PIN` | (Required) The pin connected to the data pin of the sensor. | `POINTING_DEVICE_SDIO_PIN` |
+
+The CPI range is 400-1600, with supported values of (400, 500, 600, 800, 1000, 1200 and 1600). Defaults to 1000 CPI.
### Pimoroni Trackball
@@ -124,60 +189,75 @@ POINTING_DEVICE_DRIVER = pimoroni_trackball
The Pimoroni Trackball module is a I2C based breakout board with an RGB enable trackball.
-| Setting | Description | Default |
-|-------------------------------------|------------------------------------------------------------------------------------|---------|
-|`PIMORONI_TRACKBALL_ADDRESS` | (Required) Sets the I2C Address for the Pimoroni Trackball. | `0x0A` |
-|`PIMORONI_TRACKBALL_TIMEOUT` | (Optional) The timeout for i2c communication with the trackball in milliseconds. | `100` |
-|`PIMORONI_TRACKBALL_SCALE` | (Optional) The multiplier used to generate reports from the sensor. | `5` |
-|`PIMORONI_TRACKBALL_DEBOUNCE_CYCLES` | (Optional) The number of scan cycles used for debouncing on the ball press. | `20` |
-|`PIMORONI_TRACKBALL_ERROR_COUNT` | (Optional) Specifies the number of read/write errors until the sensor is disabled. | `10` |
+| Setting | Description | Default |
+| ------------------------------------ | ---------------------------------------------------------------------------------- | ------- |
+| `PIMORONI_TRACKBALL_ADDRESS` | (Required) Sets the I2C Address for the Pimoroni Trackball. | `0x0A` |
+| `PIMORONI_TRACKBALL_TIMEOUT` | (Optional) The timeout for i2c communication with the trackball in milliseconds. | `100` |
+| `PIMORONI_TRACKBALL_SCALE` | (Optional) The multiplier used to generate reports from the sensor. | `5` |
+| `PIMORONI_TRACKBALL_DEBOUNCE_CYCLES` | (Optional) The number of scan cycles used for debouncing on the ball press. | `20` |
+| `PIMORONI_TRACKBALL_ERROR_COUNT` | (Optional) Specifies the number of read/write errors until the sensor is disabled. | `10` |
-### PMW 3360 Sensor
+### PMW 3360 and PMW 3389 Sensor
-To use the PMW 3360 sensor, add this to your `rules.mk`
+This drivers supports both the PMW 3360 and PMW 3389 sensor as well as multiple sensors of the same type _per_ controller, so 2 can be attached at the same side for split keyboards (or unsplit keyboards).
+
+To use the **PMW 3360** sensor, add this to your `rules.mk`
```make
POINTING_DEVICE_DRIVER = pmw3360
```
-The PMW 3360 is an SPI driven optical sensor, that uses a built in IR LED for surface tracking.
-
-| Setting | Description | Default |
-|-----------------------------|--------------------------------------------------------------------------------------------|---------------|
-|`PMW3360_CS_PIN` | (Required) Sets the Cable Select pin connected to the sensor. | _not defined_ |
-|`PMW3360_CLOCK_SPEED` | (Optional) Sets the clock speed that the sensor runs at. | `2000000` |
-|`PMW3360_SPI_LSBFIRST` | (Optional) Sets the Least/Most Significant Byte First setting for SPI. | `false` |
-|`PMW3360_SPI_MODE` | (Optional) Sets the SPI Mode for the sensor. | `3` |
-|`PMW3360_SPI_DIVISOR` | (Optional) Sets the SPI Divisor used for SPI communication. | _varies_ |
-|`PMW3360_LIFTOFF_DISTANCE` | (Optional) Sets the lift off distance at run time | `0x02` |
-|`ROTATIONAL_TRANSFORM_ANGLE` | (Optional) Allows for the sensor data to be rotated +/- 127 degrees directly in the sensor.| `0` |
-|`PMW3360_FIRMWARE_UPLOAD_FAST` | (Optional) Skips the 15us wait between firmware blocks. | _not defined_ |
-
The CPI range is 100-12000, in increments of 100. Defaults to 1600 CPI.
-### PMW 3389 Sensor
-
-To use the PMW 3389 sensor, add this to your `rules.mk`
+To use the **PMW 3389** sensor, add this to your `rules.mk`
```make
POINTING_DEVICE_DRIVER = pmw3389
```
-The PMW 3389 is an SPI driven optical sensor, that uses a built in IR LED for surface tracking.
+The CPI range is 50-16000, in increments of 50. Defaults to 2000 CPI.
-| Setting | Description | Default |
-|---------------------------------|--------------------------------------------------------------------------------------------|---------------|
-|`PMW3389_CS_PIN` | (Required) Sets the Cable Select pin connected to the sensor. | _not defined_ |
-|`PMW3389_CLOCK_SPEED` | (Optional) Sets the clock speed that the sensor runs at. | `2000000` |
-|`PMW3389_SPI_LSBFIRST` | (Optional) Sets the Least/Most Significant Byte First setting for SPI. | `false` |
-|`PMW3389_SPI_MODE` | (Optional) Sets the SPI Mode for the sensor. | `3` |
-|`PMW3389_SPI_DIVISOR` | (Optional) Sets the SPI Divisor used for SPI communication. | _varies_ |
-|`PMW3389_LIFTOFF_DISTANCE` | (Optional) Sets the lift off distance at run time | `0x02` |
-|`ROTATIONAL_TRANSFORM_ANGLE` | (Optional) Allows for the sensor data to be rotated +/- 30 degrees directly in the sensor. | `0` |
-|`PMW3389_FIRMWARE_UPLOAD_FAST` | (Optional) Skips the 15us wait between firmware blocks. | _not defined_ |
+Both PMW 3360 and PMW 3389 are SPI driven optical sensors, that use a built in IR LED for surface tracking.
-The CPI range is 50-16000, in increments of 50. Defaults to 2000 CPI.
+| Setting | Description | Default |
+| ---------------------------- | ------------------------------------------------------------------------------------------- | ------------------------ |
+| `PMW33XX_CS_PIN` | (Required) Sets the Cable Select pin connected to the sensor. | `POINTING_DEVICE_CS_PIN` |
+| `PMW33XX_CS_PINS` | (Alternative) Sets the Cable Select pins connected to multiple sensors. | _not defined_ |
+| `PMW33XX_CPI` | (Optional) Sets counts per inch sensitivity of the sensor. | _varies_ |
+| `PMW33XX_CLOCK_SPEED` | (Optional) Sets the clock speed that the sensor runs at. | `2000000` |
+| `PMW33XX_SPI_DIVISOR` | (Optional) Sets the SPI Divisor used for SPI communication. | _varies_ |
+| `PMW33XX_LIFTOFF_DISTANCE` | (Optional) Sets the lift off distance at run time | `0x02` |
+| `ROTATIONAL_TRANSFORM_ANGLE` | (Optional) Allows for the sensor data to be rotated +/- 127 degrees directly in the sensor. | `0` |
+
+To use multiple sensors, instead of setting `PMW33XX_CS_PIN` you need to set `PMW33XX_CS_PINS` and also handle and merge the read from this sensor in user code.
+Note that different (per sensor) values of CPI, speed liftoff, rotational angle or flipping of X/Y is not currently supported.
+
+```c
+// in config.h:
+#define PMW33XX_CS_PINS { B5, B6 }
+// in keyboard.c:
+#ifdef POINTING_DEVICE_ENABLE
+void pointing_device_init_kb(void) {
+ pmw33xx_init(1); // index 1 is the second device.
+ pmw33xx_set_cpi(0, 800); // applies to first sensor
+ pmw33xx_set_cpi(1, 800); // applies to second sensor
+ pointing_device_init_user();
+}
+// Contains report from sensor #0 already, need to merge in from sensor #1
+report_mouse_t pointing_device_task_kb(report_mouse_t mouse_report) {
+ pmw33xx_report_t report = pmw33xx_read_burst(1);
+ if (!report.motion.b.is_lifted && report.motion.b.is_motion) {
+// From quantum/pointing_device_drivers.c
+#define constrain_hid(amt) ((amt) < -127 ? -127 : ((amt) > 127 ? 127 : (amt)))
+ mouse_report.x = constrain_hid(mouse_report.x + report.delta_x);
+ mouse_report.y = constrain_hid(mouse_report.y + report.delta_y);
+ }
+ return pointing_device_task_user(mouse_report);
+}
+#endif
+
+```
### Custom Driver
@@ -200,41 +280,51 @@ void pointing_device_driver_set_cpi(uint16_t cpi) {}
## Common Configuration
-| Setting | Description | Default |
-|----------------------------------|-----------------------------------------------------------------------|-------------------|
-|`POINTING_DEVICE_ROTATION_90` | (Optional) Rotates the X and Y data by 90 degrees. | _not defined_ |
-|`POINTING_DEVICE_ROTATION_180` | (Optional) Rotates the X and Y data by 180 degrees. | _not defined_ |
-|`POINTING_DEVICE_ROTATION_270` | (Optional) Rotates the X and Y data by 270 degrees. | _not defined_ |
-|`POINTING_DEVICE_INVERT_X` | (Optional) Inverts the X axis report. | _not defined_ |
-|`POINTING_DEVICE_INVERT_Y` | (Optional) Inverts the Y axis report. | _not defined_ |
-|`POINTING_DEVICE_MOTION_PIN` | (Optional) If supported, will only read from sensor if pin is active. | _not defined_ |
-|`POINTING_DEVICE_TASK_THROTTLE_MS` | (Optional) Limits the frequency that the sensor is polled for motion. | _not defined_ |
+| Setting | Description | Default |
+| ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------- |
+| `MOUSE_EXTENDED_REPORT` | (Optional) Enables support for extended mouse reports. (-32767 to 32767, instead of just -127 to 127). | _not defined_ |
+| `POINTING_DEVICE_ROTATION_90` | (Optional) Rotates the X and Y data by 90 degrees. | _not defined_ |
+| `POINTING_DEVICE_ROTATION_180` | (Optional) Rotates the X and Y data by 180 degrees. | _not defined_ |
+| `POINTING_DEVICE_ROTATION_270` | (Optional) Rotates the X and Y data by 270 degrees. | _not defined_ |
+| `POINTING_DEVICE_INVERT_X` | (Optional) Inverts the X axis report. | _not defined_ |
+| `POINTING_DEVICE_INVERT_Y` | (Optional) Inverts the Y axis report. | _not defined_ |
+| `POINTING_DEVICE_MOTION_PIN` | (Optional) If supported, will only read from sensor if pin is active. | _not defined_ |
+| `POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW` | (Optional) If defined then the motion pin is active-low. | _varies_ |
+| `POINTING_DEVICE_TASK_THROTTLE_MS` | (Optional) Limits the frequency that the sensor is polled for motion. | _not defined_ |
+| `POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE` | (Optional) Enable inertial cursor. Cursor continues moving after a flick gesture and slows down by kinetic friction. | _not defined_ |
+| `POINTING_DEVICE_GESTURES_SCROLL_ENABLE` | (Optional) Enable scroll gesture. The gesture that activates the scroll is device dependent. | _not defined_ |
+| `POINTING_DEVICE_CS_PIN` | (Optional) Provides a default CS pin, useful for supporting multiple sensor configs. | _not defined_ |
+| `POINTING_DEVICE_SDIO_PIN` | (Optional) Provides a default SDIO pin, useful for supporting multiple sensor configs. | _not defined_ |
+| `POINTING_DEVICE_SCLK_PIN` | (Optional) Provides a default SCLK pin, useful for supporting multiple sensor configs. | _not defined_ |
!> When using `SPLIT_POINTING_ENABLE` the `POINTING_DEVICE_MOTION_PIN` functionality is not supported and `POINTING_DEVICE_TASK_THROTTLE_MS` will default to `1`. Increasing this value will increase transport performance at the cost of possible mouse responsiveness.
+The `POINTING_DEVICE_CS_PIN`, `POINTING_DEVICE_SDIO_PIN`, and `POINTING_DEVICE_SCLK_PIN` provide a convenient way to define a single pin that can be used for an interchangeable sensor config. This allows you to have a single config, without defining each device. Each sensor allows for this to be overridden with their own defines.
+
+!> Any pointing device with a lift/contact status can integrate inertial cursor feature into its driver, controlled by `POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE`. e.g. PMW3360 can use Lift_Stat from Motion register. Note that `POINTING_DEVICE_MOTION_PIN` cannot be used with this feature; continuous polling of `get_report()` is needed to generate glide reports.
## Split Keyboard Configuration
The following configuration options are only available when using `SPLIT_POINTING_ENABLE` see [data sync options](feature_split_keyboard.md?id=data-sync-options). The rotation and invert `*_RIGHT` options are only used with `POINTING_DEVICE_COMBINED`. If using `POINTING_DEVICE_LEFT` or `POINTING_DEVICE_RIGHT` use the common configuration above to configure your pointing device.
-| Setting | Description | Default |
-|----------------------------------------|-----------------------------------------------------------------------|---------------|
-|`POINTING_DEVICE_LEFT` | Pointing device on the left side (Required - pick one only) | _not defined_ |
-|`POINTING_DEVICE_RIGHT` | Pointing device on the right side (Required - pick one only) | _not defined_ |
-|`POINTING_DEVICE_COMBINED` | Pointing device on both sides (Required - pick one only) | _not defined_ |
-|`POINTING_DEVICE_ROTATION_90_RIGHT` | (Optional) Rotates the X and Y data by 90 degrees. | _not defined_ |
-|`POINTING_DEVICE_ROTATION_180_RIGHT` | (Optional) Rotates the X and Y data by 180 degrees. | _not defined_ |
-|`POINTING_DEVICE_ROTATION_270_RIGHT` | (Optional) Rotates the X and Y data by 270 degrees. | _not defined_ |
-|`POINTING_DEVICE_INVERT_X_RIGHT` | (Optional) Inverts the X axis report. | _not defined_ |
-|`POINTING_DEVICE_INVERT_Y_RIGHT` | (Optional) Inverts the Y axis report. | _not defined_ |
+| Setting | Description | Default |
+| ------------------------------------ | ----------------------------------------------------------------------------------------------------- | ------------- |
+| `POINTING_DEVICE_LEFT` | Pointing device on the left side (Required - pick one only) | _not defined_ |
+| `POINTING_DEVICE_RIGHT` | Pointing device on the right side (Required - pick one only) | _not defined_ |
+| `POINTING_DEVICE_COMBINED` | Pointing device on both sides (Required - pick one only) | _not defined_ |
+| `POINTING_DEVICE_ROTATION_90_RIGHT` | (Optional) Rotates the X and Y data by 90 degrees. | _not defined_ |
+| `POINTING_DEVICE_ROTATION_180_RIGHT` | (Optional) Rotates the X and Y data by 180 degrees. | _not defined_ |
+| `POINTING_DEVICE_ROTATION_270_RIGHT` | (Optional) Rotates the X and Y data by 270 degrees. | _not defined_ |
+| `POINTING_DEVICE_INVERT_X_RIGHT` | (Optional) Inverts the X axis report. | _not defined_ |
+| `POINTING_DEVICE_INVERT_Y_RIGHT` | (Optional) Inverts the Y axis report. | _not defined_ |
!> If there is a `_RIGHT` configuration option or callback, the [common configuration](feature_pointing_device.md?id=common-configuration) option will work for the left. For correct left/right detection you should setup a [handedness option](feature_split_keyboard?id=setting-handedness), `EE_HANDS` is usually a good option for an existing board that doesn't do handedness by hardware.
## Callbacks and Functions
-| Function | Description |
-|-----------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|
+| Function | Description |
+| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `pointing_device_init_kb(void)` | Callback to allow for keyboard level initialization. Useful for additional hardware sensors. |
| `pointing_device_init_user(void)` | Callback to allow for user level initialization. Useful for additional hardware sensors. |
| `pointing_device_task_kb(mouse_report)` | Callback that sends sensor data, so keyboard code can intercept and modify the data. Returns a mouse report. |
@@ -242,11 +332,11 @@ The following configuration options are only available when using `SPLIT_POINTIN
| `pointing_device_handle_buttons(buttons, pressed, button)` | Callback to handle hardware button presses. Returns a `uint8_t`. |
| `pointing_device_get_cpi(void)` | Gets the current CPI/DPI setting from the sensor, if supported. |
| `pointing_device_set_cpi(uint16_t)` | Sets the CPI/DPI, if supported. |
-| `pointing_device_get_report(void)` | Returns the current mouse report (as a `mouse_report_t` data structure). |
-| `pointing_device_set_report(mouse_report)` | Sets the mouse report to the assigned `mouse_report_t` data structured passed to the function. |
-| `pointing_device_send(void)` | Sends the current mouse report to the host system. Function can be replaced. |
-| `has_mouse_report_changed(new_report, old_report)` | Compares the old and new `mouse_report_t` data and returns true only if it has changed. |
-| `pointing_device_adjust_by_defines(mouse_report)` | Applies rotations and invert configurations to a raw mouse report. |
+| `pointing_device_get_report(void)` | Returns the current mouse report (as a `report_mouse_t` data structure). |
+| `pointing_device_set_report(mouse_report)` | Sets the mouse report to the assigned `report_mouse_t` data structured passed to the function. |
+| `pointing_device_send(void)` | Sends the current mouse report to the host system. Function can be replaced. |
+| `has_mouse_report_changed(new_report, old_report)` | Compares the old and new `report_mouse_t` data and returns true only if it has changed. |
+| `pointing_device_adjust_by_defines(mouse_report)` | Applies rotations and invert configurations to a raw mouse report. |
## Split Keyboard Callbacks and Functions
@@ -254,10 +344,10 @@ The following configuration options are only available when using `SPLIT_POINTIN
The combined functions below are only available when using `SPLIT_POINTING_ENABLE` and `POINTING_DEVICE_COMBINED`. The 2 callbacks `pointing_device_task_combined_*` replace the single sided equivalents above. See the [combined pointing devices example](feature_pointing_device.md?id=combined-pointing-devices)
| Function | Description |
-|-----------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|
-| `pointing_device_set_shared_report(mouse_report)` | Sets the shared mouse report to the assigned `mouse_report_t` data structured passed to the function. |
-| `pointing_device_set_cpi_on_side(bool, uint16_t)` | Sets the CPI/DPI of one side, if supported. Passing `true` will set the left and `false` the right` |
-| `pointing_device_combine_reports(left_report, right_report)` | Returns a combined mouse_report of left_report and right_report (as a `mouse_report_t` data structure) |
+| --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
+| `pointing_device_set_shared_report(mouse_report)` | Sets the shared mouse report to the assigned `report_mouse_t` data structured passed to the function. |
+| `pointing_device_set_cpi_on_side(bool, uint16_t)` | Sets the CPI/DPI of one side, if supported. Passing `true` will set the left and `false` the right |
+| `pointing_device_combine_reports(left_report, right_report)` | Returns a combined mouse_report of left_report and right_report (as a `report_mouse_t` data structure) |
| `pointing_device_task_combined_kb(left_report, right_report)` | Callback, so keyboard code can intercept and modify the data. Returns a combined mouse report. |
| `pointing_device_task_combined_user(left_report, right_report)` | Callback, so user code can intercept and modify. Returns a combined mouse report using `pointing_device_combine_reports` |
| `pointing_device_adjust_by_defines_right(mouse_report)` | Applies right side rotations and invert configurations to a raw mouse report. |
@@ -398,4 +488,243 @@ report_mouse_t pointing_device_task_combined_user(report_mouse_t left_report, re
return pointing_device_combine_reports(left_report, right_report);
}
```
-=======
+
+# Troubleshooting
+
+If you are having issues with pointing device drivers debug messages can be enabled that will give you insights in the inner workings. To enable these add to your keyboards `config.h` file:
+
+```c
+#define POINTING_DEVICE_DEBUG
+```
+
+?> The messages will be printed out to the `CONSOLE` output. For additional information, refer to [Debugging/Troubleshooting QMK](faq_debug.md).
+
+
+---
+# Automatic Mouse Layer :id=pointing-device-auto-mouse
+
+When using a pointing device combined with a keyboard the mouse buttons are often kept on a separate layer from the default keyboard layer, which requires pressing or holding a key to change layers before using the mouse. To make this easier and more efficient an additional pointing device feature may be enabled that will automatically activate a target layer as soon as the pointing device is active _(in motion, mouse button pressed etc.)_ and deactivate the target layer after a set time.
+
+Additionally if any key that is defined as a mouse key is pressed then the layer will be held as long as the key is pressed and the timer will be reset on key release. When a non-mouse key is pressed then the layer is deactivated early _(with some exceptions see below)_. Mod, mod tap, and one shot mod keys are ignored _(i.e. don't hold or activate layer but do not deactivate the layer either)_ when sending a modifier keycode _(e.g. hold for mod tap)_ allowing for mod keys to be used with the mouse without activating the target layer when typing.
+
+All of the standard layer keys (tap toggling, toggle, toggle on, one_shot, layer tap, layer mod) that activate the current target layer are uniquely handled to ensure they behave as expected _(see layer key table below)_. The target layer that can be changed at any point during by calling the `set_auto_mouse_layer();` function.
+
+### Behaviour of Layer keys that activate the target layer
+| Layer key as in `keymap.c` | Auto Mouse specific behaviour |
+| -------------------------- | --------------------------------------------------------------------------------------------------------------------- |
+| `MO()` | Treated as a mouse key holding the layer while pressed |
+| `LT()` | When tapped will be treated as non mouse key and mouse key when held |
+| `LM()` | Treated as a mouse key |
+| `TG()` | Will set flag preventing target layer deactivation or removal until pressed again |
+| `TO()` | Same as `TG()` |
+| `TT()` | Treated as a mouse key when `tap.count < TAPPING_TOGGLE` and as `TG` when `tap.count == TAPPING_TOGGLE` |
+| `DF()` | Skips auto mouse key processing similar to mod keys |
+| `OSL()` | Skips, but if current one shot layer is the target layer then it will prevent target layer deactivation or removal |
+
+
+## How to enable:
+
+```c
+// in config.h:
+#define POINTING_DEVICE_AUTO_MOUSE_ENABLE
+// only required if not setting mouse layer elsewhere
+#define AUTO_MOUSE_DEFAULT_LAYER
+
+// in keymap.c:
+void pointing_device_init_user(void) {
+ set_auto_mouse_layer(); // only required if AUTO_MOUSE_DEFAULT_LAYER is not set to index of
+ set_auto_mouse_enable(true); // always required before the auto mouse feature will work
+}
+```
+
+Because the auto mouse feature can be disabled/enabled during runtime and starts as disabled by default it must be enabled by calling `set_auto_mouse_enable(true);` somewhere in firmware before the feature will work.
+_Note: for setting the target layer during initialization either setting `AUTO_MOUSE_DEFAULT_LAYER` in `config.h` or calling `set_auto_mouse_layer()` can be used._
+
+
+## How to Customize:
+
+There are a few ways to control the auto mouse feature with both `config.h` options and functions for controlling it during runtime.
+
+### `config.h` Options:
+| Define | Description | Range | Units | Default |
+| ----------------------------------- | --------------------------------------------------------------------- | :------------------: | :---------: | -------------------------: |
+| `POINTING_DEVICE_AUTO_MOUSE_ENABLE` | (Required) Enables auto mouse layer feature | | _None_ | _Not defined_ |
+| `AUTO_MOUSE_DEFAULT_LAYER` | (Optional) Index of layer to use as default target layer | 0 - `LAYER_MAX` | _`uint8_t`_ | `1` |
+| `AUTO_MOUSE_TIME` | (Optional) Time layer remains active after activation | _ideally_ (250-1000) | _ms_ | `650 ms` |
+| `AUTO_MOUSE_DELAY` | (Optional) Lockout time after non-mouse key is pressed | _ideally_ (100-1000) | _ms_ | `TAPPING_TERM` or `200 ms` |
+| `AUTO_MOUSE_DEBOUNCE` | (Optional) Time delay from last activation to next update | _ideally_ (10 - 100) | _ms_ | `25 ms` |
+
+### Adding mouse keys
+
+While all default mouse keys and layer keys(for current mouse layer) are treated as mouse keys, additional Keyrecords can be added to mouse keys by adding them to the is_mouse_record_* stack.
+
+#### Callbacks for setting up additional key codes as mouse keys:
+| Callback | Description |
+| -------------------------------------------------------------------- | -------------------------------------------------- |
+| `bool is_mouse_record_kb(uint16_t keycode, keyrecord_t* record)` | keyboard level callback for adding mouse keys |
+| `bool is_mouse_record_user(uint16_t keycode, keyrecord_t* record)` | user/keymap level callback for adding mouse keys |
+
+##### To use the callback function to add mouse keys:
+
+The following code will cause the enter key and all of the arrow keys to be treated as mouse keys (hold target layer while they are pressed and reset active layer timer).
+```c
+
+// in .c:
+bool is_mouse_record_kb(uint16_t keycode, keyrecord_t* record) {
+ switch(keycode) {
+ case KC_ENT:
+ return true;
+ case KC_RIGHT ... KC_UP:
+ return true;
+ default:
+ return false;
+ }
+ return is_mouse_record_user(keycode, record);
+}
+```
+
+
+## Advanced control
+
+There are several functions that allow for more advanced interaction with the auto mouse feature allowing for greater control.
+
+### Functions to control auto mouse enable and target layer:
+| Function | Description | Aliases | Return type |
+| :--------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------- | --------------: |
+| `set_auto_mouse_enable(bool enable)` | Enable or disable auto mouse (true:enable, false:disable) | | `void`(None) |
+| `get_auto_mouse_enable(void)` | Return auto mouse enable state (true:enabled, false:disabled) | `AUTO_MOUSE_ENABLED` | `bool` |
+| `set_auto_mouse_layer(uint8_t LAYER)` | Change/set the target layer for auto mouse | | `void`(None) |
+| `get_auto_mouse_layer(void)` | Return auto mouse target layer index | `AUTO_MOUSE_TARGET_LAYER` | `uint8_t` |
+| `remove_auto_mouse_layer(layer_state_t state, bool force)` | Return `state` with target layer removed if appropriate (ignore criteria if `force`) | | `layer_state_t` |
+| `auto_mouse_layer_off(void)` | Disable target layer if appropriate will call (makes call to `layer_state_set`) | | `void`(None) |
+| `auto_mouse_toggle(void)` | Toggle on/off target toggle state (disables layer deactivation when true) | | `void`(None) |
+| `get_auto_mouse_toggle(void)` | Return value of toggling state variable | | `bool` |
+
+_NOTES:_
+ - _Due to the nature of how some functions work, the `auto_mouse_trigger_reset`, and `auto_mouse_layer_off` functions should never be called in the `layer_state_set_*` stack as this can cause indefinite loops._
+ - _It is recommended that `remove_auto_mouse_layer` is used in the `layer_state_set_*` stack of functions and `auto_mouse_layer_off` is used everywhere else_
+ - _`remove_auto_mouse_layer(state, false)` or `auto_mouse_layer_off()` should be called before any instance of `set_auto_mouse_enabled(false)` or `set_auto_mouse_layer(layer)` to ensure that the target layer will be removed appropriately before disabling auto mouse or changing target to avoid a stuck layer_
+
+### Functions for handling custom key events:
+| Function | Description | Return type |
+| :--------------------------------------------------------- | -------------------------------------------------------------------------------- | --------------: |
+| `auto_mouse_keyevent(bool pressed)` | Auto mouse mouse key event (true: key down, false: key up) | `void`(None) |
+| `auto_mouse_trigger_reset(bool pressed)` | Reset auto mouse status on key down and start delay timer (non-mouse key event) | `void`(None) |
+| `auto_mouse_toggle(void)` | Toggle on/off target toggle state (disables layer deactivation when true) | `void`(None) |
+| `get_auto_mouse_toggle(void)` | Return value of toggling state variable | `bool` |
+_NOTE: Generally it would be preferable to use the `is_mouse_record_*` functions to add any additional keys that should act as mouse keys rather than adding `auto_mouse_keyevent(record.event->pressed)` to `process_records_*`_
+
+### Advanced control examples
+
+#### Disable auto mouse on certain layers:
+
+The auto mouse feature can be disabled any time and this can be helpful if you want to disable the auto mouse feature under certain circumstances such as when particular layers are active. One issue however is the handling of the target layer, it needs to be removed appropriately **before** disabling auto mouse _(see notes under control functions above)_. The following function would disable the auto_mouse feature whenever the layers `_LAYER5` through `_LAYER7` are active as the top most layer _(ignoring target layer)_.
+
+```c
+// in keymap.c:
+layer_state_t layer_state_set_user(layer_state_t state) {
+ // checks highest layer other than target layer
+ switch(get_highest_layer(remove_auto_mouse_layer(state, true))) {
+ case _LAYER5 ... _LAYER7:
+ // remove_auto_mouse_target must be called to adjust state *before* setting enable
+ state = remove_auto_mouse_layer(state, false);
+ set_auto_mouse_enable(false);
+ break;
+ default:
+ set_auto_mouse_enable(true);
+ break;
+ }
+ // recommend that any code that makes adjustment based on auto mouse layer state would go here
+ return state;
+}
+```
+
+#### Set different target layer when a particular layer is active:
+
+The below code will change the auto mouse layer target to `_MOUSE_LAYER_2` when `_DEFAULT_LAYER_2` is highest default layer state.
+*NOTE: that `auto_mouse_layer_off` is used here instead of `remove_auto_mouse_layer` as `default_layer_state_set_*` stack is separate from the `layer_state_set_*` stack* if something similar was to be done in `layer_state_set_user `state = remove_auto_mouse_layer(state, false)` should be used instead
+*ADDITIONAL NOTE: `AUTO_MOUSE_TARGET_LAYER` is checked if already set to avoid deactivating the target layer unless needed*
+
+```c
+// in keymap.c
+layer_state_t default_layer_state_set_user(layer_state_t state) {
+ // switch on change in default layer need to check if target layer already set to avoid turning off layer needlessly
+ switch(get_highest_layer(state)) {
+ case _DEFAULT_LAYER_2:
+ if ((AUTO_MOUSE_TARGET_LAYER) == _MOUSE_LAYER_2) break;
+ auto_mouse_layer_off();
+ set_auto_mouse_layer(_MOUSE_LAYER_2);
+ break;
+
+ default:
+ if((AUTO_MOUSE_TARGET_LAYER) == _MOUSE_LAYER_1) break;
+ auto_mouse_layer_off();
+ set_auto_mouse_layer(_MOUSE_LAYER_1);
+ }
+ return state;
+}
+```
+
+### Use custom keys to control auto mouse:
+Custom key records could also be created that control the auto mouse feature.
+The code example below would create a custom key that would toggle the auto mouse feature on and off when pressed while also setting a bool that could be used to disable other code that may turn it on such as the layer code above.
+
+```c
+// in config.h:
+enum user_custom_keycodes {
+ AM_Toggle = SAFE_RANGE
+};
+
+// in keymap.c:
+// set up global bool to adjust other user code
+bool auto_mouse_tg_off = !AUTO_MOUSE_ENABLED;
+
+bool process_record_user(uint16_t keycode, keyrecord_t* record) {
+ switch (keycode) {
+ // toggle auto mouse enable key
+ case AM_Toggle:
+ if(record->event.pressed) { // key down
+ auto_mouse_layer_off(); // disable target layer if needed
+ set_auto_mouse_enabled((AUTO_MOUSE_ENABLED) ^ 1);
+ auto_mouse_tg_off = !get_auto_mouse_enabled();
+ } // do nothing on key up
+ return false; // prevent further processing of keycode
+ }
+}
+```
+
+
+## Customize Target Layer Activation
+
+Layer activation can be customized by overwriting the `auto_mouse_activation` function. This function is checked every time `pointing_device_task` is called when inactive and every `AUTO_MOUSE_DEBOUNCE` ms when active, and will evaluate pointing device level conditions that trigger target layer activation. When it returns true, the target layer will be activated barring the usual exceptions _(e.g. delay time has not expired)_.
+
+By default it will return true if any of the `mouse_report` axes `x`,`y`,`h`,`v` are non zero, or if there is any mouse buttons active in `mouse_report`.
+_Note: The Cirque pinnacle track pad already implements a custom activation function that will activate on touchdown as well as movement all of the default conditions, currently this only works for the master side of split keyboards._
+
+| Function | Description | Return type |
+| :--------------------------------------------------------- | -------------------------------------------------------------------------------- | --------------: |
+| `auto_mouse_activation(report_mouse_t mouse_report)` | Overwritable function that controls target layer activation (when true) | `bool` |
+
+## Auto Mouse for Custom Pointing Device Task
+
+When using a custom pointing device (overwriting `pointing_device_task`) the following code should be somewhere in the `pointing_device_task_*` stack:
+
+```c
+void pointing_device_task(void) {
+ //...Custom pointing device task code
+
+ // handle automatic mouse layer (needs report_mouse_t as input)
+ pointing_device_task_auto_mouse(local_mouse_report);
+
+ //...More custom pointing device task code
+
+ pointing_device_send();
+}
+```
+
+In general the following two functions must be implemented in appropriate locations for auto mouse to function:
+
+| Function | Description | Suggested location |
+| -------------------------------------------------------------- | ------------------------------------------------------------ | ---------------------------: |
+| `pointing_device_task_auto_mouse(report_mouse_t mouse_report)` | handles target layer activation and is_active status updates | `pointing_device_task` stack |
+| `process_auto_mouse(uint16_t keycode, keyrecord_t* record)` | Keycode processing for auto mouse | `process_record` stack |
diff --git a/docs/feature_programmable_button.md b/docs/feature_programmable_button.md
index b1ef555d16a9..43a9e7fc1602 100644
--- a/docs/feature_programmable_button.md
+++ b/docs/feature_programmable_button.md
@@ -1,74 +1,144 @@
-## Programmable Button
+# Programmable Button :id=programmable-button
-Programmable button is a feature that can be used to send keys that have no
-predefined meaning.
-This means they can be processed on the host side by custom software without
-colliding without the operating system trying to interpret these keys.
+Programmable Buttons are keys that have no predefined meaning. This means they can be processed on the host side by custom software without the operating system trying to interpret them.
-The keycodes are emitted according to the HID usage
-"Telephony Device Page" (0x0B), "Programmable button usage" (0x07).
-On Linux (> 5.14) they are handled automatically and translated to `KEY_MACRO#`
-keycodes.
-(Up to `KEY_MACRO30`)
+The keycodes are emitted according to the HID Telephony Device page (`0x0B`), Programmable Button usage (`0x07`). On Linux (> 5.14) they are handled automatically and translated to `KEY_MACRO#` keycodes (up to `KEY_MACRO30`).
-### Enabling Programmable Button support
+?> Currently there is no known support in Windows or macOS. It may be possible to write a custom HID driver to receive these usages, but this is out of the scope of the QMK documentation.
-To enable Programmable Button, add the following line to your keymap’s `rules.mk`:
+## Usage :id=usage
-```c
+Add the following to your `rules.mk`:
+
+```make
PROGRAMMABLE_BUTTON_ENABLE = yes
```
-### Mapping
-
-In your keymap you can use the following keycodes to map key presses to Programmable Buttons:
-
-|Key |Description |
-|------------------------|----------------------|
-|`PROGRAMMABLE_BUTTON_1` |Programmable button 1 |
-|`PROGRAMMABLE_BUTTON_2` |Programmable button 2 |
-|`PROGRAMMABLE_BUTTON_3` |Programmable button 3 |
-|`PROGRAMMABLE_BUTTON_4` |Programmable button 4 |
-|`PROGRAMMABLE_BUTTON_5` |Programmable button 5 |
-|`PROGRAMMABLE_BUTTON_6` |Programmable button 6 |
-|`PROGRAMMABLE_BUTTON_7` |Programmable button 7 |
-|`PROGRAMMABLE_BUTTON_8` |Programmable button 8 |
-|`PROGRAMMABLE_BUTTON_9` |Programmable button 9 |
-|`PROGRAMMABLE_BUTTON_10`|Programmable button 10|
-|`PROGRAMMABLE_BUTTON_11`|Programmable button 11|
-|`PROGRAMMABLE_BUTTON_12`|Programmable button 12|
-|`PROGRAMMABLE_BUTTON_13`|Programmable button 13|
-|`PROGRAMMABLE_BUTTON_14`|Programmable button 14|
-|`PROGRAMMABLE_BUTTON_15`|Programmable button 15|
-|`PROGRAMMABLE_BUTTON_16`|Programmable button 16|
-|`PROGRAMMABLE_BUTTON_17`|Programmable button 17|
-|`PROGRAMMABLE_BUTTON_18`|Programmable button 18|
-|`PROGRAMMABLE_BUTTON_19`|Programmable button 19|
-|`PROGRAMMABLE_BUTTON_20`|Programmable button 20|
-|`PROGRAMMABLE_BUTTON_21`|Programmable button 21|
-|`PROGRAMMABLE_BUTTON_22`|Programmable button 22|
-|`PROGRAMMABLE_BUTTON_23`|Programmable button 23|
-|`PROGRAMMABLE_BUTTON_24`|Programmable button 24|
-|`PROGRAMMABLE_BUTTON_25`|Programmable button 25|
-|`PROGRAMMABLE_BUTTON_26`|Programmable button 26|
-|`PROGRAMMABLE_BUTTON_27`|Programmable button 27|
-|`PROGRAMMABLE_BUTTON_28`|Programmable button 28|
-|`PROGRAMMABLE_BUTTON_29`|Programmable button 29|
-|`PROGRAMMABLE_BUTTON_30`|Programmable button 30|
-|`PROGRAMMABLE_BUTTON_31`|Programmable button 31|
-|`PROGRAMMABLE_BUTTON_32`|Programmable button 32|
-|`PB_1` to `PB_32` |Aliases for keymaps |
-
-### API
-
-You can also use a dedicated API defined in `programmable_button.h` to interact with this feature:
+## Keycodes :id=keycodes
-```
-void programmable_button_clear(void);
-void programmable_button_send(void);
-void programmable_button_on(uint8_t code);
-void programmable_button_off(uint8_t code);
-bool programmable_button_is_on(uint8_t code);
-uint32_t programmable_button_get_report(void);
-void programmable_button_set_report(uint32_t report);
-```
+|Key |Aliases|Description |
+|---------------------------|-------|----------------------|
+|`QK_PROGRAMMABLE_BUTTON_1` |`PB_1` |Programmable button 1 |
+|`QK_PROGRAMMABLE_BUTTON_2` |`PB_2` |Programmable button 2 |
+|`QK_PROGRAMMABLE_BUTTON_3` |`PB_3` |Programmable button 3 |
+|`QK_PROGRAMMABLE_BUTTON_4` |`PB_4` |Programmable button 4 |
+|`QK_PROGRAMMABLE_BUTTON_5` |`PB_5` |Programmable button 5 |
+|`QK_PROGRAMMABLE_BUTTON_6` |`PB_6` |Programmable button 6 |
+|`QK_PROGRAMMABLE_BUTTON_7` |`PB_7` |Programmable button 7 |
+|`QK_PROGRAMMABLE_BUTTON_8` |`PB_8` |Programmable button 8 |
+|`QK_PROGRAMMABLE_BUTTON_9` |`PB_9` |Programmable button 9 |
+|`QK_PROGRAMMABLE_BUTTON_10`|`PB_10`|Programmable button 10|
+|`QK_PROGRAMMABLE_BUTTON_11`|`PB_11`|Programmable button 11|
+|`QK_PROGRAMMABLE_BUTTON_12`|`PB_12`|Programmable button 12|
+|`QK_PROGRAMMABLE_BUTTON_13`|`PB_13`|Programmable button 13|
+|`QK_PROGRAMMABLE_BUTTON_14`|`PB_14`|Programmable button 14|
+|`QK_PROGRAMMABLE_BUTTON_15`|`PB_15`|Programmable button 15|
+|`QK_PROGRAMMABLE_BUTTON_16`|`PB_16`|Programmable button 16|
+|`QK_PROGRAMMABLE_BUTTON_17`|`PB_17`|Programmable button 17|
+|`QK_PROGRAMMABLE_BUTTON_18`|`PB_18`|Programmable button 18|
+|`QK_PROGRAMMABLE_BUTTON_19`|`PB_19`|Programmable button 19|
+|`QK_PROGRAMMABLE_BUTTON_20`|`PB_20`|Programmable button 20|
+|`QK_PROGRAMMABLE_BUTTON_21`|`PB_21`|Programmable button 21|
+|`QK_PROGRAMMABLE_BUTTON_22`|`PB_22`|Programmable button 22|
+|`QK_PROGRAMMABLE_BUTTON_23`|`PB_23`|Programmable button 23|
+|`QK_PROGRAMMABLE_BUTTON_24`|`PB_24`|Programmable button 24|
+|`QK_PROGRAMMABLE_BUTTON_25`|`PB_25`|Programmable button 25|
+|`QK_PROGRAMMABLE_BUTTON_26`|`PB_26`|Programmable button 26|
+|`QK_PROGRAMMABLE_BUTTON_27`|`PB_27`|Programmable button 27|
+|`QK_PROGRAMMABLE_BUTTON_28`|`PB_28`|Programmable button 28|
+|`QK_PROGRAMMABLE_BUTTON_29`|`PB_29`|Programmable button 29|
+|`QK_PROGRAMMABLE_BUTTON_30`|`PB_30`|Programmable button 30|
+|`QK_PROGRAMMABLE_BUTTON_31`|`PB_31`|Programmable button 31|
+|`QK_PROGRAMMABLE_BUTTON_32`|`PB_32`|Programmable button 32|
+
+## API :id=api
+
+### `void programmable_button_clear(void)` :id=api-programmable-button-clear
+
+Clear the programmable button report.
+
+---
+
+### `void programmable_button_add(uint8_t index)` :id=api-programmable-button-add
+
+Set the state of a button.
+
+#### Arguments :id=api-programmable-button-add-arguments
+
+ - `uint8_t index`
+ The index of the button to press, from 0 to 31.
+
+---
+
+### `void programmable_button_remove(uint8_t index)` :id=api-programmable-button-remove
+
+Reset the state of a button.
+
+#### Arguments :id=api-programmable-button-remove-arguments
+
+ - `uint8_t index`
+ The index of the button to release, from 0 to 31.
+
+---
+
+### `void programmable_button_register(uint8_t index)` :id=api-programmable-button-register
+
+Set the state of a button, and flush the report.
+
+#### Arguments :id=api-programmable-button-register-arguments
+
+ - `uint8_t index`
+ The index of the button to press, from 0 to 31.
+
+---
+
+### `void programmable_button_unregister(uint8_t index)` :id=api-programmable-button-unregister
+
+Reset the state of a button, and flush the report.
+
+#### Arguments :id=api-programmable-button-unregister-arguments
+
+ - `uint8_t index`
+ The index of the button to release, from 0 to 31.
+
+---
+
+### `bool programmable_button_is_on(uint8_t index)` :id=api-programmable-button-is-on
+
+Get the state of a button.
+
+#### Arguments :id=api-programmable-button-is-on-arguments
+
+ - `uint8_t index`
+ The index of the button to check, from 0 to 31.
+
+#### Return Value :id=api-programmable-button-is-on-return
+
+`true` if the button is pressed.
+
+---
+
+### `void programmable_button_flush(void)` :id=api-programmable-button-flush
+
+Send the programmable button report to the host.
+
+---
+
+### `uint32_t programmable_button_get_report(void)` :id=api-programmable-button-get-report
+
+Get the programmable button report.
+
+#### Return Value :id=api-programmable-button-get-report-return
+
+The bitmask of programmable button states.
+
+---
+
+### `void programmable_button_set_report(uint32_t report)` :id=api-programmable-button-set-report
+
+Set the programmable button report.
+
+#### Arguments :id=api-programmable-button-set-report-arguments
+
+ - `uint32_t report`
+ A bitmask of programmable button states.
diff --git a/docs/feature_ps2_mouse.md b/docs/feature_ps2_mouse.md
index c980705ae7f9..e714d9b86763 100644
--- a/docs/feature_ps2_mouse.md
+++ b/docs/feature_ps2_mouse.md
@@ -32,13 +32,14 @@ In rules.mk:
```make
PS2_MOUSE_ENABLE = yes
-PS2_USE_BUSYWAIT = yes
+PS2_ENABLE = yes
+PS2_DRIVER = busywait
```
In your keyboard config.h:
```c
-#ifdef PS2_USE_BUSYWAIT
+#ifdef PS2_DRIVER_BUSYWAIT
# define PS2_CLOCK_PIN D1
# define PS2_DATA_PIN D2
#endif
@@ -52,13 +53,14 @@ In rules.mk:
```make
PS2_MOUSE_ENABLE = yes
-PS2_USE_INT = yes
+PS2_ENABLE = yes
+PS2_DRIVER = interrupt
```
In your keyboard config.h:
```c
-#ifdef PS2_USE_INT
+#ifdef PS2_DRIVER_INTERRUPT
#define PS2_CLOCK_PIN D2
#define PS2_DATA_PIN D5
@@ -84,7 +86,8 @@ In rules.mk:
```
PS2_MOUSE_ENABLE = yes
-PS2_USE_INT = yes
+PS2_ENABLE = yes
+PS2_DRIVER = interrupt
```
In your keyboard config.h:
@@ -108,13 +111,14 @@ In rules.mk:
```make
PS2_MOUSE_ENABLE = yes
-PS2_USE_USART = yes
+PS2_ENABLE = yes
+PS2_DRIVER = usart
```
In your keyboard config.h:
```c
-#ifdef PS2_USE_USART
+#ifdef PS2_DRIVER_USART
#define PS2_CLOCK_PIN D5
#define PS2_DATA_PIN D2
diff --git a/docs/feature_rgb_matrix.md b/docs/feature_rgb_matrix.md
index 87dbc5f78062..f5653b99e01a 100644
--- a/docs/feature_rgb_matrix.md
+++ b/docs/feature_rgb_matrix.md
@@ -23,7 +23,7 @@ You can use between 1 and 4 IS31FL3731 IC's. Do not specify `DRIVER_ADDR_` de
| `ISSI_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 |
| `ISSI_3731_DEGHOST` | (Optional) Set this define to enable de-ghosting by halving Vcc during blanking time | |
| `DRIVER_COUNT` | (Required) How many RGB driver IC's are present | |
-| `DRIVER_LED_TOTAL` | (Required) How many RGB lights are present across all drivers | |
+| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | |
| `DRIVER_ADDR_1` | (Required) Address for the first RGB driver | |
| `DRIVER_ADDR_2` | (Optional) Address for the second RGB driver | |
| `DRIVER_ADDR_3` | (Optional) Address for the third RGB driver | |
@@ -45,17 +45,17 @@ Here is an example using 2 drivers.
#define DRIVER_COUNT 2
#define DRIVER_1_LED_TOTAL 25
#define DRIVER_2_LED_TOTAL 24
-#define DRIVER_LED_TOTAL (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
+#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
```
-!> Note the parentheses, this is so when `DRIVER_LED_TOTAL` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`.
+!> Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`.
For split keyboards using `RGB_MATRIX_SPLIT` with an LED driver, you can either have the same driver address or different driver addresses. If using different addresses, use `DRIVER_ADDR_1` for one and `DRIVER_ADDR_2` for the other one. Then, in `g_is31_leds`, fill out the correct driver index (0 or 1). If using one address, use `DRIVER_ADDR_1` for both, and use index 0 for `g_is31_leds`.
Define these arrays listing all the LEDs in your `.c`:
```c
-const is31_led PROGMEM g_is31_leds[DRIVER_LED_TOTAL] = {
+const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT] = {
/* Refer to IS31 manual for these locations
* driver
* | R location
@@ -86,10 +86,11 @@ You can use between 1 and 4 IS31FL3733 IC's. Do not specify `DRIVER_ADDR_` de
| `ISSI_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 |
| `ISSI_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 |
| `ISSI_PWM_FREQUENCY` | (Optional) PWM Frequency Setting - IS31FL3733B only | 0 |
+| `ISSI_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF |
| `ISSI_SWPULLUP` | (Optional) Set the value of the SWx lines on-chip de-ghosting resistors | PUR_0R (Disabled) |
| `ISSI_CSPULLUP` | (Optional) Set the value of the CSx lines on-chip de-ghosting resistors | PUR_0R (Disabled) |
| `DRIVER_COUNT` | (Required) How many RGB driver IC's are present | |
-| `DRIVER_LED_TOTAL` | (Required) How many RGB lights are present across all drivers | |
+| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | |
| `DRIVER_ADDR_1` | (Required) Address for the first RGB driver | |
| `DRIVER_ADDR_2` | (Optional) Address for the second RGB driver | |
| `DRIVER_ADDR_3` | (Optional) Address for the third RGB driver | |
@@ -130,17 +131,17 @@ Here is an example using 2 drivers.
#define DRIVER_COUNT 2
#define DRIVER_1_LED_TOTAL 58
#define DRIVER_2_LED_TOTAL 10
-#define DRIVER_LED_TOTAL (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
+#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
```
-!> Note the parentheses, this is so when `DRIVER_LED_TOTAL` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`.
+!> Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`.
Currently only 4 drivers are supported, but it would be trivial to support all 8 combinations.
Define these arrays listing all the LEDs in your `.c`:
```c
-const is31_led PROGMEM g_is31_leds[DRIVER_LED_TOTAL] = {
+const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT] = {
/* Refer to IS31 manual for these locations
* driver
* | R location
@@ -163,7 +164,7 @@ There is basic support for addressable RGB matrix lighting with the I2C IS31FL37
RGB_MATRIX_ENABLE = yes
RGB_MATRIX_DRIVER = IS31FL3737
```
-You can use between 1 and 2 IS31FL3737 IC's. Do not specify `DRIVER_ADDR_2` define for second IC if not present on your keyboard.
+You can use between 1 and 4 IS31FL3737 IC's. Do not specify `DRIVER_ADDR_` defines for IC's that are not present on your keyboard.
Configure the hardware via your `config.h`:
@@ -171,12 +172,16 @@ Configure the hardware via your `config.h`:
|----------|-------------|---------|
| `ISSI_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 |
| `ISSI_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 |
+| `ISSI_PWM_FREQUENCY` | (Optional) PWM Frequency Setting - IS31FL3737B only | 0 |
+| `ISSI_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF |
| `ISSI_SWPULLUP` | (Optional) Set the value of the SWx lines on-chip de-ghosting resistors | PUR_0R (Disabled) |
| `ISSI_CSPULLUP` | (Optional) Set the value of the CSx lines on-chip de-ghosting resistors | PUR_0R (Disabled) |
| `DRIVER_COUNT` | (Required) How many RGB driver IC's are present | |
-| `DRIVER_LED_TOTAL` | (Required) How many RGB lights are present across all drivers | |
+| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | |
| `DRIVER_ADDR_1` | (Required) Address for the first RGB driver | |
| `DRIVER_ADDR_2` | (Optional) Address for the second RGB driver | |
+| `DRIVER_ADDR_3` | (Optional) Address for the third RGB driver | |
+| `DRIVER_ADDR_4` | (Optional) Address for the fourth RGB driver | |
The IS31FL3737 IC's have on-chip resistors that can be enabled to allow for de-ghosting of the RGB matrix. By default these resistors are not enabled (`ISSI_SWPULLUP`/`ISSI_CSPULLUP` are given the value of`PUR_0R`), the values that can be set to enable de-ghosting are as follows:
@@ -209,16 +214,16 @@ Here is an example using 2 drivers.
#define DRIVER_COUNT 2
#define DRIVER_1_LED_TOTAL 30
#define DRIVER_2_LED_TOTAL 36
-#define DRIVER_LED_TOTAL (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
+#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
```
-!> Note the parentheses, this is so when `DRIVER_LED_TOTAL` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`.
+!> Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`.
Currently only 2 drivers are supported, but it would be trivial to support all 4 combinations.
Define these arrays listing all the LEDs in your `.c`:
```c
-const is31_led PROGMEM g_is31_leds[DRIVER_LED_TOTAL] = {
+const is31_led PROGMEM g_is31_leds[RGB_MATRIX_LED_COUNT] = {
/* Refer to IS31 manual for these locations
* driver
* | R location
@@ -230,7 +235,7 @@ const is31_led PROGMEM g_is31_leds[DRIVER_LED_TOTAL] = {
}
```
-Where `X_Y` is the location of the LED in the matrix defined by [the datasheet](https://www.issi.com/WW/pdf/31FL3737.pdf) and the header file `drivers/led/issi/is31fl3737.h`. The `driver` is the index of the driver you defined in your `config.h` (Only `0`, `1` for now).
+Where `X_Y` is the location of the LED in the matrix defined by [the datasheet](https://www.issi.com/WW/pdf/31FL3737.pdf) and the header file `drivers/led/issi/is31fl3737.h`. The `driver` is the index of the driver you defined in your `config.h` (Only `0`, `1`, `2`, or `3` for now).
---
### IS31FLCOMMON :id=is31flcommon
@@ -260,14 +265,14 @@ Configure the hardware via your `config.h`:
| `ISSI_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 |
| `ISSI_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 |
| `DRIVER_COUNT` | (Required) How many RGB driver IC's are present | |
-| `DRIVER_LED_TOTAL` | (Required) How many RGB lights are present across all drivers | |
+| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | |
| `DRIVER_ADDR_1` | (Optional) Address for the first RGB driver | |
| `DRIVER_ADDR_` | (Required) Address for the additional RGB drivers | |
| `ISSI_SSR_` | (Optional) Configuration for the Spread Spectrum Register | |
| `ISSI_CONFIGURATION` | (Optional) Configuration for the Configuration Register | |
| `ISSI_GLOBALCURRENT` | (Optional) Configuration for the Global Current Register | 0xFF |
| `ISSI_PULLDOWNUP` | (Optional) Configuration for the Pull Up & Pull Down Register | |
-| `ISSI_TEMP` | (Optional) Configuration for the Tempature Register | |
+| `ISSI_TEMP` | (Optional) Configuration for the Temperature Register | |
| `ISSI_PWM_ENABLE` | (Optional) Configuration for the PWM Enable Register | |
| `ISSI_PWM_SET` | (Optional) Configuration for the PWM Setting Register | |
| `ISSI_SCAL_RED` | (Optional) Configuration for the RED LEDs in Scaling Registers | 0xFF |
@@ -297,17 +302,17 @@ Here is an example using 2 drivers.
#define DRIVER_COUNT 2
#define DRIVER_1_LED_TOTAL 66
#define DRIVER_2_LED_TOTAL 42
-#define DRIVER_LED_TOTAL (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
+#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
```
-!> Note the parentheses, this is so when `DRIVER_LED_TOTAL` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`.
+!> Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`.
Currently only 4 drivers are supported, but it would be trivial to support for more. Note that using a combination of different drivers is not supported. All drivers must be of the same model.
Define these arrays listing all the LEDs in your `.c`:
```c
-const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = {
+const is31_led __flash g_is31_leds[RGB_MATRIX_LED_COUNT] = {
/* Refer to IS31 manual for these locations
* driver
* | R location
@@ -358,7 +363,7 @@ Configure the hardware via your `config.h`:
// The pin connected to the data pin of the LEDs
#define RGB_DI_PIN D7
// The number of LEDs connected
-#define DRIVER_LED_TOTAL 70
+#define RGB_MATRIX_LED_COUNT 70
```
?> There are additional configuration options for ARM controllers that offer increased performance over the default bitbang driver. Please see [WS2812 Driver](ws2812_driver.md) for more information.
@@ -382,7 +387,7 @@ Configure the hardware via your `config.h`:
// The pin connected to the clock pin of the LEDs
#define RGB_CI_PIN D6
// The number of LEDs connected
-#define DRIVER_LED_TOTAL 70
+#define RGB_MATRIX_LED_COUNT 70
```
---
@@ -405,9 +410,10 @@ You can use up to 2 AW20216 IC's. Do not specify `DRIVER__xxx` defines for IC
| `DRIVER_1_LED_TOTAL` | (Required) How many RGB lights are connected to first RGB driver | |
| `DRIVER_2_LED_TOTAL` | (Optional) How many RGB lights are connected to second RGB driver | |
| `DRIVER_COUNT` | (Required) How many RGB driver IC's are present | |
-| `DRIVER_LED_TOTAL` | (Required) How many RGB lights are present across all drivers | |
+| `RGB_MATRIX_LED_COUNT` | (Required) How many RGB lights are present across all drivers | |
| `AW_SCALING_MAX` | (Optional) LED current scaling value (0-255, higher values mean LED is brighter at full PWM) | 150 |
| `AW_GLOBAL_CURRENT_MAX` | (Optional) Driver global current limit (0-255, higher values means the driver may consume more power) | 150 |
+| `AW_SPI_MODE` | (Optional) Mode for SPI communication (0-3, defines polarity and phase of the clock) | 3 |
| `AW_SPI_DIVISOR` | (Optional) Clock divisor for SPI communication (powers of 2, smaller numbers means faster communication, should not be less than 4) | 4 |
Here is an example using 2 drivers.
@@ -422,15 +428,15 @@ Here is an example using 2 drivers.
#define DRIVER_COUNT 2
#define DRIVER_1_LED_TOTAL 66
#define DRIVER_2_LED_TOTAL 32
-#define DRIVER_LED_TOTAL (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
+#define RGB_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)
```
-!> Note the parentheses, this is so when `DRIVER_LED_TOTAL` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`.
+!> Note the parentheses, this is so when `RGB_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`.
Define these arrays listing all the LEDs in your `.c`:
```c
-const aw_led PROGMEM g_aw_leds[DRIVER_LED_TOTAL] = {
+const aw_led PROGMEM g_aw_leds[RGB_MATRIX_LED_COUNT] = {
/* Each AW20216 channel is controlled by a register at some offset between 0x00
* and 0xD7 inclusive.
* See drivers/awinic/aw20216.h for the mapping between register offsets and
@@ -519,7 +525,7 @@ All RGB keycodes are currently shared with the RGBLIGHT system:
|`RGB_VAD` | |Decrease value (brightness), increase value when Shift is held |
|`RGB_SPI` | |Increase effect speed (does not support eeprom yet), decrease speed when Shift is held|
|`RGB_SPD` | |Decrease effect speed (does not support eeprom yet), increase speed when Shift is held|
-|`RGB_MODE_PLAIN` |`RGB_M_P `|Static (no animation) mode |
+|`RGB_MODE_PLAIN` |`RGB_M_P` |Static (no animation) mode |
|`RGB_MODE_BREATHE` |`RGB_M_B` |Breathing animation mode |
|`RGB_MODE_RAINBOW` |`RGB_M_R` |Full gradient scrolling left to right (uses the `RGB_MATRIX_CYCLE_LEFT_RIGHT` mode) |
|`RGB_MODE_SWIRL` |`RGB_M_SW`|Full gradient spinning pinwheel around center of keyboard (uses `RGB_MATRIX_CYCLE_PINWHEEL` mode) |
@@ -556,7 +562,7 @@ enum rgb_matrix_effects {
RGB_MATRIX_CYCLE_UP_DOWN, // Full gradient scrolling top to bottom
RGB_MATRIX_CYCLE_OUT_IN, // Full gradient scrolling out to in
RGB_MATRIX_CYCLE_OUT_IN_DUAL, // Full dual gradients scrolling out to in
- RGB_MATRIX_RAINBOW_MOVING_CHEVRON, // Full gradent Chevron shapped scrolling left to right
+ RGB_MATRIX_RAINBOW_MOVING_CHEVRON, // Full gradient Chevron shapped scrolling left to right
RGB_MATRIX_CYCLE_PINWHEEL, // Full gradient spinning pinwheel around center of keyboard
RGB_MATRIX_CYCLE_SPIRAL, // Full gradient spinning spiral around center of keyboard
RGB_MATRIX_DUAL_BEACON, // Full gradient spinning around center of keyboard
@@ -656,18 +662,44 @@ You can enable a single effect by defining `ENABLE_[EFFECT_NAME]` in your `confi
### RGB Matrix Effect Typing Heatmap :id=rgb-matrix-effect-typing-heatmap
-This effect will color the RGB matrix according to a heatmap of recently pressed
-keys. Whenever a key is pressed its "temperature" increases as well as that of
-its neighboring keys. The temperature of each key is then decreased
-automatically every 25 milliseconds by default.
+This effect will color the RGB matrix according to a heatmap of recently pressed keys. Whenever a key is pressed its "temperature" increases as well as that of its neighboring keys. The temperature of each key is then decreased automatically every 25 milliseconds by default.
-In order to change the delay of temperature decrease define
-`RGB_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS`:
+In order to change the delay of temperature decrease define `RGB_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS`:
```c
#define RGB_MATRIX_TYPING_HEATMAP_DECREASE_DELAY_MS 50
```
+As heatmap uses the physical position of the leds set in the g_led_config, you may need to tweak the following options to get the best effect for your keyboard. Note the size of this grid is `224x64`.
+
+Limit the distance the effect spreads to surrounding keys.
+
+```c
+#define RGB_MATRIX_TYPING_HEATMAP_SPREAD 40
+```
+
+Limit how hot surrounding keys get from each press.
+
+```c
+#define RGB_MATRIX_TYPING_HEATMAP_AREA_LIMIT 16
+```
+
+Remove the spread effect entirely.
+
+```c
+#define RGB_MATRIX_TYPING_HEATMAP_SLIM
+```
+
+### RGB Matrix Effect Solid Reactive :id=rgb-matrix-effect-solid-reactive
+
+Solid reactive effects will pulse RGB light on key presses with user configurable hues. To enable gradient mode that will automatically change reactive color, add the following define:
+
+```c
+#define RGB_MATRIX_SOLID_REACTIVE_GRADIENT_MODE
+```
+
+Gradient mode will loop through the color wheel hues over time and its duration can be controlled with the effect speed keycodes (`RGB_SPI`/`RGB_SPD`).
+
## Custom RGB Matrix Effects :id=custom-rgb-matrix-effects
By setting `RGB_MATRIX_CUSTOM_USER = yes` in `rules.mk`, new effects can be defined directly from your keymap or userspace, without having to edit any QMK core files. To declare new effects, create a `rgb_matrix_user.inc` file in the user keymap directory or userspace folder.
@@ -761,20 +793,20 @@ These are defined in [`color.h`](https://github.com/qmk/qmk_firmware/blob/master
#define RGB_MATRIX_KEYPRESSES // reacts to keypresses
#define RGB_MATRIX_KEYRELEASES // reacts to keyreleases (instead of keypresses)
#define RGB_MATRIX_FRAMEBUFFER_EFFECTS // enable framebuffer effects
-#define RGB_DISABLE_TIMEOUT 0 // number of milliseconds to wait until rgb automatically turns off
-#define RGB_DISABLE_AFTER_TIMEOUT 0 // OBSOLETE: number of ticks to wait until disabling effects
+#define RGB_MATRIX_TIMEOUT 0 // number of milliseconds to wait until rgb automatically turns off
#define RGB_DISABLE_WHEN_USB_SUSPENDED // turn off effects when suspended
-#define RGB_MATRIX_LED_PROCESS_LIMIT (DRIVER_LED_TOTAL + 4) / 5 // limits the number of LEDs to process in an animation per task run (increases keyboard responsiveness)
+#define RGB_MATRIX_LED_PROCESS_LIMIT (RGB_MATRIX_LED_COUNT + 4) / 5 // limits the number of LEDs to process in an animation per task run (increases keyboard responsiveness)
#define RGB_MATRIX_LED_FLUSH_LIMIT 16 // limits in milliseconds how frequently an animation will update the LEDs. 16 (16ms) is equivalent to limiting to 60fps (increases keyboard responsiveness)
#define RGB_MATRIX_MAXIMUM_BRIGHTNESS 200 // limits maximum brightness of LEDs to 200 out of 255. If not defined maximum brightness is set to 255
-#define RGB_MATRIX_STARTUP_MODE RGB_MATRIX_CYCLE_LEFT_RIGHT // Sets the default mode, if none has been set
-#define RGB_MATRIX_STARTUP_HUE 0 // Sets the default hue value, if none has been set
-#define RGB_MATRIX_STARTUP_SAT 255 // Sets the default saturation value, if none has been set
-#define RGB_MATRIX_STARTUP_VAL RGB_MATRIX_MAXIMUM_BRIGHTNESS // Sets the default brightness value, if none has been set
-#define RGB_MATRIX_STARTUP_SPD 127 // Sets the default animation speed, if none has been set
+#define RGB_MATRIX_DEFAULT_MODE RGB_MATRIX_CYCLE_LEFT_RIGHT // Sets the default mode, if none has been set
+#define RGB_MATRIX_DEFAULT_HUE 0 // Sets the default hue value, if none has been set
+#define RGB_MATRIX_DEFAULT_SAT 255 // Sets the default saturation value, if none has been set
+#define RGB_MATRIX_DEFAULT_VAL RGB_MATRIX_MAXIMUM_BRIGHTNESS // Sets the default brightness value, if none has been set
+#define RGB_MATRIX_DEFAULT_SPD 127 // Sets the default animation speed, if none has been set
#define RGB_MATRIX_DISABLE_KEYCODES // disables control of rgb matrix by keycodes (must use code functions to control the feature)
#define RGB_MATRIX_SPLIT { X, Y } // (Optional) For split keyboards, the number of LEDs connected on each half. X = left, Y = Right.
// If RGB_MATRIX_KEYPRESSES or RGB_MATRIX_KEYRELEASES is enabled, you also will want to enable SPLIT_TRANSPORT_MIRROR
+#define RGB_TRIGGER_ON_KEYDOWN // Triggers RGB keypress events on key down. This makes RGB control feel more responsive. This may cause RGB to not function properly on some boards
```
## EEPROM storage :id=eeprom-storage
@@ -793,7 +825,7 @@ Where `28` is an unused index from `eeconfig.h`.
|Function |Description |
|--------------------------------------------|-------------|
|`rgb_matrix_set_color_all(r, g, b)` |Set all of the LEDs to the given RGB value, where `r`/`g`/`b` are between 0 and 255 (not written to EEPROM) |
-|`rgb_matrix_set_color(index, r, g, b)` |Set a single LED to the given RGB value, where `r`/`g`/`b` are between 0 and 255, and `index` is between 0 and `DRIVER_LED_TOTAL` (not written to EEPROM) |
+|`rgb_matrix_set_color(index, r, g, b)` |Set a single LED to the given RGB value, where `r`/`g`/`b` are between 0 and 255, and `index` is between 0 and `RGB_MATRIX_LED_COUNT` (not written to EEPROM) |
### Disable/Enable Effects :id=disable-enable-effects
|Function |Description |
@@ -858,16 +890,21 @@ Where `28` is an unused index from `eeconfig.h`.
If you want to set custom indicators, such as an LED for Caps Lock, or layer indication, you can use the `rgb_matrix_indicators_kb` or `rgb_matrix_indicators_user` function for that:
```c
-void rgb_matrix_indicators_kb(void) {
+bool rgb_matrix_indicators_kb(void) {
+ if (!rgb_matrix_indicators_user()) {
+ return false;
+ }
rgb_matrix_set_color(index, red, green, blue);
+ return true;
}
```
In addition, there are the advanced indicator functions. These are aimed at those with heavily customized displays, where rendering every LED per cycle is expensive. Such as some of the "drashna" layouts. This includes a special macro to help make this easier to use: `RGB_MATRIX_INDICATOR_SET_COLOR(i, r, g, b)`.
```c
-void rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {
+bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {
RGB_MATRIX_INDICATOR_SET_COLOR(index, red, green, blue);
+ return false;
}
```
@@ -875,38 +912,40 @@ void rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {
Caps Lock indicator on alphanumeric flagged keys:
```c
-void rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {
+bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {
if (host_keyboard_led_state().caps_lock) {
- for (uint8_t i = led_min; i <= led_max; i++) {
+ for (uint8_t i = led_min; i < led_max; i++) {
if (g_led_config.flags[i] & LED_FLAG_KEYLIGHT) {
rgb_matrix_set_color(i, RGB_RED);
}
}
}
+ return false;
}
```
-Layer indicator on all flagged keys:
+Layer indicator on all keys:
```c
-void rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {
- for (uint8_t i = led_min; i <= led_max; i++) {
+bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {
+ for (uint8_t i = led_min; i < led_max; i++) {
switch(get_highest_layer(layer_state|default_layer_state)) {
- case RAISE:
+ case 2:
rgb_matrix_set_color(i, RGB_BLUE);
break;
- case LOWER:
+ case 1:
rgb_matrix_set_color(i, RGB_YELLOW);
break;
default:
break;
}
}
+ return false;
}
```
-Layer indicator with only configured keys:
+Layer indicator only on keys with configured keycodes:
```c
-void rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {
+bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {
if (get_highest_layer(layer_state) > 0) {
uint8_t layer = get_highest_layer(layer_state);
@@ -914,13 +953,14 @@ void rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {
for (uint8_t col = 0; col < MATRIX_COLS; ++col) {
uint8_t index = g_led_config.matrix_co[row][col];
- if (index >= led_min && index <= led_max && index != NO_LED &&
+ if (index >= led_min && index < led_max && index != NO_LED &&
keymap_key_to_keycode(layer, (keypos_t){col,row}) > KC_TRNS) {
rgb_matrix_set_color(index, RGB_GREEN);
}
}
}
}
+ return false;
}
```
@@ -931,7 +971,7 @@ void rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {
This example sets the modifiers to be a specific color based on the layer state. You can use a switch case here, instead, if you would like. This uses HSV and then converts to RGB, because this allows the brightness to be limited (important when using the WS2812 driver).
```c
-void rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {
+bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {
HSV hsv = {0, 255, 255};
if (layer_state_is(layer_state, 2)) {
@@ -945,23 +985,25 @@ void rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {
}
RGB rgb = hsv_to_rgb(hsv);
- for (uint8_t i = led_min; i <= led_max; i++) {
+ for (uint8_t i = led_min; i < led_max; i++) {
if (HAS_FLAGS(g_led_config.flags[i], 0x01)) { // 0x01 == LED_FLAG_MODIFIER
rgb_matrix_set_color(i, rgb.r, rgb.g, rgb.b);
}
}
+ return false;
}
```
If you want to indicate a Host LED status (caps lock, num lock, etc), you can use something like this to light up the caps lock key:
```c
-void rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {
+bool rgb_matrix_indicators_advanced_user(uint8_t led_min, uint8_t led_max) {
if (host_keyboard_led_state().caps_lock) {
RGB_MATRIX_INDICATOR_SET_COLOR(5, 255, 255, 255); // assuming caps lock is at led #5
} else {
RGB_MATRIX_INDICATOR_SET_COLOR(5, 0, 0, 0);
}
+ return false;
}
```
diff --git a/docs/feature_rgblight.md b/docs/feature_rgblight.md
index 08e820c0a638..060efaf1b351 100644
--- a/docs/feature_rgblight.md
+++ b/docs/feature_rgblight.md
@@ -105,7 +105,7 @@ Your RGB lighting can be configured by placing these `#define`s in your `config.
## Effects and Animations
Not only can this lighting be whatever color you want,
-if `RGBLIGHT_EFFECT_xxxx` or `RGBLIGHT_ANIMATIONS` is defined, you also have a number of animation modes at your disposal:
+if `RGBLIGHT_EFFECT_xxxx` is defined, you also have a number of animation modes at your disposal:
|Mode number symbol |Additional number |Description |
|-----------------------------|-------------------|---------------------------------------|
@@ -125,13 +125,14 @@ Check out [this video](https://youtube.com/watch?v=VKrpPAHlisY) for a demonstrat
Note: For versions older than 0.6.117, The mode numbers were written directly. In `quantum/rgblight/rgblight.h` there is a contrast table between the old mode number and the current symbol.
+
### Effect and Animation Toggles
Use these defines to add or remove animations from the firmware. When you are running low on flash space, it can be helpful to disable animations you are not using.
|Define |Default |Description |
|------------------------------------|-------------|-------------------------------------------------------------------------|
-|`RGBLIGHT_ANIMATIONS` |*Not defined*|Enable all additional animation modes. |
+|`RGBLIGHT_ANIMATIONS` |*Not defined*|Enable all additional animation modes. (deprecated) |
|`RGBLIGHT_EFFECT_ALTERNATING` |*Not defined*|Enable alternating animation mode. |
|`RGBLIGHT_EFFECT_BREATHING` |*Not defined*|Enable breathing animation mode. |
|`RGBLIGHT_EFFECT_CHRISTMAS` |*Not defined*|Enable christmas animation mode. |
@@ -143,6 +144,8 @@ Use these defines to add or remove animations from the firmware. When you are ru
|`RGBLIGHT_EFFECT_STATIC_GRADIENT` |*Not defined*|Enable static gradient mode. |
|`RGBLIGHT_EFFECT_TWINKLE` |*Not defined*|Enable twinkle animation mode. |
+!> `RGBLIGHT_ANIMATIONS` is being deprecated and animation modes should be explicitly defined.
+
### Effect and Animation Settings
The following options are used to tweak the various animations:
@@ -162,14 +165,12 @@ The following options are used to tweak the various animations:
|`RGBLIGHT_EFFECT_TWINKLE_PROBABILITY`|`1/127` |Adjusts how likely each LED is to twinkle (on each animation step) |
### Example Usage to Reduce Memory Footprint
- 1. Remove `RGBLIGHT_ANIMATIONS` from `config.h`.
- 1. Selectively add the animations you want to enable. The following would enable two animations and save about 4KiB:
+ 1. Use `#undef` to selectively disable animations. The following would disable two animations and save about 4KiB:
```diff
#undef RGBLED_NUM
--#define RGBLIGHT_ANIMATIONS
-+#define RGBLIGHT_EFFECT_STATIC_GRADIENT
-+#define RGBLIGHT_EFFECT_RAINBOW_SWIRL
++#undef RGBLIGHT_EFFECT_STATIC_GRADIENT
++#undef RGBLIGHT_EFFECT_RAINBOW_SWIRL
#define RGBLED_NUM 12
#define RGBLIGHT_HUE_STEP 8
#define RGBLIGHT_SAT_STEP 8
@@ -301,7 +302,7 @@ void keyboard_post_init_user(void) {
// after the flag has been flipped...
void post_process_record_user(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
- case DEBUG:
+ case QK_DEBUG_TOGGLE:
rgblight_blink_layer(debug_enable ? 0 : 1, 500);
break;
@@ -318,13 +319,26 @@ You can also use `rgblight_blink_layer_repeat` to specify the amount of times th
```c
void post_process_record_user(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
- case DEBUG:
+ case QK_DEBUG_TOGGLE:
rgblight_blink_layer_repeat(debug_enable ? 0 : 1, 200, 3);
break;
}
}
```
-would turn the layer 0 (or 1) on and off again three times when `DEBUG` is pressed.
+would turn the layer 0 (or 1) on and off again three times when `DB_TOGG` is pressed.
+
+Blinking accumulates layers so if multiple layers are set blinking at the same time they will all blink for the duration and repeat times of the last layer to be blinked.
+To stop these other layers from blinking use `rgblight_unblink_layer` or `rgblight_unblink_all_but_layer`:
+
+```c
+rgblight_blink_layer(1, 500);
+rgblight_unblink_all_but_layer(1);
+```
+
+```c
+rgblight_unblink_layer(3);
+rgblight_blink_layer(2, 500);
+```
!> Lighting layers on split keyboards will require layer state synced to the slave half (e.g. `#define SPLIT_LAYER_STATE_ENABLE`). See [data sync options](feature_split_keyboard.md#data-sync-options) for more details.
diff --git a/docs/feature_secure.md b/docs/feature_secure.md
new file mode 100644
index 000000000000..eaa2b601aef6
--- /dev/null
+++ b/docs/feature_secure.md
@@ -0,0 +1,54 @@
+# Secure
+
+The secure feature aims to prevent unwanted interaction without user intervention.
+
+?> Secure does **not** currently implement encryption/decryption/etc and should not be a replacement where a strong hardware/software based solution is required.
+
+### Unlock sequence
+
+To unlock, the user must perform a set of actions. This can optionally be configured to be multiple keys.
+
+* While unlocking all keyboard input is ignored
+* Incorrect attempts will revert back to the previously locked state
+
+### Automatic Locking
+
+Once unlocked, the keyboard will revert back to a locked state after the configured timeout.
+The timeout can be refreshed by using the `secure_activity_event` function, for example from one of the various [hooks](custom_quantum_functions.md).
+
+## Usage
+
+Add the following to your `rules.mk`:
+
+```make
+SECURE_ENABLE = yes
+```
+
+## Keycodes
+
+| Key |Aliases | Description |
+|---------------------|---------|--------------------------------------------------------------------------------|
+| `QK_SECURE_LOCK` |`SE_LOCK`| Revert back to a locked state |
+| `QK_SECURE_UNLOCK` |`SE_UNLK`| Forces unlock without performing a unlock sequence |
+| `QK_SECURE_TOGGLE` |`SE_TOGG`| Toggle directly between locked and unlock without performing a unlock sequence |
+| `QK_SECURE_REQUEST` |`SE_REQ` | Request that user perform the unlock sequence |
+
+## Configuration
+
+| Define | Default | Description |
+|-------------------------|----------------|---------------------------------------------------------------------------------|
+|`SECURE_UNLOCK_TIMEOUT` | `5000` | Timeout for the user to perform the configured unlock sequence - `0` to disable |
+|`SECURE_IDLE_TIMEOUT` | `60000` | Timeout while unlocked before returning to locked - `0` to disable |
+|`SECURE_UNLOCK_SEQUENCE` | `{ { 0, 0 } }` | Array of matrix locations describing a sequential sequence of keypresses |
+
+## Functions
+
+| Function | Description |
+|---------------------------|----------------------------------------------------------------------------|
+| `secure_is_locked()` | Check if the device is currently locked |
+| `secure_is_unlocking()` | Check if an unlock sequence is currently in progress |
+| `secure_is_unlocked()` | Check if the device is currently unlocked |
+| `secure_lock()` | Lock down the device |
+| `secure_unlock()` | Force unlock the device - bypasses user unlock sequence |
+| `secure_request_unlock()` | Begin listening for an unlock sequence |
+| `secure_activity_event()` | Flag that user activity has happened and the device should remain unlocked |
diff --git a/docs/feature_send_string.md b/docs/feature_send_string.md
new file mode 100644
index 000000000000..67df0224e93b
--- /dev/null
+++ b/docs/feature_send_string.md
@@ -0,0 +1,224 @@
+# Send String
+
+The Send String API is part of QMK's macro system. It allows for sequences of keystrokes to be sent automatically.
+
+The full ASCII character set is supported, along with all of the keycodes in the Basic Keycode range (as these are the only ones that will actually be sent to the host).
+
+?> Unicode characters are **not** supported with this API -- see the [Unicode](feature_unicode.md) feature instead.
+
+## Usage
+
+Send String is enabled by default, so there is usually no need for any special setup. However, if it is disabled, add the following to your `rules.mk`:
+
+```make
+SEND_STRING_ENABLE = yes
+```
+
+## Basic Configuration
+
+Add the following to your `config.h`:
+
+|Define |Default |Description |
+|-----------------|----------------|------------------------------------------------------------------------------------------------------------|
+|`SENDSTRING_BELL`|*Not defined* |If the [Audio](feature_audio.md) feature is enabled, the `\a` character (ASCII `BEL`) will beep the speaker.|
+|`BELL_SOUND` |`TERMINAL_SOUND`|The song to play when the `\a` character is encountered. By default, this is an eighth note of C5. |
+
+## Keycodes
+
+The Send String functions accept C string literals, but specific keycodes can be injected with the below macros. All of the keycodes in the [Basic Keycode range](keycodes_basic.md) are supported (as these are the only ones that will actually be sent to the host), but with an `X_` prefix instead of `KC_`.
+
+|Macro |Description |
+|--------------|-------------------------------------------------------------------|
+|`SS_TAP(x)` |Send a keydown, then keyup, event for the given Send String keycode|
+|`SS_DOWN(x)` |Send a keydown event for the given Send String keycode |
+|`SS_UP(x)` |Send a keyup event for the given Send String keycode |
+|`SS_DELAY(ms)`|Wait for `ms` milliseconds |
+
+The following characters are also mapped to their respective keycodes for convenience:
+
+|Character|Hex |ASCII|Keycode |
+|---------|------|-----|--------------|
+|`\b` |`\x08`|`BS` |`KC_BACKSPACE`|
+|`\e` |`\x09`|`ESC`|`KC_ESCAPE` |
+|`\n` |`\x0A`|`LF` |`KC_ENTER` |
+|`\t` |`\x1B`|`TAB`|`KC_TAB` |
+| |`\x7F`|`DEL`|`KC_DELETE` |
+
+### Language Support
+
+By default, Send String assumes your OS keyboard layout is set to US ANSI. If you are using a different keyboard layout, you can [override the lookup tables used to convert ASCII characters to keystrokes](reference_keymap_extras.md#sendstring-support).
+
+## Examples
+
+### Hello World
+
+A simple custom keycode which types out "Hello, world!" and the Enter key when pressed.
+
+Add the following to your `keymap.c`:
+
+```c
+bool process_record_user(uint16_t keycode, keyrecord_t *record) {
+ switch (keycode) {
+ case SS_HELLO:
+ if (record->event.pressed) {
+ SEND_STRING("Hello, world!\n");
+ }
+ return false;
+ }
+
+ return true;
+}
+```
+
+### Keycode Injection
+
+This example types out opening and closing curly braces, then taps the left arrow key to move the cursor between the two.
+
+```c
+SEND_STRING("{}" SS_TAP(X_LEFT));
+```
+
+This example types Ctrl+A, then Ctrl+C, without releasing Ctrl.
+
+```c
+SEND_STRING(SS_LCTL("ac"));
+```
+
+## API
+
+### `void send_string(const char *string)`
+
+Type out a string of ASCII characters.
+
+This function simply calls `send_string_with_delay(string, 0)`.
+
+#### Arguments
+
+ - `const char *string`
+ The string to type out.
+
+---
+
+### `void send_string_with_delay(const char *string, uint8_t interval)`
+
+Type out a string of ASCII characters, with a delay between each character.
+
+#### Arguments
+
+ - `const char *string`
+ The string to type out.
+ - `uint8_t interval`
+ The amount of time, in milliseconds, to wait before typing the next character.
+
+---
+
+### `void send_string_P(const char *string)`
+
+Type out a PROGMEM string of ASCII characters.
+
+On ARM devices, this function is simply an alias for `send_string_with_delay(string, 0)`.
+
+#### Arguments
+
+ - `const char *string`
+ The string to type out.
+
+---
+
+### `void send_string_with_delay_P(const char *string, uint8_t interval)`
+
+Type out a PROGMEM string of ASCII characters, with a delay between each character.
+
+On ARM devices, this function is simply an alias for `send_string_with_delay(string, interval)`.
+
+#### Arguments
+
+ - `const char *string`
+ The string to type out.
+ - `uint8_t interval`
+ The amount of time, in milliseconds, to wait before typing the next character.
+
+---
+
+### `void send_char(char ascii_code)`
+
+Type out an ASCII character.
+
+#### Arguments
+
+ - `char ascii_code`
+ The character to type.
+
+---
+
+### `void send_dword(uint32_t number)`
+
+Type out an eight digit (unsigned 32-bit) hexadecimal value.
+
+The format is `[0-9a-f]{8}`, eg. `00000000` through `ffffffff`.
+
+#### Arguments
+
+ - `uint32_t number`
+ The value to type, from 0 to 4,294,967,295.
+
+---
+
+### `void send_word(uint16_t number)`
+
+Type out a four digit (unsigned 16-bit) hexadecimal value.
+
+The format is `[0-9a-f]{4}`, eg. `0000` through `ffff`.
+
+#### Arguments
+
+ - `uint16_t number`
+ The value to type, from 0 to 65,535.
+
+---
+
+### `void send_byte(uint8_t number)`
+
+Type out a two digit (8-bit) hexadecimal value.
+
+The format is `[0-9a-f]{2}`, eg. `00` through `ff`.
+
+#### Arguments
+
+ - `uint8_t number`
+ The value to type, from 0 to 255.
+
+---
+
+### `void send_nibble(uint8_t number)`
+
+Type out a single hexadecimal digit.
+
+The format is `[0-9a-f]{1}`, eg. `0` through `f`.
+
+#### Arguments
+
+ - `uint8_t number`
+ The value to type, from 0 to 15.
+
+---
+
+### `void tap_random_base64(void)`
+
+Type a pseudorandom character from the set `A-Z`, `a-z`, `0-9`, `+` and `/`.
+
+---
+
+### `SEND_STRING(string)`
+
+Shortcut macro for `send_string_with_delay_P(PSTR(string), 0)`.
+
+On ARM devices, this define evaluates to `send_string_with_delay(string, 0)`.
+
+---
+
+### `SEND_STRING_DELAY(string, interval)`
+
+Shortcut macro for `send_string_with_delay_P(PSTR(string), interval)`.
+
+On ARM devices, this define evaluates to `send_string_with_delay(string, interval)`.
diff --git a/docs/feature_space_cadet.md b/docs/feature_space_cadet.md
index e29096424193..63a3881da8e2 100644
--- a/docs/feature_space_cadet.md
+++ b/docs/feature_space_cadet.md
@@ -5,22 +5,22 @@ Steve Losh described the [Space Cadet Shift](https://stevelosh.com/blog/2012/10/
## Usage
Firstly, in your keymap, do one of the following:
-- Replace the Left Shift key with `KC_LSPO` (Left Shift, Parenthesis Open), and Right Shift with `KC_RSPC` (Right Shift, Parenthesis Close).
-- Replace the Left Control key with `KC_LCPO` (Left Control, Parenthesis Open), and Right Control with `KC_RCPC` (Right Control, Parenthesis Close).
-- Replace the Left Alt key with `KC_LAPO` (Left Alt, Parenthesis Open), and Right Alt with `KC_RAPC` (Right Alt, Parenthesis Close).
-- Replace any Shift key in your keymap with `KC_SFTENT` (Right Shift, Enter).
+- Replace the Left Shift key with `SC_LSPO` (Left Shift, Parenthesis Open), and Right Shift with `SC_RSPC` (Right Shift, Parenthesis Close).
+- Replace the Left Control key with `SC_LCPO` (Left Control, Parenthesis Open), and Right Control with `SC_RCPC` (Right Control, Parenthesis Close).
+- Replace the Left Alt key with `SC_LAPO` (Left Alt, Parenthesis Open), and Right Alt with `SC_RAPC` (Right Alt, Parenthesis Close).
+- Replace any Shift key in your keymap with `SC_SENT` (Right Shift, Enter).
## Keycodes
-|Keycode |Description |
-|-----------|-------------------------------------------|
-|`KC_LSPO` |Left Shift when held, `(` when tapped |
-|`KC_RSPC` |Right Shift when held, `)` when tapped |
-|`KC_LCPO` |Left Control when held, `(` when tapped |
-|`KC_RCPC` |Right Control when held, `)` when tapped |
-|`KC_LAPO` |Left Alt when held, `(` when tapped |
-|`KC_RAPC` |Right Alt when held, `)` when tapped |
-|`KC_SFTENT`|Right Shift when held, Enter when tapped |
+|Keycode |Aliases |Description |
+|----------------------------------------------|---------|----------------------------------------|
+|`QK_SPACE_CADET_LEFT_CTRL_PARENTHESIS_OPEN` |`SC_LCPO`|Left Control when held, `(` when tapped |
+|`QK_SPACE_CADET_RIGHT_CTRL_PARENTHESIS_CLOSE` |`SC_RCPC`|Right Control when held, `)` when tapped|
+|`QK_SPACE_CADET_LEFT_SHIFT_PARENTHESIS_OPEN` |`SC_LSPO`|Left Shift when held, `(` when tapped |
+|`QK_SPACE_CADET_RIGHT_SHIFT_PARENTHESIS_CLOSE`|`SC_RSPC`|Right Shift when held, `)` when tapped |
+|`QK_SPACE_CADET_LEFT_ALT_PARENTHESIS_OPEN` |`SC_LAPO`|Left Alt when held, `(` when tapped |
+|`QK_SPACE_CADET_RIGHT_ALT_PARENTHESIS_CLOSE` |`SC_RAPC`|Right Alt when held, `)` when tapped |
+|`QK_SPACE_CADET_RIGHT_SHIFT_ENTER` |`SC_SENT`|Right Shift when held, Enter when tapped|
## Caveats
@@ -32,7 +32,7 @@ COMMAND_ENABLE = no
## Configuration
-By default Space Cadet assumes a US ANSI layout, but if your layout uses different keys for parentheses, you can redefine them in your `config.h`. In addition, you can redefine the modifier to send on tap, or even send no modifier at all. The new configuration defines bundle all options up into a single define of 3 key codes in this order: the `Modifier` when held or when used with other keys, the `Tap Modifer` sent when tapped (no modifier if `KC_TRNS`), finally the `Keycode` sent when tapped. Now keep in mind, mods from other keys will still apply to the `Keycode` if say `KC_RSFT` is held while tapping `KC_LSPO` key with `KC_TRNS` as the `Tap Modifer`.
+By default Space Cadet assumes a US ANSI layout, but if your layout uses different keys for parentheses, you can redefine them in your `config.h`. In addition, you can redefine the modifier to send on tap, or even send no modifier at all. The new configuration defines bundle all options up into a single define of 3 key codes in this order: the `Modifier` when held or when used with other keys, the `Tap Modifer` sent when tapped (no modifier if `KC_TRNS`), finally the `Keycode` sent when tapped. Now keep in mind, mods from other keys will still apply to the `Keycode` if say `KC_RSFT` is held while tapping `SC_LSPO` key with `KC_TRNS` as the `Tap Modifer`.
|Define |Default |Description |
|----------------|-------------------------------|---------------------------------------------------------------------------------|
@@ -48,7 +48,7 @@ By default Space Cadet assumes a US ANSI layout, but if your layout uses differe
## Obsolete Configuration
-These defines are used in the above defines internally to support backwards compatibility, so you may continue to use them, however the above defines open up a larger range of flexibility than before. As an example, say you want to not send any modifier when you tap just `KC_LSPO`, with the old defines you had an all or nothing choice of using the `DISABLE_SPACE_CADET_MODIFIER` define. Now you can define that key as: `#define LSPO_KEYS KC_LSFT, KC_TRNS, KC_9`. This tells the system to set Left Shift if held or used with other keys, then on tap send no modifier (transparent) with the `KC_9`.
+These defines are used in the above defines internally to support backwards compatibility, so you may continue to use them, however the above defines open up a larger range of flexibility than before. As an example, say you want to not send any modifier when you tap just `SC_LSPO`, with the old defines you had an all or nothing choice of using the `DISABLE_SPACE_CADET_MODIFIER` define. Now you can define that key as: `#define LSPO_KEYS KC_LSFT, KC_TRNS, KC_9`. This tells the system to set Left Shift if held or used with other keys, then on tap send no modifier (transparent) with the `KC_9`.
|Define |Default |Description |
|------------------------------|-------------|------------------------------------------------------------------|
diff --git a/docs/feature_split_keyboard.md b/docs/feature_split_keyboard.md
index 6ef70bf788f9..0b475cc63d60 100644
--- a/docs/feature_split_keyboard.md
+++ b/docs/feature_split_keyboard.md
@@ -10,6 +10,8 @@ For this, we will mostly be talking about the generic implementation used by the
!> ARM split supports most QMK subsystems when using the 'serial' and 'serial_usart' drivers. I2C slave is currently unsupported.
+!> Both sides must use the same MCU family, for eg two Pro Micro-compatible controllers or two Blackpills. Currently, mixing AVR and ARM is not possible as ARM vs AVR uses different method for serial communication, and are not compatible. Moreover Blackpill's uses 3.3v logic, and atmega32u4 uses 5v logic.
+
## Compatibility Overview
| Transport | AVR | ARM |
@@ -141,6 +143,9 @@ Next, you will have to flash the EEPROM files once for the correct hand to the c
* ARM controllers with a DFU compatible bootloader (e.g. Proton-C):
* `:dfu-util-split-left`
* `:dfu-util-split-right`
+* ARM controllers with a UF2 compatible bootloader:
+ * `:uf2-split-left`
+ * `:uf2-split-right`
Example:
@@ -148,11 +153,11 @@ Example:
make crkbd:default:avrdude-split-left
```
-?> ARM controllers using `dfu-util` will require an EEPROM reset after setting handedness. This can be done using the `EEP_RST` keycode or [Bootmagic Lite](feature_bootmagic.md). Controllers using emulated EEPROM will always require handedness parameter when flashing the firmware.
+?> ARM controllers using `dfu-util` will require an EEPROM reset after setting handedness. This can be done using the `EE_CLR` keycode or [Bootmagic Lite](feature_bootmagic.md). Controllers using emulated EEPROM will always require handedness parameter when flashing the firmware.
?> [QMK Toolbox]() can also be used to flash EEPROM handedness files. Place the controller in bootloader mode and select menu option Tools -> EEPROM -> Set Left/Right Hand
-This setting is not changed when re-initializing the EEPROM using the `EEP_RST` key, or using the `eeconfig_init()` function. However, if you reset the EEPROM outside of the firmware's built in options (such as flashing a file that overwrites the `EEPROM`, like how the [QMK Toolbox]()'s "Reset EEPROM" button works), you'll need to re-flash the controller with the `EEPROM` files.
+This setting is not changed when re-initializing the EEPROM using the `EE_CLR` key, or using the `eeconfig_init()` function. However, if you reset the EEPROM outside of the firmware's built in options (such as flashing a file that overwrites the `EEPROM`, like how the [QMK Toolbox]()'s "Reset EEPROM" button works), you'll need to re-flash the controller with the `EEPROM` files.
You can find the `EEPROM` files in the QMK firmware repo, [here](https://github.com/qmk/qmk_firmware/tree/master/quantum/split_common).
@@ -365,7 +370,7 @@ There are some settings that you may need to configure, based on how the hardwar
#define MATRIX_COL_PINS_RIGHT {
}
```
-This allows you to specify a different set of pins for the matrix on the right side. This is useful if you have a board with differently-shaped halves that requires a different configuration (such as Keebio's Quefrency).
+This allows you to specify a different set of pins for the matrix on the right side. This is useful if you have a board with differently-shaped halves that requires a different configuration (such as Keebio's Quefrency). The number of pins in the right and left matrices must be the same, if you have a board with a different number of rows or columns on one side, pad out the extra spaces with `NO_PIN` and make sure you add the unused rows or columns to your matrix.
```c
#define DIRECT_PINS_RIGHT { { F1, F0, B0, C7 }, { F4, F5, F6, F7 } }
@@ -417,6 +422,17 @@ This sets the maximum timeout when detecting master/slave when using `SPLIT_USB_
```
This sets the poll frequency when detecting master/slave when using `SPLIT_USB_DETECT`
+```c
+#define SPLIT_WATCHDOG_ENABLE
+```
+
+This will enable a software watchdog on any side delegated as slave and will reboot the keyboard if no successful communication occurs within `SPLIT_WATCHDOG_TIMEOUT`. This can be particularly helpful when `SPLIT_USB_DETECT` delegates both sides as slave in some circumstances.
+
+```c
+#define SPLIT_WATCHDOG_TIMEOUT 3000
+```
+This set the maximum slave timeout when waiting for communication from master when using `SPLIT_WATCHDOG_ENABLE`
+
## Hardware Considerations and Mods
Master/slave delegation is made either by detecting voltage on VBUS connection or waiting for USB communication (`SPLIT_USB_DETECT`). Pro Micro boards can use VBUS detection out of the box and be used with or without `SPLIT_USB_DETECT`.
diff --git a/docs/feature_stenography.md b/docs/feature_stenography.md
index 691d83f97068..62d4dabf8109 100644
--- a/docs/feature_stenography.md
+++ b/docs/feature_stenography.md
@@ -8,46 +8,107 @@ The [Open Steno Project](https://www.openstenoproject.org/) has built an open-so
Plover can work with any standard QWERTY keyboard, although it is more efficient if the keyboard supports NKRO (n-key rollover) to allow Plover to see all the pressed keys at once. An example keymap for Plover can be found in `planck/keymaps/default`. Switching to the `PLOVER` layer adjusts the position of the keyboard to support the number bar.
-To use Plover with QMK just enable NKRO and optionally adjust your layout if you have anything other than a standard layout. You may also want to purchase some steno-friendly keycaps to make it easier to hit multiple keys.
+To enable NKRO, add `NKRO_ENABLE = yes` in your `rules.mk` and make sure to press `NK_ON` to turn it on because `NKRO_ENABLE = yes` merely adds the possibility of switching to NKRO mode but it doesn't automatically switch to it. If you want to automatically switch, add `#define FORCE_NKRO` in your `config.h`.
+
+You may also need to adjust your layout, either in QMK or in Plover, if you have anything other than a standard layout. You may also want to purchase some steno-friendly keycaps to make it easier to hit multiple keys.
## Plover with Steno Protocol :id=plover-with-steno-protocol
-Plover also understands the language of several steno machines. QMK can speak a couple of these languages, TX Bolt and GeminiPR. An example layout can be found in `planck/keymaps/steno`.
+Plover also understands the language of several steno machines. QMK can speak a couple of these languages: TX Bolt and GeminiPR. An example layout can be found in `planck/keymaps/steno`.
+
+When QMK speaks to Plover over a steno protocol, Plover will not use the keyboard as input. This means that you can switch back and forth between a standard keyboard and your steno keyboard, or even switch layers from Plover to standard and back without needing to activate/deactivate Plover.
+
+In this mode, Plover expects to speak with a steno machine over a serial port so QMK will present itself to the operating system as a virtual serial port in addition to a keyboard.
-When QMK speaks to Plover over a steno protocol Plover will not use the keyboard as input. This means that you can switch back and forth between a standard keyboard and your steno keyboard, or even switch layers from Plover to standard and back without needing to activate/deactivate Plover.
+> Note: Due to hardware limitations, you might not be able to run both a virtual serial port and mouse emulation at the same time.
-In this mode Plover expects to speak with a steno machine over a serial port so QMK will present itself to the operating system as a virtual serial port in addition to a keyboard. By default QMK will speak the TX Bolt protocol but can be switched to GeminiPR; the last protocol used is stored in non-volatile memory so QMK will use the same protocol on restart.
+!> Serial stenography protocols are not supported on [V-USB keyboards](compatible_microcontrollers#atmel-avr).
-> Note: Due to hardware limitations you may not be able to run both a virtual serial port and mouse emulation at the same time.
+To enable stenography protocols, add the following lines to your `rules.mk`:
+```mk
+STENO_ENABLE = yes
+```
### TX Bolt :id=tx-bolt
-TX Bolt communicates the status of 24 keys over a very simple protocol in variable-sized (1-5 byte) packets.
+TX Bolt communicates the status of 24 keys over a simple protocol in variable-sized (1–4 bytes) packets.
-### GeminiPR :id=geminipr
+To select TX Bolt, add the following lines to your `rules.mk`:
+```mk
+STENO_ENABLE = yes
+STENO_PROTOCOL = txbolt
+```
-GeminiPR encodes 42 keys into a 6-byte packet. While TX Bolt contains everything that is necessary for standard stenography, GeminiPR opens up many more options, including supporting non-English theories.
+Each byte of the packet represents a different group of steno keys. Determining the group of a certain byte of the packet is done by checking the first two bits, the remaining bits are set if the corresponding steno key was pressed for the stroke. The last set of keys (as indicated by leading `11`) needs to keep track of less keys than there are bits so one of the bits is constantly 0.
-## Configuring QMK for Steno :id=configuring-qmk-for-steno
+The start of a new packet can be detected by comparing the group “ID†(the two MSBs) of the current byte to that of the previously received byte. If the group “ID†of the current byte is smaller or equal to that of the previous byte, it means that the current byte is the beginning of a new packet.
-Firstly, enable steno in your keymap's Makefile. You may also need disable mousekeys, extra keys, or another USB endpoint to prevent conflicts. The builtin USB stack for some processors only supports a certain number of USB endpoints and the virtual serial port needed for steno fills 3 of them.
+The format of TX Bolt packets is shown below.
+```
+00HWPKTS 01UE*OAR 10GLBPRF 110#ZDST
+```
+
+Examples of steno strokes and the associated packet:
+- `EUBG` = `01110000 10101000`
+- `WAZ` = `00010000 01000010 11001000`
+- `PHAPBGS` = `00101000 01000010 10101100 11000010`
+
+### GeminiPR :id=geminipr
-```make
+GeminiPR encodes 42 keys into a 6-byte packet. While TX Bolt contains everything that is necessary for standard stenography, GeminiPR opens up many more options, including differentiating between top and bottom `S-`, and supporting non-English theories.
+
+To select GeminiPR, add the following lines to your `rules.mk`:
+```mk
STENO_ENABLE = yes
-MOUSEKEY_ENABLE = no
+STENO_PROTOCOL = geminipr
```
-In your keymap create a new layer for Plover. You will need to include `keymap_steno.h`. See `planck/keymaps/steno/keymap.c` for an example. Remember to create a key to switch to the layer as well as a key for exiting the layer. If you would like to switch modes on the fly you can use the keycodes `QK_STENO_BOLT` and `QK_STENO_GEMINI`. If you only want to use one of the protocols you may set it up in your initialization function:
+All packets in the GeminiPR protocol consist of exactly six bytes, used as bit-arrays for different groups of keys. The beginning of a packet is indicated by setting the most significant bit (MSB) to 1 while setting the MSB of the remaining five bytes to 0.
-```c
-void eeconfig_init_user() {
- steno_set_mode(STENO_MODE_GEMINI); // or STENO_MODE_BOLT
-}
+The format of GeminiPR packets is shown below.
+```
+1 Fn #1 #2 #3 #4 #5 #6
+0 S1- S2- T- K- P- W- H-
+0 R- A- O- *1 *2 res1 res2
+0 pwr *3 *4 -E -U -F -R
+0 -P -B -L -G -T -S -D
+0 #7 #8 #9 #A #B #C -Z
+```
+
+Examples of steno strokes and the associated packet:
+- `EUBG` = `10000000 00000000 00000000 00001100 00101000 00000000`
+- `WAZ` = `10000000 00000010 00100000 00000000 00000000 00000001`
+- `PHAPBGS` = `10000000 00000101 00100000 00000000 01101010 00000000`
+
+### Switching protocols on the fly :id=switching-protocols-on-the-fly
+
+If you wish to switch the serial protocol used to transfer the steno chords without having to recompile your keyboard firmware every time, you can press the `QK_STENO_BOLT` and `QK_STENO_GEMINI` keycodes in order to switch protocols on the fly.
+
+To enable these special keycodes, add the following lines to your `rules.mk`:
+```mk
+STENO_ENABLE = yes
+STENO_PROTOCOL = all
```
-Once you have your keyboard flashed launch Plover. Click the 'Configure...' button. In the 'Machine' tab select the Stenotype Machine that corresponds to your desired protocol. Click the 'Configure...' button on this tab and enter the serial port or click 'Scan'. Baud rate is fine at 9600 (although you should be able to set as high as 115200 with no issues). Use the default settings for everything else (Data Bits: 8, Stop Bits: 1, Parity: N, no flow control).
+If you want to switch protocols programatically, as part of a custom macro for example, don't use `tap_code(QK_STENO_*)`, as `tap_code` only supports [basic keycodes](keycodes_basic). Instead, you should use `steno_set_mode(STENO_MODE_*)`, whose valid arguments are `STENO_MODE_BOLT` and `STENO_MODE_GEMINI`.
+
+The default protocol is Gemini PR but the last protocol used is stored in non-volatile memory so QMK will remember your choice between reboots of your keyboard — assuming that your keyboard features (emulated) EEPROM.
+
+Naturally, this option takes the most amount of firmware space as it needs to compile the code for all the available stenography protocols. In most cases, compiling a single stenography protocol is sufficient.
+
+The default value for `STENO_PROTOCOL` is `all`.
+
+## Configuring QMK for Steno :id=configuring-qmk-for-steno
+
+After enabling stenography and optionally selecting a protocol, you may also need disable mouse keys, extra keys, or another USB endpoint to prevent conflicts. The builtin USB stack for some processors only supports a certain number of USB endpoints and the virtual serial port needed for steno fills 3 of them.
+
+!> If you had *explicitly* set `VIRSTER_ENABLE = no`, none of the serial stenography protocols (GeminiPR, TX Bolt) will work properly. You are expected to either set it to `yes`, remove the line from your `rules.mk` or send the steno chords yourself in an alternative way using the [provided interceptable hooks](#interfacing-with-the-code).
+
+In your keymap, create a new layer for Plover, that you can fill in with the [steno keycodes](#keycode-reference) (you will need to include `keymap_steno.h`, see `planck/keymaps/steno/keymap.c` for an example). Remember to create a key to switch to the layer as well as a key for exiting the layer.
+
+Once you have your keyboard flashed, launch Plover. Click the 'Configure...' button. In the 'Machine' tab, select the Stenotype Machine that corresponds to your desired protocol. Click the 'Configure...' button on this tab and enter the serial port or click 'Scan'. Baud rate is fine at 9600 (although you should be able to set as high as 115200 with no issues). Use the default settings for everything else (Data Bits: 8, Stop Bits: 1, Parity: N, no flow control).
-On the display tab click 'Open stroke display'. With Plover disabled you should be able to hit keys on your keyboard and see them show up in the stroke display window. Use this to make sure you have set up your keymap correctly. You are now ready to steno!
+To test your keymap, you can chord keys on your keyboard and either look at the output of the 'paper tape' (Tools > Paper Tape) or that of the 'layout display' (Tools > Layout Display). If your strokes correctly show up, you are now ready to steno!
## Learning Stenography :id=learning-stenography
@@ -60,7 +121,7 @@ On the display tab click 'Open stroke display'. With Plover disabled you should
The steno code has three interceptable hooks. If you define these functions, they will be called at certain points in processing; if they return true, processing continues, otherwise it's assumed you handled things.
```c
-bool send_steno_chord_user(steno_mode_t mode, uint8_t chord[6]);
+bool send_steno_chord_user(steno_mode_t mode, uint8_t chord[MAX_STROKE_SIZE]);
```
This function is called when a chord is about to be sent. Mode will be one of `STENO_MODE_BOLT` or `STENO_MODE_GEMINI`. This represents the actual chord that would be sent via whichever protocol. You can modify the chord provided to alter what gets sent. Remember to return true if you want the regular sending process to happen.
@@ -72,15 +133,23 @@ bool process_steno_user(uint16_t keycode, keyrecord_t *record) { return true; }
This function is called when a keypress has come in, before it is processed. The keycode should be one of `QK_STENO_BOLT`, `QK_STENO_GEMINI`, or one of the `STN_*` key values.
```c
-bool postprocess_steno_user(uint16_t keycode, keyrecord_t *record, steno_mode_t mode, uint8_t chord[6], int8_t pressed);
+bool post_process_steno_user(uint16_t keycode, keyrecord_t *record, steno_mode_t mode, uint8_t chord[MAX_STROKE_SIZE], int8_t n_pressed_keys);
```
-This function is called after a key has been processed, but before any decision about whether or not to send a chord. If `IS_PRESSED(record->event)` is false, and `pressed` is 0 or 1, the chord will be sent shortly, but has not yet been sent. This is where to put hooks for things like, say, live displays of steno chords or keys.
+This function is called after a key has been processed, but before any decision about whether or not to send a chord. This is where to put hooks for things like, say, live displays of steno chords or keys.
+If `IS_PRESSED(record->event)` is false, and `n_pressed_keys` is 0 or 1, the chord will be sent shortly, but has not yet been sent. This relieves you of the need of keeping track of where a packet ends and another begins.
+
+The `chord` argument contains the packet of the current chord as specified by the protocol in use. This is *NOT* simply a list of chorded steno keys of the form `[STN_E, STN_U, STN_BR, STN_GR]`. Refer to the appropriate protocol section of this document to learn more about the format of the packets in your steno protocol/mode of choice.
+
+The `n_pressed_keys` argument is the number of physical keys actually being held down.
+This is not always equal to the number of bits set to 1 (aka the [Hamming weight](https://en.wikipedia.org/wiki/Hamming_weight)) in `chord` because it is possible to simultaneously press down four keys, then release three of those four keys and then press yet another key while the fourth finger is still holding down its key.
+At the end of this scenario given as an example, `chord` would have five bits set to 1 but
+`n_pressed_keys` would be set to 2 because there are only two keys currently being pressed down.
## Keycode Reference :id=keycode-reference
-As defined in `keymap_steno.h`.
+You must include `keymap_steno.h` to your `keymap.c` with `#include "keymap_steno.h"` before you can use these keycodes
> Note: TX Bolt does not support the full set of keys. The TX Bolt implementation in QMK will map the GeminiPR keys to the nearest TX Bolt key so that one key map will work for both.
@@ -115,8 +184,8 @@ As defined in `keymap_steno.h`.
|`STN_E`|`STN_E`| `E` vowel|
|`STN_U`|`STN_U`| `U` vowel|
|`STN_FR`|`STN_FR`| `-F`|
-|`STN_PR`|`STN_PR`| `-P`|
|`STN_RR`|`STN_RR`| `-R`|
+|`STN_PR`|`STN_PR`| `-P`|
|`STN_BR`|`STN_BR`| `-B`|
|`STN_LR`|`STN_LR`| `-L`|
|`STN_GR`|`STN_GR`| `-G`|
@@ -124,10 +193,10 @@ As defined in `keymap_steno.h`.
|`STN_SR`|`STN_SR`| `-S`|
|`STN_DR`|`STN_DR`| `-D`|
|`STN_ZR`|`STN_ZR`| `-Z`|
-|`STN_FN`|| (GeminiPR only)|
-|`STN_RES1`||(GeminiPR only)|
-|`STN_RES2`||(GeminiPR only)|
-|`STN_PWR`||(GeminiPR only)|
+|`STN_FN`|| (Function)|
+|`STN_RES1`||(Reset 1)|
+|`STN_RES2`||(Reset 2)|
+|`STN_PWR`||(Power)|
If you do not want to hit two keys with one finger combined keycodes can be used. These are also defined in `keymap_steno.h`, and causes both keys to be reported as pressed or released. To use these keycodes define `STENO_COMBINEDMAP` in your `config.h` file.
diff --git a/docs/feature_swap_hands.md b/docs/feature_swap_hands.md
index 654108ae7068..6768020f124a 100644
--- a/docs/feature_swap_hands.md
+++ b/docs/feature_swap_hands.md
@@ -31,3 +31,16 @@ Note that the array indices are reversed same as the matrix and the values are o
|`SH_OS` |One shot swap hands: toggles while pressed or until next key press. |
`SH_TT` swap-hands tap-toggle key is similar to [layer tap-toggle](feature_layers.md?id=switching-and-toggling-layers). Tapping repeatedly (5 taps by default) will toggle swap-hands on or off, like `SH_TG`. Tap-toggle count can be changed by defining a value for `TAPPING_TOGGLE`.
+
+## Encoder Mapping
+
+When using an encoder mapping, it's also able to handle swapping encoders between sides, too.
+
+Encoder indexes are defined as left-to-right, and the extent of the array needs to match the number of encoders on the keyboard.
+
+As an example, if a split keyboard has a single encoder per side, you can swap the order by using the following code in your keymap:
+```c
+#if defined(SWAP_HANDS_ENABLE) && defined(ENCODER_MAP_ENABLE)
+const uint8_t PROGMEM encoder_hand_swap_config[NUM_ENCODERS] = { 1, 0 };
+#endif
+```
diff --git a/docs/feature_tap_dance.md b/docs/feature_tap_dance.md
index c055a9989a42..1062e3224395 100644
--- a/docs/feature_tap_dance.md
+++ b/docs/feature_tap_dance.md
@@ -14,55 +14,48 @@ Optionally, you might want to set a custom `TAPPING_TERM` time by adding somethi
```c
#define TAPPING_TERM 175
+#define TAPPING_TERM_PER_KEY
```
-The `TAPPING_TERM` time is the maximum time allowed between taps of your Tap Dance key, and is measured in milliseconds. For example, if you used the above `#define` statement and set up a Tap Dance key that sends `Space` on single-tap and `Enter` on double-tap, then this key will send `ENT` only if you tap this key twice in less than 175ms. If you tap the key, wait more than 175ms, and tap the key again you'll end up sending `SPC SPC` instead.
+The `TAPPING_TERM` time is the maximum time allowed between taps of your Tap Dance key, and is measured in milliseconds. For example, if you used the above `#define` statement and set up a Tap Dance key that sends `Space` on single-tap and `Enter` on double-tap, then this key will send `ENT` only if you tap this key twice in less than 175ms. If you tap the key, wait more than 175ms, and tap the key again you'll end up sending `SPC SPC` instead. The `TAPPING_TERM_PER_KEY` definition is only needed if you control the tapping term through a [custom `get_tapping_term` function](tap_hold.md#tapping_term), which may be needed because `TAPPING_TERM` affects not just tap-dance keys.
-Next, you will want to define some tap-dance keys, which is easiest to do with the `TD()` macro, that takes a number which will later be used as an index into the `tap_dance_actions` array.
+Next, you will want to define some tap-dance keys, which is easiest to do with the `TD()` macro. That macro takes a number which will later be used as an index into the `tap_dance_actions` array and turns it into a tap-dance keycode.
After this, you'll want to use the `tap_dance_actions` array to specify what actions shall be taken when a tap-dance key is in action. Currently, there are five possible options:
* `ACTION_TAP_DANCE_DOUBLE(kc1, kc2)`: Sends the `kc1` keycode when tapped once, `kc2` otherwise. When the key is held, the appropriate keycode is registered: `kc1` when pressed and held, `kc2` when tapped once, then pressed and held.
* `ACTION_TAP_DANCE_LAYER_MOVE(kc, layer)`: Sends the `kc` keycode when tapped once, or moves to `layer`. (this functions like the `TO` layer keycode).
- * This is the same as `ACTION_TAP_DANCE_DUAL_ROLE`, but renamed to something that is clearer about its functionality. Both names will work.
* `ACTION_TAP_DANCE_LAYER_TOGGLE(kc, layer)`: Sends the `kc` keycode when tapped once, or toggles the state of `layer`. (this functions like the `TG` layer keycode).
* `ACTION_TAP_DANCE_FN(fn)`: Calls the specified function - defined in the user keymap - with the final tap count of the tap dance action.
* `ACTION_TAP_DANCE_FN_ADVANCED(on_each_tap_fn, on_dance_finished_fn, on_dance_reset_fn)`: Calls the first specified function - defined in the user keymap - on every tap, the second function when the dance action finishes (like the previous option), and the last function when the tap dance action resets.
-* ~~`ACTION_TAP_DANCE_FN_ADVANCED_TIME(on_each_tap_fn, on_dance_finished_fn, on_dance_reset_fn, tap_specific_tapping_term)`~~: This functions identically to the `ACTION_TAP_DANCE_FN_ADVANCED` function, but uses a custom tapping term for it, instead of the predefined `TAPPING_TERM`.
- * This is deprecated in favor of the Per Key Tapping Term functionality, as outlined [here](tap_hold.md#tapping-term). You'd want to check for the specific `TD()` macro that you want to use (such as `TD(TD_ESC_CAPS)`) instead of using this specific Tap Dance function.
The first option is enough for a lot of cases, that just want dual roles. For example, `ACTION_TAP_DANCE_DOUBLE(KC_SPC, KC_ENT)` will result in `Space` being sent on single-tap, `Enter` otherwise.
!> Keep in mind that only [basic keycodes](keycodes_basic.md) are supported here. Custom keycodes are not supported.
-Similar to the first option, the second option is good for simple layer-switching cases.
+Similar to the first option, the second and third option are good for simple layer-switching cases.
-For more complicated cases, use the third or fourth options (examples of each are listed below).
-
-Finally, the fifth option is particularly useful if your non-Tap-Dance keys start behaving weirdly after adding the code for your Tap Dance keys. The likely problem is that you changed the `TAPPING_TERM` time to make your Tap Dance keys easier for you to use, and that this has changed the way your other keys handle interrupts.
+For more complicated cases, like blink the LEDs, fiddle with the backlighting, and so on, use the fourth or fifth option. Examples of each are listed below.
## Implementation Details :id=implementation
Well, that's the bulk of it! You should now be able to work through the examples below, and to develop your own Tap Dance functionality. But if you want a deeper understanding of what's going on behind the scenes, then read on for the explanation of how it all works!
-The main entry point is `process_tap_dance()`, called from `process_record_quantum()`, which is run for every keypress, and our handler gets to run early. This function checks whether the key pressed is a tap-dance key. If it is not, and a tap-dance was in action, we handle that first, and enqueue the newly pressed key. If it is a tap-dance key, then we check if it is the same as the already active one (if there's one active, that is). If it is not, we fire off the old one first, then register the new one. If it was the same, we increment the counter and reset the timer.
-
-This means that you have `TAPPING_TERM` time to tap the key again; you do not have to input all the taps within a single `TAPPING_TERM` timeframe. This allows for longer tap counts, with minimal impact on responsiveness.
+Let's go over the three functions mentioned in `ACTION_TAP_DANCE_FN_ADVANCED` in a little more detail. They all receive the same two arguments: a pointer to a structure that holds all dance related state information, and a pointer to a use case specific state variable. The three functions differ in when they are called. The first, `on_each_tap_fn()`, is called every time the tap dance key is *pressed*. Before it is called, the counter is incremented and the timer is reset. The second function, `on_dance_finished_fn()`, is called when the tap dance is interrupted or ends because `TAPPING_TERM` milliseconds have passed since the last tap. When the `finished` field of the dance state structure is set to `true`, the `on_dance_finished_fn()` is skipped. After `on_dance_finished_fn()` was called or would have been called, but no sooner than when the tap dance key is *released*, `on_dance_reset_fn()` is called. It is possible to end a tap dance immediately, skipping `on_dance_finished_fn()`, but not `on_dance_reset_fn`, by calling `reset_tap_dance(state)`.
-Our next stop is `tap_dance_task()`. This handles the timeout of tap-dance keys.
+To accomplish this logic, the tap dance mechanics use three entry points. The main entry point is `process_tap_dance()`, called from `process_record_quantum()` *after* `process_record_kb()` and `process_record_user()`. This function is responsible for calling `on_each_tap_fn()` and `on_dance_reset_fn()`. In order to handle interruptions of a tap dance, another entry point, `preprocess_tap_dance()` is run right at the beginning of `process_record_quantum()`. This function checks whether the key pressed is a tap-dance key. If it is not, and a tap-dance was in action, we handle that first, and enqueue the newly pressed key. If it is a tap-dance key, then we check if it is the same as the already active one (if there's one active, that is). If it is not, we fire off the old one first, then register the new one. Finally, `tap_dance_task()` periodically checks whether `TAPPING_TERM` has passed since the last key press and finishes a tap dance if that is the case.
-For the sake of flexibility, tap-dance actions can be either a pair of keycodes, or a user function. The latter allows one to handle higher tap counts, or do extra things, like blink the LEDs, fiddle with the backlighting, and so on. This is accomplished by using an union, and some clever macros.
+This means that you have `TAPPING_TERM` time to tap the key again; you do not have to input all the taps within a single `TAPPING_TERM` timeframe. This allows for longer tap counts, with minimal impact on responsiveness.
## Examples :id=examples
-### Simple Example :id=simple-example
+### Simple Example: Send `ESC` on Single Tap, `CAPS_LOCK` on Double Tap :id=simple-example
Here's a simple example for a single definition:
1. In your `rules.mk`, add `TAP_DANCE_ENABLE = yes`
-2. In your `config.h` (which you can copy from `qmk_firmware/keyboards/planck/config.h` to your keymap directory), add `#define TAPPING_TERM 200`
-3. In your `keymap.c` file, define the variables and definitions, then add to your keymap:
+2. In your `keymap.c` file, define the variables and definitions, then add to your keymap:
```c
// Tap Dance declarations
@@ -92,40 +85,15 @@ All the enums used in the examples are declared like this:
```c
// Enums defined for all examples:
enum {
- CT_SE,
- CT_CLN,
+ TD_ESC_CAPS,
CT_EGG,
CT_FLSH,
- X_TAP_DANCE
-};
-```
-
-#### Example 1: Send `:` on Single Tap, `;` on Double Tap :id=example-1
-
-```c
-void dance_cln_finished(qk_tap_dance_state_t *state, void *user_data) {
- if (state->count == 1) {
- register_code16(KC_COLN);
- } else {
- register_code(KC_SCLN);
- }
-}
-
-void dance_cln_reset(qk_tap_dance_state_t *state, void *user_data) {
- if (state->count == 1) {
- unregister_code16(KC_COLN);
- } else {
- unregister_code(KC_SCLN);
- }
-}
-
-// All tap dance functions would go here. Only showing this one.
-qk_tap_dance_action_t tap_dance_actions[] = {
- [CT_CLN] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, dance_cln_finished, dance_cln_reset),
+ CT_CLN,
+ X_CTL,
};
```
-#### Example 2: Send "Safety Dance!" After 100 Taps :id=example-2
+#### Example 1: Send "Safety Dance!" After 100 Taps :id=example-1
```c
void dance_egg(qk_tap_dance_state_t *state, void *user_data) {
@@ -140,7 +108,7 @@ qk_tap_dance_action_t tap_dance_actions[] = {
};
```
-#### Example 3: Turn LED Lights On Then Off, One at a Time :id=example-3
+#### Example 2: Turn LED Lights On Then Off, One at a Time :id=example-2
```c
// On each tap, light up one LED, from right to left
@@ -181,15 +149,74 @@ void dance_flsh_reset(qk_tap_dance_state_t *state, void *user_data) {
ergodox_right_led_3_off();
}
-// All tap dances now put together. Example 3 is "CT_FLASH"
+// All tap dances now put together. Example 2 is "CT_FLSH"
qk_tap_dance_action_t tap_dance_actions[] = {
- [CT_SE] = ACTION_TAP_DANCE_DOUBLE(KC_SPC, KC_ENT),
- [CT_CLN] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, dance_cln_finished, dance_cln_reset),
+ [TD_ESC_CAPS] = ACTION_TAP_DANCE_DOUBLE(KC_ESC, KC_CAPS),
[CT_EGG] = ACTION_TAP_DANCE_FN(dance_egg),
[CT_FLSH] = ACTION_TAP_DANCE_FN_ADVANCED(dance_flsh_each, dance_flsh_finished, dance_flsh_reset)
};
```
+#### Example 3: Send `:` on Tap, `;` on Hold :id=example-3
+
+With a little effort, powerful tap-hold configurations can be implemented as tap dances. To emit taps as early as possible, we need to act on releases of the tap dance key. There is no callback for this in the tap dance framework, so we use `process_record_user()`.
+
+```c
+typedef struct {
+ uint16_t tap;
+ uint16_t hold;
+ uint16_t held;
+} tap_dance_tap_hold_t;
+
+bool process_record_user(uint16_t keycode, keyrecord_t *record) {
+ qk_tap_dance_action_t *action;
+
+ switch (keycode) {
+ case TD(CT_CLN): // list all tap dance keycodes with tap-hold configurations
+ action = &tap_dance_actions[TD_INDEX(keycode)];
+ if (!record->event.pressed && action->state.count && !action->state.finished) {
+ tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)action->user_data;
+ tap_code16(tap_hold->tap);
+ }
+ }
+ return true;
+}
+
+void tap_dance_tap_hold_finished(qk_tap_dance_state_t *state, void *user_data) {
+ tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data;
+
+ if (state->pressed) {
+ if (state->count == 1
+#ifndef PERMISSIVE_HOLD
+ && !state->interrupted
+#endif
+ ) {
+ register_code16(tap_hold->hold);
+ tap_hold->held = tap_hold->hold;
+ } else {
+ register_code16(tap_hold->tap);
+ tap_hold->held = tap_hold->tap;
+ }
+ }
+}
+
+void tap_dance_tap_hold_reset(qk_tap_dance_state_t *state, void *user_data) {
+ tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)user_data;
+
+ if (tap_hold->held) {
+ unregister_code16(tap_hold->held);
+ tap_hold->held = 0;
+ }
+}
+
+#define ACTION_TAP_DANCE_TAP_HOLD(tap, hold) \
+ { .fn = {NULL, tap_dance_tap_hold_finished, tap_dance_tap_hold_reset}, .user_data = (void *)&((tap_dance_tap_hold_t){tap, hold, 0}), }
+
+qk_tap_dance_action_t tap_dance_actions[] = {
+ [CT_CLN] = ACTION_TAP_DANCE_TAP_HOLD(KC_COLN, KC_SCLN),
+};
+```
+
#### Example 4: 'Quad Function Tap-Dance' :id=example-4
By [DanielGGordon](https://github.com/danielggordon)
@@ -305,7 +332,8 @@ void x_finished(qk_tap_dance_state_t *state, void *user_data) {
// Last case is for fast typing. Assuming your key is `f`:
// For example, when typing the word `buffer`, and you want to make sure that you send `ff` and not `Esc`.
// In order to type `ff` when typing fast, the next character will have to be hit within the `TAPPING_TERM`, which by default is 200ms.
- case TD_DOUBLE_SINGLE_TAP: tap_code(KC_X); register_code(KC_X);
+ case TD_DOUBLE_SINGLE_TAP: tap_code(KC_X); register_code(KC_X); break;
+ default: break;
}
}
@@ -314,8 +342,9 @@ void x_reset(qk_tap_dance_state_t *state, void *user_data) {
case TD_SINGLE_TAP: unregister_code(KC_X); break;
case TD_SINGLE_HOLD: unregister_code(KC_LCTL); break;
case TD_DOUBLE_TAP: unregister_code(KC_ESC); break;
- case TD_DOUBLE_HOLD: unregister_code(KC_LALT);
- case TD_DOUBLE_SINGLE_TAP: unregister_code(KC_X);
+ case TD_DOUBLE_HOLD: unregister_code(KC_LALT); break;
+ case TD_DOUBLE_SINGLE_TAP: unregister_code(KC_X); break;
+ default: break;
}
xtap_state.state = TD_NONE;
}
@@ -327,9 +356,7 @@ qk_tap_dance_action_t tap_dance_actions[] = {
And then simply use `TD(X_CTL)` anywhere in your keymap.
-If you want to implement this in your userspace, then you may want to check out how [DanielGGordon](https://github.com/qmk/qmk_firmware/tree/master/users/gordon) has implemented this in their userspace.
-
-> In this configuration "hold" takes place **after** tap dance timeout (see `ACTION_TAP_DANCE_FN_ADVANCED_TIME`). To achieve instant hold, remove `state->interrupted` checks in conditions. As a result you may use comfortable longer tapping periods to have more time for taps and not to wait too long for holds (try starting with doubled `TAPPING_TERM`).
+> In this configuration "hold" takes place **after** tap dance timeout. To achieve instant hold, remove `state->interrupted` checks in conditions. As a result you may use comfortable longer tapping periods to have more time for taps and not to wait too long for holds (try starting with doubled `TAPPING_TERM`).
#### Example 5: Using tap dance for advanced mod-tap and layer-tap keys :id=example-5
@@ -511,8 +538,18 @@ void ql_reset(qk_tap_dance_state_t *state, void *user_data) {
// Associate our tap dance key with its functionality
qk_tap_dance_action_t tap_dance_actions[] = {
- [QUOT_LAYR] = ACTION_TAP_DANCE_FN_ADVANCED_TIME(NULL, ql_finished, ql_reset, 275)
+ [QUOT_LAYR] = ACTION_TAP_DANCE_FN_ADVANCED(NULL, ql_finished, ql_reset)
};
+
+// Set a long-ish tapping term for tap-dance keys
+uint16_t get_tapping_term(uint16_t keycode, keyrecord_t *record) {
+ switch (keycode) {
+ case QK_TAP_DANCE ... QK_TAP_DANCE_MAX:
+ return 275;
+ default:
+ return TAPPING_TERM;
+ }
+}
```
The above code is similar to that used in previous examples. The one point to note is that we need to be able to check which layers are active at any time so we can toggle them if needed. To do this we use the `layer_state_is(layer)` function which returns `true` if the given `layer` is active.
@@ -521,6 +558,6 @@ The use of `cur_dance()` and `ql_tap_state` mirrors the above examples.
The `case: TD_SINGLE_TAP` in `ql_finished` is similar to the above examples. The `TD_SINGLE_HOLD` case works in conjunction with `ql_reset()` to switch to `_MY_LAYER` while the tap dance key is held, and to switch away from `_MY_LAYER` when the key is released. This mirrors the use of `MO(_MY_LAYER)`. The `TD_DOUBLE_TAP` case works by checking whether `_MY_LAYER` is the active layer, and toggling it on or off accordingly. This mirrors the use of `TG(_MY_LAYER)`.
-`tap_dance_actions[]` works similar to the above examples. Note that I used `ACTION_TAP_DANCE_FN_ADVANCED_TIME()` instead of `ACTION_TAP_DANCE_FN_ADVANCED()`. This is because I like my `TAPPING_TERM` to be short (\~175ms) for my non-tap-dance keys but find that this is too quick for me to reliably complete tap dance actions - thus the increased time of 275ms here.
+`tap_dance_actions[]` works similar to the above examples. Note that, additionally, I set a longer tapping term for the tap dance keys. This is because I like my `TAPPING_TERM` to be short (\~175ms) for my non-tap-dance keys but find that this is too quick for me to reliably complete tap dance actions - thus the increased time of 275ms here. In order for the per-key tapping terms to take effect, `TAPPING_TERM_PER_KEY` must be defined in your `config.h`.
Finally, to get this tap dance key working, be sure to include `TD(QUOT_LAYR)` in your `keymaps[]`.
diff --git a/docs/feature_terminal.md b/docs/feature_terminal.md
deleted file mode 100644
index f85062216543..000000000000
--- a/docs/feature_terminal.md
+++ /dev/null
@@ -1,107 +0,0 @@
-# Terminal
-
-> This feature is currently *huge*, and should probably only be put on boards with a lot of memory, or for fun.
-
-The terminal feature is a command-line-like interface designed to communicate through a text editor with keystrokes. It's beneficial to turn off auto-indent features in your editor.
-
-To enable, stick this in your `rules.mk` or `Makefile`:
-
- TERMINAL_ENABLE = yes
-
-And use the `TERM_ON` and `TERM_OFF` keycodes to turn it on or off.
-
-When enabled, a `> ` prompt will appear, where you'll be able to type, backspace (a bell will ding if you reach the beginning and audio is enabled), and hit enter to send the command. Arrow keys are currently disabled so it doesn't get confused. Moving your cursor around with the mouse is discouraged.
-
-`#define TERMINAL_HELP` enables some other output helpers that aren't really needed with this page.
-
-Pressing "up" and "down" will allow you to cycle through the past 5 commands entered.
-
-## Future Ideas
-
-* Keyboard/user-extensible commands
-* Smaller footprint
-* Arrow key support
-* Command history - Done
-* SD card support
-* LCD support for buffer display
-* Keycode -> name string LUT
-* Layer status
-* *Analog/digital port read/write*
-* RGB mode stuff
-* Macro definitions
-* EEPROM read/write
-* Audio control
-
-## Current Commands
-
-### `about`
-
-Prints out the current version of QMK with a build date:
-
-```
-> about
-QMK Firmware
- v0.5.115-7-g80ed73-dirty
- Built: 2017-08-29-20:24:44
-```
-
-
-### `print-buffer`
-
-Outputs the last 5 commands entered
-
-```
-> print-buffer
-0. print-buffer
-1. help
-2. about
-3. keymap 0
-4. help
-5. flush-buffer
-```
-
-### `flush-buffer`
-
-Clears command buffer
-```
-> flush-buffer
-Buffer cleared!
-```
-
-
-### `help`
-
-
-Prints out the available commands:
-
-```
-> help
-commands available:
- about help keycode keymap exit print-buffer flush-buffer
-```
-
-### `keycode