Files
macropad/nmlkpy/keymap_manager.py

141 lines
5.4 KiB
Python
Executable File

from .keymap_delta import KeymapDelta, LayerDelta
from .pinstate_manager import PinManager
from .key_types.base import KeyBase
from .pin import Pin
# Needs to be built in a way where every "key type" only has to know how to modify it's own state
# it cannot be passed a pin or keyboard instance
# this is to help keep order in what can access what
# layer manager should be able to figure out what needs to happen to the state after a loop is run.
#
# Keys
# Keys have one way communication from KeymapManager to key and are allowed to return a result
# all keys changes together create a delta that is handled by the manager
# the manager defines an interface for changes that can happen to the state
# - Pressed
# - unpressed
# - Layer was changed
# When a delta is applied the manager can figure out what to do for that state index
# state
# Keys state is stored and managed in the manager
# One single layer of state removes the issue of when layers are changing
# each state is a class that can be consumed from different aspects, keycode, toggle, hold etc
# Where the state presents itself in the way that is asked
# Pins
# Can be asked for their current event
# Pins state is a class that holds current and last, can give you the delta to figure out what to change
_LAYERS_HOLD_NONE = None
_LAYERS_TOGGLE_NONE = 0
class LayerManager:
number_of_layers: int = None
current_hold_layer: int = None
current_toggle_layer: int = None
def __init__(self, number_of_layers: int) -> None:
assert number_of_layers is not None
assert number_of_layers > 0
self.number_of_layers = number_of_layers
self.current_hold_layer = _LAYERS_HOLD_NONE
self.current_toggle_layer = _LAYERS_TOGGLE_NONE
def get_current_layer(self) -> int:
return self.current_hold_layer if self.current_hold_layer is not _LAYERS_HOLD_NONE else self.current_toggle_layer
def step(self, changes: LayerDelta):
hold_layer = self.current_hold_layer
for layer in changes.get_hold_layers_to_remove():
if layer.get_layer() == hold_layer:
hold_layer = _LAYERS_HOLD_NONE
for layer in changes.get_hold_layers_to_apply():
hold_layer = layer.get_layer()
self.current_hold_layer = hold_layer
toggle_layer = self.current_toggle_layer
for layer in changes.get_toggle_layers_to_remove():
if layer.get_layer() == toggle_layer:
toggle_layer = _LAYERS_TOGGLE_NONE
for layer in changes.get_toggle_layers_to_apply():
toggle_layer = layer.get_layer()
self.current_toggle_layer = toggle_layer
class KeymapReport:
keymap_changes: KeymapDelta = None
current_layer: int = None
def __init__(self, keymap_changes: KeymapDelta, current_layer: int) -> None:
assert keymap_changes is not None
assert current_layer is not None
self.keymap_changes = keymap_changes
self.current_layer = current_layer
class KeymapManager:
keymap: list[list[KeyBase]] = None
pin_manager: PinManager = None
layer_manager: LayerManager = None
keys_state: list[object] = None
keycodes_currently_pressed: set = None
def __init__(self, keymap: list[list[KeyBase]], pins: list[Pin]):
self.keymap = keymap
self.pin_manager = PinManager(pins)
self.layer_manager = LayerManager(len(keymap))
self.keys_state = [None for key in keymap[0]]
self.keycodes_currently_pressed = set()
def add_to_pressed_keycodes(self, keycode: int):
self.keycodes_currently_pressed.add(keycode)
def remove_from_pressed_keycodes(self, keycode: int):
try:
self.keycodes_currently_pressed.remove(keycode)
except KeyError:
pass
def createReport(self, keymap_changes: KeymapDelta, layer_changes: LayerDelta) -> KeymapReport:
old_layer = self.layer_manager.get_current_layer()
self.layer_manager.step(layer_changes)
new_layer = self.layer_manager.get_current_layer()
layer_has_changed = new_layer != old_layer
if layer_has_changed:
delta = KeymapDelta()
for keycode in self.keycodes_currently_pressed:
delta.unpress(keycode)
self.keycodes_currently_pressed.clear()
keymap_changes.merge_deltas(delta)
[self.add_to_pressed_keycodes(keycode)
for keycode in keymap_changes.get_to_press()]
[self.remove_from_pressed_keycodes(
keycode) for keycode in keymap_changes.get_to_unpress()]
return KeymapReport(keymap_changes, new_layer)
def step(self) -> KeymapReport:
self.pin_manager.step()
pin_changes = self.pin_manager.get_state_delta()
keymap_changes = KeymapDelta()
layer_changes = LayerDelta()
current_layer_index = self.layer_manager.get_current_layer()
for pin_index in range(len(pin_changes)):
pin_state = pin_changes[pin_index]
if pin_state is None:
continue
key = self.keymap[current_layer_index][pin_index]
custom_state = self.keys_state[pin_index]
key_changes = key.calculate_changes(pin_state, custom_state)
keymap_changes.merge_deltas(key_changes.keymap_delta)
layer_changes.merge_deltas(key_changes.layer_delta)
self.keys_state[pin_index] = key_changes.special_state
return self.createReport(keymap_changes, layer_changes)