130 lines
4.1 KiB
Python
130 lines
4.1 KiB
Python
from keyboard import Keyboard
|
|
from micropython import const
|
|
|
|
|
|
class KeyState:
|
|
"""Interpretations of hardware pin state"""
|
|
PRESSED = const(0)
|
|
"""Key is pressed"""
|
|
UNPRESSED = const(1)
|
|
"""Key is not pressed"""
|
|
PRESSED_TOGGLED_ON = const(2)
|
|
"""Key is pressed and is emulating a toggle key in ON state that is not released until the physical key is released, then pressed again"""
|
|
UNPRESSED_TOGGLED_ON = const(3)
|
|
"""Key is unpressed and is emulating a toggle key in ON state that is not released unit the physical key is pressed again"""
|
|
PRESSED_TOGGLED_OFF = const(4)
|
|
"""Key is pressed and is emulating a toggle key in OFF state"""
|
|
DEBOUNCE = const(5)
|
|
"""Further state changes of any kind are ignored until key has been physically released released"""
|
|
|
|
|
|
class KeyEvent:
|
|
"""Interpretations of KeyStates as single events"""
|
|
NONE: int = const(0)
|
|
"""No new event has occurred"""
|
|
PRESSED: int = const(1)
|
|
"""The key was pressed"""
|
|
RELEASED: int = const(2)
|
|
"""The key was released"""
|
|
SOFT_RELEASE: int = const(3)
|
|
"""The key was released programatically, and not by an actual physical release of a button."""
|
|
|
|
|
|
class Key:
|
|
index: int
|
|
|
|
def __init__(self):
|
|
pass
|
|
|
|
def set_index(self, index) -> None:
|
|
assert 0 <= index
|
|
self.index = index
|
|
|
|
def handle(self, keyboard):
|
|
assert self.index is not None
|
|
|
|
|
|
class Keycode(Key):
|
|
keycode: int
|
|
|
|
def __init__(self, keycode: int):
|
|
super().__init__()
|
|
self.keycode = keycode
|
|
|
|
def __interpret_hardware_changes(self, keyboard: Keyboard) -> tuple[KeyState, KeyState]:
|
|
"""Interprets the changes to hardware pins as current and previous KeyState"""
|
|
current = keyboard.pins[self.index].value
|
|
previous = self.pin_states_last_cycle[self.index][0]
|
|
return (current, previous)
|
|
|
|
def __interpret_event(self, states: tuple[KeyState, KeyState]) -> KeyEvent:
|
|
"""Interprets the current and previous KeyState as a KeyEvent"""
|
|
current, previous = states
|
|
if previous == KeyState.UNPRESSED:
|
|
if current == KeyState.UNPRESSED:
|
|
return KeyEvent.NONE
|
|
if current == KeyState.PRESSED:
|
|
return KeyEvent.PRESSED
|
|
if previous == KeyState.PRESSED:
|
|
if current == KeyState.PRESSED:
|
|
return KeyEvent.NONE
|
|
if current == KeyState.UNPRESSED:
|
|
return KeyEvent.RELEASED
|
|
else:
|
|
raise NotImplementedError()
|
|
|
|
def react(self, keyboard: Keyboard, event: KeyEvent) -> None:
|
|
if event == KeyEvent.NONE:
|
|
return
|
|
if event == KeyEvent.PRESSED:
|
|
keyboard.pressed_keys.add(self.keycode)
|
|
if event == KeyEvent.RELEASED:
|
|
try:
|
|
keyboard.pressed_keys.remove(self.keycode)
|
|
except KeyError:
|
|
# silently pass errors when same keycode has been issued twice
|
|
pass
|
|
keyboard.pin_states_last_cycle[self.index] = ()
|
|
|
|
def handle(self, keyboard):
|
|
super().handle()
|
|
states = self.__interpret_hardware_changes(keyboard)
|
|
event = self.__interpret_event(states)
|
|
self.react(keyboard, event)
|
|
|
|
if not isinstance(key, LayerKey):
|
|
if currentlyPressed:
|
|
if not previouslyPressed:
|
|
self.pressed_keys.add(key.keycode)
|
|
else:
|
|
if previouslyPressed:
|
|
try:
|
|
self.pressed_keys.remove(key.keycode)
|
|
# Catch silenly if same keycode is pressed twice then released
|
|
except KeyError:
|
|
pass
|
|
self.pin_states_last_cycle[pin_index] = (value,)
|
|
|
|
|
|
class Modifier(Keycode):
|
|
def __init__(self, keycode: int):
|
|
super().__init__(keycode)
|
|
|
|
|
|
class LayerKey(Key):
|
|
layer: int
|
|
|
|
def __init__(self, layer: int):
|
|
super().__init__()
|
|
self.layer = layer
|
|
|
|
|
|
class Toggle(LayerKey):
|
|
def __init__(self, layer: int):
|
|
super().__init__(layer)
|
|
|
|
|
|
class Hold(LayerKey):
|
|
def __init__(self, layer: int):
|
|
super().__init__(layer)
|