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