aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/ChangeLog/20250223.md135
-rw-r--r--docs/_sidebar.json3
-rw-r--r--docs/breaking_changes.md20
-rw-r--r--docs/breaking_changes_history.md1
-rw-r--r--docs/cli_commands.md15
-rw-r--r--docs/contributing.md4
-rw-r--r--docs/custom_quantum_functions.md11
-rw-r--r--docs/drivers/i2c.md124
-rw-r--r--docs/drivers/spi.md8
-rw-r--r--docs/drivers/uart.md26
-rw-r--r--docs/features/community_modules.md142
-rw-r--r--docs/features/leader_key.md15
-rw-r--r--docs/reference_info_json.md4
-rw-r--r--docs/reference_keymap_extras.md1
-rw-r--r--docs/tap_hold.md163
15 files changed, 590 insertions, 82 deletions
diff --git a/docs/ChangeLog/20250223.md b/docs/ChangeLog/20250223.md
new file mode 100644
index 0000000000..858848a60b
--- /dev/null
+++ b/docs/ChangeLog/20250223.md
@@ -0,0 +1,135 @@
+# QMK Breaking Changes - 2025 February 23 Changelog
+
+## Notable Features
+
+### Community Modules ([#24848](https://github.com/qmk/qmk_firmware/pull/24848))
+
+Community Modules are a feature within QMK which allows code to be implemented by third parties, making it available for other people to import into their own builds.
+
+These modules can provide implementations which override or enhance normal QMK processing; initialization, key processing, suspend, and shutdown are some of the provided hooks which modules may currently implement.
+
+See the [Community Modules documentation](../features/community_modules) for more information, including the full list of available hooks.
+
+First-class support for [External Userspace](../newbs_external_userspace) is included out of the box, so there's even more reason to take the plunge and convert your keymap builds to a userspace repository!
+
+::: tip
+An example with a new keycode and some debugging information in the QMK repository [lives here](https://github.com/qmk/qmk_firmware/tree/master/modules/qmk/hello_world), and a community module port of [getreuer's SOCD Cleaner](https://getreuer.info/posts/keyboards/socd-cleaner/) can be found in [tzarc's modules repo](https://github.com/tzarc/qmk_modules).
+:::
+
+### Chordal Hold ([#24560](https://github.com/qmk/qmk_firmware/pull/24560))
+
+Chordal Hold implements, by default, an "opposite hands" rule. Suppose a tap-hold key is pressed and then, before the tapping term, another key is pressed. With Chordal Hold, the tap-hold key is settled as tapped if the two keys are on the same hand.
+
+Chordal Hold may be useful to avoid accidental modifier activation with mod-taps, particularly in rolled keypresses when using home row mods.
+
+See the [Chordal Hold documentation](../tap_hold#chordal-hold) for more information.
+
+## Changes Requiring User Action
+
+### Updated Keyboard Codebases
+
+| Old Keyboard Name | New Keyboard Name |
+|-------------------------|-----------------------------|
+| cxt_studio | cxt_studio/12e4 |
+| ergodox_stm32 | handwired/ergodox_stm32 |
+| ploopyco/mouse | ploopyco/mouse/rev1_002 |
+| ploopyco/trackball/rev1 | ploopyco/trackball/rev1_004 |
+| ymdk/id75 | ymdk/id75/f103 |
+
+## Deprecation Notices
+
+In line with the [notice period](../support_deprecation_policy#how-much-advance-notice-will-be-given), deprecation notices for larger items are listed here.
+
+### DEFAULT_FOLDER removal ([#24836](https://github.com/qmk/qmk_firmware/pull/24836))
+
+`DEFAULT_FOLDER` was originally introduced to work around limitations within the build system.
+Parent folders containing common configuration would create invalid build targets.
+
+With the introduction of [`keyboard.json`](./20240526#keyboard-json) as a configuration file, the build system now has a consistent method to detect build targets.
+The `DEFAULT_FOLDER` functionality is now redundant and the intent is for `rules.mk` to become pure configuration.
+
+Backwards compatibility of build targets has been maintained where possible.
+
+## Full changelist
+
+Core:
+* Chaining OSL and MO ([#23065](https://github.com/qmk/qmk_firmware/pull/23065))
+* Add extra keymap for EurKEY layout ([#24241](https://github.com/qmk/qmk_firmware/pull/24241))
+* Add leader_add_user callback ([#24266](https://github.com/qmk/qmk_firmware/pull/24266))
+* always return audio pin to 0 on ARM ([#24503](https://github.com/qmk/qmk_firmware/pull/24503))
+* Update Starlight matrix effects ([#24521](https://github.com/qmk/qmk_firmware/pull/24521))
+* Refactor Hue Breathing matrix effect with runner ([#24525](https://github.com/qmk/qmk_firmware/pull/24525))
+* Ensure timer_read() is safe to call from interrupt handlers on ARM ([#24529](https://github.com/qmk/qmk_firmware/pull/24529))
+* Update Raindrops effect to respect LED range limits ([#24531](https://github.com/qmk/qmk_firmware/pull/24531))
+* Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. ([#24560](https://github.com/qmk/qmk_firmware/pull/24560))
+* Azoteq - improve I2C behaviour while polling. ([#24611](https://github.com/qmk/qmk_firmware/pull/24611))
+* macOS install: remove bad ARM toolchains ([#24637](https://github.com/qmk/qmk_firmware/pull/24637))
+* small refactoring of TIMER_DIFF ([#24678](https://github.com/qmk/qmk_firmware/pull/24678))
+* Subscript alef correction ([#24707](https://github.com/qmk/qmk_firmware/pull/24707))
+* Created SH1107 driver for quantum painter ([#24724](https://github.com/qmk/qmk_firmware/pull/24724))
+* [CI] Regenerate Files ([#24772](https://github.com/qmk/qmk_firmware/pull/24772))
+* Patch up issue for inverted complementary output on Backlight ([#24794](https://github.com/qmk/qmk_firmware/pull/24794))
+* Patch up issue when compile with APA102 driver ([#24800](https://github.com/qmk/qmk_firmware/pull/24800))
+* Consolidate send_string implementations. ([#24817](https://github.com/qmk/qmk_firmware/pull/24817))
+* Consolidate timer_elapsed implementations ([#24830](https://github.com/qmk/qmk_firmware/pull/24830))
+* `i2c_master`: remove deprecated functions ([#24832](https://github.com/qmk/qmk_firmware/pull/24832))
+* Resolve keyboard_aliases when processing keyboard make targets ([#24834](https://github.com/qmk/qmk_firmware/pull/24834))
+* LED drivers: remove deprecated defines ([#24837](https://github.com/qmk/qmk_firmware/pull/24837))
+* `ferris/0_1`: update I2C API usage ([#24839](https://github.com/qmk/qmk_firmware/pull/24839))
+* Unify i2c_master headers ([#24846](https://github.com/qmk/qmk_firmware/pull/24846))
+* Community modules ([#24848](https://github.com/qmk/qmk_firmware/pull/24848))
+* Relocate base WS2812 code ([#24850](https://github.com/qmk/qmk_firmware/pull/24850))
+* Unify UART headers ([#24855](https://github.com/qmk/qmk_firmware/pull/24855))
+* Unify spi_master headers ([#24857](https://github.com/qmk/qmk_firmware/pull/24857))
+* Invoke `process_record_via` after `_user`/`_kb` have a chance to handle it. ([#24879](https://github.com/qmk/qmk_firmware/pull/24879))
+
+CLI:
+* Extend lint to reject 'blank' files ([#23994](https://github.com/qmk/qmk_firmware/pull/23994))
+* `qmk docs`: restore `--port` and `--browser` arguments ([#24623](https://github.com/qmk/qmk_firmware/pull/24623))
+* Update via2json layout macro searching ([#24640](https://github.com/qmk/qmk_firmware/pull/24640))
+* Change `new-keymap` keymap name prompt ([#24701](https://github.com/qmk/qmk_firmware/pull/24701))
+* default_keyboard.h generation tweaks ([#24715](https://github.com/qmk/qmk_firmware/pull/24715))
+* Ensure `qmk flash` rejects invalid files for uf2 compatible bootloaders ([#24802](https://github.com/qmk/qmk_firmware/pull/24802))
+* Reject readme dummy content ([#24913](https://github.com/qmk/qmk_firmware/pull/24913))
+
+Submodule updates:
+* chibios: usb_main: remove OTG sof workaround ([#24259](https://github.com/qmk/qmk_firmware/pull/24259))
+* Update ChibiOS to latest stable branch. ([#24651](https://github.com/qmk/qmk_firmware/pull/24651))
+* Update ChibiOS `stable_21.11.x`. ([#24714](https://github.com/qmk/qmk_firmware/pull/24714))
+* Update ChibiOS-Contrib. ([#24803](https://github.com/qmk/qmk_firmware/pull/24803))
+
+Keyboards:
+* refactor: move ymdk/id75 to revision ([#24590](https://github.com/qmk/qmk_firmware/pull/24590))
+* skyloong/gk61: Remove overriding of core keycode behaviour ([#24655](https://github.com/qmk/qmk_firmware/pull/24655))
+* moky/moky88: Remove use of deprecated defines ([#24656](https://github.com/qmk/qmk_firmware/pull/24656))
+* Updating Promenade keyboard (Missing keys in matrix, other minor changes to keymap) ([#24705](https://github.com/qmk/qmk_firmware/pull/24705))
+* Moving cxt_studio keyboard to own folder ([#24748](https://github.com/qmk/qmk_firmware/pull/24748))
+* Add CXT Studio 12E3 keyboard ([#24749](https://github.com/qmk/qmk_firmware/pull/24749))
+* Add Silakka54 keyboard ([#24757](https://github.com/qmk/qmk_firmware/pull/24757))
+* Add more layout for skiller_sgk50_s4 ([#24784](https://github.com/qmk/qmk_firmware/pull/24784))
+* Add watchdog service to RGBKB Sol 3 ([#24786](https://github.com/qmk/qmk_firmware/pull/24786))
+* Migrate some DEFAULT_FOLDER to keyboard_aliases.hjson ([#24835](https://github.com/qmk/qmk_firmware/pull/24835))
+* Remove DEFAULT_FOLDER where keyboard aliases already exists ([#24838](https://github.com/qmk/qmk_firmware/pull/24838))
+* Migrate some DEFAULT_FOLDER to keyboard_aliases.hjson ([#24845](https://github.com/qmk/qmk_firmware/pull/24845))
+* Update for 'A-JAZZ AKC084' ('A-JAZZ AKP846') ([#24868](https://github.com/qmk/qmk_firmware/pull/24868))
+* handwired/xealous - Remove DEFAULT_FOLDER ([#24877](https://github.com/qmk/qmk_firmware/pull/24877))
+* Updates to Ploopy Classic, Mouse, and Thumb for RP2040 hardware upgrade ([#24880](https://github.com/qmk/qmk_firmware/pull/24880))
+* Move Ergodox STM32 to handwired folder ([#24903](https://github.com/qmk/qmk_firmware/pull/24903))
+* Remove readme dummy content ([#24912](https://github.com/qmk/qmk_firmware/pull/24912))
+* Migrate some DEFAULT_FOLDER to keyboard_aliases.hjson ([#24915](https://github.com/qmk/qmk_firmware/pull/24915))
+* Migrate some DEFAULT_FOLDER to keyboard_aliases.hjson ([#24938](https://github.com/qmk/qmk_firmware/pull/24938))
+
+Keyboard fixes:
+* Fix up CI with `DEFAULT_FOLDER`. ([#24842](https://github.com/qmk/qmk_firmware/pull/24842))
+* rgbkb/pan - Remove invalid build target ([#24844](https://github.com/qmk/qmk_firmware/pull/24844))
+
+Others:
+* Formally deprecate DEFAULT_FOLDER ([#24836](https://github.com/qmk/qmk_firmware/pull/24836))
+* Correct I2C API reference ([#24840](https://github.com/qmk/qmk_firmware/pull/24840))
+
+Bugs:
+* Retro Tapping Re-Write; Key Roll Fix ([#23641](https://github.com/qmk/qmk_firmware/pull/23641))
+* Fix Quantum Painter compiliation issues with heavy optimization ([#24667](https://github.com/qmk/qmk_firmware/pull/24667))
+* Bugfix and update for AT32F415 ([#24807](https://github.com/qmk/qmk_firmware/pull/24807))
+* Fix for Chordal Hold: stuck mods when mod-taps are pressed in a stuttered sequence. ([#24878](https://github.com/qmk/qmk_firmware/pull/24878))
+* fix EEPROM driver for STM32L0/1 cat.1 devices ([#24928](https://github.com/qmk/qmk_firmware/pull/24928))
diff --git a/docs/_sidebar.json b/docs/_sidebar.json
index 95601be7de..ae4d8fe4a9 100644
--- a/docs/_sidebar.json
+++ b/docs/_sidebar.json
@@ -60,6 +60,7 @@
"items": [
{ "text": "Customizing Functionality", "link": "/custom_quantum_functions" },
{ "text": "Driver Installation with Zadig", "link": "/driver_installation_zadig" },
+ { "text": "Community Modules", "link": "/features/community_modules" },
{ "text": "Keymap Overview", "link": "/keymap" },
{
"text": "Development Environments",
@@ -206,7 +207,7 @@
{ "text": "My Pull Request Was Flagged", "link": "/breaking_changes_instructions" },
{
"text": "Most Recent ChangeLog",
- "link": "/ChangeLog/20241124"
+ "link": "/ChangeLog/20250223"
},
{ "text": "Past Breaking Changes", "link": "/breaking_changes_history" },
{ "text": "Deprecation Policy", "link": "/support_deprecation_policy" }
diff --git a/docs/breaking_changes.md b/docs/breaking_changes.md
index 132c5943cc..a5a57ccd12 100644
--- a/docs/breaking_changes.md
+++ b/docs/breaking_changes.md
@@ -10,25 +10,25 @@ Practically, this means QMK merges the `develop` branch into the `master` branch
## What has been included in past Breaking Changes?
+* [2025 Feb 23](ChangeLog/20250223)
* [2024 Nov 24](ChangeLog/20241124)
* [2024 Aug 25](ChangeLog/20240825)
-* [2024 May 26](ChangeLog/20240526)
* [Older Breaking Changes](breaking_changes_history)
## When is the next Breaking Change?
-The next Breaking Change is scheduled for February 23, 2025.
+The next Breaking Change is scheduled for May 25, 2025.
### Important Dates
-* 2024 Nov 24 - `develop` is tagged with a new release version. Each push to `master` is subsequently merged to `develop` by GitHub actions.
-* 2025 Jan 26 - `develop` closed to new PRs.
-* 2025 Jan 26 - Call for testers.
-* 2025 Feb 9 - Last day for merges -- after this point `develop` is locked for testing and accepts only bugfixes
-* 2025 Feb 16 - `develop` is locked, only critical bugfix PRs merged.
-* 2024 Nov 21 - `master` is locked, no PRs merged.
-* 2025 Feb 23 - Merge `develop` to `master`.
-* 2025 Feb 23 - `master` is unlocked. PRs can be merged again.
+* 2025 Feb 23 - `develop` is tagged with a new release version. Each push to `master` is subsequently merged to `develop` by GitHub actions.
+* 2025 Apr 27 - `develop` closed to new PRs.
+* 2025 Apr 27 - Call for testers.
+* 2025 May 11 - Last day for merges -- after this point `develop` is locked for testing and accepts only bugfixes
+* 2025 May 18 - `develop` is locked, only critical bugfix PRs merged.
+* 2025 May 23 - `master` is locked, no PRs merged.
+* 2025 May 25 - Merge `develop` to `master`.
+* 2025 May 25 - `master` is unlocked. PRs can be merged again.
## What changes will be included?
diff --git a/docs/breaking_changes_history.md b/docs/breaking_changes_history.md
index f877f42639..af8c1c04d5 100644
--- a/docs/breaking_changes_history.md
+++ b/docs/breaking_changes_history.md
@@ -2,6 +2,7 @@
This page links to all previous changelogs from the QMK Breaking Changes process.
+* [2025 Feb 23](ChangeLog/20250223) - version 0.28.0
* [2024 Nov 24](ChangeLog/20241124) - version 0.27.0
* [2024 Aug 25](ChangeLog/20240825) - version 0.26.0
* [2024 May 26](ChangeLog/20240526) - version 0.25.0
diff --git a/docs/cli_commands.md b/docs/cli_commands.md
index 4cd5ae98c3..d17b0eda23 100644
--- a/docs/cli_commands.md
+++ b/docs/cli_commands.md
@@ -723,23 +723,26 @@ Now open your dev environment and live a squiggly-free life.
## `qmk docs`
-This command starts a local HTTP server which you can use for browsing or improving the docs. Default port is 5173.
+This command starts a local HTTP server which you can use for browsing or improving the docs, and provides live reload capability whilst editing. Default port is 8936.
+Use the `-b`/`--browser` flag to automatically open the local webserver in your default browser.
-This command requires `node` and `yarn` to be installed as prerequisites, and provides live reload capability whilst editing.
+Requires `node` and `yarn` to be installed as prerequisites.
**Usage**:
```
-usage: qmk docs [-h]
+usage: qmk docs [-h] [-b] [-p PORT]
options:
- -h, --help show this help message and exit
+ -h, --help show this help message and exit
+ -b, --browser Open the docs in the default browser.
+ -p, --port PORT Port number to use.
```
## `qmk generate-docs`
-This command allows you to generate QMK documentation locally. It can be uses for general browsing or improving the docs.
-Use the `-s`/`--serve` flag to also serve the static site once built. Default port is 4173.
+This command generates QMK documentation for production.
+Use the `-s`/`--serve` flag to also serve the static site on port 4173 once built. Note that this does not provide live reloading; use `qmk docs` instead for development purposes.
This command requires `node` and `yarn` to be installed as prerequisites, and requires the operating system to support symlinks.
diff --git a/docs/contributing.md b/docs/contributing.md
index bbb1997a6f..70a00b706d 100644
--- a/docs/contributing.md
+++ b/docs/contributing.md
@@ -106,10 +106,10 @@ enum my_keycodes {
Before opening a pull request, you can preview your changes if you have set up the development environment by running this command from the `qmk_firmware/` folder:
```
-qmk docs
+qmk docs -b
```
-and navigating to `http://localhost:5173/`.
+Which should automatically open your browser; otherwise, navigate to `http://localhost:8936/`.
## Keyboards
diff --git a/docs/custom_quantum_functions.md b/docs/custom_quantum_functions.md
index 1479eb53f6..c69beb055e 100644
--- a/docs/custom_quantum_functions.md
+++ b/docs/custom_quantum_functions.md
@@ -9,12 +9,19 @@ This page does not assume any special knowledge about QMK, but reading [Understa
We have structured QMK as a hierarchy:
* Core (`_quantum`)
+ * Community Module (`_<module>`)
+ * Community Module -> Keyboard/Revision (`_<module>_kb`)
+ * Community Module -> Keymap (`_<module>_user`)
* Keyboard/Revision (`_kb`)
* Keymap (`_user`)
Each of the functions described below can be defined with a `_kb()` suffix or a `_user()` suffix. We intend for you to use the `_kb()` suffix at the Keyboard/Revision level, while the `_user()` suffix should be used at the Keymap level.
-When defining functions at the Keyboard/Revision level it is important that your `_kb()` implementation call `_user()` before executing anything else- otherwise the keymap level function will never be called.
+When defining functions at the Keyboard/Revision level it is important that your `_kb()` implementation call `_user()` at an appropriate location, otherwise the keymap level function will never be called.
+
+Functions at the `_<module>_xxx()` level are intended to allow keyboards or keymaps to override or enhance the processing associated with a [community module](/features/community_modules).
+
+When defining module overrides such as `process_record_<module>()`, the same pattern should be used; the module must invoke `process_record_<module>_kb()` as appropriate.
# Custom Keycodes
@@ -99,7 +106,7 @@ These are the three main initialization functions, listed in the order that they
* `keyboard_post_init_*` - Happens at the end of the firmware's startup process. This is where you'd want to put "customization" code, for the most part.
::: warning
-For most people, the `keyboard_post_init_user` function is what you want to call. For instance, this is where you want to set up things for RGB Underglow.
+For most people, the `keyboard_post_init_user` function is what you want to implement. For instance, this is where you want to set up things for RGB Underglow.
:::
## Keyboard Pre Initialization code
diff --git a/docs/drivers/i2c.md b/docs/drivers/i2c.md
index c806a090c5..ad74d0e481 100644
--- a/docs/drivers/i2c.md
+++ b/docs/drivers/i2c.md
@@ -16,17 +16,22 @@ You can then call the I2C API by including `i2c_master.h` in your code.
## I2C Addressing {#note-on-i2c-addresses}
-All of the addresses expected by this driver should be pushed to the upper 7 bits of the address byte. Setting
-the lower bit (indicating read/write) will be done by the respective functions. Almost all I2C addresses listed
-on datasheets and the internet will be represented as 7 bits occupying the lower 7 bits and will need to be
-shifted to the left (more significant) by one bit. This is easy to do via the bitwise shift operator `<< 1`.
+I2C addresses listed on datasheets and the internet are usually represented as a 7-bit value. The eighth bit (the least significant bit) controls whether the operation is a read or a write.
-You can either do this on each call to the functions below, or once in your definition of the address. For example, if your device has an address of `0x18`:
+All of the address parameters expected by the driver API should therefore be pushed to the upper 7 bits of the address byte; the driver will take care of setting the read/write bit as appropriate.
+
+This is easy to do via the bitwise left shift operator. For example, if your device has an address of `0x18` you might create a define for convenience:
```c
#define MY_I2C_ADDRESS (0x18 << 1)
```
+Or, you can shift the address ahead of time:
+
+```c
+#define MY_I2C_ADDRESS 0x30
+```
+
See https://www.robot-electronics.co.uk/i2c-tutorial for more information about I2C addressing and other technical details.
## AVR Configuration {#avr-configuration}
@@ -39,12 +44,12 @@ The following defines can be used to configure the I2C master driver:
No further setup is required - just connect the `SDA` and `SCL` pins of your I2C devices to the matching pins on the MCU:
-|MCU |`SCL`|`SDA`|
-|------------------|-----|-----|
-|ATmega16/32U4 |`D0` |`D1` |
-|AT90USB64/128 |`D0` |`D1` |
-|ATmega32A |`C0` |`C1` |
-|ATmega328/P |`C5` |`C4` |
+|MCU |`SCL`|`SDA`|
+|-------------|-----|-----|
+|ATmega16/32U4|`D0` |`D1` |
+|AT90USB64/128|`D0` |`D1` |
+|ATmega32A |`C0` |`C1` |
+|ATmega328/P |`C5` |`C4` |
::: tip
The ATmega16/32U2 does not possess I2C functionality, and so cannot use this driver.
@@ -52,7 +57,7 @@ The ATmega16/32U2 does not possess I2C functionality, and so cannot use this dri
## ChibiOS/ARM Configuration {#arm-configuration}
-You'll need to determine which pins can be used for I2C -- a an example, STM32 parts generally have multiple I2C peripherals, labeled I2C1, I2C2, I2C3 etc.
+You'll need to determine which pins can be used for I2C -- as an example, STM32 parts generally have multiple I2C peripherals, labeled I2C1, I2C2, I2C3 etc.
To enable I2C, modify your board's `halconf.h` to enable I2C, then modify your board's `mcuconf.h` to enable the peripheral you've chosen:
@@ -83,15 +88,19 @@ To enable I2C, modify your board's `halconf.h` to enable I2C, then modify your b
Configuration-wise, you'll need to set up the peripheral as per your MCU's datasheet -- the defaults match the pins for a Proton-C, i.e. STM32F303.
-|`config.h` Overrride |Description |Default|
-|------------------------|--------------------------------------------------------------|-------|
-|`I2C_DRIVER` |I2C peripheral to use - I2C1 -> `I2CD1`, I2C2 -> `I2CD2` etc. |`I2CD1`|
-|`I2C1_SCL_PIN` |The pin definition for SCL |`B6` |
-|`I2C1_SCL_PAL_MODE` |The alternate function mode for SCL |`4` |
-|`I2C1_SDA_PIN` |The pin definition for SDA |`B7` |
-|`I2C1_SDA_PAL_MODE` |The alternate function mode for SDA |`4` |
+|`config.h` Override|Description |Default|
+|-------------------|-------------------------------------------------------------|-------|
+|`I2C_DRIVER` |I2C peripheral to use - I2C1 -> `I2CD1`, I2C2 -> `I2CD2` etc.|`I2CD1`|
+|`I2C1_SCL_PIN` |The pin to use for SCL |`B6` |
+|`I2C1_SCL_PAL_MODE`|The alternate function mode for SCL |`4` |
+|`I2C1_SDA_PIN` |The pin to use for SDA |`B7` |
+|`I2C1_SDA_PAL_MODE`|The alternate function mode for SDA |`4` |
-The following configuration values depend on the specific MCU in use.
+::: tip
+Currently only a single I2C peripheral is supported, therefore the `I2C1_*` defines are used for configuration regardless of the selected peripheral.
+:::
+
+The following configuration values are dependent on the ChibiOS I2C LLD, which is dictated by the microcontroller.
### I2Cv1 {#arm-configuration-i2cv1}
@@ -147,7 +156,7 @@ void i2c_init(void) {
---
-### `i2c_status_t i2c_transmit(uint8_t address, uint8_t *data, uint16_t length, uint16_t timeout)` {#api-i2c-transmit}
+### `i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-transmit}
Send multiple bytes to the selected I2C device.
@@ -155,10 +164,10 @@ Send multiple bytes to the selected I2C device.
- `uint8_t address`
The 7-bit I2C address of the device.
- - `uint8_t *data`
+ - `const uint8_t* data`
A pointer to the data to transmit.
- `uint16_t length`
- The number of bytes to write. Take care not to overrun the length of `data`.
+ The number of bytes to write. Take care not to overrun the length of `data`.
- `uint16_t timeout`
The time in milliseconds to wait for a response from the target device.
@@ -168,6 +177,29 @@ Send multiple bytes to the selected I2C device.
---
+### `i2c_status_t i2c_transmit_P(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-transmit-p}
+
+Send multiple bytes from PROGMEM to the selected I2C device.
+
+On ARM devices, this function is simply an alias for `i2c_transmit(address, data, length, timeout)`.
+
+#### Arguments {#api-i2c-transmit-p-arguments}
+
+ - `uint8_t address`
+ The 7-bit I2C address of the device.
+ - `const uint8_t* data`
+ A pointer to the data to transmit.
+ - `uint16_t length`
+ The number of bytes to write. Take care not to overrun the length of `data`.
+ - `uint16_t timeout`
+ The time in milliseconds to wait for a response from the target device.
+
+#### Return Value {#api-i2c-transmit-p-return}
+
+`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
+
+---
+
### `i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-receive}
Receive multiple bytes from the selected I2C device.
@@ -176,10 +208,10 @@ Receive multiple bytes from the selected I2C device.
- `uint8_t address`
The 7-bit I2C address of the device.
- - `uint8_t *data`
- A pointer to the buffer to read into.
+ - `uint8_t* data`
+ A pointer to a buffer to read into.
- `uint16_t length`
- The number of bytes to read. Take care not to overrun the length of `data`.
+ The number of bytes to read. Take care not to overrun the length of `data`.
- `uint16_t timeout`
The time in milliseconds to wait for a response from the target device.
@@ -189,9 +221,9 @@ Receive multiple bytes from the selected I2C device.
---
-### `i2c_status_t i2c_write_register(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-write-register}
+### `i2c_status_t i2c_write_register(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-write-register}
-Writes to a register with an 8-bit address on the I2C device.
+Write to a register with an 8-bit address on the I2C device.
#### Arguments {#api-i2c-write-register-arguments}
@@ -199,10 +231,10 @@ Writes to a register with an 8-bit address on the I2C device.
The 7-bit I2C address of the device.
- `uint8_t regaddr`
The register address to write to.
- - `uint8_t *data`
+ - `const uint8_t* data`
A pointer to the data to transmit.
- `uint16_t length`
- The number of bytes to write. Take care not to overrun the length of `data`.
+ The number of bytes to write. Take care not to overrun the length of `data`.
- `uint16_t timeout`
The time in milliseconds to wait for a response from the target device.
@@ -212,9 +244,9 @@ Writes to a register with an 8-bit address on the I2C device.
---
-### `i2c_status_t i2c_write_register16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-write-register16}
+### `i2c_status_t i2c_write_register16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-write-register16}
-Writes to a register with a 16-bit address (big endian) on the I2C device.
+Write to a register with a 16-bit address (big endian) on the I2C device.
#### Arguments {#api-i2c-write-register16-arguments}
@@ -222,10 +254,10 @@ Writes to a register with a 16-bit address (big endian) on the I2C device.
The 7-bit I2C address of the device.
- `uint16_t regaddr`
The register address to write to.
- - `uint8_t *data`
+ - `const uint8_t* data`
A pointer to the data to transmit.
- `uint16_t length`
- The number of bytes to write. Take care not to overrun the length of `data`.
+ The number of bytes to write. Take care not to overrun the length of `data`.
- `uint16_t timeout`
The time in milliseconds to wait for a response from the target device.
@@ -237,7 +269,7 @@ Writes to a register with a 16-bit address (big endian) on the I2C device.
### `i2c_status_t i2c_read_register(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-read-register}
-Reads from a register with an 8-bit address on the I2C device.
+Read from a register with an 8-bit address on the I2C device.
#### Arguments {#api-i2c-read-register-arguments}
@@ -245,8 +277,10 @@ Reads from a register with an 8-bit address on the I2C device.
The 7-bit I2C address of the device.
- `uint8_t regaddr`
The register address to read from.
+ - `uint8_t data`
+ A pointer to a buffer to read into.
- `uint16_t length`
- The number of bytes to read. Take care not to overrun the length of `data`.
+ The number of bytes to read. Take care not to overrun the length of `data`.
- `uint16_t timeout`
The time in milliseconds to wait for a response from the target device.
@@ -258,7 +292,7 @@ Reads from a register with an 8-bit address on the I2C device.
### `i2c_status_t i2c_read_register16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-read-register16}
-Reads from a register with a 16-bit address (big endian) on the I2C device.
+Read from a register with a 16-bit address (big endian) on the I2C device.
#### Arguments {#api-i2c-read-register16-arguments}
@@ -266,8 +300,10 @@ Reads from a register with a 16-bit address (big endian) on the I2C device.
The 7-bit I2C address of the device.
- `uint16_t regaddr`
The register address to read from.
+ - `uint8_t* data`
+ A pointer to a buffer to read into.
- `uint16_t length`
- The number of bytes to read. Take care not to overrun the length of `data`.
+ The number of bytes to read. Take care not to overrun the length of `data`.
- `uint16_t timeout`
The time in milliseconds to wait for a response from the target device.
@@ -279,19 +315,19 @@ Reads from a register with a 16-bit address (big endian) on the I2C device.
### `i2c_status_t i2c_ping_address(uint8_t address, uint16_t timeout)` {#api-i2c-ping-address}
-Pings the I2C bus for a specific address.
+Ping the I2C bus for a specific address.
-On ChibiOS a "best effort" attempt is made by reading a single byte from register 0 at the requested address. This should generally work except for I2C devices that do not not respond to a register 0 read request, which will result in a false negative result (unsucessful response to ping attempt).
+On ChibiOS a "best effort" attempt is made by reading a single byte from register 0 at the given address. This should generally work except for I2C devices that do not not respond to a register 0 read request, which will result in a false negative result (unsuccessful response to ping attempt).
-This function is weakly defined, meaning it can be overridden if necessary for your particular use case:
+This function is weakly defined, meaning it can be overridden if necessary for your particular use case.
-#### Arguments
+#### Arguments {#api-i2c-ping-address-arguments}
- `uint8_t address`
- The 7-bit I2C address of the device (ie. without the read/write bit - this will be set automatically).
+ The 7-bit I2C address of the device.
- `uint16_t timeout`
The time in milliseconds to wait for a response from the target device.
-#### Return Value
+#### Return Value {#api-i2c-ping-address-return}
`I2C_STATUS_TIMEOUT` if the timeout period elapses, `I2C_STATUS_ERROR` if some other error occurs, otherwise `I2C_STATUS_SUCCESS`.
diff --git a/docs/drivers/spi.md b/docs/drivers/spi.md
index 43d2a056d5..140b204945 100644
--- a/docs/drivers/spi.md
+++ b/docs/drivers/spi.md
@@ -88,7 +88,7 @@ Start an SPI transaction.
#### Arguments {#api-spi-start-arguments}
- `pin_t slavePin`
- The QMK pin to assert as the slave select pin, eg. `B4`.
+ The GPIO pin connected to the desired device's `SS` line.
- `bool lsbFirst`
Determines the endianness of the transmission. If `true`, the least significant bit of each byte is sent first.
- `uint8_t mode`
@@ -106,7 +106,7 @@ Start an SPI transaction.
#### Return Value {#api-spi-start-return}
-`false` if the supplied parameters are invalid or the SPI peripheral is already in use, or `true`.
+`true` if the operation was successful, otherwise `false` if the supplied parameters are invalid or the SPI peripheral is already in use.
---
@@ -131,7 +131,7 @@ Read a byte from the selected SPI device.
#### Return Value {#api-spi-read-return}
-`SPI_STATUS_TIMEOUT` if the timeout period elapses, or the byte read from the device.
+`SPI_STATUS_TIMEOUT` if the timeout period elapses, otherwise the byte read from the device.
---
@@ -159,7 +159,7 @@ Receive multiple bytes from the selected SPI device.
#### Arguments {#api-spi-receive-arguments}
- `uint8_t *data`
- A pointer to the buffer to read into.
+ A pointer to a buffer to read into.
- `uint16_t length`
The number of bytes to read. Take care not to overrun the length of `data`.
diff --git a/docs/drivers/uart.md b/docs/drivers/uart.md
index 7cc68727ee..b895266cab 100644
--- a/docs/drivers/uart.md
+++ b/docs/drivers/uart.md
@@ -45,17 +45,17 @@ To enable UART, modify your board's `mcuconf.h` to enable the peripheral you've
Configuration-wise, you'll need to set up the peripheral as per your MCU's datasheet -- the defaults match the pins for a Proton-C, i.e. STM32F303.
-| `config.h` override | Description | Default Value |
-| --------------------------- | --------------------------------------------------------------- | ------------- |
-| `#define UART_DRIVER` | USART peripheral to use - USART1 -> `SD1`, USART2 -> `SD2` etc. | `SD1` |
-| `#define UART_TX_PIN` | The pin to use for TX | `A9` |
-| `#define UART_TX_PAL_MODE` | The alternate function mode for TX | `7` |
-| `#define UART_RX_PIN` | The pin to use for RX | `A10` |
-| `#define UART_RX_PAL_MODE` | The alternate function mode for RX | `7` |
-| `#define UART_CTS_PIN` | The pin to use for CTS | `A11` |
-| `#define UART_CTS_PAL_MODE` | The alternate function mode for CTS | `7` |
-| `#define UART_RTS_PIN` | The pin to use for RTS | `A12` |
-| `#define UART_RTS_PAL_MODE` | The alternate function mode for RTS | `7` |
+|`config.h` Override|Description |Default|
+|-------------------|---------------------------------------------------------------|-------|
+|`UART_DRIVER` |USART peripheral to use - USART1 -> `SD1`, USART2 -> `SD2` etc.|`SD1` |
+|`UART_TX_PIN` |The pin to use for TX |`A9` |
+|`UART_TX_PAL_MODE` |The alternate function mode for TX |`7` |
+|`UART_RX_PIN` |The pin to use for RX |`A10` |
+|`UART_RX_PAL_MODE` |The alternate function mode for RX |`7` |
+|`UART_CTS_PIN` |The pin to use for CTS |`A11` |
+|`UART_CTS_PAL_MODE`|The alternate function mode for CTS |`7` |
+|`UART_RTS_PIN` |The pin to use for RTS |`A12` |
+|`UART_RTS_PAL_MODE`|The alternate function mode for RTS |`7` |
## API {#api}
@@ -111,7 +111,7 @@ Receive multiple bytes.
#### Arguments {#api-uart-receive-arguments}
- `uint8_t *data`
- A pointer to the buffer to read into.
+ A pointer to a buffer to read into.
- `uint16_t length`
The number of bytes to read. Take care not to overrun the length of `data`.
@@ -123,4 +123,4 @@ Return whether the receive buffer contains data. Call this function to determine
#### Return Value {#api-uart-available-return}
-`true` if the receive buffer length is non-zero.
+`true` if there is data available to read.
diff --git a/docs/features/community_modules.md b/docs/features/community_modules.md
new file mode 100644
index 0000000000..3a1a82e7bc
--- /dev/null
+++ b/docs/features/community_modules.md
@@ -0,0 +1,142 @@
+# Community Modules
+
+Community Modules are a feature within QMK which allows code to be implemented by third parties, making it available for other people to import into their own builds.
+
+These modules can provide implementations which override or enhance normal QMK processing; initialization, key processing, suspend, and shutdown are some of the provided hooks which modules may implement.
+
+## Adding a Community Module to your build
+
+Community Modules have first-class support for [External Userspace](/newbs_external_userspace), and QMK strongly recommends using External Userspace for hosting keymaps and Community Modules together.
+
+Modules must live in either of two locations:
+
+* `<QMK_USERSPACE>/modules/`
+* `<QMK_FIRMWARE>/modules/`
+
+A basic module is provided within QMK itself -- `qmk/hello_world` -- which prints out a notification over [HID console](/faq_debug) after 10 seconds, and adds a new keycode, `COMMUNITY_MODULE_HELLO` (aliased to `CM_HELO`) which types `Hello there.` to the active application when the corresponding key is pressed.
+
+To add this module to your build, in your keymap's directory create a `keymap.json` with the following content:
+
+```json
+{
+ "modules": [
+ "qmk/hello_world"
+ ]
+}
+```
+
+If you already have a `keymap.json`, you'll need to manually merge the `modules` section into your keymap.
+
+::: warning
+Community Modules are not supported by QMK Configurator. If you wish to use Community Modules, you must build your own firmware.
+:::
+
+## Adding a Community Module to your External Userspace
+
+Module authors are encouraged to provide a git repository on GitHub which may be imported into a user's external userspace. If a user wishes to import a module repository, they can do the following:
+
+```sh
+cd /path/to/your/external/userspace
+mkdir -p modules
+# Replace the following {user} and {repo} with the author's community module repository
+git submodule add https://github.com/{user}/{repo}.git modules/{user}
+git submdule update --init --recursive
+```
+
+This will ensure the copy of the module is made in your userspace.
+
+Add a new entry into your `keymap.json` with the desired modules, replacing `{user}` and `{module_name}` as appropriate:
+
+```json
+{
+ "modules": [
+ "qmk/hello_world",
+ "{user}/{module_name}"
+ ]
+}
+```
+
+::: info
+The module listed in `keymap.json` is the relative path within the `modules/` directory. So long as the module is present _somewhere_ under `modules/`, then the `keymap.json` can refer to that path.
+:::
+
+## Writing a QMK Community Module
+
+As stated earlier, Community Module authors are strongly encouraged to provide their modules through git, allowing users to leverage submodules to import functionality.
+
+### `qmk_module.json`
+
+A Community Module is denoted by a `qmk_module.json` file such as the following:
+
+```json
+{
+ "module_name": "Hello World",
+ "maintainer": "QMK Maintainers",
+ "features": {
+ "deferred_exec": true
+ },
+ "keycodes": [
+ {
+ "key": "COMMUNITY_MODULE_HELLO",
+ "aliases": ["CM_HELO"]
+ }
+ ]
+}
+```
+
+At minimum, the module must provide the `module_name` and `maintainer` fields.
+
+The use of `features` matches the definition normally provided within `keyboard.json` and `info.json`, allowing a module to signal to the build system that it has its own dependencies. In the example above, it enables the _deferred executor_ feature whenever the above module is used in a build.
+
+The `keycodes` array allows a module to provide new keycodes (as well as corresponding aliases) to a keymap.
+
+### `rules.mk` / `post_rules.mk`
+
+These two files follows standard QMK build system logic, allowing for `Makefile`-style customisation as if it were present in the keyboard or keymap.
+
+### `<module>.c`
+
+This file will be automatically added to the build if the filename matches the directory name. For example, the `qmk/hello_world` module contains a `hello_world.c` file, which is automatically added to the build.
+
+::: info
+Other files intended to be included must use the normal method of `SRC += my_file.c` inside `rules.mk`.
+:::
+
+::: tip
+This file should use `ASSERT_COMMUNITY_MODULES_MIN_API_VERSION(1,0,0);` to enforce a minimum version of the API that it requires, ensuring the Community Module is built with a compatible version of QMK. The list of APIs and corresponding version is given at the bottom of this document. Note the use of commas instead of periods.
+:::
+
+### `introspection.c` / `introspection.h`
+
+These two files hook into the keymap introspection logic -- the header is prepended before the user keymap, and the C source file is appended after the user keymap.
+
+The header may provide definitions which are useful to the user's `keymap.c`.
+
+The source file may provide functions which allow access to information specified in the user's `keymap.c`.
+
+::: warning
+Introspection is a relatively advanced topic within QMK, and existing patterns should be followed. If you need help please [open an issue](https://github.com/qmk/qmk_firmware/issues/new) or [chat with us on Discord](https://discord.gg/qmk).
+:::
+
+### Compatible APIs
+
+Community Modules may provide specializations for the following APIs:
+
+| Base API | API Format | Example (`hello_world` module) | API Version |
+|----------------------------|-------------------------------------|----------------------------------------|-------------|
+| `keyboard_pre_init` | `keyboard_pre_init_<module>` | `keyboard_pre_init_hello_world` | `0.1.0` |
+| `keyboard_post_init` | `keyboard_post_init_<module>` | `keyboard_post_init_hello_world` | `0.1.0` |
+| `pre_process_record` | `pre_process_record_<module>` | `pre_process_record_hello_world` | `0.1.0` |
+| `process_record` | `process_record_<module>` | `process_record_hello_world` | `0.1.0` |
+| `post_process_record` | `post_process_record_<module>` | `post_process_record_hello_world` | `0.1.0` |
+| `housekeeping_task` | `housekeeping_task_<module>` | `housekeeping_task_hello_world` | `1.0.0` |
+| `suspend_power_down` | `suspend_power_down_<module>` | `suspend_power_down_hello_world` | `1.0.0` |
+| `suspend_wakeup_init` | `suspend_wakeup_init_<module>` | `suspend_wakeup_init_hello_world` | `1.0.0` |
+| `shutdown` | `shutdown_<module>` | `shutdown_hello_world` | `1.0.0` |
+| `process_detected_host_os` | `process_detected_host_os_<module>` | `process_detected_host_os_hello_world` | `1.0.0` |
+
+::: info
+An unspecified API is disregarded if a Community Module does not provide a specialization for it.
+:::
+
+Each API has an equivalent `_<module>_kb()` and `_<module>_user()` hook, as per the normal QMK [`_quantum`, `_kb`, and `_user` functions](/custom_quantum_functions#a-word-on-core-vs-keyboards-vs-keymap).
diff --git a/docs/features/leader_key.md b/docs/features/leader_key.md
index a36e630a36..9f9086e1ae 100644
--- a/docs/features/leader_key.md
+++ b/docs/features/leader_key.md
@@ -154,6 +154,21 @@ User callback, invoked when the leader sequence ends.
---
+### `bool leader_add_user(uint16_t keycode)` {#api-leader-add-user}
+
+User callback, invoked when a keycode is added to the leader sequence.
+
+#### Arguments {#api-leader-add-user-arguments}
+
+ - `uint16_t keycode`
+ The keycode to added to the leader sequence.
+
+#### Return Value {#api-leader-add-user-return}
+
+`true` to finish the key sequence, `false` to continue.
+
+---
+
### `void leader_start(void)` {#api-leader-start}
Begin the leader sequence, resetting the buffer and timer.
diff --git a/docs/reference_info_json.md b/docs/reference_info_json.md
index 99ff7b1f7a..29b999c32e 100644
--- a/docs/reference_info_json.md
+++ b/docs/reference_info_json.md
@@ -74,6 +74,8 @@ You can create `info.json` files at every level under `qmk_firmware/keyboards/<k
* The delay between keydown and keyup for tap events in milliseconds.
* Default: `0` (no delay)
* `tapping`
+ * `chordal_hold` <Badge type="info">Boolean</Badge>
+ * Default: `false`
* `hold_on_other_key_press` <Badge type="info">Boolean</Badge>
* Default: `false`
* `hold_on_other_key_press_per_key` <Badge type="info">Boolean</Badge>
@@ -328,6 +330,8 @@ The ISO enter key is represented by a 1.25u×2uh key. Renderers which utilize in
* `h` <Badge type="info">KeyUnit</Badge>
* The height of the key, in key units.
* Default: `1` (1u)
+ * `hand` <Badge type="info">String</Badge>
+ * The handedness of the key for Chordal Hold, either `"L"` (left hand), `"R"` (right hand), or `"*"` (either or exempted handedness).
* `label` <Badge type="info">String</Badge>
* What to name the key. This is *not* a key assignment as in the keymap, but should usually correspond to the keycode for the first layer of the default keymap.
* Example: `"Escape"`
diff --git a/docs/reference_keymap_extras.md b/docs/reference_keymap_extras.md
index f6b4b8faf6..c871773651 100644
--- a/docs/reference_keymap_extras.md
+++ b/docs/reference_keymap_extras.md
@@ -55,6 +55,7 @@ These headers are located in [`quantum/keymap_extras/`](https://github.com/qmk/q
|English (US International) |`keymap_us_international.h` |`sendstring_us_international.h` |
|English (US International, Linux)|`keymap_us_international_linux.h`| |
|Estonian |`keymap_estonian.h` |`sendstring_estonian.h` |
+|EurKEY |`keymap_eurkey.h` | |
|Farsi |`keymap_farsi.h` | |
|Finnish |`keymap_finnish.h` |`sendstring_finnish.h` |
|French |`keymap_french.h` |`sendstring_french.h` |
diff --git a/docs/tap_hold.md b/docs/tap_hold.md
index 9b7f6552cb..254d5de5ec 100644
--- a/docs/tap_hold.md
+++ b/docs/tap_hold.md
@@ -425,6 +425,169 @@ uint16_t get_quick_tap_term(uint16_t keycode, keyrecord_t *record) {
If `QUICK_TAP_TERM` is set higher than `TAPPING_TERM`, it will default to `TAPPING_TERM`.
:::
+## Chordal Hold
+
+Chordal Hold is intended to be used together with either Permissive Hold or Hold
+On Other Key Press. Chordal Hold is enabled by adding to your `config.h`:
+
+```c
+#define CHORDAL_HOLD
+```
+
+Chordal Hold implements, by default, an "opposite hands" rule. Suppose a
+tap-hold key is pressed and then, before the tapping term, another key is
+pressed. With Chordal Hold, the tap-hold key is settled as tapped if the two
+keys are on the same hand.
+
+Otherwise, if the keys are on opposite hands, Chordal Hold introduces no new
+behavior. Hold On Other Key Press or Permissive Hold may be used together with
+Chordal Hold to configure the behavior in the opposite hands case. With Hold On
+Other Key Press, an opposite hands chord is settled immediately as held. Or with
+Permissive Hold, an opposite hands chord is settled as held provided the other
+key is pressed and released (nested press) before releasing the tap-hold key.
+
+Chordal Hold may be useful to avoid accidental modifier activation with
+mod-taps, particularly in rolled keypresses when using home row mods.
+
+Notes:
+
+* Chordal Hold has no effect after the tapping term.
+
+* Combos are exempt from the opposite hands rule, since "handedness" is
+ ill-defined in this case. Even so, Chordal Hold's behavior involving combos
+ may be customized through the `get_chordal_hold()` callback.
+
+An example of a sequence that is affected by “chordal hold”:
+
+- `SFT_T(KC_A)` Down
+- `KC_C` Down
+- `KC_C` Up
+- `SFT_T(KC_A)` Up
+
+```
+ TAPPING_TERM
+ +---------------------------|--------+
+ | +----------------------+ | |
+ | | SFT_T(KC_A) | | |
+ | +----------------------+ | |
+ | +--------------+ | |
+ | | KC_C | | |
+ | +--------------+ | |
+ +---------------------------|--------+
+```
+
+If the two keys are on the same hand, then this will produce `ac` with
+`SFT_T(KC_A)` settled as tapped the moment that `KC_C` is pressed.
+
+If the two keys are on opposite hands and the `HOLD_ON_OTHER_KEY_PRESS` option
+enabled, this will produce `C` with `SFT_T(KC_A)` settled as held when `KC_C` is
+pressed.
+
+Or if the two keys are on opposite hands and the `PERMISSIVE_HOLD` option is
+enabled, this will produce `C` with `SFT_T(KC_A)` settled as held when that
+`KC_C` is released.
+
+### Chordal Hold Handedness
+
+Determining whether keys are on the same or opposite hands involves defining the
+"handedness" of each key position. By default, if nothing is specified,
+handedness is guessed based on keyboard geometry.
+
+Handedness may be specified with `chordal_hold_layout`. In keymap.c, define
+`chordal_hold_layout` in the following form:
+
+```c
+const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM =
+ LAYOUT(
+ 'L', 'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R', 'R',
+ 'L', 'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R', 'R',
+ 'L', 'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R', 'R',
+ 'L', 'L', 'L', 'R', 'R', 'R'
+ );
+```
+
+Use the same `LAYOUT` macro as used to define your keymap layers. Each entry is
+a character indicating the handedness of one key, either `'L'` for left, `'R'`
+for right, or `'*'` to exempt keys from the "opposite hands rule." A key with
+`'*'` handedness may settle as held in chords with any other key. This could be
+used perhaps on thumb keys or other places where you want to allow same-hand
+chords.
+
+Keyboard makers may specify handedness in keyboard.json. Under `"layouts"`,
+specify the handedness of a key by adding a `"hand"` field with a value of
+either `"L"`, `"R"`, or `"*"`. Note that if `"layouts"` contains multiple
+layouts, only the first one is read. For example:
+
+```json
+{"matrix": [5, 6], "x": 0, "y": 5.5, "w": 1.25, "hand", "*"},
+```
+
+Alternatively, handedness may be defined functionally with
+`chordal_hold_handedness()`. For example, in keymap.c define:
+
+```c
+char chordal_hold_handedness(keypos_t key) {
+ if (key.col == 0 || key.col == MATRIX_COLS - 1) {
+ return '*'; // Exempt the outer columns.
+ }
+ // On split keyboards, typically, the first half of the rows are on the
+ // left, and the other half are on the right.
+ return key.row < MATRIX_ROWS / 2 ? 'L' : 'R';
+}
+```
+
+Given the matrix position of a key, the function should return `'L'`, `'R'`, or
+`'*'`. Adapt the logic in this function according to the keyboard's matrix.
+
+::: warning
+Note the matrix may have irregularities around larger keys, around the edges of
+the board, and around thumb clusters. You may find it helpful to use [this
+debugging example](faq_debug#which-matrix-position-is-this-keypress) to
+correspond physical keys to matrix positions.
+:::
+
+::: tip If you define both `chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS]` and
+`chordal_hold_handedness(keypos_t key)` for handedness, the latter takes
+precedence.
+:::
+
+
+### Per-chord customization
+
+Beyond the per-key configuration possible through handedness, Chordal Hold may
+be configured at a *per-chord* granularity for detailed tuning. In keymap.c,
+define `get_chordal_hold()`. Returning `true` allows the chord to be held, while
+returning `false` settles as tapped.
+
+For example:
+
+```c
+bool get_chordal_hold(uint16_t tap_hold_keycode, keyrecord_t* tap_hold_record,
+ uint16_t other_keycode, keyrecord_t* other_record) {
+ // Exceptionally allow some one-handed chords for hotkeys.
+ switch (tap_hold_keycode) {
+ case LCTL_T(KC_Z):
+ if (other_keycode == KC_C || other_keycode == KC_V) {
+ return true;
+ }
+ break;
+
+ case RCTL_T(KC_SLSH):
+ if (other_keycode == KC_N) {
+ return true;
+ }
+ break;
+ }
+ // Otherwise defer to the opposite hands rule.
+ return get_chordal_hold_default(tap_hold_record, other_record);
+}
+```
+
+As shown in the last line above, you may use
+`get_chordal_hold_default(tap_hold_record, other_record)` to get the default tap
+vs. hold decision according to the opposite hands rule.
+
+
## Retro Tapping
To enable `retro tapping`, add the following to your `config.h`: