mirror of
https://github.com/qmk/qmk_firmware
synced 2025-01-06 15:01:39 +00:00
Compare commits
37 Commits
068a716270
...
72465cb045
Author | SHA1 | Date | |
---|---|---|---|
|
72465cb045 | ||
|
5eb60827d5 | ||
|
c9f9475243 | ||
|
a25df1ab7a | ||
|
9dba024d76 | ||
|
c0cf45c241 | ||
|
a2bbbfa5b6 | ||
|
4c5f603dc4 | ||
|
bb7a3c3011 | ||
|
2b1eff29c4 | ||
|
5af4ae7c34 | ||
|
503752a991 | ||
|
c4d91801b9 | ||
|
ae9fa23395 | ||
|
da32973054 | ||
|
788f4aa72a | ||
|
355f9f910e | ||
|
2014205e09 | ||
|
234fb97313 | ||
|
4f3f5b31ae | ||
|
a525048a5f | ||
|
4e46c16be8 | ||
|
60e8288e58 | ||
|
5b5ff41d79 | ||
|
8f86425ffc | ||
|
fb6c2d8c6a | ||
|
e924a0cd36 | ||
|
6b558247db | ||
|
bd7e54a31d | ||
|
1606d67f51 | ||
|
da8ccf0d18 | ||
|
99d49aca58 | ||
|
3fdbb079cb | ||
|
ed53b7dadc | ||
|
352f4fa095 | ||
|
e0f648d260 | ||
|
fa857db172 |
@ -200,6 +200,7 @@
|
||||
"SPLIT_WPM_ENABLE": {"info_key": "split.transport.sync.wpm", "value_type": "flag"},
|
||||
|
||||
// Tapping
|
||||
"CHORDAL_HOLD": {"info_key": "tapping.chordal_hold", "value_type": "flag"},
|
||||
"HOLD_ON_OTHER_KEY_PRESS": {"info_key": "tapping.hold_on_other_key_press", "value_type": "flag"},
|
||||
"HOLD_ON_OTHER_KEY_PRESS_PER_KEY": {"info_key": "tapping.hold_on_other_key_press_per_key", "value_type": "flag"},
|
||||
"PERMISSIVE_HOLD": {"info_key": "tapping.permissive_hold", "value_type": "flag"},
|
||||
|
@ -422,7 +422,11 @@
|
||||
"h": {"$ref": "qmk.definitions.v1#/key_unit"},
|
||||
"w": {"$ref": "qmk.definitions.v1#/key_unit"},
|
||||
"x": {"$ref": "qmk.definitions.v1#/key_unit"},
|
||||
"y": {"$ref": "qmk.definitions.v1#/key_unit"}
|
||||
"y": {"$ref": "qmk.definitions.v1#/key_unit"},
|
||||
"hand": {
|
||||
"type": "string",
|
||||
"enum": ["L", "R", "*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -915,6 +919,7 @@
|
||||
"tapping": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"chordal_hold": {"type": "boolean"},
|
||||
"force_hold": {"type": "boolean"},
|
||||
"force_hold_per_key": {"type": "boolean"},
|
||||
"ignore_mod_tap_interrupt": {"type": "boolean"},
|
||||
|
@ -74,6 +74,8 @@ You can create `info.json` files at every level under `qmk_firmware/keyboards/<k
|
||||
* The delay between keydown and keyup for tap events in milliseconds.
|
||||
* Default: `0` (no delay)
|
||||
* `tapping`
|
||||
* `chordal_hold` <Badge type="info">Boolean</Badge>
|
||||
* Default: `false`
|
||||
* `hold_on_other_key_press` <Badge type="info">Boolean</Badge>
|
||||
* Default: `false`
|
||||
* `hold_on_other_key_press_per_key` <Badge type="info">Boolean</Badge>
|
||||
@ -328,6 +330,8 @@ The ISO enter key is represented by a 1.25u×2uh key. Renderers which utilize in
|
||||
* `h` <Badge type="info">KeyUnit</Badge>
|
||||
* The height of the key, in key units.
|
||||
* Default: `1` (1u)
|
||||
* `hand` <Badge type="info">String</Badge>
|
||||
* The handedness of the key for Chordal Hold, either `"L"` (left hand), `"R"` (right hand), or `"*"` (either or exempted handedness).
|
||||
* `label` <Badge type="info">String</Badge>
|
||||
* What to name the key. This is *not* a key assignment as in the keymap, but should usually correspond to the keycode for the first layer of the default keymap.
|
||||
* Example: `"Escape"`
|
||||
|
163
docs/tap_hold.md
163
docs/tap_hold.md
@ -425,6 +425,169 @@ uint16_t get_quick_tap_term(uint16_t keycode, keyrecord_t *record) {
|
||||
If `QUICK_TAP_TERM` is set higher than `TAPPING_TERM`, it will default to `TAPPING_TERM`.
|
||||
:::
|
||||
|
||||
## Chordal Hold
|
||||
|
||||
Chordal Hold is intended to be used together with either Permissive Hold or Hold
|
||||
On Other Key Press. Chordal Hold is enabled by adding to your `config.h`:
|
||||
|
||||
```c
|
||||
#define CHORDAL_HOLD
|
||||
```
|
||||
|
||||
Chordal Hold implements, by default, an "opposite hands" rule. Suppose a
|
||||
tap-hold key is pressed and then, before the tapping term, another key is
|
||||
pressed. With Chordal Hold, the tap-hold key is settled as tapped if the two
|
||||
keys are on the same hand.
|
||||
|
||||
Otherwise, if the keys are on opposite hands, Chordal Hold introduces no new
|
||||
behavior. Hold On Other Key Press or Permissive Hold may be used together with
|
||||
Chordal Hold to configure the behavior in the opposite hands case. With Hold On
|
||||
Other Key Press, an opposite hands chord is settled immediately as held. Or with
|
||||
Permissive Hold, an opposite hands chord is settled as held provided the other
|
||||
key is pressed and released (nested press) before releasing the tap-hold key.
|
||||
|
||||
Chordal Hold may be useful to avoid accidental modifier activation with
|
||||
mod-taps, particularly in rolled keypresses when using home row mods.
|
||||
|
||||
Notes:
|
||||
|
||||
* Chordal Hold has no effect after the tapping term.
|
||||
|
||||
* Combos are exempt from the opposite hands rule, since "handedness" is
|
||||
ill-defined in this case. Even so, Chordal Hold's behavior involving combos
|
||||
may be customized through the `get_chordal_hold()` callback.
|
||||
|
||||
An example of a sequence that is affected by “chordal hold”:
|
||||
|
||||
- `SFT_T(KC_A)` Down
|
||||
- `KC_C` Down
|
||||
- `KC_C` Up
|
||||
- `SFT_T(KC_A)` Up
|
||||
|
||||
```
|
||||
TAPPING_TERM
|
||||
+---------------------------|--------+
|
||||
| +----------------------+ | |
|
||||
| | SFT_T(KC_A) | | |
|
||||
| +----------------------+ | |
|
||||
| +--------------+ | |
|
||||
| | KC_C | | |
|
||||
| +--------------+ | |
|
||||
+---------------------------|--------+
|
||||
```
|
||||
|
||||
If the two keys are on the same hand, then this will produce `ac` with
|
||||
`SFT_T(KC_A)` settled as tapped the moment that `KC_C` is pressed.
|
||||
|
||||
If the two keys are on opposite hands and the `HOLD_ON_OTHER_KEY_PRESS` option
|
||||
enabled, this will produce `C` with `SFT_T(KC_A)` settled as held when `KC_C` is
|
||||
pressed.
|
||||
|
||||
Or if the two keys are on opposite hands and the `PERMISSIVE_HOLD` option is
|
||||
enabled, this will produce `C` with `SFT_T(KC_A)` settled as held when that
|
||||
`KC_C` is released.
|
||||
|
||||
### Chordal Hold Handedness
|
||||
|
||||
Determining whether keys are on the same or opposite hands involves defining the
|
||||
"handedness" of each key position. By default, if nothing is specified,
|
||||
handedness is guessed based on keyboard geometry.
|
||||
|
||||
Handedness may be specified with `chordal_hold_layout`. In keymap.c, define
|
||||
`chordal_hold_layout` in the following form:
|
||||
|
||||
```c
|
||||
const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM =
|
||||
LAYOUT(
|
||||
'L', 'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R', 'R',
|
||||
'L', 'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R', 'R',
|
||||
'L', 'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R', 'R',
|
||||
'L', 'L', 'L', 'R', 'R', 'R'
|
||||
);
|
||||
```
|
||||
|
||||
Use the same `LAYOUT` macro as used to define your keymap layers. Each entry is
|
||||
a character indicating the handedness of one key, either `'L'` for left, `'R'`
|
||||
for right, or `'*'` to exempt keys from the "opposite hands rule." A key with
|
||||
`'*'` handedness may settle as held in chords with any other key. This could be
|
||||
used perhaps on thumb keys or other places where you want to allow same-hand
|
||||
chords.
|
||||
|
||||
Keyboard makers may specify handedness in keyboard.json. Under `"layouts"`,
|
||||
specify the handedness of a key by adding a `"hand"` field with a value of
|
||||
either `"L"`, `"R"`, or `"*"`. Note that if `"layouts"` contains multiple
|
||||
layouts, only the first one is read. For example:
|
||||
|
||||
```json
|
||||
{"matrix": [5, 6], "x": 0, "y": 5.5, "w": 1.25, "hand", "*"},
|
||||
```
|
||||
|
||||
Alternatively, handedness may be defined functionally with
|
||||
`chordal_hold_handedness()`. For example, in keymap.c define:
|
||||
|
||||
```c
|
||||
char chordal_hold_handedness(keypos_t key) {
|
||||
if (key.col == 0 || key.col == MATRIX_COLS - 1) {
|
||||
return '*'; // Exempt the outer columns.
|
||||
}
|
||||
// On split keyboards, typically, the first half of the rows are on the
|
||||
// left, and the other half are on the right.
|
||||
return key.row < MATRIX_ROWS / 2 ? 'L' : 'R';
|
||||
}
|
||||
```
|
||||
|
||||
Given the matrix position of a key, the function should return `'L'`, `'R'`, or
|
||||
`'*'`. Adapt the logic in this function according to the keyboard's matrix.
|
||||
|
||||
::: warning
|
||||
Note the matrix may have irregularities around larger keys, around the edges of
|
||||
the board, and around thumb clusters. You may find it helpful to use [this
|
||||
debugging example](faq_debug#which-matrix-position-is-this-keypress) to
|
||||
correspond physical keys to matrix positions.
|
||||
:::
|
||||
|
||||
::: tip If you define both `chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS]` and
|
||||
`chordal_hold_handedness(keypos_t key)` for handedness, the latter takes
|
||||
precedence.
|
||||
:::
|
||||
|
||||
|
||||
### Per-chord customization
|
||||
|
||||
Beyond the per-key configuration possible through handedness, Chordal Hold may
|
||||
be configured at a *per-chord* granularity for detailed tuning. In keymap.c,
|
||||
define `get_chordal_hold()`. Returning `true` allows the chord to be held, while
|
||||
returning `false` settles as tapped.
|
||||
|
||||
For example:
|
||||
|
||||
```c
|
||||
bool get_chordal_hold(uint16_t tap_hold_keycode, keyrecord_t* tap_hold_record,
|
||||
uint16_t other_keycode, keyrecord_t* other_record) {
|
||||
// Exceptionally allow some one-handed chords for hotkeys.
|
||||
switch (tap_hold_keycode) {
|
||||
case LCTL_T(KC_Z):
|
||||
if (other_keycode == KC_C || other_keycode == KC_V) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case RCTL_T(KC_SLSH):
|
||||
if (other_keycode == KC_N) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Otherwise defer to the opposite hands rule.
|
||||
return get_chordal_hold_default(tap_hold_record, other_record);
|
||||
}
|
||||
```
|
||||
|
||||
As shown in the last line above, you may use
|
||||
`get_chordal_hold_default(tap_hold_record, other_record)` to get the default tap
|
||||
vs. hold decision according to the opposite hands rule.
|
||||
|
||||
|
||||
## Retro Tapping
|
||||
|
||||
To enable `retro tapping`, add the following to your `config.h`:
|
||||
|
@ -6,7 +6,7 @@ This is a standard off-the-shelf Durgod Taurus K310 full-sized 104/105-key
|
||||
keyboard without backlight. This supports both the ANSI and ISO variants.
|
||||
|
||||
* Keyboard Maintainers: [dkjer](https://github.com/dkjer) and [tylert](https://github.com/tylert)
|
||||
* Hardware Supported: [Durgod Taurus K310 board with STM32F070RBT6](https://www.durgod.com/page9?product_id=53&_l=en "Durgod.com Product Page")
|
||||
* Hardware Supported: [Durgod Taurus K310 board with STM32F070RBT6](https://www.durgod.com/product/k310-space-gray/)
|
||||
* Hardware Availability: [Amazon.com](https://www.amazon.com/Durgod-Taurus-K310-Mechanical-Keyboard/dp/B07TXB4XF3)
|
||||
|
||||
## Instructions
|
||||
|
@ -6,7 +6,7 @@ This is a standard off-the-shelf Durgod Taurus K320 TKL (87/88-key)
|
||||
keyboard without backlight. This supports both the ANSI and ISO variants.
|
||||
|
||||
* Keyboard Maintainers: [dkjer](https://github.com/dkjer) and [tylert](https://github.com/tylert)
|
||||
* Hardware Supported: [Durgod Taurus K320 board with STM32F070RBT6](https://www.durgod.com/page9?product_id=47&_l=en "Durgod.com Product Page")
|
||||
* Hardware Supported: [Durgod Taurus K320 board with STM32F070RBT6](https://www.durgod.com/product/k320-space-gray/)
|
||||
* Hardware Availability: [Amazon.com](https://www.amazon.com/Durgod-Taurus-Corona-Mechanical-Keyboard/dp/B078H3WPHM)
|
||||
|
||||
## Instructions
|
||||
@ -15,11 +15,11 @@ keyboard without backlight. This supports both the ANSI and ISO variants.
|
||||
|
||||
Make command example for this keyboard (after setting up your build environment):
|
||||
|
||||
make durgod/k3x0/k320/base:default
|
||||
make durgod/k320/base:default
|
||||
|
||||
Flashing example for this keyboard:
|
||||
|
||||
make durgod/k3x0/k320/base:default:flash
|
||||
make durgod/k320/base:default:flash
|
||||
|
||||
See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).
|
||||
|
||||
|
@ -5,8 +5,8 @@ K310 full-sized 104/105-key and K320 TKL 87/88-key keyboards.
|
||||
|
||||
* Keyboard Maintainers: [dkjer](https://github.com/dkjer) and [tylert](https://github.com/tylert)
|
||||
* Hardware Supported:
|
||||
* [Durgod Taurus K310 with STM32F070RBT6](https://www.durgod.com/page9?product_id=53&_l=en "Taurus K310 Product Page | Durgod.com")
|
||||
* [Durgod Taurus K320 with STM32F070RBT6](https://www.durgod.com/page9?product_id=47&_l=en "Taurus K320 Product Page | Durgod.com")
|
||||
* [Durgod Taurus K310 with STM32F070RBT6](https://www.durgod.com/product/k310-space-gray/)
|
||||
* [Durgod Taurus K320 with STM32F070RBT6](https://www.durgod.com/product/k320-space-gray/)
|
||||
* Hardware Availability:
|
||||
* [K310 on Amazon.com](https://www.amazon.com/Durgod-Taurus-K310-Mechanical-Keyboard/dp/B07TXB4XF3)
|
||||
* [K320 on Amazon.com](https://www.amazon.com/Durgod-Taurus-Corona-Mechanical-Keyboard/dp/B078H3WPHM)
|
||||
@ -16,8 +16,8 @@ K310 full-sized 104/105-key and K320 TKL 87/88-key keyboards.
|
||||
### Build
|
||||
|
||||
Instructions for building the K310 and K320 firmware can be found here:
|
||||
* [K310](k310/readme.md)
|
||||
* [K320](k320/readme.md)
|
||||
* [K310](../k310/readme.md)
|
||||
* [K320](../k320/readme.md)
|
||||
|
||||
### Initial Flash
|
||||
|
||||
@ -67,10 +67,10 @@ dfu-util -a 0 -d 0483:DF11 -s 0x08000000 -U k3x0_original.bin
|
||||
|
||||
```bash
|
||||
# k310
|
||||
qmk flash -kb durgod/k3x0/k310 -km default
|
||||
qmk flash -kb durgod/k310 -km default
|
||||
|
||||
# k320
|
||||
qmk flash -kb durgod/k3x0/k320 -km default
|
||||
qmk flash -kb durgod/k320 -km default
|
||||
```
|
||||
|
||||
### Subsequent Flashing
|
||||
|
6
keyboards/takashicompany/klec_02/config.h
Normal file
6
keyboards/takashicompany/klec_02/config.h
Normal file
@ -0,0 +1,6 @@
|
||||
// Copyright 2024 takashicompany (@takashicompany)
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#define EE_HANDS
|
@ -1,5 +1,9 @@
|
||||
"""Used by the make system to generate keyboard.c from info.json.
|
||||
"""
|
||||
import bisect
|
||||
import dataclasses
|
||||
from typing import Optional
|
||||
|
||||
from milc import cli
|
||||
|
||||
from qmk.info import info_json
|
||||
@ -87,6 +91,7 @@ def _gen_matrix_mask(info_data):
|
||||
lines.append(f' 0b{"".join(reversed(mask[i]))},')
|
||||
lines.append('};')
|
||||
lines.append('#endif')
|
||||
lines.append('')
|
||||
|
||||
return lines
|
||||
|
||||
@ -122,6 +127,128 @@ def _gen_joystick_axes(info_data):
|
||||
|
||||
lines.append('};')
|
||||
lines.append('#endif')
|
||||
lines.append('')
|
||||
|
||||
return lines
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class LayoutKey:
|
||||
"""Geometric info for one key in a layout."""
|
||||
row: int
|
||||
col: int
|
||||
x: float
|
||||
y: float
|
||||
w: float = 1.0
|
||||
h: float = 1.0
|
||||
hand: Optional[str] = None
|
||||
|
||||
@staticmethod
|
||||
def from_json(key_json):
|
||||
row, col = key_json['matrix']
|
||||
return LayoutKey(
|
||||
row=row,
|
||||
col=col,
|
||||
x=key_json['x'],
|
||||
y=key_json['y'],
|
||||
w=key_json.get('w', 1.0),
|
||||
h=key_json.get('h', 1.0),
|
||||
hand=key_json.get('hand', None),
|
||||
)
|
||||
|
||||
@property
|
||||
def cx(self):
|
||||
"""Center x coordinate of the key."""
|
||||
return self.x + self.w / 2.0
|
||||
|
||||
@property
|
||||
def cy(self):
|
||||
"""Center y coordinate of the key."""
|
||||
return self.y + self.h / 2.0
|
||||
|
||||
|
||||
class Layout:
|
||||
"""Geometric info of a layout."""
|
||||
def __init__(self, layout_json):
|
||||
self.keys = [LayoutKey.from_json(key_json) for key_json in layout_json['layout']]
|
||||
self.x_min = min(key.cx for key in self.keys)
|
||||
self.x_max = max(key.cx for key in self.keys)
|
||||
self.x_mid = (self.x_min + self.x_max) / 2
|
||||
# If there is one key with width >= 6u, it is probably the spacebar.
|
||||
i = [i for i, key in enumerate(self.keys) if key.w >= 6.0]
|
||||
self.spacebar = self.keys[i[0]] if len(i) == 1 else None
|
||||
|
||||
def is_symmetric(self, tol: float = 0.02):
|
||||
"""Whether the key positions are symmetric about x_mid."""
|
||||
x = sorted([key.cx for key in self.keys])
|
||||
for i in range(len(x)):
|
||||
x_i_mirrored = 2.0 * self.x_mid - x[i]
|
||||
# Find leftmost x element greater than or equal to (x_i_mirrored - tol).
|
||||
j = bisect.bisect_left(x, x_i_mirrored - tol)
|
||||
if j == len(x) or abs(x[j] - x_i_mirrored) > tol:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def widest_horizontal_gap(self):
|
||||
"""Finds the x midpoint of the widest horizontal gap between keys."""
|
||||
x = sorted([key.cx for key in self.keys])
|
||||
x_mid = self.x_mid
|
||||
max_sep = 0
|
||||
for i in range(len(x) - 1):
|
||||
sep = x[i + 1] - x[i]
|
||||
if sep > max_sep:
|
||||
max_sep = sep
|
||||
x_mid = (x[i + 1] + x[i]) / 2
|
||||
|
||||
return x_mid
|
||||
|
||||
|
||||
def _gen_chordal_hold_layout(info_data):
|
||||
"""Convert info.json content to chordal_hold_layout
|
||||
"""
|
||||
# NOTE: If there are multiple layouts, only the first is read.
|
||||
for layout_name, layout_json in info_data['layouts'].items():
|
||||
layout = Layout(layout_json)
|
||||
break
|
||||
|
||||
if layout.is_symmetric():
|
||||
# If the layout is symmetric (e.g. most split keyboards), guess the
|
||||
# handedness based on the sign of (x - layout.x_mid).
|
||||
hand_signs = [key.x - layout.x_mid for key in layout.keys]
|
||||
elif layout.spacebar is not None:
|
||||
# If the layout has a spacebar, form a dividing line through the spacebar,
|
||||
# nearly vertical but with a slight angle to follow typical row stagger.
|
||||
x0 = layout.spacebar.cx - 0.05
|
||||
y0 = layout.spacebar.cy - 1.0
|
||||
hand_signs = [(key.x - x0) - (key.y - y0) / 3.0 for key in layout.keys]
|
||||
else:
|
||||
# Fallback: assume handedness based on the widest horizontal separation.
|
||||
x_mid = layout.widest_horizontal_gap()
|
||||
hand_signs = [key.x - x_mid for key in layout.keys]
|
||||
|
||||
for key, hand_sign in zip(layout.keys, hand_signs):
|
||||
if key.hand is None:
|
||||
if key == layout.spacebar or abs(hand_sign) <= 0.02:
|
||||
key.hand = '*'
|
||||
else:
|
||||
key.hand = 'L' if hand_sign < 0.0 else 'R'
|
||||
|
||||
lines = []
|
||||
lines.append('#ifdef CHORDAL_HOLD')
|
||||
line = ('__attribute__((weak)) const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM = ' + layout_name + '(')
|
||||
|
||||
x_prev = None
|
||||
for key in layout.keys:
|
||||
if x_prev is None or key.x < x_prev:
|
||||
lines.append(line)
|
||||
line = ' '
|
||||
line += f"'{key.hand}', "
|
||||
x_prev = key.x
|
||||
|
||||
lines.append(line[:-2])
|
||||
lines.append(');')
|
||||
lines.append('#endif')
|
||||
|
||||
return lines
|
||||
|
||||
@ -136,11 +263,12 @@ def generate_keyboard_c(cli):
|
||||
kb_info_json = info_json(cli.args.keyboard)
|
||||
|
||||
# Build the layouts.h file.
|
||||
keyboard_h_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#include QMK_KEYBOARD_H', '']
|
||||
keyboard_c_lines = [GPL2_HEADER_C_LIKE, GENERATED_HEADER_C_LIKE, '#include QMK_KEYBOARD_H', '']
|
||||
|
||||
keyboard_h_lines.extend(_gen_led_configs(kb_info_json))
|
||||
keyboard_h_lines.extend(_gen_matrix_mask(kb_info_json))
|
||||
keyboard_h_lines.extend(_gen_joystick_axes(kb_info_json))
|
||||
keyboard_c_lines.extend(_gen_led_configs(kb_info_json))
|
||||
keyboard_c_lines.extend(_gen_matrix_mask(kb_info_json))
|
||||
keyboard_c_lines.extend(_gen_joystick_axes(kb_info_json))
|
||||
keyboard_c_lines.extend(_gen_chordal_hold_layout(kb_info_json))
|
||||
|
||||
# Show the results
|
||||
dump_lines(cli.args.output, keyboard_h_lines, cli.args.quiet)
|
||||
dump_lines(cli.args.output, keyboard_c_lines, cli.args.quiet)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -46,6 +46,73 @@ bool get_permissive_hold(uint16_t keycode, keyrecord_t *record);
|
||||
bool get_retro_tapping(uint16_t keycode, keyrecord_t *record);
|
||||
bool get_hold_on_other_key_press(uint16_t keycode, keyrecord_t *record);
|
||||
|
||||
#ifdef CHORDAL_HOLD
|
||||
/**
|
||||
* Callback to say when a key chord before the tapping term may be held.
|
||||
*
|
||||
* In keymap.c, define the callback
|
||||
*
|
||||
* bool get_chordal_hold(uint16_t tap_hold_keycode,
|
||||
* keyrecord_t* tap_hold_record,
|
||||
* uint16_t other_keycode,
|
||||
* keyrecord_t* other_record) {
|
||||
* // Conditions...
|
||||
* }
|
||||
*
|
||||
* This callback is called when:
|
||||
*
|
||||
* 1. `tap_hold_keycode` is pressed.
|
||||
* 2. `other_keycode` is pressed while `tap_hold_keycode` is still held,
|
||||
* provided `other_keycode` is *not* also a tap-hold key and it is pressed
|
||||
* before the tapping term.
|
||||
*
|
||||
* If false is returned, this has the effect of immediately settling the
|
||||
* tap-hold key as tapped. If true is returned, the tap-hold key is still
|
||||
* unsettled, and may be settled as held depending on configuration and
|
||||
* subsequent events.
|
||||
*
|
||||
* @param tap_hold_keycode Keycode of the tap-hold key.
|
||||
* @param tap_hold_record Record from the tap-hold press event.
|
||||
* @param other_keycode Keycode of the other key.
|
||||
* @param other_record Record from the other key's press event.
|
||||
* @return True if the tap-hold key may be considered held; false if tapped.
|
||||
*/
|
||||
bool get_chordal_hold(uint16_t tap_hold_keycode, keyrecord_t *tap_hold_record, uint16_t other_keycode, keyrecord_t *other_record);
|
||||
|
||||
/**
|
||||
* Default "opposite hands rule" for whether a key chord may settle as held.
|
||||
*
|
||||
* This function returns true when the tap-hold key and other key are on
|
||||
* "opposite hands." In detail, handedness of the two keys are compared. If
|
||||
* handedness values differ, or if either handedness is '*', the function
|
||||
* returns true, indicating that it may be held. Otherwise, it returns false,
|
||||
* in which case the tap-hold key is immediately settled at tapped.
|
||||
*
|
||||
* @param tap_hold_record Record of the active tap-hold key press.
|
||||
* @param other_record Record of the other, interrupting key press.
|
||||
* @return True if the tap-hold key may be considered held; false if tapped.
|
||||
*/
|
||||
bool get_chordal_hold_default(keyrecord_t *tap_hold_record, keyrecord_t *other_record);
|
||||
|
||||
/**
|
||||
* Gets the handedness of a key.
|
||||
*
|
||||
* This function returns:
|
||||
* 'L' for keys pressed by the left hand,
|
||||
* 'R' for keys on the right hand,
|
||||
* '*' for keys exempt from the "opposite hands rule." This could be used
|
||||
* perhaps on thumb keys or keys that might be pressed by either hand.
|
||||
*
|
||||
* @param key A key matrix position.
|
||||
* @return Handedness value.
|
||||
*/
|
||||
char chordal_hold_handedness(keypos_t key);
|
||||
|
||||
# ifdef CHORDAL_HOLD_LAYOUT
|
||||
extern const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef DYNAMIC_TAPPING_TERM_ENABLE
|
||||
extern uint16_t g_tapping_term;
|
||||
#endif
|
||||
|
21
tests/tap_hold_configurations/chordal_hold/default/config.h
Normal file
21
tests/tap_hold_configurations/chordal_hold/default/config.h
Normal file
@ -0,0 +1,21 @@
|
||||
/* Copyright 2022 Vladislav Kucheriavykh
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "test_common.h"
|
||||
#define CHORDAL_HOLD
|
17
tests/tap_hold_configurations/chordal_hold/default/test.mk
Normal file
17
tests/tap_hold_configurations/chordal_hold/default/test.mk
Normal file
@ -0,0 +1,17 @@
|
||||
# Copyright 2022 Vladislav Kucheriavykh
|
||||
# Copyright 2024 Google LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
INTROSPECTION_KEYMAP_C = test_keymap.c
|
@ -0,0 +1,22 @@
|
||||
// Copyright 2024 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "quantum.h"
|
||||
|
||||
const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM = {
|
||||
{'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
|
||||
{'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
|
||||
{'*', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
|
||||
{'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
|
||||
};
|
@ -0,0 +1,168 @@
|
||||
/* Copyright 2021 Stefan Kerkmann
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "action_util.h"
|
||||
#include "keyboard_report_util.hpp"
|
||||
#include "test_common.hpp"
|
||||
|
||||
using testing::_;
|
||||
using testing::InSequence;
|
||||
|
||||
class OneShot : public TestFixture {};
|
||||
class OneShotParametrizedTestFixture : public ::testing::WithParamInterface<std::pair<KeymapKey, KeymapKey>>, public OneShot {};
|
||||
|
||||
TEST_P(OneShotParametrizedTestFixture, OSMWithAdditionalKeypress) {
|
||||
TestDriver driver;
|
||||
KeymapKey osm_key = GetParam().first;
|
||||
KeymapKey regular_key = GetParam().second;
|
||||
|
||||
set_keymap({osm_key, regular_key});
|
||||
|
||||
// Press and release OSM.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
tap_key(osm_key);
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Press regular key.
|
||||
EXPECT_REPORT(driver, (osm_key.report_code, regular_key.report_code));
|
||||
regular_key.press();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Release regular key.
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
regular_key.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_P(OneShotParametrizedTestFixture, OSMAsRegularModifierWithAdditionalKeypress) {
|
||||
TestDriver driver;
|
||||
KeymapKey osm_key = GetParam().first;
|
||||
KeymapKey regular_key = GetParam().second;
|
||||
|
||||
set_keymap({osm_key, regular_key});
|
||||
|
||||
// Press OSM.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
osm_key.press();
|
||||
run_one_scan_loop();
|
||||
// Press regular key.
|
||||
regular_key.press();
|
||||
run_one_scan_loop();
|
||||
// Release regular key.
|
||||
regular_key.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Release OSM.
|
||||
EXPECT_REPORT(driver, (regular_key.report_code, osm_key.report_code));
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
osm_key.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
OneShotModifierTests,
|
||||
OneShotParametrizedTestFixture,
|
||||
::testing::Values(
|
||||
// First is osm key, second is regular key.
|
||||
std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LSFT), KC_LSFT}, KeymapKey{0, 1, 1, KC_A}),
|
||||
std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LCTL), KC_LCTL}, KeymapKey{0, 1, 1, KC_A}),
|
||||
std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LALT), KC_LALT}, KeymapKey{0, 1, 1, KC_A}),
|
||||
std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LGUI), KC_LGUI}, KeymapKey{0, 1, 1, KC_A}),
|
||||
std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RCTL), KC_RCTL}, KeymapKey{0, 1, 1, KC_A}),
|
||||
std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RSFT), KC_RSFT}, KeymapKey{0, 1, 1, KC_A}),
|
||||
std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RALT), KC_RALT}, KeymapKey{0, 1, 1, KC_A}),
|
||||
std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RGUI), KC_RGUI}, KeymapKey{0, 1, 1, KC_A})
|
||||
));
|
||||
// clang-format on
|
||||
|
||||
TEST_F(OneShot, OSLWithAdditionalKeypress) {
|
||||
TestDriver driver;
|
||||
InSequence s;
|
||||
KeymapKey osl_key = KeymapKey{0, 0, 0, OSL(1)};
|
||||
KeymapKey osl_key1 = KeymapKey{1, 0, 0, KC_X};
|
||||
KeymapKey regular_key0 = KeymapKey{0, 1, 0, KC_Y};
|
||||
KeymapKey regular_key1 = KeymapKey{1, 1, 0, KC_A};
|
||||
|
||||
set_keymap({osl_key, osl_key1, regular_key0, regular_key1});
|
||||
|
||||
// Press OSL key.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
osl_key.press();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Release OSL key.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
osl_key.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Press regular key.
|
||||
EXPECT_REPORT(driver, (regular_key1.report_code));
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
regular_key1.press();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Release regular key.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
regular_key1.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_F(OneShot, OSLWithOsmAndAdditionalKeypress) {
|
||||
TestDriver driver;
|
||||
InSequence s;
|
||||
KeymapKey osl_key = KeymapKey{0, 0, 0, OSL(1)};
|
||||
KeymapKey osm_key = KeymapKey{1, 1, 0, OSM(MOD_LSFT), KC_LSFT};
|
||||
KeymapKey regular_key = KeymapKey{1, 1, 1, KC_A};
|
||||
KeymapKey blank_key = KeymapKey{1, 0, 0, KC_NO};
|
||||
|
||||
set_keymap({osl_key, osm_key, regular_key, blank_key});
|
||||
|
||||
// Press OSL key.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
osl_key.press();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Release OSL key.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
osl_key.release();
|
||||
run_one_scan_loop();
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Press and release OSM.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
tap_key(osm_key);
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Tap regular key.
|
||||
EXPECT_REPORT(driver, (osm_key.report_code, regular_key.report_code));
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
tap_key(regular_key);
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
@ -0,0 +1,264 @@
|
||||
// Copyright 2024 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "keyboard_report_util.hpp"
|
||||
#include "keycode.h"
|
||||
#include "test_common.hpp"
|
||||
#include "action_tapping.h"
|
||||
#include "test_fixture.hpp"
|
||||
#include "test_keymap_key.hpp"
|
||||
|
||||
using testing::_;
|
||||
using testing::InSequence;
|
||||
|
||||
class ChordalHoldDefault : public TestFixture {};
|
||||
|
||||
TEST_F(ChordalHoldDefault, chord_nested_press_settled_as_tap) {
|
||||
TestDriver driver;
|
||||
InSequence s;
|
||||
// Mod-tap key on the left hand.
|
||||
auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
|
||||
// Regular key on the right hand.
|
||||
auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);
|
||||
|
||||
set_keymap({mod_tap_key, regular_key});
|
||||
|
||||
// Press mod-tap key.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
mod_tap_key.press();
|
||||
run_one_scan_loop();
|
||||
// Tap regular key.
|
||||
tap_key(regular_key);
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Release mod-tap key.
|
||||
EXPECT_REPORT(driver, (KC_P));
|
||||
EXPECT_REPORT(driver, (KC_P, KC_A));
|
||||
EXPECT_REPORT(driver, (KC_P));
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
mod_tap_key.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_F(ChordalHoldDefault, chord_rolled_press_settled_as_tap) {
|
||||
TestDriver driver;
|
||||
InSequence s;
|
||||
// Mod-tap key on the left hand.
|
||||
auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
|
||||
// Regular key on the right hand.
|
||||
auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);
|
||||
|
||||
set_keymap({mod_tap_key, regular_key});
|
||||
|
||||
// Press mod-tap key and regular key.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
mod_tap_key.press();
|
||||
run_one_scan_loop();
|
||||
regular_key.press();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Release mod-tap key.
|
||||
EXPECT_REPORT(driver, (KC_P));
|
||||
EXPECT_REPORT(driver, (KC_P, KC_A));
|
||||
EXPECT_REPORT(driver, (KC_A));
|
||||
mod_tap_key.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Release regular key.
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
regular_key.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_F(ChordalHoldDefault, non_chord_with_mod_tap_settled_as_tap) {
|
||||
TestDriver driver;
|
||||
InSequence s;
|
||||
// Mod-tap key and regular key both on the left hand.
|
||||
auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
|
||||
auto regular_key = KeymapKey(0, 2, 0, KC_A);
|
||||
|
||||
set_keymap({mod_tap_key, regular_key});
|
||||
|
||||
// Press mod-tap-hold key.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
mod_tap_key.press();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Press regular key.
|
||||
EXPECT_REPORT(driver, (KC_P));
|
||||
EXPECT_REPORT(driver, (KC_P, KC_A));
|
||||
regular_key.press();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Release regular key.
|
||||
EXPECT_REPORT(driver, (KC_P));
|
||||
regular_key.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Release mod-tap-hold key.
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
mod_tap_key.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_F(ChordalHoldDefault, tap_mod_tap_key) {
|
||||
TestDriver driver;
|
||||
InSequence s;
|
||||
auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
|
||||
|
||||
set_keymap({mod_tap_key});
|
||||
|
||||
EXPECT_NO_REPORT(driver);
|
||||
mod_tap_key.press();
|
||||
idle_for(TAPPING_TERM - 1);
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
EXPECT_REPORT(driver, (KC_P));
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
mod_tap_key.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_F(ChordalHoldDefault, hold_mod_tap_key) {
|
||||
TestDriver driver;
|
||||
InSequence s;
|
||||
auto mod_tap_key = KeymapKey(0, 1, 0, SFT_T(KC_P));
|
||||
|
||||
set_keymap({mod_tap_key});
|
||||
|
||||
EXPECT_REPORT(driver, (KC_LEFT_SHIFT));
|
||||
mod_tap_key.press();
|
||||
idle_for(TAPPING_TERM + 1);
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
mod_tap_key.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_F(ChordalHoldDefault, two_mod_taps_same_hand_hold_til_timeout) {
|
||||
TestDriver driver;
|
||||
InSequence s;
|
||||
auto mod_tap_key1 = KeymapKey(0, MATRIX_COLS - 2, 0, RCTL_T(KC_A));
|
||||
auto mod_tap_key2 = KeymapKey(0, MATRIX_COLS - 1, 0, RSFT_T(KC_B));
|
||||
|
||||
set_keymap({mod_tap_key1, mod_tap_key2});
|
||||
|
||||
// Press mod-tap keys.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
mod_tap_key1.press();
|
||||
run_one_scan_loop();
|
||||
mod_tap_key2.press();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Continue holding til the tapping term.
|
||||
EXPECT_REPORT(driver, (KC_RIGHT_CTRL));
|
||||
EXPECT_REPORT(driver, (KC_RIGHT_CTRL, KC_RIGHT_SHIFT));
|
||||
idle_for(TAPPING_TERM);
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Release mod-tap keys.
|
||||
EXPECT_REPORT(driver, (KC_RIGHT_SHIFT));
|
||||
mod_tap_key1.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
mod_tap_key2.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_F(ChordalHoldDefault, three_mod_taps_same_hand_streak_roll) {
|
||||
TestDriver driver;
|
||||
InSequence s;
|
||||
auto mod_tap_key1 = KeymapKey(0, 1, 0, SFT_T(KC_A));
|
||||
auto mod_tap_key2 = KeymapKey(0, 2, 0, CTL_T(KC_B));
|
||||
auto mod_tap_key3 = KeymapKey(0, 3, 0, RSFT_T(KC_C));
|
||||
|
||||
set_keymap({mod_tap_key1, mod_tap_key2, mod_tap_key3});
|
||||
|
||||
// Press mod-tap keys.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
mod_tap_key1.press();
|
||||
run_one_scan_loop();
|
||||
mod_tap_key2.press();
|
||||
run_one_scan_loop();
|
||||
mod_tap_key3.press();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Release keys 1, 2, 3.
|
||||
//
|
||||
// NOTE: The correct order of events should be
|
||||
// EXPECT_REPORT(driver, (KC_A, KC_B, KC_C));
|
||||
// EXPECT_REPORT(driver, (KC_B, KC_C));
|
||||
// EXPECT_REPORT(driver, (KC_C));
|
||||
// EXPECT_EMPTY_REPORT(driver);
|
||||
//
|
||||
// However, due to a workaround for https://github.com/tmk/tmk_keyboard/issues/60,
|
||||
// the events are processed out of order, with the first two keys released
|
||||
// before pressing KC_C.
|
||||
EXPECT_REPORT(driver, (KC_A));
|
||||
EXPECT_REPORT(driver, (KC_A, KC_B));
|
||||
EXPECT_REPORT(driver, (KC_B));
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
EXPECT_REPORT(driver, (KC_C));
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
mod_tap_key1.release();
|
||||
run_one_scan_loop();
|
||||
mod_tap_key2.release();
|
||||
run_one_scan_loop();
|
||||
mod_tap_key3.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_F(ChordalHoldDefault, tap_regular_key_while_layer_tap_key_is_held) {
|
||||
TestDriver driver;
|
||||
InSequence s;
|
||||
auto layer_tap_hold_key = KeymapKey(0, 1, 0, LT(1, KC_P));
|
||||
auto regular_key = KeymapKey(0, MATRIX_COLS - 1, 0, KC_A);
|
||||
auto layer_key = KeymapKey(1, MATRIX_COLS - 1, 0, KC_B);
|
||||
|
||||
set_keymap({layer_tap_hold_key, regular_key, layer_key});
|
||||
|
||||
EXPECT_NO_REPORT(driver);
|
||||
layer_tap_hold_key.press(); // Press layer-tap-hold key.
|
||||
run_one_scan_loop();
|
||||
regular_key.press(); // Press regular key.
|
||||
run_one_scan_loop();
|
||||
regular_key.release(); // Release regular key.
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
EXPECT_REPORT(driver, (KC_P));
|
||||
EXPECT_REPORT(driver, (KC_P, KC_A));
|
||||
EXPECT_REPORT(driver, (KC_P));
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
layer_tap_hold_key.release(); // Release layer-tap-hold key.
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/* Copyright 2022 Vladislav Kucheriavykh
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "test_common.h"
|
||||
#define CHORDAL_HOLD
|
||||
#define HOLD_ON_OTHER_KEY_PRESS
|
@ -0,0 +1,17 @@
|
||||
# Copyright 2022 Vladislav Kucheriavykh
|
||||
# Copyright 2024 Google LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
INTROSPECTION_KEYMAP_C = test_keymap.c
|
@ -0,0 +1,22 @@
|
||||
// Copyright 2024 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "quantum.h"
|
||||
|
||||
const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM = {
|
||||
{'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
|
||||
{'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
|
||||
{'*', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
|
||||
{'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
|
||||
};
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,22 @@
|
||||
/* Copyright 2022 Vladislav Kucheriavykh
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "test_common.h"
|
||||
#define CHORDAL_HOLD
|
||||
#define PERMISSIVE_HOLD
|
@ -0,0 +1,17 @@
|
||||
# Copyright 2022 Vladislav Kucheriavykh
|
||||
# Copyright 2024 Google LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
INTROSPECTION_KEYMAP_C = test_keymap.c
|
@ -0,0 +1,22 @@
|
||||
// Copyright 2024 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "quantum.h"
|
||||
|
||||
const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM = {
|
||||
{'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
|
||||
{'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
|
||||
{'*', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
|
||||
{'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
|
||||
};
|
@ -0,0 +1,174 @@
|
||||
/* Copyright 2021 Stefan Kerkmann
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "action_util.h"
|
||||
#include "keyboard_report_util.hpp"
|
||||
#include "test_common.hpp"
|
||||
|
||||
using testing::_;
|
||||
using testing::InSequence;
|
||||
|
||||
class OneShot : public TestFixture {};
|
||||
class OneShotParametrizedTestFixture : public ::testing::WithParamInterface<std::pair<KeymapKey, KeymapKey>>, public OneShot {};
|
||||
|
||||
TEST_P(OneShotParametrizedTestFixture, OSMWithAdditionalKeypress) {
|
||||
TestDriver driver;
|
||||
KeymapKey osm_key = GetParam().first;
|
||||
KeymapKey regular_key = GetParam().second;
|
||||
|
||||
set_keymap({osm_key, regular_key});
|
||||
|
||||
// Press and release OSM.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
tap_key(osm_key);
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Press regular key.
|
||||
EXPECT_REPORT(driver, (osm_key.report_code, regular_key.report_code));
|
||||
regular_key.press();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Release regular key.
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
regular_key.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_P(OneShotParametrizedTestFixture, OSMAsRegularModifierWithAdditionalKeypress) {
|
||||
TestDriver driver;
|
||||
KeymapKey osm_key = GetParam().first;
|
||||
KeymapKey regular_key = GetParam().second;
|
||||
|
||||
set_keymap({osm_key, regular_key});
|
||||
|
||||
// Press OSM.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
osm_key.press();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Press regular key.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
regular_key.press();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Release regular key.
|
||||
EXPECT_REPORT(driver, (osm_key.report_code)).Times(2);
|
||||
EXPECT_REPORT(driver, (regular_key.report_code, osm_key.report_code));
|
||||
regular_key.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Release OSM.
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
osm_key.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
OneShotModifierTests,
|
||||
OneShotParametrizedTestFixture,
|
||||
::testing::Values(
|
||||
// First is osm key, second is regular key.
|
||||
std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LSFT), KC_LSFT}, KeymapKey{0, 1, 1, KC_A}),
|
||||
std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LCTL), KC_LCTL}, KeymapKey{0, 1, 1, KC_A}),
|
||||
std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LALT), KC_LALT}, KeymapKey{0, 1, 1, KC_A}),
|
||||
std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_LGUI), KC_LGUI}, KeymapKey{0, 1, 1, KC_A}),
|
||||
std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RCTL), KC_RCTL}, KeymapKey{0, 1, 1, KC_A}),
|
||||
std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RSFT), KC_RSFT}, KeymapKey{0, 1, 1, KC_A}),
|
||||
std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RALT), KC_RALT}, KeymapKey{0, 1, 1, KC_A}),
|
||||
std::make_pair(KeymapKey{0, 0, 0, OSM(MOD_RGUI), KC_RGUI}, KeymapKey{0, 1, 1, KC_A})
|
||||
));
|
||||
// clang-format on
|
||||
|
||||
TEST_F(OneShot, OSLWithAdditionalKeypress) {
|
||||
TestDriver driver;
|
||||
InSequence s;
|
||||
KeymapKey osl_key = KeymapKey{0, 0, 0, OSL(1)};
|
||||
KeymapKey osl_key1 = KeymapKey{1, 0, 0, KC_X};
|
||||
KeymapKey regular_key0 = KeymapKey{0, 1, 0, KC_Y};
|
||||
KeymapKey regular_key1 = KeymapKey{1, 1, 0, KC_A};
|
||||
|
||||
set_keymap({osl_key, osl_key1, regular_key0, regular_key1});
|
||||
|
||||
// Press OSL key.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
osl_key.press();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Release OSL key.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
osl_key.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Press regular key.
|
||||
EXPECT_REPORT(driver, (regular_key1.report_code));
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
regular_key1.press();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Release regular key.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
regular_key1.release();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
||||
|
||||
TEST_F(OneShot, OSLWithOsmAndAdditionalKeypress) {
|
||||
TestDriver driver;
|
||||
InSequence s;
|
||||
KeymapKey osl_key = KeymapKey{0, 0, 0, OSL(1)};
|
||||
KeymapKey osm_key = KeymapKey{1, 1, 0, OSM(MOD_LSFT), KC_LSFT};
|
||||
KeymapKey regular_key = KeymapKey{1, 1, 1, KC_A};
|
||||
KeymapKey blank_key = KeymapKey{1, 0, 0, KC_NO};
|
||||
|
||||
set_keymap({osl_key, osm_key, regular_key, blank_key});
|
||||
|
||||
// Press OSL key.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
osl_key.press();
|
||||
run_one_scan_loop();
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Release OSL key.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
osl_key.release();
|
||||
run_one_scan_loop();
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Press and release OSM.
|
||||
EXPECT_NO_REPORT(driver);
|
||||
tap_key(osm_key);
|
||||
EXPECT_TRUE(layer_state_is(1));
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
|
||||
// Tap regular key.
|
||||
EXPECT_REPORT(driver, (osm_key.report_code, regular_key.report_code));
|
||||
EXPECT_EMPTY_REPORT(driver);
|
||||
tap_key(regular_key);
|
||||
VERIFY_AND_CLEAR(driver);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,28 @@
|
||||
/* Copyright 2022 Isaac Elenbaas
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "test_common.h"
|
||||
|
||||
#define CHORDAL_HOLD
|
||||
#define PERMISSIVE_HOLD
|
||||
|
||||
#define RETRO_SHIFT 2 * TAPPING_TERM
|
||||
// releases between AUTO_SHIFT_TIMEOUT and TAPPING_TERM are not tested
|
||||
#define AUTO_SHIFT_TIMEOUT TAPPING_TERM
|
||||
#define AUTO_SHIFT_MODIFIERS
|
@ -0,0 +1,18 @@
|
||||
# Copyright 2022 Isaac Elenbaas
|
||||
# Copyright 2024 Google LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
AUTO_SHIFT_ENABLE = yes
|
||||
INTROSPECTION_KEYMAP_C = test_keymap.c
|
@ -0,0 +1,22 @@
|
||||
// Copyright 2024 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// https://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "quantum.h"
|
||||
|
||||
const char chordal_hold_layout[MATRIX_ROWS][MATRIX_COLS] PROGMEM = {
|
||||
{'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
|
||||
{'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
|
||||
{'*', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
|
||||
{'L', 'L', 'L', 'L', 'L', 'R', 'R', 'R', 'R', 'R'},
|
||||
};
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user