aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/ChangeLog/20250831/pr25415.md47
-rw-r--r--docs/ChangeLog/20251130/PR25515.md3
-rw-r--r--docs/ChangeLog/20251130/PR25632.md4
-rw-r--r--docs/custom_matrix.md4
-rw-r--r--docs/drivers/i2c.md25
-rw-r--r--docs/feature_converters.md348
-rw-r--r--docs/features/dynamic_macros.md13
-rw-r--r--docs/features/haptic_feedback.md2
-rw-r--r--docs/features/led_matrix.md59
-rw-r--r--docs/features/pointing_device.md17
-rw-r--r--docs/features/rgb_matrix.md59
-rw-r--r--docs/features/tap_dance.md12
-rw-r--r--docs/isp_flashing_guide.md16
-rw-r--r--docs/keycodes.md4
-rw-r--r--docs/public/pin_compatible_elite_c.svg61
-rw-r--r--docs/public/pin_compatible_promicro.svg61
-rw-r--r--docs/quantum_painter.md2
-rw-r--r--docs/reference_info_json.md12
-rw-r--r--docs/squeezing_avr.md20
-rw-r--r--docs/tap_hold.md33
20 files changed, 663 insertions, 139 deletions
diff --git a/docs/ChangeLog/20250831/pr25415.md b/docs/ChangeLog/20250831/pr25415.md
new file mode 100644
index 0000000000..54eb8c737e
--- /dev/null
+++ b/docs/ChangeLog/20250831/pr25415.md
@@ -0,0 +1,47 @@
+# Tap dance state removed from `tap_dance_action_t`
+
+The tap dance state has been separated from the action structure. Custom tap dance functions now receive the state as a separate parameter instead of accessing it through `action->state`.
+
+## User Action Required
+
+If your keymap uses custom tap dance functions that access the tap dance state, you need to update your code.
+
+ - You can't use `action->state`. Instead you need to call `tap_dance_state_t *tap_dance_get_state(uint8_t tap_dance_idx)` to get the state.
+ - You now get a pointer to the state, so use `->` notation rather than `.` notation to get fields from it.
+
+### Before:
+```c
+bool process_record_user(uint16_t keycode, keyrecord_t *record) {
+ tap_dance_action_t *action;
+
+ switch (keycode) {
+ case TD(CT_CLN):
+ action = tap_dance_get(QK_TAP_DANCE_GET_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;
+}
+```
+
+### After:
+```c
+bool process_record_user(uint16_t keycode, keyrecord_t *record) {
+ tap_dance_action_t *action;
+ tap_dance_state_t* state;
+
+ switch (keycode) {
+ case TD(CT_CLN):
+ action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(keycode));
+ state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(keycode));
+ if (!record->event.pressed && state != NULL && state->count && !state->finished) {
+ tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)action->user_data;
+ tap_code16(tap_hold->tap);
+ }
+ }
+ return true;
+}
+```
diff --git a/docs/ChangeLog/20251130/PR25515.md b/docs/ChangeLog/20251130/PR25515.md
new file mode 100644
index 0000000000..ccc88e0c64
--- /dev/null
+++ b/docs/ChangeLog/20251130/PR25515.md
@@ -0,0 +1,3 @@
+# Refactor debounce algorithm with static allocation [#25515](https://github.com/qmk/qmk_firmware/pull/25515)
+
+Removed dynamic memory allocation (malloc, free) from all debounce implementations for improved efficiency on embedded systems and to avoid runtime allocation overhead. Refactored state arrays to use direct indexing, simplifying code and eliminating pointer arithmetic. Standardized usage of MATRIX_ROWS_PER_HAND throughout the codebase to ensure consistent support for split keyboards.
diff --git a/docs/ChangeLog/20251130/PR25632.md b/docs/ChangeLog/20251130/PR25632.md
new file mode 100644
index 0000000000..39136067b9
--- /dev/null
+++ b/docs/ChangeLog/20251130/PR25632.md
@@ -0,0 +1,4 @@
+# Changes Requiring User Action
+## Debounce: Deprecate init and remove num_rows parameter [#25632](https://github.com/qmk/qmk_firmware/pull/25632)
+
+With dynamic memory allocation removed from all debounce implementations ([#25515](https://github.com/qmk/qmk_firmware/pull/25515)), the `num_rows` parameter has been removed from the `debounce_init()` and `debounce()` functions. The `MATRIX_ROWS_PER_HAND` is now used by default in every implementation.
diff --git a/docs/custom_matrix.md b/docs/custom_matrix.md
index ef206944e1..294b88bb0b 100644
--- a/docs/custom_matrix.md
+++ b/docs/custom_matrix.md
@@ -74,7 +74,7 @@ void matrix_init(void) {
// TODO: initialize hardware and global matrix state here
// Unless hardware debouncing - Init the configured debounce routine
- debounce_init(MATRIX_ROWS);
+ debounce_init();
// This *must* be called for correct keyboard behavior
matrix_init_kb();
@@ -86,7 +86,7 @@ uint8_t matrix_scan(void) {
// TODO: add matrix scanning routine here
// Unless hardware debouncing - use the configured debounce routine
- changed = debounce(raw_matrix, matrix, MATRIX_ROWS, changed);
+ changed = debounce(raw_matrix, matrix, changed);
// This *must* be called for correct keyboard behavior
matrix_scan_kb();
diff --git a/docs/drivers/i2c.md b/docs/drivers/i2c.md
index ad74d0e481..75823c682b 100644
--- a/docs/drivers/i2c.md
+++ b/docs/drivers/i2c.md
@@ -221,6 +221,31 @@ Receive multiple bytes from the selected I2C device.
---
+### `i2c_status_t i2c_transmit_and_receive(uint8_t address, const uint8_t* tx_data, uint16_t tx_length, uint8_t* rx_data, uint16_t rx_length, uint16_t timeout)` {#api-i2c-transmit-and-receive}
+
+Send and receive multiple bytes from the selected I2C device.
+
+#### Arguments {#api-i2c-transmit-and-receive-arguments}
+
+ - `uint8_t address`
+ The 7-bit I2C address of the device.
+ - `const uint8_t* tx_data`
+ A pointer to the data to transmit.
+ - `uint16_t tx_length`
+ The number of bytes to write. Take care not to overrun the length of `tx_data`.
+ - `uint8_t* rx_data`
+ A pointer to a buffer to read into.
+ - `uint16_t rx_length`
+ 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.
+
+#### Return Value {#api-i2c-transmit-and-receive-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_write_register(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout)` {#api-i2c-write-register}
Write to a register with an 8-bit address on the I2C device.
diff --git a/docs/feature_converters.md b/docs/feature_converters.md
index 06cc7b3a11..4e0a23752c 100644
--- a/docs/feature_converters.md
+++ b/docs/feature_converters.md
@@ -2,8 +2,22 @@
This page documents the automated process for converting keyboards to use drop-in replacement controllers. This process is designed to be easy to use and can be completed in a few simple steps.
+You can generate the firmware by appending `-e CONVERT_TO=<target>` 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 configure this within your [keymap](#keymap) to accomplish the same thing.
+
+::: tip
+If you get build errors, you will need to convert the keyboard's code to be [compatible](#keyboard-req) with the converter feature, or provide additional [keymap](#keymap-add) configuration.
+:::
+
## Supported Converters
+Each converter category is broken down by its declared `pin compatibility`. This ensures that only valid combinations are attempted.
+
The following converters are available at this time:
| From | To |
@@ -28,86 +42,56 @@ The following converters are available at this time:
| `elite_c` | `helios` |
| `elite_c` | `liatris` |
+## Configuration
-## Overview
+Configuring a converter to use can be done by adding one of the following lines to your keymaps's configuration:
-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=<target>` to your compile/flash command. For example:
+:::::tabs
-```sh
-qmk flash -c -kb keebio/bdn9/rev1 -km default -e CONVERT_TO=proton_c
-```
-
-You can also add the same `CONVERT_TO=<target>` to your keymap's `rules.mk`, which will accomplish the same thing.
-
-::: tip
-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](drivers/gpio) that will work for both ARM and AVR. This shouldn't affect the AVR builds at all.
-:::
+==== keymap.json
-### Conditional Configuration
-
-Once a converter is enabled, it exposes the `CONVERT_TO_<target_uppercase>` 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
+```json [keymap.json]
+{
+ "version": 1,
+ "keyboard": "keebio/bdn9/rev1",
+ "keymap": "keebio_bdn9_rev1_layout_2025-05-20",
+ "converter": "proton_c", // [!code focus]
+ "layout": "LAYOUT",
+}
```
-### Pin Compatibility
-
-To ensure compatibility, provide validation, and enable future workflows, a keyboard should declare its `pin compatibility`. For legacy reasons, this is currently assumed to be `promicro`. The following pin compatibility interfaces are currently defined:
-
-| Pin Compatibility | 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`:
+==== rules.mk
```makefile
-PIN_COMPATIBLE = elite_c
+CONVERT_TO = proton_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) | `sparkfun_pm2040` |
-| [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` |
-| [0xCB Helios](https://keeb.supply/products/0xcb-helios) | `helios` |
-| [Liatris](https://splitkb.com/products/liatris) | `liatris` |
-| [Imera](https://splitkb.com/products/imera) | `imera` |
-| [Michi](https://github.com/ci-bus/michi-promicro-rp2040) | `michi` |
-| [Svlinky](https://github.com/sadekbaroudi/svlinky) | `svlinky` |
-
-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` |
-| `sparkfun_pm2040` | `-e CONVERT_TO=sparkfun_pm2040` | `CONVERT_TO=sparkfun_pm2040` | `#ifdef CONVERT_TO_SPARKFUN_PM2040` |
-| `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` |
-| `rp2040_ce` | `-e CONVERT_TO=rp2040_ce` | `CONVERT_TO=rp2040_ce` | `#ifdef CONVERT_TO_RP2040_CE` |
-| `elite_pi` | `-e CONVERT_TO=elite_pi` | `CONVERT_TO=elite_pi` | `#ifdef CONVERT_TO_ELITE_PI` |
-| `helios` | `-e CONVERT_TO=helios` | `CONVERT_TO=helios` | `#ifdef CONVERT_TO_HELIOS` |
-| `liatris` | `-e CONVERT_TO=liatris` | `CONVERT_TO=liatris` | `#ifdef CONVERT_TO_LIATRIS` |
-| `imera` | `-e CONVERT_TO=imera` | `CONVERT_TO=imera` | `#ifdef CONVERT_TO_IMERA` |
-| `michi` | `-e CONVERT_TO=michi` | `CONVERT_TO=michi` | `#ifdef CONVERT_TO_MICHI` |
-| `svlinky` | `-e CONVERT_TO=svlinky` | `CONVERT_TO=svlinky` | `#ifdef CONVERT_TO_SVLINKY` |
+:::::
+
+::: tip
+If you get build errors, you will need to convert the keyboard's code to be [compatible](#keyboard-req) with the converter feature, or provide additional [keymap](#keymap-add) configuration.
+:::
+
+## Pro Micro Converters
+
+If a board currently supported by QMK uses a [Pro Micro](https://www.sparkfun.com/products/12640) (or compatible board), the supported alternative controllers are:
+
+| Device | Target | CLI Argument | `rules.mk` | Condition |
+|------------------------------------------------------------------------------------------|-------------------|---------------------------------|------------------------------|-------------------------------------|
+| [Proton C](https://qmk.fm/proton-c/) | `proton_c` | `-e CONVERT_TO=proton_c` | `CONVERT_TO=proton_c` | `#ifdef CONVERT_TO_PROTON_C` |
+| [Adafruit KB2040](https://learn.adafruit.com/adafruit-kb2040) | `kb2040` | `-e CONVERT_TO=kb2040` | `CONVERT_TO=kb2040` | `#ifdef CONVERT_TO_KB2040` |
+| [SparkFun Pro Micro - RP2040](https://www.sparkfun.com/products/18288) | `sparkfun_pm2040` | `-e CONVERT_TO=sparkfun_pm2040` | `CONVERT_TO=sparkfun_pm2040` | `#ifdef CONVERT_TO_SPARKFUN_PM2040` |
+| [Blok](https://boardsource.xyz/store/628b95b494dfa308a6581622) | `blok` | `-e CONVERT_TO=blok` | `CONVERT_TO=blok` | `#ifdef CONVERT_TO_BLOK` |
+| [Bit-C PRO](https://nullbits.co/bit-c-pro) | `bit_c_pro` | `-e CONVERT_TO=bit_c_pro` | `CONVERT_TO=bit_c_pro` | `#ifdef CONVERT_TO_BIT_C_PRO` |
+| [STeMCell](https://github.com/megamind4089/STeMCell) | `stemcell` | `-e CONVERT_TO=stemcell` | `CONVERT_TO=stemcell` | `#ifdef CONVERT_TO_STEMCELL` |
+| [customMK Bonsai C4](https://shop.custommk.com/products/bonsai-c4-microcontroller-board) | `bonsai_c4` | `-e CONVERT_TO=bonsai_c4` | `CONVERT_TO=bonsai_c4` | `#ifdef CONVERT_TO_BONSAI_C4` |
+| [RP2040 Community Edition](#rp2040_ce) | `rp2040_ce` | `-e CONVERT_TO=rp2040_ce` | `CONVERT_TO=rp2040_ce` | `#ifdef CONVERT_TO_RP2040_CE` |
+| [Elite-Pi](https://keeb.io/products/elite-pi-usb-c-pro-micro-replacement-rp2040) | `elite_pi` | `-e CONVERT_TO=elite_pi` | `CONVERT_TO=elite_pi` | `#ifdef CONVERT_TO_ELITE_PI` |
+| [0xCB Helios](https://keeb.supply/products/0xcb-helios) | `helios` | `-e CONVERT_TO=helios` | `CONVERT_TO=helios` | `#ifdef CONVERT_TO_HELIOS` |
+| [Liatris](https://splitkb.com/products/liatris) | `liatris` | `-e CONVERT_TO=liatris` | `CONVERT_TO=liatris` | `#ifdef CONVERT_TO_LIATRIS` |
+| [Imera](https://splitkb.com/products/imera) | `imera` | `-e CONVERT_TO=imera` | `CONVERT_TO=imera` | `#ifdef CONVERT_TO_IMERA` |
+| [Michi](https://github.com/ci-bus/michi-promicro-rp2040) | `michi` | `-e CONVERT_TO=michi` | `CONVERT_TO=michi` | `#ifdef CONVERT_TO_MICHI` |
+| [Svlinky](https://github.com/sadekbaroudi/svlinky) | `svlinky` | `-e CONVERT_TO=svlinky` | `CONVERT_TO=svlinky` | `#ifdef CONVERT_TO_SVLINKY` |
### Proton C {#proton_c}
@@ -119,26 +103,26 @@ The Proton C only has one on-board LED (C13), and by default, the TXLED (D5) is
The following defaults are based on what has been implemented for STM32 boards.
-| Feature | Notes |
-|----------------------------------------------|------------------------------------------------------------------------------------------------------------------|
-| [Audio](features/audio) | Enabled |
-| [RGB Lighting](features/rgblight) | Disabled |
+| Feature | Notes |
+|--------------------------------------------|----------------------------------------------------------------------------------------------------------------|
+| [Audio](features/audio) | Enabled |
+| [RGB Lighting](features/rgblight) | Disabled |
| [Backlight](features/backlight) | Forces [task driven PWM](features/backlight#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](features/split_keyboard) | Partial - heavily dependent on enabled features |
+| USB Host (e.g. USB-USB converter) | Not supported (USB host code is AVR specific and is not currently supported on ARM) |
+| [Split keyboards](features/split_keyboard) | Partial - heavily dependent on enabled features |
### Adafruit KB2040 {#kb2040}
The following defaults are based on what has been implemented for [RP2040](platformdev_rp2040) boards.
-| Feature | Notes |
-|----------------------------------------------|------------------------------------------------------------------------------------------------------------------|
-| [RGB Lighting](features/rgblight) | Enabled via `PIO` vendor driver |
+| Feature | Notes |
+|--------------------------------------------|----------------------------------------------------------------------------------------------------------------|
+| [RGB Lighting](features/rgblight) | Enabled via `PIO` vendor driver |
| [Backlight](features/backlight) | Forces [task driven PWM](features/backlight#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](features/split_keyboard) | Partial via `PIO` vendor driver - heavily dependent on enabled features |
+| USB Host (e.g. USB-USB converter) | Not supported (USB host code is AVR specific and is not currently supported on ARM) |
+| [Split keyboards](features/split_keyboard) | Partial via `PIO` vendor driver - heavily dependent on enabled features |
-### SparkFun Pro Micro - RP2040, Blok, Bit-C PRO and Michi {#sparkfun_pm2040 }
+### SparkFun Pro Micro - RP2040, Blok, Bit-C PRO and Michi {#sparkfun_pm2040}
Feature set is identical to [Adafruit KB2040](#kb2040).
@@ -177,31 +161,193 @@ Feature set is identical to [Adafruit KB2040](#kb2040). VBUS detection is enable
Feature set is a pro micro equivalent of the [RP2040 Community Edition](#rp2040_ce), except that two of the analog GPIO have been replaced with digital only GPIO. These two were moved to the FPC connector to support the [VIK specification](https://github.com/sadekbaroudi/vik). This means that if you are expecting analog support on all 4 pins as provided on a RP2040 Community Edition pinout, you will not have that. Please see the [Svlinky github page](https://github.com/sadekbaroudi/svlinky) for more details.
-## 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:
+## Elite-C Converters
-| 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` |
-| [0xCB Helios](https://keeb.supply/products/0xcb-helios) | `helios` |
-| [Liatris](https://splitkb.com/products/liatris) | `liatris` |
+If a board currently supported by 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:
-Converter summary:
+| Device | Target | CLI Argument | `rules.mk` | Condition |
+|----------------------------------------------------------------------------------|-------------|---------------------------|------------------------|-------------------------------|
+| [STeMCell](https://github.com/megamind4089/STeMCell) | `stemcell` | `-e CONVERT_TO=stemcell` | `CONVERT_TO=stemcell` | `#ifdef CONVERT_TO_STEMCELL` |
+| [RP2040 Community Edition](#rp2040_ce_elite) | `rp2040_ce` | `-e CONVERT_TO=rp2040_ce` | `CONVERT_TO=rp2040_ce` | `#ifdef CONVERT_TO_RP2040_CE` |
+| [Elite-Pi](https://keeb.io/products/elite-pi-usb-c-pro-micro-replacement-rp2040) | `elite_pi` | `-e CONVERT_TO=elite_pi` | `CONVERT_TO=elite_pi` | `#ifdef CONVERT_TO_ELITE_PI` |
+| [0xCB Helios](https://keeb.supply/products/0xcb-helios) | `helios` | `-e CONVERT_TO=helios` | `CONVERT_TO=helios` | `#ifdef CONVERT_TO_HELIOS` |
+| [Liatris](https://splitkb.com/products/liatris) | `liatris` | `-e CONVERT_TO=liatris` | `CONVERT_TO=liatris` | `#ifdef CONVERT_TO_LIATRIS` |
-| Target | Argument | `rules.mk` | Condition |
-|-------------------|---------------------------------|------------------------------|-------------------------------------|
-| `stemcell` | `-e CONVERT_TO=stemcell` | `CONVERT_TO=stemcell` | `#ifdef CONVERT_TO_STEMCELL` |
-| `rp2040_ce` | `-e CONVERT_TO=rp2040_ce` | `CONVERT_TO=rp2040_ce` | `#ifdef CONVERT_TO_RP2040_CE` |
-| `elite_pi` | `-e CONVERT_TO=elite_pi` | `CONVERT_TO=elite_pi` | `#ifdef CONVERT_TO_ELITE_PI` |
-| `helios` | `-e CONVERT_TO=helios` | `CONVERT_TO=helios` | `#ifdef CONVERT_TO_HELIOS` |
-| `liatris` | `-e CONVERT_TO=liatris` | `CONVERT_TO=liatris` | `#ifdef CONVERT_TO_LIATRIS` |
-
-### STeMCell {#stemcell}_elite
+### STeMCell {#stemcell_elite}
Identical to [Pro Micro - STeMCell](#stemcell) with support for the additional bottom row of pins.
### RP2040 Community Edition {#rp2040_ce_elite}
Identical to [Pro Micro - RP2040 Community Edition](#rp2040_ce) with support for the additional bottom row of pins.
+
+## Advanced Topics
+
+### Keyboard Configuration
+
+To configure a keyboard to allow the converter feature, add the following line to your keyboard's `.json` configuration:
+
+```json [keyboard.json]
+{
+ "maintainer": "QMK",
+ "development_board": "promicro", // [!code focus]
+ "diode_direction": "COL2ROW",
+}
+```
+
+See the [pin compatibility](#pin_compatible) for more information.
+
+#### Additional Requirements {#keyboard-req}
+
+Keyboards must use the platform agnostic abstractions provided by QMK. This includes:
+
+* Use of [GPIO Controls](drivers/gpio).
+
+### Additional Keymap Configuration {#keymap-add}
+
+While effort has been made to make converters as compatible as possible, sometimes additional platform specific configuration is required.
+
+For example, enabling hardware peripherals by adding a keymap level `mcuconf.h` with something like the following:
+```c
+#pragma once
+
+#include_next <mcuconf.h>
+
+#undef RP_SIO_USE_UART0
+#define RP_SIO_USE_UART0 TRUE
+```
+
+You can find details on how to configure drivers on their respective pages.
+
+Alternatively, you may have to disable incompatible features. For example:
+
+:::::tabs
+
+==== keymap.json
+
+```json [keymap.json]
+{
+ "version": 1,
+ "keyboard": "keebio/bdn9/rev1",
+ "keymap": "keebio_bdn9_rev1_layout_2025-05-20",
+ "converter": "proton_c",
+ "config": { // [!code focus]
+ "features": { // [!code focus]
+ "audio": false // [!code focus]
+ }
+ }
+ "layout": "LAYOUT",
+}
+```
+
+==== rules.mk
+
+```makefile
+AUDIO_ENABLE = no
+```
+
+:::::
+
+### Conditional Configuration
+
+Once a converter is enabled, it exposes the `CONVERT_TO_<target_uppercase>` 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 {#pin_compatible}
+
+To ensure compatibility, provide validation, and power future workflows, a keyboard should declare its `pin compatibility`. This ensures that only valid combinations are attempted.
+
+::: tip Note
+This will already be configured for you if are using the `promicro` development board preset.
+:::
+
+To declare the base interface for conversions, add the following line to your keyboard's configuration:
+
+```json [keyboard.json]
+{
+ "maintainer": "QMK",
+ "development_board": "elite_c", // [!code focus]
+ "pin_compatible": "elite_c", // [!code focus]
+ "diode_direction": "COL2ROW",
+}
+```
+
+The above example, configures a keyboard for a default of `elite_c` while allowing any of the `elite_c` converter targets.
+
+The framework then allows mapping of pins from `<PIN_COMPATIBLE>` to converter `<target>`.
+
+::: warning
+Mapped pins should adhere strictly to the defined interface, any extras present on the hardware should be ignored.
+:::
+
+#### Available Pin Compatibility
+
+:::::tabs
+
+==== promicro
+
+![promicro](/pin_compatible_promicro.svg)
+
+<!-- ```svgbob
+ pins
+ .-------------. LEDs
+ | | _|_ _|_
+D3 -+-O | \ /B0 \ /D5
+D2 -+-O | -+- -+-
+ | | | |
+ | |
+D1 -+-O O-+- F4
+D0 -+-O O-+- F5
+D4 -+-O O-+- F6
+C6 -+-O O-+- F7
+D7 -+-O O-+- B1
+E6 -+-O O-+- B3
+B4 -+-O O-+- B2
+B5 -+-O O-+- B6
+ | |
+ '---+-+-+-+-+---'
+``` -->
+
+::: info Notes:
+Includes LEDs - these may be mapped to unused/unavailable pins when not present.
+:::
+
+==== elite_c
+
+![elite_c](/pin_compatible_elite_c.svg)
+
+<!-- ```svgbob
+ pins
+ .-------------.
+ | |
+D3 -+-O |
+D2 -+-O |
+ | |
+ | |
+D1 -+-O O-+- F4
+D0 -+-O O-+- F5
+D4 -+-O O-+- F6
+C6 -+-O O-+- F7
+D7 -+-O O-+- B1
+E6 -+-O O-+- B3
+B4 -+-O O-+- B2
+B5 -+-O O O O O O O-+- B6
+ | | | | | | |
+ '---+-+-+-+-+---'
+ + + + + +
+ B D C F F
+ 7 5 7 1 0
+``` -->
+
+::: info Notes:
+Includes bottom row pins, no LEDs.
+:::
+
+:::::
diff --git a/docs/features/dynamic_macros.md b/docs/features/dynamic_macros.md
index a642ced43e..fa7373a4bd 100644
--- a/docs/features/dynamic_macros.md
+++ b/docs/features/dynamic_macros.md
@@ -32,12 +32,13 @@ For the details about the internals of the dynamic macros, please read the comme
There are a number of options added that should allow some additional degree of customization
-|Define |Default |Description |
-|----------------------------|----------------|-----------------------------------------------------------------------------------------------------------------|
-|`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. |
+|Define |Default |Description |
+|------------------------------------------|----------------|-----------------------------------------------------------------------------------------------------------------|
+|`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. |
+|`DYNAMIC_MACRO_KEEP_ORIGINAL_LAYER_STATE` |*Not Defined* |Defining this keeps the layer state when starting to record a macro |
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).
diff --git a/docs/features/haptic_feedback.md b/docs/features/haptic_feedback.md
index 08e66ea8e1..d380b1b999 100644
--- a/docs/features/haptic_feedback.md
+++ b/docs/features/haptic_feedback.md
@@ -121,7 +121,7 @@ Linear resonant actuators (LRA, also know as a linear vibrator) works different
#### DRV2605L waveform library
-DRV2605L comes with preloaded library of various waveform sequences that can be called and played. If writing a macro, these waveforms can be played using `DRV_pulse(*sequence name or number*)`
+DRV2605L comes with preloaded library of various waveform sequences that can be called and played. If writing a macro, these waveforms can be played using `drv2605l_pulse(*sequence name or number*)` after adding `#include "drv2605l.h"`.
List of waveform sequences from the datasheet:
diff --git a/docs/features/led_matrix.md b/docs/features/led_matrix.md
index 28d24bc400..f14fb47d62 100644
--- a/docs/features/led_matrix.md
+++ b/docs/features/led_matrix.md
@@ -88,6 +88,8 @@ As mentioned earlier, the center of the keyboard by default is expected to be `{
|`QK_LED_MATRIX_BRIGHTNESS_DOWN`|`LM_BRID`|Decrease the brightness level |
|`QK_LED_MATRIX_SPEED_UP` |`LM_SPDU`|Increase the animation speed |
|`QK_LED_MATRIX_SPEED_DOWN` |`LM_SPDD`|Decrease the animation speed |
+|`QK_LED_MATRIX_FLAG_NEXT` |`LM_FLGN`|Cycle through flags |
+|`QK_LED_MATRIX_FLAG_PREVIOUS` |`LM_FLGP`|Cycle through flags in reverse |
## LED Matrix Effects {#led-matrix-effects}
@@ -253,6 +255,7 @@ const char* effect_name = led_matrix_get_mode_name(led_matrix_get_mode());
#define LED_MATRIX_DEFAULT_FLAGS LED_FLAG_ALL // Sets the default LED flags, 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 reactive effects are enabled, you also will want to enable SPLIT_TRANSPORT_MIRROR
+#define LED_MATRIX_FLAG_STEPS { LED_FLAG_ALL, LED_FLAG_KEYLIGHT | LED_FLAG_MODIFIER, LED_FLAG_NONE } // Sets the flags which can be cycled through.
```
## EEPROM storage {#eeprom-storage}
@@ -505,6 +508,62 @@ The current effect speed, from 0 to 255.
---
+### `void led_matrix_set_flags(led_flags_t flags)` {#api-led-matrix-set-flags}
+
+Set the global effect flags.
+
+#### Arguments {#api-led-matrix-set-flags-arguments}
+
+ - `led_flags_t flags`
+ The [flags](#flags) value to set.
+
+---
+
+### `void led_matrix_set_flags_noeeprom(led_flags_t flags)` {#api-led-matrix-set-flags-noeeprom}
+
+Set the global effect flags. New state is not written to EEPROM.
+
+#### Arguments {#api-led-matrix-set-flags-noeeprom-arguments}
+
+ - `led_flags_t flags`
+ The [flags](#flags) value to set.
+
+---
+
+### `void led_matrix_flags_step(void)` {#api-led-matrix-flags-step}
+
+Move to the next flag combination.
+
+---
+
+### `void led_matrix_flags_step_noeeprom(void)` {#api-led-matrix-flags-step-noeeprom}
+
+Move to the next flag combination. New state is not written to EEPROM.
+
+---
+
+### `void led_matrix_flags_step_reverse(void)` {#api-led-matrix-flags-step-reverse}
+
+Move to the previous flag combination.
+
+---
+
+### `void led_matrix_flags_step_reverse_noeeprom(void)` {#api-led-matrix-flags-step-reverse-noeeprom}
+
+Move to the previous flag combination. New state is not written to EEPROM.
+
+---
+
+### `uint8_t led_matrix_get_flags(void)` {#api-led-matrix-get-flags}
+
+Get the current global effect flags.
+
+#### Return Value {#api-led-matrix-get-flags-return}
+
+The current effect [flags](#flags).
+
+---
+
### `void led_matrix_reload_from_eeprom(void)` {#api-led-matrix-reload-from-eeprom}
Reload the effect configuration (enabled, mode and brightness) from EEPROM.
diff --git a/docs/features/pointing_device.md b/docs/features/pointing_device.md
index d6dcddcdf0..eedbf59b95 100644
--- a/docs/features/pointing_device.md
+++ b/docs/features/pointing_device.md
@@ -267,6 +267,23 @@ The paw 3204 sensor uses a serial type protocol for communication, and requires
The CPI range is 400-1600, with supported values of (400, 500, 600, 800, 1000, 1200 and 1600). Defaults to 1000 CPI.
+### PAW-3222 Sensor
+
+To use the PAW-3222 sensor, add this to your `rules.mk`:
+
+```make
+POINTING_DEVICE_DRIVER = paw3222
+```
+
+The following pins must be defined in `config.h`:
+
+| Setting (`config.h`) | Description | Default |
+| --------------------- | ------------------------------------------------------------------ | ---------------------------- |
+| `PAW3222_CS_PIN` | (Required) The pin connected to the chip select pin of the sensor. | `POINTING_DEVICE_CS_PIN` |
+| `PAW3222_SPI_DIVISOR` | (Required) The SPI clock divisor. This is dependent on your MCU. | _not defined_ |
+
+The CPI range is up to 4,000. Defaults to 1,000 CPI.
+
### Pimoroni Trackball
To use the Pimoroni Trackball module, add this to your `rules.mk`:
diff --git a/docs/features/rgb_matrix.md b/docs/features/rgb_matrix.md
index 95ee4c4896..36680f24a2 100644
--- a/docs/features/rgb_matrix.md
+++ b/docs/features/rgb_matrix.md
@@ -96,6 +96,8 @@ As mentioned earlier, the center of the keyboard by default is expected to be `{
|`QK_RGB_MATRIX_VALUE_DOWN` |`RM_VALD`|Decrease the brightness level |
|`QK_RGB_MATRIX_SPEED_UP` |`RM_SPDU`|Increase the animation speed |
|`QK_RGB_MATRIX_SPEED_DOWN` |`RM_SPDD`|Decrease the animation speed |
+|`QK_RGB_MATRIX_FLAG_NEXT` |`RM_FLGN`|Cycle through flags |
+|`QK_RGB_MATRIX_FLAG_PREVIOUS` |`RM_FLGP`|Cycle through flags in reverse |
## RGB Matrix Effects {#rgb-matrix-effects}
@@ -409,6 +411,7 @@ const char* effect_name = rgb_matrix_get_mode_name(rgb_matrix_get_mode());
#define RGB_MATRIX_SPLIT { X, Y } // (Optional) For split keyboards, the number of LEDs connected on each half. X = left, Y = Right.
// If reactive effects are 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
+#define RGB_MATRIX_FLAG_STEPS { LED_FLAG_ALL, LED_FLAG_KEYLIGHT | LED_FLAG_MODIFIER, LED_FLAG_UNDERGLOW, LED_FLAG_NONE } // Sets the flags which can be cycled through.
```
## EEPROM storage {#eeprom-storage}
@@ -852,6 +855,62 @@ The current effect speed, from 0 to 255.
---
+### `void rgb_matrix_set_flags(led_flags_t flags)` {#api-rgb-matrix-set-flags}
+
+Set the global effect flags.
+
+#### Arguments {#api-rgb-matrix-set-flags-arguments}
+
+ - `led_flags_t flags`
+ The [flags](#flags) value to set.
+
+---
+
+### `void rgb_matrix_set_flags_noeeprom(led_flags_t flags)` {#api-rgb-matrix-set-flags-noeeprom}
+
+Set the global effect flags. New state is not written to EEPROM.
+
+#### Arguments {#api-rgb-matrix-set-flags-noeeprom-arguments}
+
+ - `led_flags_t flags`
+ The [flags](#flags) value to set.
+
+---
+
+### `void rgb_matrix_flags_step(void)` {#api-rgb-matrix-flags-step}
+
+Move to the next flag combination.
+
+---
+
+### `void rgb_matrix_flags_step_noeeprom(void)` {#api-rgb-matrix-flags-step-noeeprom}
+
+Move to the next flag combination. New state is not written to EEPROM.
+
+---
+
+### `void rgb_matrix_flags_step_reverse(void)` {#api-rgb-matrix-flags-step-reverse}
+
+Move to the previous flag combination.
+
+---
+
+### `void rgb_matrix_flags_step_reverse_noeeprom(void)` {#api-rgb-matrix-flags-step-reverse-noeeprom}
+
+Move to the previous flag combination. New state is not written to EEPROM.
+
+---
+
+### `uint8_t rgb_matrix_get_flags(void)` {#api-rgb-matrix-get-flags}
+
+Get the current global effect flags.
+
+#### Return Value {#api-rgb-matrix-get-flags-return}
+
+The current effect [flags](#flags).
+
+---
+
### `void rgb_matrix_sethsv(uint8_t h, uint8_t s, uint8_t v)` {#api-rgb-matrix-sethsv}
Set the global effect hue, saturation, and value (brightness).
diff --git a/docs/features/tap_dance.md b/docs/features/tap_dance.md
index d533e41aaa..688241a16b 100644
--- a/docs/features/tap_dance.md
+++ b/docs/features/tap_dance.md
@@ -40,6 +40,10 @@ Similar to the first option, the second and third option are good for simple lay
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.
+::: tip
+If too many tap dances are active at the same time, later ones won't have any effect. You need to increase `TAP_DANCE_MAX_SIMULTANEOUS` by adding `#define TAP_DANCE_MAX_SIMULTANEOUS 5` (or higher) to your keymap's `config.h` file if you expect that users may hold down many tap dance keys simultaneously. By default, only 3 tap dance keys can be used together at the same time.
+:::
+
## Implementation Details {#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!
@@ -209,11 +213,13 @@ tap_dance_action_t tap_dance_actions[] = {
bool process_record_user(uint16_t keycode, keyrecord_t *record) {
tap_dance_action_t *action;
+ tap_dance_state_t* state;
switch (keycode) {
- case TD(CT_CLN): // list all tap dance keycodes with tap-hold configurations
- action = &tap_dance_actions[QK_TAP_DANCE_GET_INDEX(keycode)];
- if (!record->event.pressed && action->state.count && !action->state.finished) {
+ case TD(CT_CLN):
+ action = tap_dance_get(QK_TAP_DANCE_GET_INDEX(keycode));
+ state = tap_dance_get_state(QK_TAP_DANCE_GET_INDEX(keycode));
+ if (!record->event.pressed && state != NULL && state->count && !state->finished) {
tap_dance_tap_hold_t *tap_hold = (tap_dance_tap_hold_t *)action->user_data;
tap_code16(tap_hold->tap);
}
diff --git a/docs/isp_flashing_guide.md b/docs/isp_flashing_guide.md
index 6f3f0a8f7d..1eaec3b59d 100644
--- a/docs/isp_flashing_guide.md
+++ b/docs/isp_flashing_guide.md
@@ -220,14 +220,18 @@ This bootloader is primarily for keyboards originally designed for the PS2AVRGB
USBaspLoader is a bootloader based on V-USB that emulates a hardware USBasp device. It runs on ATmega32A and ATmega328P MCUs.
-Precompiled `.hex` files are generally not available, but you can compile it yourself by setting up the QMK environment and following Coseyfannitutti's guide for the appropriate MCU:
+Precompiled `.hex` files are generally not available, but you can compile it yourself by setting up the QMK environment and cloning the appropriate branch of Coseyfannitutti's USBaspLoader fork:
-|MCU |Low |High |Extended|USB ID |
-|-------------------------------------------------------------------------------------|------|------|--------|-----------|
-|[ATmega32A](https://github.com/coseyfannitutti/discipline/tree/master/doc/bootloader)|`0x1F`|`0xC0`|*n/a* |`16C0:05DC`|
-|[ATmega328P](https://github.com/coseyfannitutti/discipad/tree/master/doc/bootloader) |`0xD7`|`0xD0`|`0x04` |`16C0:05DC`|
+|MCU |Low |High |Extended|USB ID |
+|-----------------------------------------------------------------------------|------|------|--------|-----------|
+|[ATmega32A](https://github.com/coseyfannitutti/USBaspLoader/tree/atmega32a) |`0x1F`|`0xC0`|*n/a* |`16C0:05DC`|
+|[ATmega328P](https://github.com/coseyfannitutti/USBaspLoader/tree/atmega328p)|`0xD7`|`0xD0`|`0x04` |`16C0:05DC`|
-Note that some boards may have their own specialized build of this bootloader in a separate repository. This will usually be linked to in the board's readme.
+From there, simply `cd` to the `firmware/` directory and run `make`, which should produce a file called `main.hex`.
+
+:::tip
+Some boards may have their own specialized build of this bootloader in a separate repository. This will usually be linked to in the board's readme.
+:::
## Flashing the Bootloader
diff --git a/docs/keycodes.md b/docs/keycodes.md
index c540a7ce7f..9c918b7676 100644
--- a/docs/keycodes.md
+++ b/docs/keycodes.md
@@ -433,6 +433,8 @@ See also: [LED Matrix](features/led_matrix)
|`QK_LED_MATRIX_BRIGHTNESS_DOWN`|`LM_BRID`|Decrease the brightness level |
|`QK_LED_MATRIX_SPEED_UP` |`LM_SPDU`|Increase the animation speed |
|`QK_LED_MATRIX_SPEED_DOWN` |`LM_SPDD`|Decrease the animation speed |
+|`QK_LED_MATRIX_FLAG_NEXT` |`LM_FLGN`|Cycle through flags |
+|`QK_LED_MATRIX_FLAG_PREVIOUS` |`LM_FLGP`|Cycle through flags in reverse |
## Magic Keycodes {#magic-keycodes}
@@ -783,6 +785,8 @@ See also: [RGB Matrix](features/rgb_matrix)
|`QK_RGB_MATRIX_VALUE_DOWN` |`RM_VALD`|Decrease the brightness level |
|`QK_RGB_MATRIX_SPEED_UP` |`RM_SPDU`|Increase the animation speed |
|`QK_RGB_MATRIX_SPEED_DOWN` |`RM_SPDD`|Decrease the animation speed |
+|`QK_RGB_MATRIX_FLAG_NEXT` |`RM_FLGN`|Cycle through flags |
+|`QK_RGB_MATRIX_FLAG_PREVIOUS` |`RM_FLGP`|Cycle through flags in reverse |
## US ANSI Shifted Symbols {#us-ansi-shifted-symbols}
diff --git a/docs/public/pin_compatible_elite_c.svg b/docs/public/pin_compatible_elite_c.svg
new file mode 100644
index 0000000000..cb54d9dfe4
--- /dev/null
+++ b/docs/public/pin_compatible_elite_c.svg
@@ -0,0 +1,61 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="208" height="352" class="svgbob"><style>.svgbob line, .svgbob path, .svgbob circle, .svgbob rect, .svgbob polygon {
+ stroke: black;
+ stroke-width: 2;
+ stroke-opacity: 1;
+ fill-opacity: 1;
+ stroke-linecap: round;
+ stroke-linejoin: miter;
+}
+.svgbob text {
+ white-space: pre;
+ fill: black;
+ font-family: Iosevka Fixed, monospace;
+ font-size: 14px;
+}
+.svgbob rect.backdrop {
+ stroke: none;
+ fill: white;
+}
+.svgbob .broken {
+ stroke-dasharray: 8;
+}
+.svgbob .filled {
+ fill: black;
+}
+.svgbob .bg_filled {
+ fill: white;
+ stroke-width: 1;
+}
+.svgbob .nofill {
+ fill: white;
+}
+.svgbob .end_marked_arrow {
+ marker-end: url(#arrow);
+}
+.svgbob .start_marked_arrow {
+ marker-start: url(#arrow);
+}
+.svgbob .end_marked_diamond {
+ marker-end: url(#diamond);
+}
+.svgbob .start_marked_diamond {
+ marker-start: url(#diamond);
+}
+.svgbob .end_marked_circle {
+ marker-end: url(#circle);
+}
+.svgbob .start_marked_circle {
+ marker-start: url(#circle);
+}
+.svgbob .end_marked_open_circle {
+ marker-end: url(#open_circle);
+}
+.svgbob .start_marked_open_circle {
+ marker-start: url(#open_circle);
+}
+.svgbob .end_marked_big_open_circle {
+ marker-end: url(#big_open_circle);
+}
+.svgbob .start_marked_big_open_circle {
+ marker-start: url(#big_open_circle);
+}<!--separator--></style><defs><marker id="arrow" viewBox="-2 -2 8 8" refX="4" refY="2" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><polygon points="0,0 0,4 4,2 0,0"></polygon></marker><marker id="diamond" viewBox="-2 -2 8 8" refX="4" refY="2" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><polygon points="0,2 2,0 4,2 2,4 0,2"></polygon></marker><marker id="circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><circle cx="4" cy="4" r="2" class="filled"></circle></marker><marker id="open_circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><circle cx="4" cy="4" r="2" class="bg_filled"></circle></marker><marker id="big_open_circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><circle cx="4" cy="4" r="3" class="bg_filled"></circle></marker></defs><rect class="backdrop" x="0" y="0" width="208" height="352"></rect><text x="82" y="28" >pins</text><line x1="24" y1="72" x2="52" y2="72" class="solid end_marked_big_open_circle"></line><line x1="24" y1="88" x2="52" y2="88" class="solid end_marked_big_open_circle"></line><line x1="24" y1="136" x2="52" y2="136" class="solid end_marked_big_open_circle"></line><line x1="160" y1="136" x2="148" y2="136" class="solid end_marked_big_open_circle"></line><line x1="160" y1="136" x2="176" y2="136" class="solid"></line><line x1="24" y1="152" x2="52" y2="152" class="solid end_marked_big_open_circle"></line><line x1="160" y1="152" x2="148" y2="152" class="solid end_marked_big_open_circle"></line><line x1="160" y1="152" x2="176" y2="152" class="solid"></line><line x1="24" y1="168" x2="52" y2="168" class="solid end_marked_big_open_circle"></line><line x1="160" y1="168" x2="148" y2="168" class="solid end_marked_big_open_circle"></line><line x1="160" y1="168" x2="176" y2="168" class="solid"></line><line x1="24" y1="184" x2="52" y2="184" class="solid end_marked_big_open_circle"></line><line x1="160" y1="184" x2="148" y2="184" class="solid end_marked_big_open_circle"></line><line x1="160" y1="184" x2="176" y2="184" class="solid"></line><line x1="24" y1="200" x2="52" y2="200" class="solid end_marked_big_open_circle"></line><line x1="160" y1="200" x2="148" y2="200" class="solid end_marked_big_open_circle"></line><line x1="160" y1="200" x2="176" y2="200" class="solid"></line><line x1="24" y1="216" x2="52" y2="216" class="solid end_marked_big_open_circle"></line><line x1="160" y1="216" x2="148" y2="216" class="solid end_marked_big_open_circle"></line><line x1="160" y1="216" x2="176" y2="216" class="solid"></line><line x1="24" y1="232" x2="52" y2="232" class="solid end_marked_big_open_circle"></line><line x1="160" y1="232" x2="148" y2="232" class="solid end_marked_big_open_circle"></line><line x1="160" y1="232" x2="176" y2="232" class="solid"></line><line x1="24" y1="248" x2="52" y2="248" class="solid end_marked_big_open_circle"></line><line x1="68" y1="252" x2="68" y2="248" class="solid end_marked_big_open_circle"></line><line x1="84" y1="252" x2="84" y2="248" class="solid end_marked_big_open_circle"></line><line x1="100" y1="252" x2="100" y2="248" class="solid end_marked_big_open_circle"></line><line x1="116" y1="252" x2="116" y2="248" class="solid end_marked_big_open_circle"></line><line x1="132" y1="252" x2="132" y2="248" class="solid end_marked_big_open_circle"></line><line x1="160" y1="248" x2="148" y2="248" class="solid end_marked_big_open_circle"></line><line x1="160" y1="248" x2="176" y2="248" class="solid"></line><line x1="68" y1="256" x2="68" y2="296" class="solid"></line><line x1="84" y1="256" x2="84" y2="296" class="solid"></line><line x1="100" y1="256" x2="100" y2="296" class="solid"></line><line x1="116" y1="256" x2="116" y2="296" class="solid"></line><line x1="132" y1="256" x2="132" y2="296" class="solid"></line><text x="66" y="316" >B</text><text x="82" y="316" >D</text><text x="98" y="316" >C</text><text x="114" y="316" >F</text><text x="130" y="316" >F</text><text x="66" y="332" >7</text><text x="82" y="332" >5</text><text x="98" y="332" >7</text><text x="114" y="332" >1</text><text x="130" y="332" >0</text><text x="2" y="76" >D3</text><text x="2" y="92" >D2</text><text x="2" y="140" >D1</text><text x="2" y="156" >D0</text><text x="2" y="172" >D4</text><text x="2" y="188" >C6</text><text x="2" y="204" >D7</text><text x="2" y="220" >E6</text><text x="2" y="236" >B4</text><text x="2" y="252" >B5</text><text x="186" y="140" >F4</text><text x="186" y="156" >F5</text><text x="186" y="172" >F6</text><text x="186" y="188" >F7</text><text x="186" y="204" >B1</text><text x="186" y="220" >B3</text><text x="186" y="236" >B2</text><text x="186" y="252" >B6</text><g><path d="M 44,40 A 8,8 0,0,0 36,48" class="nofill"></path><line x1="44" y1="40" x2="156" y2="40" class="solid"></line><path d="M 156,40 A 8,8 0,0,1 164,48" class="nofill"></path><line x1="36" y1="48" x2="36" y2="276" class="solid"></line><line x1="164" y1="48" x2="164" y2="276" class="solid"></line><path d="M 36,276 A 4,4 0,0,0 40,280" class="nofill"></path><line x1="40" y1="280" x2="160" y2="280" class="solid"></line><path d="M 164,276 A 4,4 0,0,1 160,280" class="nofill"></path></g></svg> \ No newline at end of file
diff --git a/docs/public/pin_compatible_promicro.svg b/docs/public/pin_compatible_promicro.svg
new file mode 100644
index 0000000000..044b265a57
--- /dev/null
+++ b/docs/public/pin_compatible_promicro.svg
@@ -0,0 +1,61 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="344" height="288" class="svgbob"><style>.svgbob line, .svgbob path, .svgbob circle, .svgbob rect, .svgbob polygon {
+ stroke: black;
+ stroke-width: 2;
+ stroke-opacity: 1;
+ fill-opacity: 1;
+ stroke-linecap: round;
+ stroke-linejoin: miter;
+}
+.svgbob text {
+ white-space: pre;
+ fill: black;
+ font-family: Iosevka Fixed, monospace;
+ font-size: 14px;
+}
+.svgbob rect.backdrop {
+ stroke: none;
+ fill: white;
+}
+.svgbob .broken {
+ stroke-dasharray: 8;
+}
+.svgbob .filled {
+ fill: black;
+}
+.svgbob .bg_filled {
+ fill: white;
+ stroke-width: 1;
+}
+.svgbob .nofill {
+ fill: white;
+}
+.svgbob .end_marked_arrow {
+ marker-end: url(#arrow);
+}
+.svgbob .start_marked_arrow {
+ marker-start: url(#arrow);
+}
+.svgbob .end_marked_diamond {
+ marker-end: url(#diamond);
+}
+.svgbob .start_marked_diamond {
+ marker-start: url(#diamond);
+}
+.svgbob .end_marked_circle {
+ marker-end: url(#circle);
+}
+.svgbob .start_marked_circle {
+ marker-start: url(#circle);
+}
+.svgbob .end_marked_open_circle {
+ marker-end: url(#open_circle);
+}
+.svgbob .start_marked_open_circle {
+ marker-start: url(#open_circle);
+}
+.svgbob .end_marked_big_open_circle {
+ marker-end: url(#big_open_circle);
+}
+.svgbob .start_marked_big_open_circle {
+ marker-start: url(#big_open_circle);
+}<!--separator--></style><defs><marker id="arrow" viewBox="-2 -2 8 8" refX="4" refY="2" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><polygon points="0,0 0,4 4,2 0,0"></polygon></marker><marker id="diamond" viewBox="-2 -2 8 8" refX="4" refY="2" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><polygon points="0,2 2,0 4,2 2,4 0,2"></polygon></marker><marker id="circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><circle cx="4" cy="4" r="2" class="filled"></circle></marker><marker id="open_circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><circle cx="4" cy="4" r="2" class="bg_filled"></circle></marker><marker id="big_open_circle" viewBox="0 0 8 8" refX="4" refY="4" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><circle cx="4" cy="4" r="3" class="bg_filled"></circle></marker></defs><rect class="backdrop" x="0" y="0" width="344" height="288"></rect><text x="82" y="12" >pins</text><line x1="24" y1="56" x2="52" y2="56" class="solid end_marked_big_open_circle"></line><line x1="24" y1="72" x2="52" y2="72" class="solid end_marked_big_open_circle"></line><line x1="24" y1="120" x2="52" y2="120" class="solid end_marked_big_open_circle"></line><line x1="160" y1="120" x2="148" y2="120" class="solid end_marked_big_open_circle"></line><line x1="160" y1="120" x2="176" y2="120" class="solid"></line><line x1="24" y1="136" x2="52" y2="136" class="solid end_marked_big_open_circle"></line><line x1="160" y1="136" x2="148" y2="136" class="solid end_marked_big_open_circle"></line><line x1="160" y1="136" x2="176" y2="136" class="solid"></line><line x1="24" y1="152" x2="52" y2="152" class="solid end_marked_big_open_circle"></line><line x1="160" y1="152" x2="148" y2="152" class="solid end_marked_big_open_circle"></line><line x1="160" y1="152" x2="176" y2="152" class="solid"></line><line x1="24" y1="168" x2="52" y2="168" class="solid end_marked_big_open_circle"></line><line x1="160" y1="168" x2="148" y2="168" class="solid end_marked_big_open_circle"></line><line x1="160" y1="168" x2="176" y2="168" class="solid"></line><line x1="24" y1="184" x2="52" y2="184" class="solid end_marked_big_open_circle"></line><line x1="160" y1="184" x2="148" y2="184" class="solid end_marked_big_open_circle"></line><line x1="160" y1="184" x2="176" y2="184" class="solid"></line><line x1="24" y1="200" x2="52" y2="200" class="solid end_marked_big_open_circle"></line><line x1="160" y1="200" x2="148" y2="200" class="solid end_marked_big_open_circle"></line><line x1="160" y1="200" x2="176" y2="200" class="solid"></line><line x1="24" y1="216" x2="52" y2="216" class="solid end_marked_big_open_circle"></line><line x1="160" y1="216" x2="148" y2="216" class="solid end_marked_big_open_circle"></line><line x1="160" y1="216" x2="176" y2="216" class="solid"></line><line x1="24" y1="232" x2="52" y2="232" class="solid end_marked_big_open_circle"></line><line x1="160" y1="232" x2="148" y2="232" class="solid end_marked_big_open_circle"></line><line x1="160" y1="232" x2="176" y2="232" class="solid"></line><text x="250" y="28" >LEDs</text><text x="242" y="60" >B0</text><text x="322" y="60" >D5</text><text x="2" y="60" >D3</text><text x="2" y="76" >D2</text><text x="2" y="124" >D1</text><text x="2" y="140" >D0</text><text x="2" y="156" >D4</text><text x="2" y="172" >C6</text><text x="2" y="188" >D7</text><text x="2" y="204" >E6</text><text x="2" y="220" >B4</text><text x="2" y="236" >B5</text><text x="186" y="124" >F4</text><text x="186" y="140" >F5</text><text x="186" y="156" >F6</text><text x="186" y="172" >F7</text><text x="186" y="188" >B1</text><text x="186" y="204" >B3</text><text x="186" y="220" >B2</text><text x="186" y="236" >B6</text><g><path d="M 44,24 A 8,8 0,0,0 36,32" class="nofill"></path><line x1="44" y1="24" x2="156" y2="24" class="solid"></line><path d="M 156,24 A 8,8 0,0,1 164,32" class="nofill"></path><line x1="36" y1="32" x2="36" y2="260" class="solid"></line><line x1="164" y1="32" x2="164" y2="260" class="solid"></line><path d="M 36,260 A 4,4 0,0,0 40,264" class="nofill"></path><line x1="40" y1="264" x2="160" y2="264" class="solid"></line><path d="M 164,260 A 4,4 0,0,1 160,264" class="nofill"></path></g><g><line x1="216" y1="48" x2="240" y2="48" class="solid"></line><line x1="228" y1="32" x2="228" y2="48" class="solid"></line><line x1="216" y1="48" x2="228" y2="72" class="solid"></line><line x1="240" y1="48" x2="228" y2="72" class="solid"></line><line x1="216" y1="72" x2="240" y2="72" class="solid"></line><line x1="228" y1="72" x2="228" y2="96" class="solid"></line></g><g><line x1="296" y1="48" x2="320" y2="48" class="solid"></line><line x1="308" y1="32" x2="308" y2="48" class="solid"></line><line x1="296" y1="48" x2="308" y2="72" class="solid"></line><line x1="320" y1="48" x2="308" y2="72" class="solid"></line><line x1="296" y1="72" x2="320" y2="72" class="solid"></line><line x1="308" y1="72" x2="308" y2="96" class="solid"></line></g></svg> \ No newline at end of file
diff --git a/docs/quantum_painter.md b/docs/quantum_painter.md
index bbe8944516..fbc72cb053 100644
--- a/docs/quantum_painter.md
+++ b/docs/quantum_painter.md
@@ -534,6 +534,8 @@ QUANTUM_PAINTER_DRIVERS += surface
Creating a surface in firmware can then be done with the following APIs:
```c
+// 24bpp RGB888 surface:
+painter_device_t qp_make_rgb888_surface(uint16_t panel_width, uint16_t panel_height, void *buffer);
// 16bpp RGB565 surface:
painter_device_t qp_make_rgb565_surface(uint16_t panel_width, uint16_t panel_height, void *buffer);
// 1bpp monochrome surface:
diff --git a/docs/reference_info_json.md b/docs/reference_info_json.md
index 84377ef36c..91ab7f4577 100644
--- a/docs/reference_info_json.md
+++ b/docs/reference_info_json.md
@@ -415,6 +415,9 @@ Configures the [LED Matrix](features/led_matrix) feature.
* `center_point` <Badge type="info">Array: Number</Badge>
* The centroid (geometric center) of the LEDs. Used for certain effects.
* Default: `[112, 32]`
+ * `flag_steps` <Badge type="info">Array: Number</Badge>
+ * A list of flag bitfields that can be cycled through.
+ * Default: `[255, 5, 0]`
* `default`
* `animation` <Badge type="info">String</Badge>
* The default effect. Must be one of `led_matrix.animations`
@@ -428,6 +431,9 @@ Configures the [LED Matrix](features/led_matrix) feature.
* `speed` <Badge type="info">Number</Badge>
* The default animation speed.
* Default: `128`
+ * `flags` <Badge type="info">Number</Badge>
+ * The default LED flags.
+ * Default: `255`
* `driver` <Badge type="info">String</Badge> <Badge>Required</Badge>
* The driver to use. Must be one of `custom`, `is31fl3218`, `is31fl3731`, `is31fl3733`, `is31fl3736`, `is31fl3737`, `is31fl3741`, `is31fl3742a`, `is31fl3743a`, `is31fl3745`, `is31fl3746a`, `snled27351`.
* `layout` <Badge type="info">Array: Object</Badge> <Badge>Required</Badge>
@@ -660,6 +666,9 @@ Configures the [RGB Matrix](features/rgb_matrix) feature.
* `center_point` <Badge type="info">Array: Number</Badge>
* The centroid (geometric center) of the LEDs. Used for certain effects.
* Default: `[112, 32]`
+ * `flag_steps` <Badge type="info">Array: Number</Badge>
+ * A list of flag bitfields that can be cycled through.
+ * Default: `[255, 5, 2, 0]`
* `default`
* `animation` <Badge type="info">String</Badge>
* The default effect. Must be one of `rgb_matrix.animations`
@@ -679,6 +688,9 @@ Configures the [RGB Matrix](features/rgb_matrix) feature.
* `speed` <Badge type="info">Number</Badge>
* The default animation speed.
* Default: `128`
+ * `flags` <Badge type="info">Number</Badge>
+ * The default LED flags.
+ * Default: `255`
* `driver` <Badge type="info">String</Badge> <Badge>Required</Badge>
* The driver to use. Must be one of `aw20216s`, `custom`, `is31fl3218`, `is31fl3236`, `is31fl3729`, `is31fl3731`, `is31fl3733`, `is31fl3736`, `is31fl3737`, `is31fl3741`, `is31fl3742a`, `is31fl3743a`, `is31fl3745`, `is31fl3746a`, `snled27351`, `ws2812`.
* `hue_steps` <Badge type="info">Number</Badge>
diff --git a/docs/squeezing_avr.md b/docs/squeezing_avr.md
index 458b442d59..3eed847f07 100644
--- a/docs/squeezing_avr.md
+++ b/docs/squeezing_avr.md
@@ -87,26 +87,6 @@ Or if you're not using layers at all, you can outright remove the functionality
#define NO_ACTION_LAYER
```
-## Magic Functions
-
-There are two `__attribute__ ((weak))` placeholder functions available to customize magic keycodes. If you are not using that feature to swap keycodes, such as backslash with backspace, add the following to your `keymap.c` or user space code:
-```c
-#ifndef MAGIC_ENABLE
-uint16_t keycode_config(uint16_t keycode) {
- return keycode;
-}
-#endif
-```
-Likewise, if you are not using magic keycodes to swap modifiers, such as Control with GUI, add the following to your `keymap.c` or user space code:
-```c
-#ifndef MAGIC_ENABLE
-uint8_t mod_config(uint8_t mod) {
- return mod;
-}
-#endif
-```
-Both of them will overwrite the placeholder functions with a simple return statement to reduce firmware size.
-
## OLED tweaks
One place you can save a bunch of space here is by not using `sprintf` or `snprintf`. This function call takes up ~1.5kB of firmware space, and can be rewritten. For instance, WPM uses this a lot.
diff --git a/docs/tap_hold.md b/docs/tap_hold.md
index bc2c505040..2aa628c535 100644
--- a/docs/tap_hold.md
+++ b/docs/tap_hold.md
@@ -779,6 +779,39 @@ Do not use `MOD_xxx` constants like `MOD_LSFT` or `MOD_RALT`, since they're 5-bi
[Auto Shift](features/auto_shift) has its own version of `retro tapping` called `retro shift`. It is extremely similar to `retro tapping`, but holding the key past `AUTO_SHIFT_TIMEOUT` results in the value it sends being shifted. Other configurations also affect it differently; see [here](features/auto_shift#retro-shift) for more information.
+### Speculative Hold
+
+Speculative Hold makes mod-tap keys more responsive by applying the modifier instantly on keydown, before the tap-hold decision is made. This is especially useful for actions like Shift+Click with a mouse, which can feel laggy with standard mod-taps.
+
+The firmware holds the modifier speculatively. Once the key's behavior is settled:
+
+* If held, the modifier remains active as expected until the key is released.
+* If tapped, the speculative modifier is canceled just before the tapping keycode is sent.
+
+Speculative Hold applies the modifier early but does not change the underlying tap-hold decision logic. Speculative Hold is compatible to use in combination with any other tap-hold options.
+
+To enable Speculative Hold, add the following to your `config.h`:
+
+```c
+#define SPECULATIVE_HOLD
+```
+
+By default, Speculative Hold applies to mod-taps using Shift, Ctrl, or Shift + Ctrl. You can override this behavior by defining the `get_speculative_hold()` callback in your keymap, for instance:
+
+```c
+bool get_speculative_hold(uint16_t keycode, keyrecord_t* record) {
+ switch (keycode) { // These keys may be speculatively held.
+ case LCTL_T(KC_ESC):
+ case LSFT_T(KC_Z):
+ case RSFT_T(KC_SLSH):
+ return true;
+ }
+ return false; // Disable otherwise.
+}
+```
+
+Some operating systems or applications assign actions to tapping a modifier key by itself, e.g., tapping GUI to open a start menu. Because Speculative Hold sends a lone modifier key press in some cases, it can falsely trigger these actions. To prevent this, set `DUMMY_MOD_NEUTRALIZER_KEYCODE` (and optionally `MODS_TO_NEUTRALIZE`) in your `config.h` in the same way as described above for [Retro Tapping](#retro-tapping).
+
## Why do we include the key record for the per key functions?
One thing that you may notice is that we include the key record for all of the "per key" functions, and may be wondering why we do that.