Add LeaderMode.TIMEOUT (QMK default Leader mode)

This allows leader sequences to "time out" rather than requiring an
Enter keypress to end.

This also rolls back some unnecessary changes from #72 to the matrix
scanner for performance reasons.

In theory we can use this in the future for Tap Dance support (#40)

Resolves #1
Resolves #37
This commit is contained in:
Josh Klar
2018-10-19 01:49:37 -07:00
parent aa4b164067
commit 2d1290a12c
5 changed files with 77 additions and 50 deletions

View File

@@ -150,7 +150,7 @@ class UnicodeModes:
class LeaderMode:
DEFAULT = 0
DEFAULT_ACTIVE = 1
TIMEOUT = 0
TIMEOUT_ACTIVE = 1
ENTER = 2
ENTER_ACTIVE = 3

View File

@@ -55,6 +55,7 @@ class Firmware:
tap_time = 300
leader_mode = LeaderMode.ENTER
leader_dictionary = {}
leader_timeout = 1000
hid_helper = USB_HID
@@ -94,28 +95,30 @@ class Firmware:
print("Firin' lazers. Keyboard is booted.")
while True:
for update in self.matrix.scan_for_changes():
if update is not None:
self._state.matrix_changed(
update[0],
update[1],
update[2],
)
update = self.matrix.scan_for_changes()
if update is not None:
self._state.matrix_changed(
update[0],
update[1],
update[2],
)
if self._state.hid_pending:
self._send_hid()
if self._state.hid_pending:
self._send_hid()
for key in self._state.pending_keys:
self._send_key(key)
self._state.pending_key_handled()
if self.debug_enabled:
print('New State: {}'.format(self._state._to_dict()))
if self._state.macro_pending:
for key in self._state.macro_pending(self):
self._send_key(key)
self._state.process_timeouts()
self._state.resolve_macro()
for key in self._state.pending_keys:
self._send_key(key)
self._state.pending_key_handled()
if self.debug_enabled:
print('New State: {}'.format(self._state._to_dict()))
if self._state.macro_pending:
for key in self._state.macro_pending(self):
self._send_key(key)
self._state.resolve_macro()
gc.collect()

View File

@@ -1,3 +1,4 @@
from kmk.consts import LeaderMode
from kmk.keycodes import FIRST_KMK_INTERNAL_KEYCODE, Keycodes, RawKeycodes
from kmk.kmktime import sleep_ms, ticks_diff, ticks_ms
from kmk.util import intify_coordinate
@@ -26,12 +27,10 @@ class InternalState:
'lm': None,
'leader': None,
}
timeouts = {}
def __init__(self, config):
self.config = config
self.leader_mode = config.leader_mode
self.internal_key_handlers = {
RawKeycodes.KC_DF: self._layer_df,
RawKeycodes.KC_MO: self._layer_mo,
@@ -56,6 +55,7 @@ class InternalState:
'keys_pressed': self.keys_pressed,
'active_layers': self.active_layers,
'leader_mode_history': self.leader_mode_history,
'leader_mode': self.config.leader_mode,
'start_time': self.start_time,
}
@@ -75,6 +75,21 @@ class InternalState:
return layer_key
def set_timeout(self, after_ticks, callback):
timeout_key = ticks_ms() + after_ticks
self.timeouts[timeout_key] = callback
return self
def process_timeouts(self):
current_time = ticks_ms()
for k, v in self.timeouts.items():
if k <= current_time:
v()
del self.timeouts[k]
return self
def matrix_changed(self, row, col, is_pressed):
if self.config.debug_enabled:
print('Matrix changed (col, row, pressed?): {}, {}, {}'.format(
@@ -88,24 +103,24 @@ class InternalState:
print('No key accessible for col, row: {}, {}'.format(row, col))
return self
if is_pressed:
self.keys_pressed.add(kc_changed)
self.coord_keys_pressed[int_coord] = kc_changed
else:
self.keys_pressed.discard(kc_changed)
self.keys_pressed.discard(self.coord_keys_pressed[int_coord])
self.coord_keys_pressed[int_coord] = None
if kc_changed.code >= FIRST_KMK_INTERNAL_KEYCODE:
self._process_internal_key_event(
kc_changed,
is_pressed,
)
else:
if is_pressed:
self.keys_pressed.add(kc_changed)
self.coord_keys_pressed[int_coord] = kc_changed
else:
self.keys_pressed.discard(kc_changed)
self.keys_pressed.discard(self.coord_keys_pressed[int_coord])
self.coord_keys_pressed[int_coord] = None
self.hid_pending = True
if self.leader_mode % 2 == 1:
self._process_leader_mode()
if self.config.leader_mode % 2 == 1:
self._process_leader_mode()
return self
@@ -304,13 +319,24 @@ class InternalState:
return self
def _begin_leader_mode(self):
if self.leader_mode % 2 == 0:
if self.config.leader_mode % 2 == 0:
self.keys_pressed.discard(Keycodes.KMK.KC_LEAD)
# All leader modes are one number higher when activating
self.leader_mode += 1
self.config.leader_mode += 1
if self.config.leader_mode == LeaderMode.TIMEOUT_ACTIVE:
self.set_timeout(self.config.leader_timeout, self._handle_leader_sequence)
return self
def _handle_leader_sequence(self):
lmh = tuple(self.leader_mode_history)
if lmh in self.config.leader_dictionary:
self.macro_pending = self.config.leader_dictionary[lmh].keydown
return self._exit_leader_mode()
def _process_leader_mode(self):
keys_pressed = self.keys_pressed
@@ -322,15 +348,11 @@ class InternalState:
self.leader_last_len = len(self.keys_pressed)
for key in keys_pressed:
if key == Keycodes.Common.KC_ENT:
# Process the action and remove the extra KC.ENT that was added to get here
lmh = tuple(self.leader_mode_history)
if lmh in self.config.leader_dictionary:
self.macro_pending = self.config.leader_dictionary[lmh].keydown
self._exit_leader_mode()
if (
self.config.leader_mode == LeaderMode.ENTER_ACTIVE and
key == Keycodes.Common.KC_ENT
):
self._handle_leader_sequence()
break
elif key == Keycodes.Common.KC_ESC or key == Keycodes.KMK.KC_GESC:
# Clean self and turn leader mode off.
@@ -348,7 +370,7 @@ class InternalState:
def _exit_leader_mode(self):
self.leader_mode_history.clear()
self.leader_mode -= 1
self.config.leader_mode -= 1
self.leader_last_len = 0
self.keys_pressed.clear()
return self

View File

@@ -98,12 +98,13 @@ class MatrixScanner:
self.report[2] = new_val
self.state[ba_idx] = new_val
any_changed = True
yield self.report
break
ba_idx += 1
opin.value(False)
if any_changed:
break
if not any_changed:
yield None
if any_changed:
return self.report