Compare commits
2 Commits
master
...
feature-au
Author | SHA1 | Date | |
---|---|---|---|
|
d7eb293af8 | ||
|
62984d34f7 |
@ -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
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)
|
@ -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
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