Implement autoshift module
This commit is contained in:
parent
62984d34f7
commit
d7eb293af8
@ -229,6 +229,15 @@ class AbstractHID:
|
|||||||
for idx in range(2, len(self._pd_report)):
|
for idx in range(2, len(self._pd_report)):
|
||||||
self._pd_report[idx] = 0x00
|
self._pd_report[idx] = 0x00
|
||||||
|
|
||||||
|
def has_key(self, key):
|
||||||
|
if isinstance(key, ModifierKey):
|
||||||
|
return bool(self.report_mods[0] & key.code)
|
||||||
|
else:
|
||||||
|
for code in self.report_non_mods:
|
||||||
|
if code == key.code:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class USBHID(AbstractHID):
|
class USBHID(AbstractHID):
|
||||||
REPORT_BYTES = 9
|
REPORT_BYTES = 9
|
||||||
|
83
kmk/modules/autoshift.py
Normal file
83
kmk/modules/autoshift.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
from kmk.keys import KC, Key
|
||||||
|
from kmk.modules import Module
|
||||||
|
from kmk.scheduler import cancel_task, create_task
|
||||||
|
from kmk.utils import Debug
|
||||||
|
|
||||||
|
debug = Debug(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Autoshift(Module):
|
||||||
|
def __init__(self, tap_time=300):
|
||||||
|
self.tap_time = tap_time
|
||||||
|
|
||||||
|
self._active = False
|
||||||
|
self._task = None
|
||||||
|
self._key = None
|
||||||
|
|
||||||
|
def during_bootup(self, keyboard):
|
||||||
|
self._task = create_task(lambda: self._shift(keyboard), after_ms=-1)
|
||||||
|
|
||||||
|
def before_matrix_scan(self, keyboard):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def after_matrix_scan(self, keyboard):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def process_key(self, keyboard, key, is_pressed, int_coord):
|
||||||
|
# Unshift on any key event
|
||||||
|
if self._active:
|
||||||
|
self._unshift(keyboard)
|
||||||
|
return key
|
||||||
|
|
||||||
|
# Only shift from an unshifted state
|
||||||
|
if keyboard._hid_helper.has_key(KC.LSHIFT):
|
||||||
|
return key
|
||||||
|
|
||||||
|
# Ignore rolls from tapped to hold
|
||||||
|
if not is_pressed and key is not self._key:
|
||||||
|
return key
|
||||||
|
|
||||||
|
# Only shift alpha keys, iff there's no pending potential shift
|
||||||
|
if (
|
||||||
|
is_pressed
|
||||||
|
and not self._key
|
||||||
|
and isinstance(key, Key)
|
||||||
|
and key.code
|
||||||
|
and KC.A.code <= key.code <= KC.Z.code
|
||||||
|
):
|
||||||
|
create_task(self._task, after_ms=self.tap_time)
|
||||||
|
self._key = key
|
||||||
|
else:
|
||||||
|
cancel_task(self._task)
|
||||||
|
keyboard.resume_process_key(self, self._key, True)
|
||||||
|
if key is self._key:
|
||||||
|
keyboard.resume_process_key(self, self._key, False)
|
||||||
|
else:
|
||||||
|
keyboard.resume_process_key(self, key, True)
|
||||||
|
self._key = None
|
||||||
|
|
||||||
|
def before_hid_send(self, keyboard):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def after_hid_send(self, keyboard):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_powersave_enable(self, keyboard):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def on_powersave_disable(self, keyboard):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _shift(self, keyboard):
|
||||||
|
if debug.enabled:
|
||||||
|
debug('activate')
|
||||||
|
self._active = True
|
||||||
|
keyboard.keys_pressed.add(KC.LSFT)
|
||||||
|
keyboard.resume_process_key(self, self._key, True)
|
||||||
|
|
||||||
|
def _unshift(self, keyboard):
|
||||||
|
if debug.enabled:
|
||||||
|
debug('deactivate')
|
||||||
|
self._active = False
|
||||||
|
self._key = None
|
||||||
|
keyboard.keys_pressed.remove(KC.LSFT)
|
91
tests/test_autoshift.py
Normal file
91
tests/test_autoshift.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import unittest
|
||||||
|
|
||||||
|
from kmk.keys import KC
|
||||||
|
from kmk.modules.autoshift import Autoshift
|
||||||
|
from tests.keyboard_test import KeyboardTest
|
||||||
|
|
||||||
|
tap_time = 3 * KeyboardTest.loop_delay_ms
|
||||||
|
t_after = 4 * KeyboardTest.loop_delay_ms
|
||||||
|
|
||||||
|
|
||||||
|
class TestAutoshift(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.kb = KeyboardTest(
|
||||||
|
[Autoshift(tap_time=tap_time)],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
KC.A,
|
||||||
|
KC.N1,
|
||||||
|
KC.HASH,
|
||||||
|
KC.NO,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
debug_enabled=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_tap_alpha(self):
|
||||||
|
self.kb.test(
|
||||||
|
'',
|
||||||
|
[(0, True), (0, False)],
|
||||||
|
[{KC.A}, {}],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_hold_alpha(self):
|
||||||
|
self.kb.test(
|
||||||
|
'',
|
||||||
|
[(0, True), t_after, (0, False)],
|
||||||
|
[{KC.A, KC.LSHIFT}, {}],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_hold_num(self):
|
||||||
|
self.kb.test(
|
||||||
|
'',
|
||||||
|
[(1, True), t_after, (1, False)],
|
||||||
|
[{KC.N1}, {}],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_hold_alpha_tap_num_within(self):
|
||||||
|
self.kb.test(
|
||||||
|
'',
|
||||||
|
[(0, True), (1, True), t_after, (1, False), (0, False)],
|
||||||
|
[{KC.A}, {KC.A, KC.N1}, {KC.A}, {}],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_hold_alpha_tap_num_after(self):
|
||||||
|
self.kb.test(
|
||||||
|
'',
|
||||||
|
[(0, True), t_after, (1, True), (1, False), (0, False)],
|
||||||
|
[{KC.A, KC.LSHIFT}, {KC.A, KC.N1}, {KC.A}, {}],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_hold_num_hold_alpha(self):
|
||||||
|
self.kb.test(
|
||||||
|
'',
|
||||||
|
[(1, True), (0, True), t_after, (0, False), (1, False)],
|
||||||
|
[{KC.N1}, {KC.N1, KC.A, KC.LSHIFT}, {KC.N1}, {}],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_roll_num_hold_alpha(self):
|
||||||
|
self.kb.test(
|
||||||
|
'',
|
||||||
|
[(1, True), (0, True), (1, False), t_after, (0, False)],
|
||||||
|
[{KC.N1}, {}, {KC.A, KC.LSHIFT}, {}],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_hold_shifted_hold_alpha(self):
|
||||||
|
self.kb.test(
|
||||||
|
'',
|
||||||
|
[(2, True), (0, True), t_after, (2, False), (0, False)],
|
||||||
|
[{KC.LSHIFT, KC.HASH}, {KC.LSHIFT, KC.HASH, KC.A}, {KC.A}, {}],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_hold_internal(self):
|
||||||
|
self.kb.test(
|
||||||
|
'',
|
||||||
|
[(3, True), t_after, (3, False)],
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
Loading…
Reference in New Issue
Block a user