2022-05-15 22:39:29 +01:00
""" Used by the make system to generate keyboard.c from info.json.
"""
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 03:32:23 -08:00
import bisect
import dataclasses
from typing import Optional
2022-05-15 22:39:29 +01:00
from milc import cli
from qmk . info import info_json
from qmk . commands import dump_lines
from qmk . keyboard import keyboard_completer , keyboard_folder
from qmk . path import normpath
2024-06-18 03:44:22 +01:00
from qmk . constants import GPL2_HEADER_C_LIKE , GENERATED_HEADER_C_LIKE , JOYSTICK_AXES
2022-05-15 22:39:29 +01:00
2024-01-14 21:33:58 +11:00
def _gen_led_configs ( info_data ) :
lines = [ ]
if ' layout ' in info_data . get ( ' rgb_matrix ' , { } ) :
lines . extend ( _gen_led_config ( info_data , ' rgb_matrix ' ) )
if ' layout ' in info_data . get ( ' led_matrix ' , { } ) :
lines . extend ( _gen_led_config ( info_data , ' led_matrix ' ) )
return lines
def _gen_led_config ( info_data , config_type ) :
2022-05-15 22:39:29 +01:00
""" Convert info.json content to g_led_config
"""
cols = info_data [ ' matrix_size ' ] [ ' cols ' ]
rows = info_data [ ' matrix_size ' ] [ ' rows ' ]
lines = [ ]
2023-02-10 23:39:13 +01:00
matrix = [ [ ' NO_LED ' ] * cols for _ in range ( rows ) ]
2022-05-15 22:39:29 +01:00
pos = [ ]
flags = [ ]
2023-02-10 23:39:13 +01:00
led_layout = info_data [ config_type ] [ ' layout ' ]
for index , led_data in enumerate ( led_layout ) :
if ' matrix ' in led_data :
row , col = led_data [ ' matrix ' ]
matrix [ row ] [ col ] = str ( index )
pos . append ( f ' {{ { led_data . get ( " x " , 0 ) } , { led_data . get ( " y " , 0 ) } }} ' )
flags . append ( str ( led_data . get ( ' flags ' , 0 ) ) )
2022-05-15 22:39:29 +01:00
if config_type == ' rgb_matrix ' :
lines . append ( ' #ifdef RGB_MATRIX_ENABLE ' )
lines . append ( ' #include " rgb_matrix.h " ' )
elif config_type == ' led_matrix ' :
lines . append ( ' #ifdef LED_MATRIX_ENABLE ' )
lines . append ( ' #include " led_matrix.h " ' )
lines . append ( ' __attribute__ ((weak)) led_config_t g_led_config = { ' )
lines . append ( ' { ' )
for line in matrix :
2023-02-10 23:39:13 +01:00
lines . append ( f ' {{ { " , " . join ( line ) } }} , ' )
2022-05-15 22:39:29 +01:00
lines . append ( ' }, ' )
2023-02-10 23:39:13 +01:00
lines . append ( f ' {{ { " , " . join ( pos ) } }} , ' )
lines . append ( f ' {{ { " , " . join ( flags ) } }} , ' )
2022-05-15 22:39:29 +01:00
lines . append ( ' }; ' )
lines . append ( ' #endif ' )
2024-01-14 21:33:58 +11:00
lines . append ( ' ' )
2022-05-15 22:39:29 +01:00
return lines
2023-10-29 01:09:02 +01:00
def _gen_matrix_mask ( info_data ) :
""" Convert info.json content to matrix_mask
"""
cols = info_data [ ' matrix_size ' ] [ ' cols ' ]
rows = info_data [ ' matrix_size ' ] [ ' rows ' ]
2023-11-01 00:55:48 +00:00
# Default mask to everything disabled
2023-12-05 13:32:07 +00:00
mask = [ [ ' 0 ' ] * cols for _ in range ( rows ) ]
2023-10-29 01:09:02 +01:00
2023-11-01 00:55:48 +00:00
# Mirror layout macros squashed on top of each other
2023-12-05 13:32:07 +00:00
for layout_name , layout_data in info_data [ ' layouts ' ] . items ( ) :
2023-11-01 00:55:48 +00:00
for key_data in layout_data [ ' layout ' ] :
row , col = key_data [ ' matrix ' ]
2023-12-05 13:32:07 +00:00
if row > = rows or col > = cols :
cli . log . error ( f ' Skipping matrix_mask due to { layout_name } containing invalid matrix values ' )
return [ ]
2023-11-01 00:55:48 +00:00
mask [ row ] [ col ] = ' 1 '
2023-10-29 01:09:02 +01:00
lines = [ ]
lines . append ( ' #ifdef MATRIX_MASKED ' )
lines . append ( ' __attribute__((weak)) const matrix_row_t matrix_mask[] = { ' )
for i in range ( rows ) :
lines . append ( f ' 0b { " " . join ( reversed ( mask [ i ] ) ) } , ' )
lines . append ( ' }; ' )
lines . append ( ' #endif ' )
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 03:32:23 -08:00
lines . append ( ' ' )
2023-10-29 01:09:02 +01:00
return lines
2024-06-18 03:44:22 +01:00
def _gen_joystick_axes ( info_data ) :
""" Convert info.json content to joystick_axes
"""
if ' axes ' not in info_data . get ( ' joystick ' , { } ) :
return [ ]
axes = info_data [ ' joystick ' ] [ ' axes ' ]
axes_keys = list ( axes . keys ( ) )
lines = [ ]
lines . append ( ' #ifdef JOYSTICK_ENABLE ' )
lines . append ( ' joystick_config_t joystick_axes[JOYSTICK_AXIS_COUNT] = { ' )
# loop over all available axes - injecting virtual axis for those not specified
for index , cur in enumerate ( JOYSTICK_AXES ) :
# bail out if we have generated all requested axis
if len ( axes_keys ) == 0 :
break
axis = ' virtual '
if cur in axes :
axis = axes [ cur ]
axes_keys . remove ( cur )
if axis == ' virtual ' :
lines . append ( f " [ { index } ] = JOYSTICK_AXIS_VIRTUAL, " )
else :
lines . append ( f " [ { index } ] = JOYSTICK_AXIS_IN( { axis [ ' input_pin ' ] } , { axis [ ' low ' ] } , { axis [ ' rest ' ] } , { axis [ ' high ' ] } ), " )
lines . append ( ' }; ' )
lines . append ( ' #endif ' )
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 03:32:23 -08:00
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 ' )
2024-06-18 03:44:22 +01:00
return lines
2022-05-15 22:39:29 +01:00
@cli.argument ( ' -o ' , ' --output ' , arg_only = True , type = normpath , help = ' File to write to ' )
@cli.argument ( ' -q ' , ' --quiet ' , arg_only = True , action = ' store_true ' , help = " Quiet mode, only output error messages " )
@cli.argument ( ' -kb ' , ' --keyboard ' , arg_only = True , type = keyboard_folder , completer = keyboard_completer , required = True , help = ' Keyboard to generate keyboard.c for. ' )
@cli.subcommand ( ' Used by the make system to generate keyboard.c from info.json ' , hidden = True )
def generate_keyboard_c ( cli ) :
""" Generates the keyboard.h file.
"""
kb_info_json = info_json ( cli . args . keyboard )
# Build the layouts.h file.
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 03:32:23 -08:00
keyboard_c_lines = [ GPL2_HEADER_C_LIKE , GENERATED_HEADER_C_LIKE , ' #include QMK_KEYBOARD_H ' , ' ' ]
2022-05-15 22:39:29 +01:00
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 03:32:23 -08:00
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 ) )
2022-05-15 22:39:29 +01:00
# Show the results
[Core] Add Chordal Hold, an "opposite hands rule" tap-hold option similar to Achordion, Bilateral Combinations. (#24560)
* Chordal Hold: restrict what chords settle as hold
* Chordal Hold: docs and further improvements
* Fix formatting.
* Doc rewording and minor edit.
* Support Chordal Hold of multiple tap-hold keys.
* Fix formatting.
* Simplification and additional test.
* Fix formatting.
* Tighten tests.
* Add test two_mod_taps_same_hand_hold_til_timeout.
* Revise handing of pairs of tap-hold keys.
* Generate a default chordal_hold_layout.
* Document chordal_hold_handedness().
* Add license notice to new and branched files in PR.
* Add `tapping.chordal_hold` property for info.json.
* Update docs/reference_info_json.md
* Revise "hand" jsonschema.
* Chordal Hold: Improved layout handedness heuristic.
This commit improves the heuristic used in generate-keyboard-c for
inferring key handedness from keyboard.json geometry data.
Heuristic summary:
1. If the layout is symmetric (e.g. most split keyboards), guess the
handedness based on the sign of (x - layout_x_midpoint).
2. Otherwise, if the layout has a key of >=6u width, it is probably the
spacebar. Form a dividing line through the spacebar, nearly vertical
but with a slight angle to follow typical row stagger.
3. Otherwise, assume handedness based on the widest horizontal
separation.
I have tested this strategy on a couple dozen keyboards and found it to
work reliably.
* Use Optional instead of `| None`.
* Refactor to avoid lambdas.
* Remove trailing comma in chordal_hold_layout.
* Minor docs edits.
* Revise to allow combining multiple same-hand mods.
This commit revises Chordal Hold as described in discussion in
https://github.com/qmk/qmk_firmware/pull/24560#discussion_r1894655238
1. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RCTL_T(KC_A)↑" before the tapping
term, RCTL_T(KC_A) is settled as tapped.
2. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, RSFT_T(KC_C)↑", both RCTL_T(KC_A)
and RSFT_T(KC_C) are settled as tapped.
3. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, KC_U↓" (all keys on the same side),
both RCTL_T(KC_A) and RSFT_T(KC_C) are settled as tapped.
4. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓, LSFT_T(KC_T)↓", with the third key
on the other side, we allow Permissive Hold or Hold On Other Keypress
to decide how/when to settle the keys.
5. In "RCTL_T(KC_A)↓, RSFT_T(KC_C)↓" held until the tapping term, the
keys are settled as held.
1–3 provide same-hand roll protection. 4–5 are for combining multiple
same-hand modifiers.
I've updated the unit tests and have been running it on my keyboard, for
a few hours so far, and all seems good. I really like this scheme. It
allows combining same-side mods, yet it also has roll protection on
streaks. For me, this feels like Achordion, but clearly better streak
handling and improved responsiveness.
* Fix formatting.
* Add a couple tests with LT keys.
* Remove stale use of CHORDAL_HOLD_LAYOUT.
* Fix misspelling lastest -> latest
* Handling tweak for LTs and tests.
* Fix formatting.
* More tests with LT keys.
* Fix formatting.
2025-01-27 03:32:23 -08:00
dump_lines ( cli . args . output , keyboard_c_lines , cli . args . quiet )