Compare commits

..

2 Commits

Author SHA1 Message Date
xs5871
d7eb293af8
Implement autoshift module 2023-04-09 22:36:25 +00:00
xs5871
62984d34f7
Extend create_task to allow for re- and no-scheduling 2023-04-09 22:36:18 +00:00
4 changed files with 190 additions and 6 deletions

View File

@ -229,6 +229,15 @@ class AbstractHID:
for idx in range(2, len(self._pd_report)):
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):
REPORT_BYTES = 9

83
kmk/modules/autoshift.py Normal file
View 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)

View File

@ -32,21 +32,22 @@ class PeriodicTaskMeta:
def create_task(
func: Callable[[None], None],
func: [Callable[[None], None], Task, PeriodicTaskMeta],
*,
after_ms: int = 0,
period_ms: int = 0,
) -> [Task, PeriodicTaskMeta]:
if period_ms:
if isinstance(func, (Task, PeriodicTaskMeta)):
t = r = func
elif period_ms:
r = PeriodicTaskMeta(func, period_ms)
t = r._task
else:
t = r = Task(func)
if after_ms:
after_ms = ticks_add(ticks_ms(), after_ms)
_task_queue.push_sorted(t, after_ms)
else:
if after_ms > 0:
_task_queue.push_sorted(t, ticks_add(ticks_ms(), after_ms))
elif after_ms == 0:
_task_queue.push_head(t)
return r

91
tests/test_autoshift.py Normal file
View 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()