Checkpoint alpha: Reflow macros and keycodes into a consistent structure. Most internal state functionality largely untouched (just moved)

This commit is contained in:
Josh Klar
2018-12-29 04:44:52 -08:00
parent af140a16a6
commit 39a6465658
12 changed files with 767 additions and 967 deletions

View File

@@ -1,19 +1,13 @@
from kmk.consts import LeaderMode
from kmk.keycodes import (FIRST_KMK_INTERNAL_KEYCODE, Keycodes, RawKeycodes,
TapDanceKeycode)
from kmk.kmktime import sleep_ms, ticks_diff, ticks_ms
from kmk.keycodes import KC
from kmk.kmktime import ticks_ms
from kmk.types import TapDanceKeyMeta
from kmk.util import intify_coordinate
GESC_TRIGGERS = {
Keycodes.Modifiers.KC_LSHIFT, Keycodes.Modifiers.KC_RSHIFT,
Keycodes.Modifiers.KC_LGUI, Keycodes.Modifiers.KC_RGUI,
}
class InternalState:
keys_pressed = set()
coord_keys_pressed = {}
macros_pending = []
leader_pending = None
leader_last_len = 0
hid_pending = False
@@ -34,22 +28,6 @@ class InternalState:
def __init__(self, config):
self.config = config
self.internal_key_handlers = {
RawKeycodes.KC_DF: self._layer_df,
RawKeycodes.KC_MO: self._layer_mo,
RawKeycodes.KC_LM: self._layer_lm,
RawKeycodes.KC_LT: self._layer_lt,
RawKeycodes.KC_TG: self._layer_tg,
RawKeycodes.KC_TO: self._layer_to,
RawKeycodes.KC_TT: self._layer_tt,
Keycodes.KMK.KC_GESC.code: self._kc_gesc,
RawKeycodes.KC_UC_MODE: self._kc_uc_mode,
RawKeycodes.KC_MACRO: self._kc_macro,
Keycodes.KMK.KC_LEAD.code: self._kc_lead,
Keycodes.KMK.KC_NO.code: self._kc_no,
Keycodes.KMK.KC_DEBUG.code: self._kc_debug_mode,
RawKeycodes.KC_TAP_DANCE: self._kc_tap_dance,
}
def __repr__(self):
return 'InternalState({})'.format(self._to_dict())
@@ -74,7 +52,7 @@ class InternalState:
for layer in self.reversed_active_layers:
layer_key = self.config.keymap[layer][row][col]
if not layer_key or layer_key == Keycodes.KMK.KC_TRNS:
if not layer_key or layer_key == KC.TRNS:
continue
if self.config.debug_enabled:
@@ -122,16 +100,16 @@ class InternalState:
print('No key accessible for col, row: {}, {}'.format(row, col))
return self
if self.tapping and not isinstance(kc_changed, TapDanceKeycode):
self._process_tap_dance(kc_changed, is_pressed)
return self.process_key(kc_changed, is_pressed, int_coord, (row, col))
def process_key(self, key, is_pressed, coord_int=None, coord_raw=None):
if self.tapping and not isinstance(key.meta, TapDanceKeyMeta):
self._process_tap_dance(key, is_pressed)
else:
if is_pressed:
self.coord_keys_pressed[int_coord] = kc_changed
self.add_key(kc_changed)
key.on_press(self, coord_int, coord_raw)
else:
self.remove_key(kc_changed)
self.keys_pressed.discard(self.coord_keys_pressed.get(int_coord, None))
self.coord_keys_pressed[int_coord] = None
key.on_release(self, coord_int, coord_raw)
if self.config.leader_mode % 2 == 1:
self._process_leader_mode()
@@ -140,27 +118,11 @@ class InternalState:
def remove_key(self, keycode):
self.keys_pressed.discard(keycode)
if keycode.code >= FIRST_KMK_INTERNAL_KEYCODE:
self._process_internal_key_event(keycode, False)
else:
self.hid_pending = True
return self
return self.process_key(keycode, False)
def add_key(self, keycode):
# TODO Make this itself a macro keycode with a keyup handler
# rather than handling this inline here. Gross.
if keycode.code == Keycodes.KMK.KC_MACRO_SLEEP_MS:
sleep_ms(keycode.ms)
else:
self.keys_pressed.add(keycode)
if keycode.code >= FIRST_KMK_INTERNAL_KEYCODE:
self._process_internal_key_event(keycode, True)
else:
self.hid_pending = True
return self
self.keys_pressed.add(keycode)
return self.process_key(keycode, True)
def tap_key(self, keycode):
self.add_key(keycode)
@@ -175,13 +137,6 @@ class InternalState:
self.hid_pending = False
return self
def resolve_macro(self):
if self.config.debug_enabled:
print('Macro complete!')
self.macros_pending.pop()
return self
def _process_internal_key_event(self, changed_key, is_pressed):
# Since the key objects can be chained into new objects
# with, for example, no_press set, always check against
@@ -192,164 +147,9 @@ class InternalState:
changed_key, is_pressed,
)
def _layer_df(self, changed_key, is_pressed):
"""Switches the default layer"""
if is_pressed:
self.active_layers[0] = changed_key.layer
self.reversed_active_layers = list(reversed(self.active_layers))
return self
def _layer_mo(self, changed_key, is_pressed):
"""Momentarily activates layer, switches off when you let go"""
if is_pressed:
self.active_layers.append(changed_key.layer)
else:
self.active_layers = [
layer for layer in self.active_layers
if layer != changed_key.layer
]
self.reversed_active_layers = list(reversed(self.active_layers))
return self
def _layer_lm(self, changed_key, is_pressed):
"""As MO(layer) but with mod active"""
self.hid_pending = True
if is_pressed:
# Sets the timer start and acts like MO otherwise
self.start_time['lm'] = ticks_ms()
self.keys_pressed.add(changed_key.kc)
else:
self.keys_pressed.discard(changed_key.kc)
self.start_time['lm'] = None
return self._layer_mo(changed_key, is_pressed)
def _layer_lt(self, changed_key, is_pressed):
"""Momentarily activates layer if held, sends kc if tapped"""
if is_pressed:
# Sets the timer start and acts like MO otherwise
self.start_time['lt'] = ticks_ms()
self._layer_mo(changed_key, is_pressed)
else:
# On keyup, check timer, and press key if needed.
if self.start_time['lt'] and (
ticks_diff(ticks_ms(), self.start_time['lt']) < self.config.tap_time
):
self.hid_pending = True
self.tap_key(changed_key.kc)
self._layer_mo(changed_key, is_pressed)
self.start_time['lt'] = None
return self
def _layer_tg(self, changed_key, is_pressed):
"""Toggles the layer (enables it if not active, and vise versa)"""
if is_pressed:
if changed_key.layer in self.active_layers:
self.active_layers = [
layer for layer in self.active_layers
if layer != changed_key.layer
]
else:
self.active_layers.append(changed_key.layer)
self.reversed_active_layers = list(reversed(self.active_layers))
return self
def _layer_to(self, changed_key, is_pressed):
"""Activates layer and deactivates all other layers"""
if is_pressed:
self.active_layers = [changed_key.layer]
self.reversed_active_layers = list(reversed(self.active_layers))
return self
def _layer_tt(self, changed_key, is_pressed):
"""Momentarily activates layer if held, toggles it if tapped repeatedly"""
# TODO Make this work with tap dance to function more correctly, but technically works.
if is_pressed:
if self.start_time['tt'] is None:
# Sets the timer start and acts like MO otherwise
self.start_time['tt'] = ticks_ms()
return self._layer_mo(changed_key, is_pressed)
elif ticks_diff(ticks_ms(), self.start_time['tt']) < self.config.tap_time:
self.start_time['tt'] = None
return self.tg(changed_key, is_pressed)
elif (
self.start_time['tt'] is None or
ticks_diff(ticks_ms(), self.start_time['tt']) >= self.config.tap_time
):
# On first press, works like MO. On second press, does nothing unless let up within
# time window, then acts like TG.
self.start_time['tt'] = None
return self._layer_mo(changed_key, is_pressed)
return self
def _kc_uc_mode(self, changed_key, is_pressed):
if is_pressed:
self.config.unicode_mode = changed_key.mode
return self
def _kc_macro(self, changed_key, is_pressed):
if is_pressed:
if changed_key.keyup:
self.macros_pending.append(changed_key.keyup)
else:
if changed_key.keydown:
self.macros_pending.append(changed_key.keydown)
return self
def _kc_lead(self, changed_key, is_pressed):
if is_pressed:
self._begin_leader_mode()
return self
def _kc_gesc(self, changed_key, is_pressed):
self.hid_pending = True
if is_pressed:
if GESC_TRIGGERS.intersection(self.keys_pressed):
# if Shift is held, KC_GRAVE will become KC_TILDE on OS level
self.keys_pressed.add(Keycodes.Common.KC_GRAVE)
return self
# else return KC_ESC
self.keys_pressed.add(Keycodes.Common.KC_ESCAPE)
return self
self.keys_pressed.discard(Keycodes.Common.KC_ESCAPE)
self.keys_pressed.discard(Keycodes.Common.KC_GRAVE)
return self
def _kc_no(self, changed_key, is_pressed):
return self
def _kc_debug_mode(self, changed_key, is_pressed):
if is_pressed:
if self.config.debug_enabled:
print('Disabling debug mode, bye!')
else:
print('Enabling debug mode. Welcome to the jungle.')
self.config.debug_enabled = not self.config.debug_enabled
return self
def _kc_tap_dance(self, changed_key, is_pressed):
return self._process_tap_dance(changed_key, is_pressed)
def _process_tap_dance(self, changed_key, is_pressed):
if is_pressed:
if not isinstance(changed_key, TapDanceKeycode):
if not isinstance(changed_key.meta, TapDanceKeyMeta):
# If we get here, changed_key is not a TapDanceKeycode and thus
# the user kept typing elsewhere (presumably). End ALL of the
# currently outstanding tap dance runs.
@@ -408,7 +208,7 @@ class InternalState:
def _begin_leader_mode(self):
if self.config.leader_mode % 2 == 0:
self.keys_pressed.discard(Keycodes.KMK.KC_LEAD)
self.keys_pressed.discard(KC.LEAD)
# All leader modes are one number higher when activating
self.config.leader_mode += 1
@@ -421,7 +221,7 @@ class InternalState:
lmh = tuple(self.leader_mode_history)
if lmh in self.config.leader_dictionary:
self.macros_pending.append(self.config.leader_dictionary[lmh].keydown)
self.process_key(self.config.leader_dictionary[lmh], True)
return self._exit_leader_mode()
@@ -438,15 +238,15 @@ class InternalState:
for key in keys_pressed:
if (
self.config.leader_mode == LeaderMode.ENTER_ACTIVE and
key == Keycodes.Common.KC_ENT
key == KC.ENT
):
self._handle_leader_sequence()
break
elif key == Keycodes.Common.KC_ESC or key == Keycodes.KMK.KC_GESC:
elif key == KC.ESC or key == KC.GESC:
# Clean self and turn leader mode off.
self._exit_leader_mode()
break
elif key == Keycodes.KMK.KC_LEAD:
elif key == KC.LEAD:
break
else:
# Add key if not needing to escape