Instead of handling resumed key events in a deep stack, buffer them until the next main loop iteration. New resume events that may be emitted during handling of old resumes are prepended to that buffer, i.e. take precedence over events that happen deeper into the buffer/event stack. Logical replay order is thus preserved.
347 lines
9.7 KiB
Python
347 lines
9.7 KiB
Python
import unittest
|
|
|
|
from kmk.keys import KC
|
|
from kmk.modules.layers import Layers
|
|
from kmk.modules.modtap import ModTap
|
|
from kmk.modules.oneshot import OneShot
|
|
from tests.keyboard_test import KeyboardTest
|
|
|
|
|
|
class TestHoldTap(unittest.TestCase):
|
|
def test_holdtap(self):
|
|
keyboard = KeyboardTest(
|
|
[Layers(), ModTap(), OneShot()],
|
|
[
|
|
[KC.MT(KC.A, KC.LCTL), KC.LT(1, KC.B), KC.C, KC.D, KC.OS(KC.E)],
|
|
[KC.N1, KC.N2, KC.N3, KC.N4, KC.N5],
|
|
],
|
|
debug_enabled=False,
|
|
)
|
|
|
|
keyboard.test('MT tap behaviour', [(0, True), 100, (0, False)], [{KC.A}, {}])
|
|
|
|
keyboard.test(
|
|
'MT hold behaviour', [(0, True), 350, (0, False)], [{KC.LCTL}, {}]
|
|
)
|
|
|
|
# TODO test multiple mods being held
|
|
|
|
# MT
|
|
keyboard.test(
|
|
'MT within tap time sequential -> tap behavior',
|
|
[(0, True), 100, (0, False), (3, True), (3, False)],
|
|
[{KC.A}, {}, {KC.D}, {}],
|
|
)
|
|
|
|
keyboard.test(
|
|
'MT within tap time rolling -> hold behavior',
|
|
[(0, True), 100, (3, True), 250, (0, False), (3, False)],
|
|
[{KC.LCTL}, {KC.LCTL, KC.D}, {KC.D}, {}],
|
|
)
|
|
|
|
keyboard.test(
|
|
'MT within tap time nested -> hold behavior',
|
|
[(0, True), 100, (3, True), (3, False), 250, (0, False)],
|
|
[{KC.LCTL}, {KC.LCTL, KC.D}, {KC.LCTL}, {}],
|
|
)
|
|
|
|
keyboard.test(
|
|
'MT after tap time sequential -> hold behavior',
|
|
[(0, True), 350, (0, False), (3, True), (3, False)],
|
|
[{KC.LCTL}, {}, {KC.D}, {}],
|
|
)
|
|
|
|
keyboard.test(
|
|
'MT after tap time rolling -> hold behavior',
|
|
[(0, True), 350, (3, True), (0, False), (3, False)],
|
|
[{KC.LCTL}, {KC.LCTL, KC.D}, {KC.D}, {}],
|
|
)
|
|
|
|
keyboard.test(
|
|
'MT after tap time nested -> hold behavior',
|
|
[(0, True), 350, (3, True), (3, False), (0, False)],
|
|
[{KC.LCTL}, {KC.LCTL, KC.D}, {KC.LCTL}, {}],
|
|
)
|
|
|
|
# LT
|
|
keyboard.test(
|
|
'LT within tap time sequential -> tap behavior',
|
|
[(1, True), 100, (1, False), (3, True), (3, False)],
|
|
[{KC.B}, {}, {KC.D}, {}],
|
|
)
|
|
|
|
keyboard.test(
|
|
'LT within tap time rolling -> tap behavior',
|
|
[(1, True), 100, (3, True), 250, (1, False), (3, False)],
|
|
[{KC.B}, {KC.B, KC.D}, {KC.D}, {}],
|
|
)
|
|
|
|
keyboard.test(
|
|
'LT within tap time nested -> tap behavior',
|
|
[(1, True), 100, (3, True), (3, False), 250, (1, False)],
|
|
[{KC.B}, {KC.B, KC.D}, {KC.B}, {}],
|
|
)
|
|
|
|
keyboard.test(
|
|
'LT after tap time sequential -> hold behavior',
|
|
[(1, True), 350, (1, False), (3, True), (3, False)],
|
|
[{KC.D}, {}],
|
|
)
|
|
|
|
keyboard.test(
|
|
'LT after tap time rolling -> hold behavior',
|
|
[(1, True), 350, (3, True), (1, False), (3, False)],
|
|
[{KC.N4}, {}],
|
|
)
|
|
|
|
keyboard.test(
|
|
'LT after tap time nested -> hold behavior',
|
|
[(1, True), 350, (3, True), (3, False), (1, False)],
|
|
[{KC.N4}, {}],
|
|
)
|
|
|
|
keyboard.test(
|
|
'LT after tap time nested -> hold behavior',
|
|
[
|
|
(0, True),
|
|
350,
|
|
(1, True),
|
|
350,
|
|
(3, True),
|
|
(3, False),
|
|
(1, False),
|
|
(0, False),
|
|
],
|
|
[{KC.LCTL}, {KC.LCTL, KC.N4}, {KC.LCTL}, {}],
|
|
)
|
|
|
|
def test_holdtap_chain(self):
|
|
keyboard = KeyboardTest(
|
|
[ModTap()],
|
|
[
|
|
[
|
|
KC.N0,
|
|
KC.MT(KC.N1, KC.LCTL, tap_time=50),
|
|
KC.MT(KC.N2, KC.LSFT, tap_interrupted=True, tap_time=50),
|
|
KC.MT(
|
|
KC.N3,
|
|
KC.LALT,
|
|
prefer_hold=False,
|
|
tap_interrupted=True,
|
|
tap_time=50,
|
|
),
|
|
],
|
|
],
|
|
debug_enabled=False,
|
|
)
|
|
# t_within = 40
|
|
t_after = 60
|
|
|
|
keyboard.test(
|
|
'chained 0',
|
|
[(1, True), (2, True), (0, True), (0, False), (2, False), (1, False)],
|
|
[
|
|
{KC.LCTL},
|
|
{KC.LCTL, KC.LSFT},
|
|
{KC.LCTL, KC.LSFT, KC.N0},
|
|
{KC.LCTL, KC.LSFT},
|
|
{KC.LCTL},
|
|
{},
|
|
],
|
|
)
|
|
|
|
keyboard.test(
|
|
'chained 1',
|
|
[(2, True), (1, True), (0, True), (0, False), (1, False), (2, False)],
|
|
[
|
|
{KC.LCTL},
|
|
{KC.LCTL, KC.LSFT},
|
|
{KC.LCTL, KC.LSFT, KC.N0},
|
|
{KC.LCTL, KC.LSFT},
|
|
{KC.LSFT},
|
|
{},
|
|
],
|
|
)
|
|
|
|
keyboard.test(
|
|
'chained 2',
|
|
[(1, True), (2, True), (0, True), (1, False), (2, False), (0, False)],
|
|
[
|
|
{KC.LCTL},
|
|
{KC.LCTL, KC.LSFT},
|
|
{KC.LCTL, KC.LSFT, KC.N0},
|
|
{KC.LSFT, KC.N0},
|
|
{KC.N0},
|
|
{},
|
|
],
|
|
)
|
|
|
|
keyboard.test(
|
|
'chained 3',
|
|
[(1, True), (3, True), (0, True), (0, False), (1, False), (3, False)],
|
|
[
|
|
{KC.LCTL},
|
|
{KC.LCTL, KC.N3},
|
|
{KC.LCTL, KC.N3, KC.N0},
|
|
{KC.LCTL, KC.N3},
|
|
{KC.N3},
|
|
{},
|
|
],
|
|
)
|
|
|
|
keyboard.test(
|
|
'chained 4',
|
|
[(1, True), (3, True), (0, True), (3, False), (1, False), (0, False)],
|
|
[
|
|
{KC.LCTL},
|
|
{KC.LCTL, KC.N3},
|
|
{KC.LCTL, KC.N0, KC.N3},
|
|
{KC.LCTL, KC.N0},
|
|
{KC.N0},
|
|
{},
|
|
],
|
|
)
|
|
|
|
keyboard.test(
|
|
'chained 5',
|
|
[(3, True), (1, True), (0, True), (0, False), (1, False), (3, False)],
|
|
[
|
|
{KC.LCTL},
|
|
{KC.LCTL, KC.N3},
|
|
{KC.LCTL, KC.N3, KC.N0},
|
|
{KC.LCTL, KC.N3},
|
|
{KC.N3},
|
|
{},
|
|
],
|
|
)
|
|
|
|
keyboard.test(
|
|
'chained 6',
|
|
[
|
|
(3, True),
|
|
(1, True),
|
|
t_after,
|
|
(0, True),
|
|
(0, False),
|
|
(1, False),
|
|
(3, False),
|
|
],
|
|
[
|
|
{KC.LALT},
|
|
{KC.LCTL, KC.LALT},
|
|
{KC.LCTL, KC.LALT, KC.N0},
|
|
{KC.LCTL, KC.LALT},
|
|
{KC.LALT},
|
|
{},
|
|
],
|
|
)
|
|
|
|
keyboard.test(
|
|
'chained 7',
|
|
[
|
|
(1, True),
|
|
(3, True),
|
|
t_after,
|
|
(0, True),
|
|
(0, False),
|
|
(1, False),
|
|
(3, False),
|
|
],
|
|
[
|
|
{KC.LCTL},
|
|
{KC.LCTL, KC.LALT},
|
|
{KC.LCTL, KC.LALT, KC.N0},
|
|
{KC.LCTL, KC.LALT},
|
|
{KC.LALT},
|
|
{},
|
|
],
|
|
)
|
|
|
|
keyboard.test(
|
|
'chained 8',
|
|
[(2, True), (3, True), (0, True), (0, False), (2, False), (3, False)],
|
|
[
|
|
{KC.LSFT},
|
|
{KC.LSFT, KC.N3},
|
|
{KC.LSFT, KC.N3, KC.N0},
|
|
{KC.LSFT, KC.N3},
|
|
{KC.N3},
|
|
{},
|
|
],
|
|
)
|
|
|
|
# TODO test TT
|
|
|
|
def test_oneshot(self):
|
|
keyboard = KeyboardTest(
|
|
[Layers(), ModTap(), OneShot()],
|
|
[
|
|
[
|
|
KC.MT(KC.A, KC.LCTL),
|
|
KC.LT(1, KC.B),
|
|
KC.C,
|
|
KC.D,
|
|
KC.OS(KC.E, tap_time=50),
|
|
],
|
|
[KC.N1, KC.N2, KC.N3, KC.N4, KC.N5],
|
|
],
|
|
debug_enabled=False,
|
|
)
|
|
t_within = 40
|
|
t_after = 60
|
|
|
|
# OS
|
|
keyboard.test(
|
|
'OS timed out',
|
|
[(4, True), (4, False), t_after],
|
|
[{KC.E}, {}],
|
|
)
|
|
|
|
keyboard.test(
|
|
'OS interrupt within tap time',
|
|
[(4, True), (4, False), t_within, (3, True), (3, False)],
|
|
[{KC.E}, {KC.D, KC.E}, {KC.E}, {}],
|
|
)
|
|
|
|
keyboard.test(
|
|
'OS interrupt, multiple within tap time',
|
|
[(4, True), (4, False), (3, True), (3, False), (2, True), (2, False)],
|
|
[{KC.E}, {KC.D, KC.E}, {KC.E}, {}, {KC.C}, {}],
|
|
)
|
|
|
|
keyboard.test(
|
|
'OS interrupt, multiple interleaved',
|
|
[(4, True), (4, False), (3, True), (2, True), (2, False), (3, False)],
|
|
[{KC.E}, {KC.D, KC.E}, {KC.D}, {KC.C, KC.D}, {KC.D}, {}],
|
|
)
|
|
|
|
keyboard.test(
|
|
'OS interrupt, multiple interleaved',
|
|
[(4, True), (4, False), (3, True), (2, True), (3, False), (2, False)],
|
|
[{KC.E}, {KC.D, KC.E}, {KC.D}, {KC.C, KC.D}, {KC.C}, {}],
|
|
)
|
|
|
|
keyboard.test(
|
|
'OS interrupt within tap time, hold',
|
|
[(4, True), (3, True), (4, False), t_after, (3, False)],
|
|
[{KC.E}, {KC.D, KC.E}, {KC.D}, {}],
|
|
)
|
|
|
|
keyboard.test(
|
|
'OS hold with multiple interrupt keys',
|
|
[
|
|
(4, True),
|
|
t_within,
|
|
(3, True),
|
|
(3, False),
|
|
(2, True),
|
|
(2, False),
|
|
(4, False),
|
|
],
|
|
[{KC.E}, {KC.D, KC.E}, {KC.E}, {KC.C, KC.E}, {KC.E}, {}],
|
|
)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|