Checkpoint alpha: Reflow macros and keycodes into a consistent structure. Most internal state functionality largely untouched (just moved)
This commit is contained in:
parent
af140a16a6
commit
39a6465658
@ -202,16 +202,6 @@ class Firmware:
|
||||
if old_timeouts_len != new_timeouts_len:
|
||||
state_changed = True
|
||||
|
||||
if self._state.macros_pending:
|
||||
# Blindly assume macros are going to change state, which is almost
|
||||
# always a safe assumption
|
||||
state_changed = True
|
||||
for macro in self._state.macros_pending:
|
||||
for key in macro(self):
|
||||
self._send_key(key)
|
||||
|
||||
self._state.resolve_macro()
|
||||
|
||||
if self.debug_enabled and state_changed:
|
||||
print('New State: {}'.format(self._state._to_dict()))
|
||||
|
||||
|
0
kmk/handlers/__init__.py
Normal file
0
kmk/handlers/__init__.py
Normal file
108
kmk/handlers/layers.py
Normal file
108
kmk/handlers/layers.py
Normal file
@ -0,0 +1,108 @@
|
||||
from kmk.kmktime import ticks_diff, ticks_ms
|
||||
|
||||
|
||||
def df_pressed(key, state, *args, **kwargs):
|
||||
"""Switches the default layer"""
|
||||
state.active_layers[0] = key.meta.layer
|
||||
state.reversed_active_layers = list(reversed(state.active_layers))
|
||||
return state
|
||||
|
||||
|
||||
def mo_pressed(key, state, *args, **kwargs):
|
||||
"""Momentarily activates layer, switches off when you let go"""
|
||||
state.active_layers.append(key.meta.layer)
|
||||
state.reversed_active_layers = list(reversed(state.active_layers))
|
||||
return state
|
||||
|
||||
|
||||
def mo_released(key, state, KC, *args, **kwargs):
|
||||
state.active_layers = [
|
||||
layer for layer in state.active_layers
|
||||
if layer != key.meta.layer
|
||||
]
|
||||
state.reversed_active_layers = list(reversed(state.active_layers))
|
||||
return state
|
||||
|
||||
|
||||
def lm_pressed(key, state, *args, **kwargs):
|
||||
"""As MO(layer) but with mod active"""
|
||||
state.hid_pending = True
|
||||
# Sets the timer start and acts like MO otherwise
|
||||
state.start_time['lm'] = ticks_ms()
|
||||
state.keys_pressed.add(key.meta.kc)
|
||||
return mo_pressed(key, state, *args, **kwargs)
|
||||
|
||||
|
||||
def lm_released(key, state, *args, **kwargs):
|
||||
"""As MO(layer) but with mod active"""
|
||||
state.hid_pending = True
|
||||
state.keys_pressed.discard(key.meta.kc)
|
||||
state.start_time['lm'] = None
|
||||
return mo_released(key, state, *args, **kwargs)
|
||||
|
||||
|
||||
def lt_pressed(key, state, *args, **kwargs):
|
||||
# Sets the timer start and acts like MO otherwise
|
||||
state.start_time['lt'] = ticks_ms()
|
||||
return mo_pressed(key, state, *args, **kwargs)
|
||||
|
||||
|
||||
def lt_released(key, state, *args, **kwargs):
|
||||
# On keyup, check timer, and press key if needed.
|
||||
if state.start_time['lt'] and (
|
||||
ticks_diff(ticks_ms(), state.start_time['lt']) < state.config.tap_time
|
||||
):
|
||||
state.hid_pending = True
|
||||
state.tap_key(key.meta.kc)
|
||||
|
||||
mo_released(key, state, *args, **kwargs)
|
||||
state.start_time['lt'] = None
|
||||
return state
|
||||
|
||||
|
||||
def tg_pressed(key, state, *args, **kwargs):
|
||||
"""Toggles the layer (enables it if not active, and vise versa)"""
|
||||
if key.meta.layer in state.active_layers:
|
||||
state.active_layers = [
|
||||
layer for layer in state.active_layers
|
||||
if layer != key.meta.layer
|
||||
]
|
||||
else:
|
||||
state.active_layers.append(key.meta.layer)
|
||||
|
||||
state.reversed_active_layers = list(reversed(state.active_layers))
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def to_pressed(key, state, *args, **kwargs):
|
||||
"""Activates layer and deactivates all other layers"""
|
||||
state.active_layers = [key.meta.kc]
|
||||
state.reversed_active_layers = list(reversed(state.active_layers))
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def tt_pressed(key, state, *args, **kwargs):
|
||||
"""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 state.start_time['tt'] is None:
|
||||
# Sets the timer start and acts like MO otherwise
|
||||
state.start_time['tt'] = ticks_ms()
|
||||
return mo_pressed(key, state, *args, **kwargs)
|
||||
elif ticks_diff(ticks_ms(), state.start_time['tt']) < state.config.tap_time:
|
||||
state.start_time['tt'] = None
|
||||
return tg_pressed(key, state, *args, **kwargs)
|
||||
|
||||
|
||||
def tt_released(key, state, *args, **kwargs):
|
||||
if (
|
||||
state.start_time['tt'] is None or
|
||||
ticks_diff(ticks_ms(), state.start_time['tt']) >= state.config.tap_time
|
||||
):
|
||||
# On first press, works like MO. On second press, does nothing unless let up within
|
||||
# time window, then acts like TG.
|
||||
state.start_time['tt'] = None
|
||||
return mo_released(key, state, *args, **kwargs)
|
||||
|
||||
return state
|
40
kmk/handlers/sequences.py
Normal file
40
kmk/handlers/sequences.py
Normal file
@ -0,0 +1,40 @@
|
||||
from kmk.keycodes import ALL_KEYS, KC, make_key
|
||||
from kmk.types import KeySequenceMeta
|
||||
|
||||
|
||||
def sequence_press_handler(key, state, KC, *args, **kwargs):
|
||||
old_keys_pressed = state.keys_pressed
|
||||
state.keys_pressed = set()
|
||||
|
||||
for ikey in key.meta.seq:
|
||||
if not getattr(ikey, 'no_press', None):
|
||||
state.process_key(ikey, True)
|
||||
state.config._send_hid()
|
||||
if not getattr(ikey, 'no_release', None):
|
||||
state.process_key(ikey, False)
|
||||
state.config._send_hid()
|
||||
|
||||
state.keys_pressed = old_keys_pressed
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def simple_key_sequence(seq):
|
||||
return make_key(
|
||||
meta=KeySequenceMeta(seq),
|
||||
on_press=sequence_press_handler,
|
||||
)
|
||||
|
||||
|
||||
def send_string(message):
|
||||
seq = []
|
||||
|
||||
for char in message:
|
||||
kc = ALL_KEYS[char]
|
||||
|
||||
if char.isupper():
|
||||
kc = KC.LSHIFT(kc)
|
||||
|
||||
seq.append(kc)
|
||||
|
||||
return simple_key_sequence(seq)
|
89
kmk/handlers/stock.py
Normal file
89
kmk/handlers/stock.py
Normal file
@ -0,0 +1,89 @@
|
||||
from kmk.kmktime import sleep_ms
|
||||
from kmk.util import reset_bootloader, reset_keyboard
|
||||
|
||||
|
||||
def passthrough(key, state, *args, **kwargs):
|
||||
return state
|
||||
|
||||
|
||||
def default_pressed(key, state, KC, coord_int=None, coord_raw=None):
|
||||
if coord_int is not None:
|
||||
state.coord_keys_pressed[coord_int] = key
|
||||
|
||||
state.add_key(key)
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def default_released(key, state, KC, coord_int=None, coord_raw=None):
|
||||
state.remove_key(key)
|
||||
|
||||
if coord_int is not None:
|
||||
state.keys_pressed.discard(key.coord_keys_pressed.get(coord_int, None))
|
||||
state.coord_keys_pressed[coord_int] = None
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def reset(*args, **kwargs):
|
||||
reset_keyboard()
|
||||
|
||||
|
||||
def bootloader(*args, **kwargs):
|
||||
reset_bootloader()
|
||||
|
||||
|
||||
def debug_pressed(key, state, KC, *args, **kwargs):
|
||||
if state.config.debug_enabled:
|
||||
print('Disabling debug mode, bye!')
|
||||
else:
|
||||
print('Enabling debug mode. Welcome to the jungle.')
|
||||
|
||||
state.config.debug_enabled = not state.config.debug_enabled
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def gesc_pressed(key, state, KC, *args, **kwargs):
|
||||
GESC_TRIGGERS = {KC.LSHIFT, KC.RSHIFT, KC.LGUI, KC.RGUI}
|
||||
|
||||
if GESC_TRIGGERS.intersection(state.keys_pressed):
|
||||
# if Shift is held, KC_GRAVE will become KC_TILDE on OS level
|
||||
state.keys_pressed.add(KC.GRAVE)
|
||||
return state
|
||||
|
||||
# else return KC_ESC
|
||||
state.keys_pressed.add(KC.ESCAPE)
|
||||
state.hid_pending = True
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def gesc_released(key, state, KC, *args, **kwargs):
|
||||
state.keys_pressed.discard(KC.ESCAPE)
|
||||
state.keys_pressed.discard(KC.GRAVE)
|
||||
state.hid_pending = True
|
||||
return state
|
||||
|
||||
|
||||
def sleep_pressed(key, state, KC, *args, **kwargs):
|
||||
sleep_ms(key.meta.ms)
|
||||
return state
|
||||
|
||||
|
||||
def uc_mode_pressed(key, state, *args, **kwargs):
|
||||
state.config.unicode_mode = key.meta.mode
|
||||
|
||||
return state
|
||||
|
||||
|
||||
def leader_pressed(key, state, *args, **kwargs):
|
||||
return state._begin_leader_mode()
|
||||
|
||||
|
||||
def tap_dance_pressed(key, state, *args, **kwargs):
|
||||
return state._process_tap_dance(key, True)
|
||||
|
||||
|
||||
def tap_dance_released(key, state, *args, **kwargs):
|
||||
return state._process_tap_dance(key, False)
|
@ -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
|
||||
|
1025
kmk/keycodes.py
1025
kmk/keycodes.py
File diff suppressed because it is too large
Load Diff
@ -1,76 +0,0 @@
|
||||
import math
|
||||
|
||||
from kmk.event_defs import (hid_report_event, keycode_down_event,
|
||||
keycode_up_event)
|
||||
from kmk.keycodes import Media
|
||||
from kmk.rotary_encoder import RotaryEncoder
|
||||
|
||||
VAL_FALSE = False + 1
|
||||
VAL_NONE = True + 2
|
||||
VAL_TRUE = True + 1
|
||||
VOL_UP_PRESS = keycode_down_event(Media.KC_AUDIO_VOL_UP)
|
||||
VOL_UP_RELEASE = keycode_up_event(Media.KC_AUDIO_VOL_UP)
|
||||
VOL_DOWN_PRESS = keycode_down_event(Media.KC_AUDIO_VOL_DOWN)
|
||||
VOL_DOWN_RELEASE = keycode_up_event(Media.KC_AUDIO_VOL_DOWN)
|
||||
|
||||
|
||||
class RotaryEncoderMacro:
|
||||
def __init__(self, pos_pin, neg_pin, slop_history=1, slop_threshold=1):
|
||||
self.encoder = RotaryEncoder(pos_pin, neg_pin)
|
||||
self.max_history = slop_history
|
||||
self.history = bytearray(slop_history)
|
||||
self.history_idx = 0
|
||||
self.history_threshold = math.floor(slop_threshold * slop_history)
|
||||
|
||||
def scan(self):
|
||||
# Anti-slop logic
|
||||
self.history[self.history_idx] = 0
|
||||
|
||||
reading = self.encoder.direction()
|
||||
self.history[self.history_idx] = VAL_NONE if reading is None else reading + 1
|
||||
|
||||
self.history_idx += 1
|
||||
|
||||
if self.history_idx >= self.max_history:
|
||||
self.history_idx = 0
|
||||
|
||||
nones = 0
|
||||
trues = 0
|
||||
falses = 0
|
||||
|
||||
for val in self.history:
|
||||
if val == VAL_NONE:
|
||||
nones += 1
|
||||
elif val == VAL_TRUE:
|
||||
trues += 1
|
||||
elif val == VAL_FALSE:
|
||||
falses += 1
|
||||
|
||||
if nones >= self.history_threshold:
|
||||
return None
|
||||
|
||||
if trues >= self.history_threshold:
|
||||
return self.on_increase()
|
||||
|
||||
if falses >= self.history_threshold:
|
||||
return self.on_decrease()
|
||||
|
||||
def on_decrease(self):
|
||||
pass
|
||||
|
||||
def on_increase(self):
|
||||
pass
|
||||
|
||||
|
||||
class VolumeRotaryEncoder(RotaryEncoderMacro):
|
||||
def on_decrease(self):
|
||||
yield VOL_DOWN_PRESS
|
||||
yield hid_report_event
|
||||
yield VOL_DOWN_RELEASE
|
||||
yield hid_report_event
|
||||
|
||||
def on_increase(self):
|
||||
yield VOL_UP_PRESS
|
||||
yield hid_report_event
|
||||
yield VOL_UP_RELEASE
|
||||
yield hid_report_event
|
@ -1,28 +0,0 @@
|
||||
from kmk.keycodes import Keycodes, Macro, char_lookup, lookup_kc_with_cache
|
||||
from kmk.string import ascii_letters, digits
|
||||
|
||||
|
||||
def simple_key_sequence(seq):
|
||||
def _simple_key_sequence(state):
|
||||
return seq
|
||||
|
||||
return Macro(keydown=_simple_key_sequence)
|
||||
|
||||
|
||||
def send_string(message):
|
||||
seq = []
|
||||
|
||||
for char in message:
|
||||
kc = None
|
||||
|
||||
if char in char_lookup:
|
||||
kc = char_lookup[char]
|
||||
elif char in ascii_letters + digits:
|
||||
kc = lookup_kc_with_cache(char)
|
||||
|
||||
if char.isupper():
|
||||
kc = Keycodes.Modifiers.KC_LSHIFT(kc)
|
||||
|
||||
seq.append(kc)
|
||||
|
||||
return simple_key_sequence(seq)
|
@ -1,16 +1,15 @@
|
||||
from kmk.consts import UnicodeMode
|
||||
from kmk.keycodes import (Common, Macro, Modifiers,
|
||||
generate_codepoint_keysym_seq)
|
||||
from kmk.keycodes import ALL_KEYS, KC, Macro
|
||||
from kmk.macros.simple import simple_key_sequence
|
||||
from kmk.types import AttrDict
|
||||
from kmk.util import get_wide_ordinal
|
||||
|
||||
IBUS_KEY_COMBO = Modifiers.KC_LCTRL(Modifiers.KC_LSHIFT(Common.KC_U))
|
||||
RALT_KEY = Modifiers.KC_RALT
|
||||
U_KEY = Common.KC_U
|
||||
ENTER_KEY = Common.KC_ENTER
|
||||
RALT_DOWN_NO_RELEASE = Modifiers.KC_RALT(no_release=True)
|
||||
RALT_UP_NO_PRESS = Modifiers.KC_RALT(no_press=True)
|
||||
IBUS_KEY_COMBO = KC.LCTRL(KC.LSHIFT(KC.U))
|
||||
RALT_KEY = KC.RALT
|
||||
U_KEY = KC.U
|
||||
ENTER_KEY = KC.ENTER
|
||||
RALT_DOWN_NO_RELEASE = KC.RALT(no_release=True)
|
||||
RALT_UP_NO_PRESS = KC.RALT(no_press=True)
|
||||
|
||||
|
||||
def compile_unicode_string_sequences(string_table):
|
||||
@ -31,6 +30,26 @@ def unicode_string_sequence(unistring):
|
||||
])
|
||||
|
||||
|
||||
def generate_codepoint_keysym_seq(codepoint, expected_length=4):
|
||||
# To make MacOS and Windows happy, always try to send
|
||||
# sequences that are of length 4 at a minimum
|
||||
# On Linux systems, we can happily send longer strings.
|
||||
# They will almost certainly break on MacOS and Windows,
|
||||
# but this is a documentation problem more than anything.
|
||||
# Not sure how to send emojis on Mac/Windows like that,
|
||||
# though, since (for example) the Canadian flag is assembled
|
||||
# from two five-character codepoints, 1f1e8 and 1f1e6
|
||||
#
|
||||
# As a bonus, this function can be pretty useful for
|
||||
# leader dictionary keys as strings.
|
||||
seq = [KC.N0 for _ in range(max(len(codepoint), expected_length))]
|
||||
|
||||
for idx, codepoint_fragment in enumerate(reversed(codepoint)):
|
||||
seq[-(idx + 1)] = ALL_KEYS.get(codepoint_fragment)
|
||||
|
||||
return seq
|
||||
|
||||
|
||||
def unicode_codepoint_sequence(codepoints):
|
||||
kc_seqs = (
|
||||
generate_codepoint_keysym_seq(codepoint)
|
||||
|
@ -1,57 +0,0 @@
|
||||
from kmk.pins import PULL_UP
|
||||
|
||||
|
||||
class RotaryEncoder:
|
||||
# Please don't ask. I don't know. All I know is bit_value
|
||||
# works as expected. Here be dragons, etc. etc.
|
||||
MIN_VALUE = False + 1 << 1 | True + 1
|
||||
MAX_VALUE = True + 1 << 1 | True + 1
|
||||
|
||||
def __init__(self, pos_pin, neg_pin):
|
||||
self.pos_pin = pos_pin
|
||||
self.neg_pin = neg_pin
|
||||
|
||||
self.pos_pin.switch_to_input(pull=PULL_UP)
|
||||
self.neg_pin.switch_to_input(pull=PULL_UP)
|
||||
|
||||
self.prev_bit_value = self.bit_value()
|
||||
|
||||
def value(self):
|
||||
return (self.pos_pin.value(), self.neg_pin.value())
|
||||
|
||||
def bit_value(self):
|
||||
'''
|
||||
Returns 2, 3, 5, or 6 based on the state of the rotary encoder's two
|
||||
bits. This is a total hack but it does what we need pretty efficiently.
|
||||
Shrug.
|
||||
'''
|
||||
return self.pos_pin.value() + 1 << 1 | self.neg_pin.value() + 1
|
||||
|
||||
def direction(self):
|
||||
'''
|
||||
Compares the current rotary position against the last seen position.
|
||||
|
||||
Returns True if we're rotating "positively", False if we're rotating "negatively",
|
||||
and None if no change could safely be detected for any reason (usually this
|
||||
means the encoder itself did not change)
|
||||
'''
|
||||
new_value = self.bit_value()
|
||||
rolling_under = self.prev_bit_value == self.MIN_VALUE and new_value == self.MAX_VALUE
|
||||
rolling_over = self.prev_bit_value == self.MAX_VALUE and new_value == self.MIN_VALUE
|
||||
increasing = new_value > self.prev_bit_value
|
||||
decreasing = new_value < self.prev_bit_value
|
||||
self.prev_bit_value = new_value
|
||||
|
||||
if rolling_over:
|
||||
return True
|
||||
elif rolling_under:
|
||||
return False
|
||||
|
||||
if increasing:
|
||||
return True
|
||||
if decreasing:
|
||||
return False
|
||||
|
||||
# Either no change, or not a type of change we can safely detect,
|
||||
# so safely do nothing
|
||||
return None
|
26
kmk/types.py
26
kmk/types.py
@ -24,3 +24,29 @@ class Anything:
|
||||
class Passthrough:
|
||||
def __getattr__(self, attr):
|
||||
return Anything(attr)
|
||||
|
||||
|
||||
class LayerKeyMeta:
|
||||
def __init__(self, layer, kc=None):
|
||||
self.layer = layer
|
||||
self.kc = kc
|
||||
|
||||
|
||||
class KeySequenceMeta:
|
||||
def __init__(self, seq):
|
||||
self.seq = seq
|
||||
|
||||
|
||||
class KeySeqSleepMeta:
|
||||
def __init__(self, ms):
|
||||
self.ms = ms
|
||||
|
||||
|
||||
class UnicodeModeKeyMeta:
|
||||
def __init__(self, mode):
|
||||
self.mode = mode
|
||||
|
||||
|
||||
class TapDanceKeyMeta:
|
||||
def __init__(self, codes):
|
||||
self.codes = codes
|
||||
|
Loading…
Reference in New Issue
Block a user