diff options
Diffstat (limited to 'docs')
-rw-r--r-- | docs/_summary.md | 1 | ||||
-rw-r--r-- | docs/config_options.md | 6 | ||||
-rw-r--r-- | docs/feature_autocorrect.md | 295 | ||||
-rw-r--r-- | docs/feature_combo.md | 2 | ||||
-rw-r--r-- | docs/feature_converters.md | 52 | ||||
-rw-r--r-- | docs/feature_encoders.md | 14 | ||||
-rw-r--r-- | docs/feature_joystick.md | 81 | ||||
-rw-r--r-- | docs/feature_led_matrix.md | 30 | ||||
-rw-r--r-- | docs/feature_pointing_device.md | 263 | ||||
-rw-r--r-- | docs/feature_ps2_mouse.md | 18 | ||||
-rw-r--r-- | docs/feature_rgb_matrix.md | 75 | ||||
-rw-r--r-- | docs/feature_unicode.md | 13 | ||||
-rw-r--r-- | docs/ja/feature_ps2_mouse.md | 16 | ||||
-rw-r--r-- | docs/keycodes.md | 2 | ||||
-rw-r--r-- | docs/keycodes_basic.md | 2 | ||||
-rw-r--r-- | docs/platformdev_rp2040.md | 1 | ||||
-rw-r--r-- | docs/quantum_painter.md | 566 | ||||
-rw-r--r-- | docs/squeezing_avr.md | 1 |
18 files changed, 1076 insertions, 362 deletions
diff --git a/docs/_summary.md b/docs/_summary.md index a0d2b2a949..ca6fb91a79 100644 --- a/docs/_summary.md +++ b/docs/_summary.md @@ -76,6 +76,7 @@ * Software Features * [Auto Shift](feature_auto_shift.md) + * [Autocorrect](feature_autocorrect.md) * [Caps Word](feature_caps_word.md) * [Combos](feature_combo.md) * [Debounce API](feature_debounce_type.md) diff --git a/docs/config_options.md b/docs/config_options.md index 02cfe83c69..5d5cae2184 100644 --- a/docs/config_options.md +++ b/docs/config_options.md @@ -49,11 +49,11 @@ This is a C header file that is one of the first things included, and will persi * defines your VID, and for most DIY projects, can be whatever you want * `#define PRODUCT_ID 0x5678` * defines your PID, and for most DIY projects, can be whatever you want -* `#define DEVICE_VER 0` +* `#define DEVICE_VER 0x0100` * defines the device version (often used for revisions) -* `#define MANUFACTURER Me` +* `#define MANUFACTURER "Me"` * generally who/whatever brand produced the board -* `#define PRODUCT Board` +* `#define PRODUCT "Board"` * the name of the keyboard * `#define MATRIX_ROWS 5` * the number of rows in your keyboard's matrix diff --git a/docs/feature_autocorrect.md b/docs/feature_autocorrect.md new file mode 100644 index 0000000000..480131e5fc --- /dev/null +++ b/docs/feature_autocorrect.md @@ -0,0 +1,295 @@ +# Autocorrect + +There are a lot of words that are prone to being typed incorrectly, due to habit, sequence or just user error. This feature leverages your firmware to automatically correct these errors, to help reduce typos. + +## How does it work? :id=how-does-it-work + +The feature maintains a small buffer of recent key presses. On each key press, it checks whether the buffer ends in a recognized typo, and if so, automatically sends keystrokes to correct it. + +The tricky part is how to efficiently check the buffer for typos. We don’t want to spend too much memory or time on storing or searching the typos. A good solution is to represent the typos with a trie data structure. A trie is a tree data structure where each node is a letter, and words are formed by following a path to one of the leaves. + +![An example trie](https://i.imgur.com/HL5DP8H.png) + +Since we search whether the buffer ends in a typo, we store the trie writing in reverse. The trie is queried starting from the last letter, then second to last letter, and so on, until either a letter doesn’t match or we reach a leaf, meaning a typo was found. + +## How do I enable Autocorrection :id=how-do-i-enable-autocorrection + +In your `rules.mk`, add this: + +```make +AUTOCORRECT_ENABLE = yes +``` + +Additionally, you will need a library for autocorrection. A small sample library is included by default, so that you can get up and running right away, but you can provide a customized library. + +By default, autocorrect is disabled. To enable it, you need to use the `AUTOCORRECT_TOGGLE` keycode to enable it. The status is stored in persistent memory, so you shouldn't need to enabled it again. + +## Customizing autocorrect library :id=customizing-autocorrect-library + +To provide a custom library, you need to create a text file with the corrections. For instance: + +```text +:thier -> their +fitler -> filter +lenght -> length +ouput -> output +widht -> width +``` + +The syntax is `typo -> correction`. Typos and corrections are case insensitive, and any whitespace before or after the typo and correction is ignored. The typo must be only the letters a–z, or the special character : representing a word break. The correction may have any non-unicode characters. + +Then, run: + +```sh +qmk generate-autocorrect-data autocorrect_dictionary.txt +``` + +This will process the file and produce an `autocorrect_data.h` file with the trie library, in the folder that you are at. You can specify the keyboard and keymap (eg `-kb planck/rev6 -km jackhumbert`), and it will place the file in that folder instead. But as long as the file is located in your keymap folder, or user folder, it should be picked up automatically. + +This file will look like this: + +```c +// :thier -> their +// fitler -> filter +// lenght -> length +// ouput -> output +// widht -> width + +#define AUTOCORRECT_MIN_LENGTH 5 // "ouput" +#define AUTOCORRECT_MAX_LENGTH 6 // ":thier" + +#define DICTIONARY_SIZE 74 + +static const uint8_t autocorrect_data[DICTIONARY_SIZE] PROGMEM = {85, 7, 0, 23, 35, 0, 0, 8, 0, 76, 16, 0, 15, 25, 0, 0, + 11, 23, 44, 0, 130, 101, 105, 114, 0, 23, 12, 9, 0, 131, 108, 116, 101, 114, 0, 75, 42, 0, 24, 64, 0, 0, 71, 49, 0, + 10, 56, 0, 0, 12, 26, 0, 129, 116, 104, 0, 17, 8, 15, 0, 129, 116, 104, 0, 19, 24, 18, 0, 130, 116, 112, 117, 116, + 0}; +``` + +### Avoiding false triggers :id=avoiding-false-triggers + +By default, typos are searched within words, to find typos within longer identifiers like maxFitlerOuput. While this is useful, a consequence is that autocorrection will falsely trigger when a typo happens to be a substring of a correctly-spelled word. For instance, if we had thier -> their as an entry, it would falsely trigger on (correct, though relatively uncommon) words like “wealthier” and “filthier.” + +The solution is to set a word break : before and/or after the typo to constrain matching. : matches space, period, comma, underscore, digits, and most other non-alpha characters. + +|Text |thier |:thier |thier: |:thier: | +|-----------------|:------:|:------:|:------:|:------:| +|see `thier` typo |matches |matches |matches |matches | +|it’s `thiers` |matches |matches |no |no | +|wealthier words |matches |no |matches |no | + +:thier: is most restrictive, matching only when thier is a whole word. + +The `qmk generate-autocorrect-data` commands can make an effort to check for entries that would false trigger as substrings of correct words. It searches each typo against a dictionary of 25K English words from the english_words Python package, provided it’s installed. (run `python3 -m pip install english_words` to install it.) + +?> Unfortunately, this is limited to just english words, at this point. + +## Overriding Autocorrect + +Occasionally you might actually want to type a typo (for instance, while editing autocorrection_dict.txt) without being autocorrected. There are a couple of ways to do this: + +1. Begin typing the typo. +2. Before typing the last letter, press and release the Ctrl or Alt key. +3. Type the remaining letters. + +This works because the autocorrection implementation doesn’t understand hotkeys, so it resets itself whenever a modifier other than shift is held. + +Additionally, you can use the `AUTOCORRECT_TOGGLE` keycode to toggle the on/off status for Autocorrect. + +### Keycodes :id=keycodes + +|Keycode | Short keycode | Description | +|---------------------|---------------|------------------------------------------------| +|`AUTOCORRECT_ON` | `CRT_ON` | Turns on the Autocorrect feature. | +|`AUTOCORRECT_OFF` | `CRT_OFF` | Turns off the Autocorrect feature. | +|`AUTOCORRECT_TOGGLE` | `CRT_TOG` | Toggles the status of the Autocorrect feature. | + +## User Callback Functions + +### Process Autocorrect + +Callback function `bool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, uint8_t *mods)` is available to customise incoming keycodes and handle exceptions. You can use this function to sanitise input before they are passed onto the autocorrect engine + +?> Sanitisation of input is required because autocorrect will only match 8-bit [basic keycodes](keycodes_basic.md) for typos. If valid modifier keys or 16-bit keycodes that are part of a user's word input (such as Shift + A) is passed through, they will fail typo letter detection. For example a [Mod-Tap](mod_tap.md) key such as `LCTL_T(KC_A)` is 16-bit and should be masked for the 8-bit `KC_A`. + +The default user callback function is found inside `quantum/process_keycode/process_autocorrect.c`. It covers most use-cases for QMK special functions and quantum keycodes, including [overriding autocorrect](#overriding-autocorrect) with a modifier other than shift. The `process_autocorrect_user` function is `weak` defined to allow user's copy inside `keymap.c` (or code files) to overwrite it. + +#### Process Autocorrect Example + +If you have a custom keycode `QMKBEST` that should be ignored as part of a word, and another custom keycode `QMKLAYER` that should override autocorrect, both can be added to the bottom of the `process_autocorrect_user` `switch` statement in your source code: + +```c +bool process_autocorrect_user(uint16_t *keycode, keyrecord_t *record, uint8_t *typo_buffer_size, uint8_t *mods) { + // See quantum_keycodes.h for reference on these matched ranges. + switch (*keycode) { + // Exclude these keycodes from processing. + case KC_LSFT: + case KC_RSFT: + case KC_CAPS: + case QK_TO ... QK_ONE_SHOT_LAYER_MAX: + case QK_LAYER_TAP_TOGGLE ... QK_LAYER_MOD_MAX: + case QK_ONE_SHOT_MOD ... QK_ONE_SHOT_MOD_MAX: + return false; + + // Mask for base keycode from shifted keys. + case QK_LSFT ... QK_LSFT + 255: + case QK_RSFT ... QK_RSFT + 255: + if (*keycode >= QK_LSFT && *keycode <= (QK_LSFT + 255)) { + *mods |= MOD_LSFT; + } else { + *mods |= MOD_RSFT; + } + *keycode &= 0xFF; // Get the basic keycode. + return true; +#ifndef NO_ACTION_TAPPING + // Exclude tap-hold keys when they are held down + // and mask for base keycode when they are tapped. + case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: +# ifdef NO_ACTION_LAYER + // Exclude Layer Tap, if layers are disabled + // but action tapping is still enabled. + return false; +# endif + case QK_MOD_TAP ... QK_MOD_TAP_MAX: + // Exclude hold if mods other than Shift is not active + if (!record->tap.count) { + return false; + } + *keycode &= 0xFF; + break; +#else + case QK_MOD_TAP ... QK_MOD_TAP_MAX: + case QK_LAYER_TAP ... QK_LAYER_TAP_MAX: + // Exclude if disabled + return false; +#endif + // Exclude swap hands keys when they are held down + // and mask for base keycode when they are tapped. + case QK_SWAP_HANDS ... QK_SWAP_HANDS_MAX: +#ifdef SWAP_HANDS_ENABLE + if (*keycode >= 0x56F0 || !record->tap.count) { + return false; + } + *keycode &= 0xFF; + break; +#else + // Exclude if disabled + return false; +#endif + // Handle custom keycodes + case QMKBEST: + return false; + case QMKLAYER: + *typo_buffer_size = 0; + return false; + } + + // Disable autocorrect while a mod other than shift is active. + if ((*mods & ~MOD_MASK_SHIFT) != 0) { + *typo_buffer_size = 0; + return false; + } + + return true; +} +``` + +?> In this callback function, `return false` will skip processing of that keycode for autocorrect. Adding `*typo_buffer_size = 0` will also reset the autocorrect buffer at the same time, cancelling any current letters already stored in the buffer. + +### Apply Autocorrect + +Additionally, `apply_autocorrect(uint8_t backspaces, const char *str)` allows for users to add additional handling to the autocorrection, or replace the functionality entirely. This passes on the number of backspaces needed to replace the words, as well as the replacement string (partial word, not the full word). + +#### Apply Autocorrect Example + +This following example will play a sound when a typo is autocorrected and execute the autocorrection itself: + +```c +#ifdef AUDIO_ENABLE +float autocorrect_song[][2] = SONG(TERMINAL_SOUND); +#endif + +bool apply_autocorrect(uint8_t backspaces, const char *str) { +#ifdef AUDIO_ENABLE + PLAY_SONG(autocorrect_song); +#endif + for (uint8_t i = 0; i < backspaces; ++i) { + tap_code(KC_BSPC); + } + send_string_P(str); + return false; +} +``` + +?> In this callback function, `return false` will stop the normal processing of autocorrect, which requires manually handling of removing the "bad" characters and typing the new characters. + +!> ***IMPORTANT***: `str` is a pointer to `PROGMEM` data for the autocorrection. If you return false, and want to send the string, this needs to use `send_string_P` and not `send_string` or `SEND_STRING`. + +You can also use `apply_autocorrect` to detect and display the event but allow internal code to execute the autocorrection with `return true`: + +```c +bool apply_autocorrect(uint8_t backspaces, const char *str) { +#ifdef OLED_ENABLE + oled_write_P(PSTR("Auto-corrected"), false); +#endif + return true; +} +``` + +## Appendix: Trie binary data format :id=appendix + +This section details how the trie is serialized to byte data in autocorrection_data. You don’t need to care about this to use this autocorrection implementation. But it is documented for the record in case anyone is interested in modifying the implementation, or just curious how it works. + +What I did here is fairly arbitrary, but it is simple to decode and gets the job done. + +### Encoding :id=encoding + +All autocorrection data is stored in a single flat array autocorrection_data. Each trie node is associated with a byte offset into this array, where data for that node is encoded, beginning with root at offset 0. There are three kinds of nodes. The highest two bits of the first byte of the node indicate what kind: + +* 00 ⇒ chain node: a trie node with a single child. +* 01 ⇒ branching node: a trie node with multiple children. +* 10 ⇒ leaf node: a leaf, corresponding to a typo and storing its correction. + +![An example trie](https://i.imgur.com/HL5DP8H.png) + +**Branching node**. Each branch is encoded with one byte for the keycode (KC_A–KC_Z) followed by a link to the child node. Links between nodes are 16-bit byte offsets relative to the beginning of the array, serialized in little endian order. + +All branches are serialized this way, one after another, and terminated with a zero byte. As described above, the node is identified as a branch by setting the two high bits of the first byte to 01, done by bitwise ORing the first keycode with 64. keycode. The root node for the above figure would be serialized like: + +``` ++-------+-------+-------+-------+-------+-------+-------+ +| R|64 | node 2 | T | node 3 | 0 | ++-------+-------+-------+-------+-------+-------+-------+ +``` + +**Chain node**. Tries tend to have long chains of single-child nodes, as seen in the example above with f-i-t-l in fitler. So to save space, we use a different format to encode chains than branching nodes. A chain is encoded as a string of keycodes, beginning with the node closest to the root, and terminated with a zero byte. The child of the last node in the chain is encoded immediately after. That child could be either a branching node or a leaf. + +In the figure above, the f-i-t-l chain is encoded as + +``` ++-------+-------+-------+-------+-------+ +| L | T | I | F | 0 | ++-------+-------+-------+-------+-------+ +``` + +If we were to encode this chain using the same format used for branching nodes, we would encode a 16-bit node link with every node, costing 8 more bytes in this example. Across the whole trie, this adds up. Conveniently, we can point to intermediate points in the chain and interpret the bytes in the same way as before. E.g. starting at the i instead of the l, and the subchain has the same format. + +**Leaf node**. A leaf node corresponds to a particular typo and stores data to correct the typo. The leaf begins with a byte for the number of backspaces to type, and is followed by a null-terminated ASCII string of the replacement text. The idea is, after tapping backspace the indicated number of times, we can simply pass this string to the `send_string_P` function. For fitler, we need to tap backspace 3 times (not 4, because we catch the typo as the final ‘r’ is pressed) and replace it with lter. To identify the node as a leaf, the two high bits are set to 10 by ORing the backspace count with 128: + +``` ++-------+-------+-------+-------+-------+-------+ +| 3|128 | 'l' | 't' | 'e' | 'r' | 0 | ++-------+-------+-------+-------+-------+-------+ +``` + +### Decoding :id=decoding + +This format is by design decodable with fairly simple logic. A 16-bit variable state represents our current position in the trie, initialized with 0 to start at the root node. Then, for each keycode, test the highest two bits in the byte at state to identify the kind of node. + +* 00 ⇒ **chain node**: If the node’s byte matches the keycode, increment state by one to go to the next byte. If the next byte is zero, increment again to go to the following node. +* 01 ⇒ **branching node**: Search the branches for one that matches the keycode, and follow its node link. +* 10 ⇒ **leaf node**: a typo has been found! We read its first byte for the number of backspaces to type, then pass its following bytes to send_string_P to type the correction. + +## Credits + +Credit goes to [getreuer](https://github.com/getreuer) for originally implementing this [here](https://getreuer.info/posts/keyboards/autocorrection/#how-does-it-work). As well as to [filterpaper](https://github.com/filterpaper) for converting the code to use PROGMEM, and additional improvements. diff --git a/docs/feature_combo.md b/docs/feature_combo.md index 42d965509b..bb0b5d7aa0 100644 --- a/docs/feature_combo.md +++ b/docs/feature_combo.md @@ -255,7 +255,7 @@ bool combo_should_trigger(uint16_t combo_index, combo_t *combo, uint16_t keycode ``` ## Variable Length Combos -If you leave `COMBO_COUNT` undefined in `config.h`, it allows you to programmatically declare the size of the Combo data structure and avoid updating `COMBO_COUNT`. Instead a variable called `COMBO_LEN` has to be set. It can be set with something similar to the following in `keymap.c`: `uint16_t COMBO_LEN = sizeof(key_combos) / sizeof(key_combos[0]);` or by adding `COMBO_LENGTH` as the *last* entry in the combo enum and then `uint16_t COMBO_LEN = COMBO_LENGTH;` as such: +If you leave `COMBO_COUNT` undefined in `config.h`, it allows you to programmatically declare the size of the Combo data structure and avoid updating `COMBO_COUNT`. Instead a variable called `COMBO_LEN` has to be set. It can be set with something similar to the following in `keymap.c`: `uint16_t COMBO_LEN = ARRAY_SIZE(key_combos);` or by adding `COMBO_LENGTH` as the *last* entry in the combo enum and then `uint16_t COMBO_LEN = COMBO_LENGTH;` as such: ```c enum myCombos { ..., diff --git a/docs/feature_converters.md b/docs/feature_converters.md index fe12254efe..3dabae915d 100644 --- a/docs/feature_converters.md +++ b/docs/feature_converters.md @@ -17,6 +17,9 @@ Currently the following converters are available: | `promicro` | `bit_c_pro` | | `promicro` | `stemcell` | | `promicro` | `bonsai_c4` | +| `promicro` | `elite_pi` | +| `elite_c` | `stemcell` | +| `elite_c` | `elite_pi` | See below for more in depth information on each converter. @@ -47,6 +50,23 @@ Once a converter is enabled, it exposes the `CONVERT_TO_<target_uppercase>` flag #endif ``` +### Pin Compatibility + +To ensure compatibility, provide validation, and power future workflows, a keyboard should declare its `pin compatibility`. For legacy reasons, this is currently assumed to be `promicro`. + +Currently the following pin compatibility interfaces are defined: + +| Pinout | Notes | +|------------|-----------------------------------| +| `promicro` | Includes RX/TX LEDs | +| `elite_c` | Includes bottom row pins, no LEDs | + +To declare the base for conversions, add this line to your keyboard's `rules.mk`: + +```makefile +PIN_COMPATIBLE = elite_c +``` + ## Pro Micro If a board currently supported in QMK uses a [Pro Micro](https://www.sparkfun.com/products/12640) (or compatible board), the supported alternative controllers are: @@ -60,6 +80,7 @@ If a board currently supported in QMK uses a [Pro Micro](https://www.sparkfun.co | [Bit-C PRO](https://nullbits.co/bit-c-pro) | `bit_c_pro` | | [STeMCell](https://github.com/megamind4089/STeMCell) | `stemcell` | | [customMK Bonsai C4](https://shop.custommk.com/products/bonsai-c4-microcontroller-board) | `bonsai_c4` | +| [Elite-Pi](https://keeb.io/products/elite-pi-usb-c-pro-micro-replacement-rp2040) | `elite_pi` | Converter summary: @@ -72,6 +93,7 @@ Converter summary: | `bit_c_pro` | `-e CONVERT_TO=bit_c_pro` | `CONVERT_TO=bit_c_pro` | `#ifdef CONVERT_TO_BIT_C_PRO` | | `stemcell` | `-e CONVERT_TO=stemcell` | `CONVERT_TO=stemcell` | `#ifdef CONVERT_TO_STEMCELL` | | `bonsai_c4` | `-e CONVERT_TO=bonsai_c4` | `CONVERT_TO=bonsai_c4` | `#ifdef CONVERT_TO_BONSAI_C4` | +| `elite_pi` | `-e CONVERT_TO=elite_pi` | `CONVERT_TO=elite_pi` | `#ifdef CONVERT_TO_ELITE_PI` | ### Proton C :id=proton_c @@ -102,9 +124,9 @@ The following defaults are based on what has been implemented for [RP2040](platf | USB Host (e.g. USB-USB converter) | Not supported (USB host code is AVR specific and is not currently supported on ARM) | | [Split keyboards](feature_split_keyboard.md) | Partial via `PIO` vendor driver - heavily dependent on enabled features | -### SparkFun Pro Micro - RP2040, Blok, and Bit-C PRO :id=promicro_rp2040 +### SparkFun Pro Micro - RP2040, Blok, Bit-C PRO, and Elite-Pi :id=promicro_rp2040 -Currently identical to [Adafruit KB2040](#kb2040). +Currently identical to [Adafruit KB2040](#kb2040). ### STeMCell :id=stemcell @@ -135,4 +157,28 @@ The Bonsai C4 only has one on-board LED (B2), and by default, both the Pro Micro #define B0 PAL_LINE(GPIOA, 9) ``` -No peripherals are enabled by default at this time, but example code to enable SPI, I2C, PWM, and Serial communications can be found [here](/keyboards/custommk/bonsai_c4_template)
\ No newline at end of file +No peripherals are enabled by default at this time, but example code to enable SPI, I2C, PWM, and Serial communications can be found [here](/keyboards/custommk/bonsai_c4_template). + +## Elite-C + +If a board currently supported in QMK uses an [Elite-C](https://keeb.io/products/elite-c-low-profile-version-usb-c-pro-micro-replacement-atmega32u4), the supported alternative controllers are: + +| Device | Target | +|----------------------------------------------------------------------------------|-------------------| +| [STeMCell](https://github.com/megamind4089/STeMCell) | `stemcell` | +| [Elite-Pi](https://keeb.io/products/elite-pi-usb-c-pro-micro-replacement-rp2040) | `elite_pi` | + +Converter summary: + +| Target | Argument | `rules.mk` | Condition | +|-------------------|---------------------------------|------------------------------|-------------------------------------| +| `stemcell` | `-e CONVERT_TO=stemcell` | `CONVERT_TO=stemcell` | `#ifdef CONVERT_TO_STEMCELL` | +| `elite_pi` | `-e CONVERT_TO=elite_pi` | `CONVERT_TO=elite_pi` | `#ifdef CONVERT_TO_ELITE_PI` | + +### STeMCell :id=stemcell_elite + +Currently identical to [STeMCell](#stemcell) with support for the additional bottom row of pins. + +### Elite-Pi :id=elite_pi + +Currently identical to [Adafruit KB2040](#kb2040), with support for the additional bottom row of pins. diff --git a/docs/feature_encoders.md b/docs/feature_encoders.md index 2e4a4fe324..60b613d6a5 100644 --- a/docs/feature_encoders.md +++ b/docs/feature_encoders.md @@ -90,6 +90,14 @@ const uint16_t PROGMEM encoder_map[][NUM_ENCODERS][2] = { ?> This should only be enabled at the keymap level. +Using encoder mapping pumps events through the normal QMK keycode processing pipeline, resulting in a _keydown/keyup_ combination pushed through `process_record_xxxxx()`. To configure the amount of time between the encoder "keyup" and "keydown", you can add the following to your `config.h`: + +```c +#define ENCODER_MAP_KEY_DELAY 10 +``` + +?> By default, the encoder map delay matches the value of `TAP_CODE_DELAY`. + ## Callbacks When not using `ENCODER_MAP_ENABLE = yes`, the callback functions can be inserted into your `<keyboard>.c`: @@ -121,7 +129,7 @@ bool encoder_update_user(uint8_t index, bool clockwise) { } ``` -!> If you return `true`, it will allow the keyboard level code to run as well. Returning `false` will override the keyboard level code, depending on how the keyboard function is set up. +!> If you return `true`, it will allow the keyboard level code to run as well. Returning `false` will override the keyboard level code, depending on how the keyboard function is set up. Layer conditions can also be used with the callback function like the following: @@ -174,7 +182,7 @@ The A an B lines of the encoders should be wired directly to the MCU, and the C/ Multiple encoders may share pins so long as each encoder has a distinct pair of pins when the following conditions are met: - using detent encoders - pads must be high at the detent stability point which is called 'default position' in QMK -- no more than two encoders sharing a pin can be turned at the same time +- no more than two encoders sharing a pin can be turned at the same time For example you can support two encoders using only 3 pins like this ``` @@ -187,4 +195,4 @@ You could even support three encoders using only three pins (one per encoder) ho #define ENCODERS_PAD_A { B1, B1, B2 } #define ENCODERS_PAD_B { B2, B3, B3 } ``` -Here rotating Encoder 0 `B1 B2` and Encoder 1 `B1 B3` could be interpreted as rotating Encoder 2 `B2 B3` or `B3 B2` depending on the timing. This may still be a useful configuration depending on your use case +Here rotating Encoder 0 `B1 B2` and Encoder 1 `B1 B3` could be interpreted as rotating Encoder 2 `B2 B3` or `B3 B2` depending on the timing. This may still be a useful configuration depending on your use case diff --git a/docs/feature_joystick.md b/docs/feature_joystick.md index 2635298587..a82192bfe1 100644 --- a/docs/feature_joystick.md +++ b/docs/feature_joystick.md @@ -70,71 +70,44 @@ When the ADC reads 900 or higher, the returned axis value will be -127, whereas In this example, the first axis will be read from the `A4` pin while `B0` is set high and `A7` is set low, using `analogReadPin()`, whereas the second axis will not be read. -In order to give a value to the second axis, you can do so in any customizable entry point: as an action, in `process_record_user()` or in `matrix_scan_user()`, or even in `joystick_task()` which is called even when no key has been pressed. -You assign a value by writing to `joystick_status.axes[axis_index]` a signed 8-bit value (ranging from -127 to 127). Then it is necessary to assign the flag `JS_UPDATED` to `joystick_status.status` in order for an updated HID report to be sent. +#### Virtual Axes -The following example writes two axes based on keypad presses, with `KC_P5` as a precision modifier: +To give a value to virtual axes, call `joystick_set_axis(axis, value)`. + +The following example adjusts two virtual axes (X and Y) based on keypad presses, with `KC_P5` as a precision modifier: ```c -#ifdef ANALOG_JOYSTICK_ENABLE -static uint8_t precision_val = 70; -static uint8_t axesFlags = 0; -enum axes { - Precision = 1, - Axis1High = 2, - Axis1Low = 4, - Axis2High = 8, - Axis2Low = 16 +joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = { + [0] = JOYSTICK_AXIS_VIRTUAL, // x + [1] = JOYSTICK_AXIS_VIRTUAL // y }; -#endif + +static bool precision = false; +static uint16_t precision_mod = 64; +static uint16_t axis_val = 127; bool process_record_user(uint16_t keycode, keyrecord_t *record) { - switch(keycode) { -#ifdef ANALOG_JOYSTICK_ENABLE - // virtual joystick -# if JOYSTICK_AXES_COUNT > 1 + int16_t precision_val = axis_val; + if (precision) { + precision_val -= precision_mod; + } + + switch (keycode) { case KC_P8: - if (record->event.pressed) { - axesFlags |= Axis2Low; - } else { - axesFlags &= ~Axis2Low; - } - joystick_status.status |= JS_UPDATED; - break; + joystick_set_axis(1, record->event.pressed ? -precision_val : 0); + return false; case KC_P2: - if (record->event.pressed) { - axesFlags |= Axis2High; - } else { - axesFlags &= ~Axis2High; - } - joystick_status.status |= JS_UPDATED; - break; -# endif + joystick_set_axis(1, record->event.pressed ? precision_val : 0); + return false; case KC_P4: - if (record->event.pressed) { - axesFlags |= Axis1Low; - } else { - axesFlags &= ~Axis1Low; - } - joystick_status.status |= JS_UPDATED; - break; + joystick_set_axis(0, record->event.pressed ? -precision_val : 0); + return false; case KC_P6: - if (record->event.pressed) { - axesFlags |= Axis1High; - } else { - axesFlags &= ~Axis1High; - } - joystick_status.status |= JS_UPDATED; - break; + joystick_set_axis(0, record->event.pressed ? precision_val : 0); + return false; case KC_P5: - if (record->event.pressed) { - axesFlags |= Precision; - } else { - axesFlags &= ~Precision; - } - joystick_status.status |= JS_UPDATED; - break; -#endif + precision = record->event.pressed; + return false; } return true; } diff --git a/docs/feature_led_matrix.md b/docs/feature_led_matrix.md index 563fa149a2..280db2a383 100644 --- a/docs/feature_led_matrix.md +++ b/docs/feature_led_matrix.md @@ -22,7 +22,7 @@ You can use between 1 and 4 IS31FL3731 IC's. Do not specify `LED_DRIVER_ADDR_<N> | `ISSI_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 | | `ISSI_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 | | `LED_DRIVER_COUNT` | (Required) How many LED driver IC's are present | | -| `DRIVER_LED_TOTAL` | (Required) How many LED lights are present across all drivers | | +| `LED_MATRIX_LED_COUNT` | (Required) How many LED lights are present across all drivers | | | `LED_DRIVER_ADDR_1` | (Required) Address for the first LED driver | | | `LED_DRIVER_ADDR_2` | (Optional) Address for the second LED driver | | | `LED_DRIVER_ADDR_3` | (Optional) Address for the third LED driver | | @@ -44,17 +44,17 @@ Here is an example using 2 drivers. #define LED_DRIVER_COUNT 2 #define LED_DRIVER_1_LED_TOTAL 25 #define LED_DRIVER_2_LED_TOTAL 24 -#define DRIVER_LED_TOTAL (LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL) +#define LED_MATRIX_LED_COUNT (LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL) ``` -!> Note the parentheses, this is so when `LED_DRIVER_LED_TOTAL` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL)` will give very different results than `rand() % LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL`. +!> Note the parentheses, this is so when `LED_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL)` will give very different results than `rand() % LED_DRIVER_1_LED_TOTAL + LED_DRIVER_2_LED_TOTAL`. For split keyboards using `LED_MATRIX_SPLIT` with an LED driver, you can either have the same driver address or different driver addresses. If using different addresses, use `DRIVER_ADDR_1` for one and `DRIVER_ADDR_2` for the other one. Then, in `g_is31_leds`, fill out the correct driver index (0 or 1). If using one address, use `DRIVER_ADDR_1` for both, and use index 0 for `g_is31_leds`. Define these arrays listing all the LEDs in your `<keyboard>.c`: ```c -const is31_led PROGMEM g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led PROGMEM g_is31_leds[LED_MATRIX_LED_COUNT] = { /* Refer to IS31 manual for these locations * driver * | LED address @@ -95,7 +95,7 @@ Configure the hardware via your `config.h`: | `ISSI_TIMEOUT` | (Optional) How long to wait for i2c messages, in milliseconds | 100 | | `ISSI_PERSISTENCE` | (Optional) Retry failed messages this many times | 0 | | `DRIVER_COUNT` | (Required) How many LED driver IC's are present | | -| `DRIVER_LED_TOTAL` | (Required) How many LED lights are present across all drivers | | +| `LED_MATRIX_LED_COUNT` | (Required) How many LED lights are present across all drivers | | | `DRIVER_ADDR_1` | (Optional) Address for the first LED driver | | | `DRIVER_ADDR_<N>` | (Required) Address for the additional LED drivers | | | `ISSI_SSR_<N>` | (Optional) Configuration for the Spread Spectrum Register | | @@ -130,16 +130,16 @@ Here is an example using 2 drivers. #define DRIVER_COUNT 2 #define DRIVER_1_LED_TOTAL 66 #define DRIVER_2_LED_TOTAL 42 -#define DRIVER_LED_TOTAL (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL) +#define LED_MATRIX_LED_COUNT (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL) ``` -!> Note the parentheses, this is so when `DRIVER_LED_TOTAL` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`. +!> Note the parentheses, this is so when `LED_MATRIX_LED_COUNT` is used in code and expanded, the values are added together before any additional math is applied to them. As an example, `rand() % (DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL)` will give very different results than `rand() % DRIVER_1_LED_TOTAL + DRIVER_2_LED_TOTAL`. Currently only 4 drivers are supported, but it would be trivial to support for more. Note that using a combination of different drivers is not supported. All drivers must be of the same model. Define these arrays listing all the LEDs in your `<keyboard>.c`: ```c -const is31_led __flash g_is31_leds[DRIVER_LED_TOTAL] = { +const is31_led __flash g_is31_leds[LED_MATRIX_LED_COUNT] = { /* Refer to IS31 manual for these locations * driver * | LED address @@ -364,10 +364,9 @@ For inspiration and examples, check out the built-in effects under `quantum/led_ #define LED_MATRIX_KEYPRESSES // reacts to keypresses #define LED_MATRIX_KEYRELEASES // reacts to keyreleases (instead of keypresses) #define LED_MATRIX_FRAMEBUFFER_EFFECTS // enable framebuffer effects -#define LED_DISABLE_TIMEOUT 0 // number of milliseconds to wait until led automatically turns off -#define LED_DISABLE_AFTER_TIMEOUT 0 // OBSOLETE: number of ticks to wait until disabling effects +#define LED_MATRIX_TIMEOUT 0 // number of milliseconds to wait until led automatically turns off #define LED_DISABLE_WHEN_USB_SUSPENDED // turn off ef |