diff options
Diffstat (limited to 'docs')
-rw-r--r-- | docs/cli_commands.md | 15 | ||||
-rw-r--r-- | docs/contributing.md | 4 | ||||
-rw-r--r-- | docs/drivers/i2c.md | 124 | ||||
-rw-r--r-- | docs/drivers/spi.md | 8 | ||||
-rw-r--r-- | docs/drivers/uart.md | 26 | ||||
-rw-r--r-- | docs/features/leader_key.md | 15 | ||||
-rw-r--r-- | docs/reference_info_json.md | 4 | ||||
-rw-r--r-- | docs/reference_keymap_extras.md | 1 | ||||
-rw-r--r-- | docs/tap_hold.md | 163 |
9 files changed, 291 insertions, 69 deletions
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/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/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`: |