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)