unpress previously pressed keys on layer change
This commit is contained in:
@@ -1,34 +1,33 @@
|
|||||||
from nmlkpy.key_codes.se import SE
|
from nmlkpy.key_codes.se import SE
|
||||||
from nmlkpy.key_types.keycode import Keycode, Modifier
|
from nmlkpy.key_types.keycode import Keycode, Modifier
|
||||||
|
from nmlkpy.key_types.layer import Hold, Toggle
|
||||||
from nmlkpy.layer_manager import LayerManager
|
from nmlkpy.layer_manager import LayerManager
|
||||||
|
|
||||||
lm = LayerManager(5)
|
|
||||||
|
|
||||||
keymap = [[
|
keymap = [[
|
||||||
lm.hold(1), lm.toggle(2), lm.toggle(3), lm.toggle(4),
|
Hold(1), Toggle(2), Toggle(3), Toggle(4),
|
||||||
lm.hold(2), Keycode(SE.B), Keycode(SE.C), Keycode(SE.D),
|
Hold(2), Keycode(SE.B), Keycode(SE.C), Keycode(SE.D),
|
||||||
Keycode(SE.LEFT), Keycode(SE.DOWN), Keycode(
|
Keycode(SE.LEFT), Keycode(SE.DOWN), Keycode(
|
||||||
SE.UP), Keycode(SE.RIGHT),
|
SE.UP), Keycode(SE.RIGHT),
|
||||||
Keycode(SE.ENTER), Keycode(SE.J), Keycode(
|
Keycode(SE.ENTER), Keycode(SE.J), Keycode(
|
||||||
SE.K), Modifier(SE.LEFT_SHIFT)
|
SE.K), Modifier(SE.LEFT_SHIFT)
|
||||||
], [
|
], [
|
||||||
lm.hold(1), lm.toggle(2), lm.toggle(3), lm.toggle(4),
|
Hold(1), Toggle(2), Toggle(3), Toggle(4),
|
||||||
Keycode(SE.ONE), lm.hold(1), Keycode(SE.C), Keycode(SE.D),
|
Keycode(SE.ONE), Hold(1), Keycode(SE.C), Keycode(SE.D),
|
||||||
Keycode(SE.E), Keycode(SE.F), Keycode(SE.G), Keycode(SE.H),
|
Keycode(SE.E), Keycode(SE.F), Keycode(SE.G), Keycode(SE.H),
|
||||||
Keycode(SE.BACKSPACE), Keycode(SE.J), Keycode(SE.K), Keycode(SE.L)
|
Keycode(SE.BACKSPACE), Keycode(SE.J), Keycode(SE.K), Keycode(SE.L)
|
||||||
], [
|
], [
|
||||||
lm.hold(1), lm.toggle(2), lm.toggle(3), lm.toggle(4),
|
Hold(1), Toggle(2), Toggle(3), Toggle(4),
|
||||||
Keycode(SE.A), lm.hold(1), lm.hold(2), Keycode(SE.D),
|
Hold(2), Hold(1), Hold(2), Keycode(SE.D),
|
||||||
Keycode(SE.E), Keycode(SE.F), Keycode(SE.G), Keycode(SE.H),
|
Keycode(SE.E), Keycode(SE.F), Keycode(SE.G), Keycode(SE.H),
|
||||||
Keycode(SE.I), Keycode(SE.J), Keycode(SE.K), Keycode(SE.L)
|
Keycode(SE.I), Keycode(SE.J), Keycode(SE.K), Keycode(SE.L)
|
||||||
], [
|
], [
|
||||||
lm.hold(1), lm.toggle(2), lm.toggle(3), lm.toggle(4),
|
Hold(1), Toggle(2), Toggle(3), Toggle(4),
|
||||||
Keycode(SE.A), Keycode(SE.B), lm.hold(2), lm.hold(3),
|
Keycode(SE.A), Keycode(SE.B), Hold(2), Hold(3),
|
||||||
Keycode(SE.E), Keycode(SE.F), Keycode(SE.G), Keycode(SE.H),
|
Keycode(SE.E), Keycode(SE.F), Keycode(SE.G), Keycode(SE.H),
|
||||||
Keycode(SE.I), Keycode(SE.J), Keycode(SE.K), Keycode(SE.L)
|
Keycode(SE.I), Keycode(SE.J), Keycode(SE.K), Keycode(SE.L)
|
||||||
], [
|
], [
|
||||||
lm.hold(1), lm.toggle(2), lm.toggle(3), lm.toggle(4),
|
Hold(1), Toggle(2), Toggle(3), Toggle(4),
|
||||||
Keycode(SE.A), Keycode(SE.B), Keycode(SE.C), lm.hold(3),
|
Keycode(SE.A), Keycode(SE.B), Keycode(SE.C), Hold(3),
|
||||||
Keycode(SE.E), Keycode(SE.F), Keycode(SE.G), Keycode(SE.H),
|
Keycode(SE.E), Keycode(SE.F), Keycode(SE.G), Keycode(SE.H),
|
||||||
Keycode(SE.I), Keycode(SE.J), Keycode(SE.K), Modifier(SE.LEFT_ALT)
|
Keycode(SE.I), Keycode(SE.J), Keycode(SE.K), Modifier(SE.LEFT_ALT)
|
||||||
]]
|
]]
|
||||||
|
|||||||
0
keyboards/macropad_rev1/default/pinouts.py
Normal file → Executable file
0
keyboards/macropad_rev1/default/pinouts.py
Normal file → Executable file
0
lib/adafruit_mcp230xx/__init__.mpy
Normal file → Executable file
0
lib/adafruit_mcp230xx/__init__.mpy
Normal file → Executable file
0
lib/adafruit_mcp230xx/digital_inout.mpy
Normal file → Executable file
0
lib/adafruit_mcp230xx/digital_inout.mpy
Normal file → Executable file
0
lib/adafruit_mcp230xx/mcp23008.mpy
Normal file → Executable file
0
lib/adafruit_mcp230xx/mcp23008.mpy
Normal file → Executable file
0
lib/adafruit_mcp230xx/mcp23016.mpy
Normal file → Executable file
0
lib/adafruit_mcp230xx/mcp23016.mpy
Normal file → Executable file
0
lib/adafruit_mcp230xx/mcp23017.mpy
Normal file → Executable file
0
lib/adafruit_mcp230xx/mcp23017.mpy
Normal file → Executable file
0
lib/adafruit_mcp230xx/mcp230xx.mpy
Normal file → Executable file
0
lib/adafruit_mcp230xx/mcp230xx.mpy
Normal file → Executable file
0
lib/adafruit_mcp230xx/mcp23sxx.mpy
Normal file → Executable file
0
lib/adafruit_mcp230xx/mcp23sxx.mpy
Normal file → Executable file
128
nmlkpy/key_types/base.py
Normal file → Executable file
128
nmlkpy/key_types/base.py
Normal file → Executable file
@@ -1,135 +1,29 @@
|
|||||||
from micropython import const
|
from nmlkpy.keymap_delta import KeyIdentifier, KeymapDelta, LayerDelta
|
||||||
from nmlkpy import keymap_delta
|
|
||||||
from nmlkpy.keymap_delta import KeymapDelta, LayerDelta
|
|
||||||
from nmlkpy.pinstate_manager import PinState
|
from nmlkpy.pinstate_manager import PinState
|
||||||
|
|
||||||
|
|
||||||
class KeyDelta:
|
class KeyChanges:
|
||||||
layer_delta: LayerDelta
|
layer_delta: LayerDelta
|
||||||
keymap_delta: KeymapDelta
|
keymap_delta: KeymapDelta
|
||||||
|
special_state: object
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.layer_delta = LayerDelta()
|
self.layer_delta = LayerDelta()
|
||||||
self.keymap_delta = KeymapDelta()
|
self.keymap_delta = KeymapDelta()
|
||||||
|
self.special_state = None
|
||||||
|
|
||||||
|
def set_special_state(self, state: object) -> None:
|
||||||
class KeyEvent:
|
self.special_state = state
|
||||||
"""Interpretations of KeyStates as single events"""
|
|
||||||
NO_EVENT: int = const(0)
|
|
||||||
"""No new event has occurred"""
|
|
||||||
PRESSED_: int = const(1)
|
|
||||||
"""The key was pressed"""
|
|
||||||
RELEASED: int = const(2)
|
|
||||||
"""The key was released"""
|
|
||||||
PRESSED_TOGGLED_ON = const(3)
|
|
||||||
"""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"""
|
|
||||||
RELEASED_TOGGLED_ON = const(4)
|
|
||||||
"""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(5)
|
|
||||||
"""Key is pressed and is emulating a toggle key in OFF state"""
|
|
||||||
|
|
||||||
|
|
||||||
def _event_to_string(event: int) -> str:
|
|
||||||
if event == KeyEvent.NO_EVENT:
|
|
||||||
return "NO EVENT"
|
|
||||||
if event == KeyEvent.PRESSED_:
|
|
||||||
return "PRESSED"
|
|
||||||
if event == KeyEvent.RELEASED:
|
|
||||||
return "RELEASED"
|
|
||||||
if event == KeyEvent.PRESSED_TOGGLED_ON:
|
|
||||||
return "PRESSED TOGGLED ON"
|
|
||||||
if event == KeyEvent.RELEASED_TOGGLED_ON:
|
|
||||||
return "RELEASED TOGGLED ON"
|
|
||||||
if event == KeyEvent.PRESSED_TOGGLED_OFF:
|
|
||||||
return "PRESSED TOGGLED OFF"
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
|
|
||||||
class SoftRelease:
|
|
||||||
_NOT_QUEUED = 0
|
|
||||||
_QUEUED = 1
|
|
||||||
_HANDLED = 2
|
|
||||||
_state: int
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self._state = self._NOT_QUEUED
|
|
||||||
|
|
||||||
def needs_attention(self) -> bool:
|
|
||||||
return self._state != self._NOT_QUEUED
|
|
||||||
|
|
||||||
def is_queued(self) -> bool:
|
|
||||||
return self._state == self._QUEUED
|
|
||||||
|
|
||||||
def is_handled(self) -> bool:
|
|
||||||
return self._state == self._HANDLED
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
self._state = self._NOT_QUEUED
|
|
||||||
|
|
||||||
def queue(self):
|
|
||||||
self._state = self._QUEUED
|
|
||||||
|
|
||||||
def mark_as_handled(self):
|
|
||||||
self._state = self._HANDLED
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
if self._state == self._NOT_QUEUED:
|
|
||||||
return "Not queued"
|
|
||||||
if self._state == self._QUEUED:
|
|
||||||
return "Queued"
|
|
||||||
if self._state == self._HANDLED:
|
|
||||||
return "Handled"
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
|
|
||||||
class KeyEvents:
|
|
||||||
current: int
|
|
||||||
previous: int
|
|
||||||
soft_release: SoftRelease
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.current = KeyEvent.RELEASED
|
|
||||||
self.previous = KeyEvent.RELEASED
|
|
||||||
self.soft_release = SoftRelease()
|
|
||||||
|
|
||||||
def shift(self, next):
|
|
||||||
self.previous = self.current
|
|
||||||
self.current = next
|
|
||||||
|
|
||||||
|
|
||||||
class KeyBase:
|
class KeyBase:
|
||||||
events: KeyEvents
|
#identifier: KeyIdentifier
|
||||||
allowed_events: list[KeyEvent]
|
# def enrich(self, identifier: KeyIdentifier):
|
||||||
|
# assert identifier is not None
|
||||||
|
# self.identifier = identifier
|
||||||
|
|
||||||
def __init__(self, allowed_events: list[int]):
|
def calculate_changes(self, pin: PinState, custom_state: object) -> KeyChanges:
|
||||||
self.allowed_events = allowed_events
|
|
||||||
self.events = KeyEvents()
|
|
||||||
|
|
||||||
def get_delta(self, pin: PinState) -> KeyDelta:
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def _validate_next_event(self, next_event: int):
|
|
||||||
if next_event is None or next_event not in self.allowed_events:
|
|
||||||
raise Exception(
|
|
||||||
f"KeyEvent with value of {next_event} is not a valid event for this type of key")
|
|
||||||
|
|
||||||
def _set_event(self, next_event: int, index: int = 99) -> bool:
|
|
||||||
"""Sets previous state if new state is valid
|
|
||||||
Returns: A boolean that determines if the state was updated or not"""
|
|
||||||
self._validate_next_event(next_event)
|
|
||||||
if next_event is KeyEvent.NO_EVENT:
|
|
||||||
return False
|
|
||||||
print(
|
|
||||||
f"[{index}] next: {_event_to_string(next_event)}, current: {_event_to_string(self.events.current)}, previous: {_event_to_string(self.events.previous)}, soft_release: {self.events.soft_release}")
|
|
||||||
self.events.shift(next_event)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def handle(self, keyboard, is_active: bool = False):
|
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def self_test(self, keymap: list[list[object]]):
|
def self_test(self, keymap: list[list[object]]):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def soft_release(self, keyboard):
|
|
||||||
"""Release key programatically and wait until it's released physically before registrating additional key presses"""
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|||||||
79
nmlkpy/key_types/keycode.py
Normal file → Executable file
79
nmlkpy/key_types/keycode.py
Normal file → Executable file
@@ -1,7 +1,6 @@
|
|||||||
from .base import KeyBase, KeyDelta, KeyEvent
|
from .base import KeyBase, KeyChanges
|
||||||
from ..pin import PinEvent
|
from ..pin import PinEvent
|
||||||
from ..pinstate_manager import PinState
|
from ..pinstate_manager import PinState
|
||||||
from ..keymap_delta import KeymapDelta
|
|
||||||
|
|
||||||
_VALID_KEYCODES = range(0, 255)
|
_VALID_KEYCODES = range(0, 255)
|
||||||
_VALID_MODIFIERS = [0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000]
|
_VALID_MODIFIERS = [0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000]
|
||||||
@@ -10,85 +9,13 @@ _VALID_MODIFIERS = [0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000]
|
|||||||
class KeycodeBase(KeyBase):
|
class KeycodeBase(KeyBase):
|
||||||
"""Basic press and release functionality that appends a keycode to the report when active"""
|
"""Basic press and release functionality that appends a keycode to the report when active"""
|
||||||
keycode: int
|
keycode: int
|
||||||
currently_pressed: bool
|
|
||||||
|
|
||||||
def __init__(self, keycode: int):
|
def __init__(self, keycode: int):
|
||||||
allowed_events = [
|
|
||||||
KeyEvent.NO_EVENT,
|
|
||||||
KeyEvent.PRESSED_,
|
|
||||||
KeyEvent.RELEASED
|
|
||||||
]
|
|
||||||
super().__init__(allowed_events)
|
|
||||||
self.keycode = keycode
|
self.keycode = keycode
|
||||||
self.currently_pressed = False
|
|
||||||
|
|
||||||
def self_test(self, keymap):
|
def calculate_changes(self, pin: PinState, custom_state: object) -> KeyChanges:
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def soft_release(self, keyboard):
|
|
||||||
if self.currently_pressed:
|
|
||||||
self.events.soft_release.queue()
|
|
||||||
|
|
||||||
def _press(self, keyboard):
|
|
||||||
self.currently_pressed = True
|
|
||||||
keyboard.add_keycode_to_report(self.keycode)
|
|
||||||
|
|
||||||
def _release(self, keyboard):
|
|
||||||
self.currently_pressed = False
|
|
||||||
keyboard.remove_keycode_from_report(self.keycode)
|
|
||||||
|
|
||||||
def _handle_soft_release(self, keyboard):
|
|
||||||
if self.events.soft_release.is_queued():
|
|
||||||
if self.currently_pressed:
|
|
||||||
self._release(keyboard)
|
|
||||||
self._set_event(KeyEvent.RELEASED)
|
|
||||||
self.events.soft_release.reset()
|
|
||||||
return
|
|
||||||
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def _consume_next_event(self, pin) -> bool:
|
|
||||||
"""Returns: a boolean representing if the event property was updated with a new event or not"""
|
|
||||||
next_pin_event = pin.read_event()
|
|
||||||
current_key_event = self.events.current
|
|
||||||
|
|
||||||
if current_key_event == KeyEvent.RELEASED:
|
|
||||||
if next_pin_event == PinEvent.PRESSED:
|
|
||||||
return self._set_event(KeyEvent.PRESSED_)
|
|
||||||
return self._set_event(KeyEvent.NO_EVENT)
|
|
||||||
if current_key_event == KeyEvent.PRESSED_:
|
|
||||||
if next_pin_event == PinEvent.RELEASED:
|
|
||||||
return self._set_event(KeyEvent.RELEASED)
|
|
||||||
return self._set_event(KeyEvent.NO_EVENT)
|
|
||||||
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def _handle_event(self, keyboard) -> None:
|
|
||||||
current = self.events.current
|
|
||||||
if self.events.soft_release.is_queued():
|
|
||||||
pass
|
|
||||||
if self.events.soft_release.is_handled():
|
|
||||||
pass
|
|
||||||
if current == KeyEvent.PRESSED_:
|
|
||||||
self._press(keyboard)
|
|
||||||
return
|
|
||||||
if current == KeyEvent.RELEASED:
|
|
||||||
self._release(keyboard)
|
|
||||||
return
|
|
||||||
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def handle(self, keyboard, pin, is_active: bool = False):
|
|
||||||
new_event_available = self._consume_next_event(pin)
|
|
||||||
if self.events.soft_release.needs_attention():
|
|
||||||
self._handle_soft_release(keyboard)
|
|
||||||
return
|
|
||||||
if new_event_available and is_active:
|
|
||||||
self._handle_event(keyboard)
|
|
||||||
|
|
||||||
def get_delta(self, pin: PinState) -> KeyDelta:
|
|
||||||
assert pin is not None
|
assert pin is not None
|
||||||
changes = KeyDelta()
|
changes = KeyChanges()
|
||||||
if pin.is_pressed():
|
if pin.is_pressed():
|
||||||
changes.keymap_delta.press(self.keycode)
|
changes.keymap_delta.press(self.keycode)
|
||||||
else:
|
else:
|
||||||
|
|||||||
218
nmlkpy/key_types/layer.py
Normal file → Executable file
218
nmlkpy/key_types/layer.py
Normal file → Executable file
@@ -1,193 +1,63 @@
|
|||||||
from ..keymap_delta import KeymapDelta, LayerChange
|
from ..keymap_delta import LayerChange
|
||||||
from ..pinstate_manager import PinState
|
from ..pinstate_manager import PinState
|
||||||
from .base import KeyDelta, KeyEvent, KeyBase
|
from .base import KeyChanges, KeyBase
|
||||||
from ..pin import PinEvent, Pin
|
|
||||||
|
|
||||||
|
|
||||||
class LayerKeyBase(KeyBase):
|
class LayerKeyBase(KeyBase):
|
||||||
layer_to_switch_to: int
|
layer_to_switch_to: int
|
||||||
|
|
||||||
def __init__(self, layer: int, allowed_events):
|
def __init__(self, layer: int):
|
||||||
super().__init__(allowed_events)
|
|
||||||
self.layer_to_switch_to = layer
|
self.layer_to_switch_to = layer
|
||||||
|
|
||||||
def self_test(self, keymap: list[list[KeyBase]]):
|
def self_test(self, keymap: list[list[object]]):
|
||||||
# to be implemented, can not use enrichment of indexes
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _release_all_other_keys(self, keyboard):
|
|
||||||
for layer in keyboard.keymap:
|
|
||||||
for key in layer:
|
|
||||||
if key is self:
|
|
||||||
continue
|
|
||||||
key.soft_release(keyboard)
|
|
||||||
|
|
||||||
def _release_all_other_non_toggle_keys(self, keyboard):
|
class ToggleCustomState:
|
||||||
for layer in keyboard.keymap:
|
_TOGGLE_ON = const(0)
|
||||||
for key in layer:
|
_TOGGLE_OFF = const(1)
|
||||||
if key is self or isinstance(key, Toggle):
|
|
||||||
continue
|
current_state: int
|
||||||
key.soft_release(keyboard)
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.toggle_off
|
||||||
|
|
||||||
|
def toggle_on(self):
|
||||||
|
self.current_state = ToggleCustomState._TOGGLE_ON
|
||||||
|
|
||||||
|
def toggle_off(self):
|
||||||
|
self.current_state = ToggleCustomState._TOGGLE_OFF
|
||||||
|
|
||||||
|
def is_on(self) -> int:
|
||||||
|
return self.current_state == ToggleCustomState._TOGGLE_ON
|
||||||
|
|
||||||
|
def is_toggle_state(obj: object) -> bool:
|
||||||
|
return isinstance(obj, ToggleCustomState)
|
||||||
|
|
||||||
|
|
||||||
class Toggle(LayerKeyBase):
|
class Toggle(LayerKeyBase):
|
||||||
def __init__(self, layer: int):
|
def __init__(self, layer: int):
|
||||||
allowed_events = [
|
super().__init__(layer)
|
||||||
KeyEvent.NO_EVENT,
|
|
||||||
KeyEvent.PRESSED_TOGGLED_ON,
|
|
||||||
KeyEvent.RELEASED_TOGGLED_ON,
|
|
||||||
KeyEvent.PRESSED_TOGGLED_OFF,
|
|
||||||
KeyEvent.RELEASED
|
|
||||||
]
|
|
||||||
super().__init__(layer, allowed_events)
|
|
||||||
|
|
||||||
def _press(self, keyboard):
|
def calculate_changes(self, pin: PinState, custom_state: object) -> KeyChanges:
|
||||||
keyboard.layer.toggle(self.layer_to_switch_to)
|
changes = KeyChanges()
|
||||||
self._release_all_other_keys(keyboard)
|
if pin.is_pressed():
|
||||||
|
changes.layer_delta.apply_layer(
|
||||||
def _release(self, keyboard):
|
LayerChange(self.layer_to_switch_to))
|
||||||
keyboard.layer.release_toggled()
|
else:
|
||||||
self._release_all_other_keys(keyboard)
|
changes.layer_delta.remove_layer(
|
||||||
|
LayerChange(self.layer_to_switch_to))
|
||||||
def soft_release(self, keyboard):
|
return changes
|
||||||
self.events.soft_release.queue()
|
|
||||||
|
|
||||||
def _handle_soft_release(self, keyboard):
|
class Hold(LayerKeyBase):
|
||||||
if self.events.soft_release.is_queued():
|
currently_pressed: bool
|
||||||
# leads to recursion
|
|
||||||
self._release(keyboard)
|
def __init__(self, layer: int):
|
||||||
self.events.soft_release.mark_as_handled()
|
super().__init__(layer)
|
||||||
return
|
|
||||||
if self.events.soft_release.is_handled():
|
def calculate_changes(self, pin: PinState, custom_state: object) -> KeyChanges:
|
||||||
self.events.soft_release.reset()
|
changes = KeyChanges()
|
||||||
return
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def _consume_next_event(self, pin: Pin) -> bool:
|
|
||||||
"""Returns: a boolean representing if the event property was updated with a new event or not"""
|
|
||||||
next_pin_event = pin.read_event()
|
|
||||||
current_key_event = self.events.current
|
|
||||||
|
|
||||||
if current_key_event == KeyEvent.RELEASED:
|
|
||||||
if next_pin_event == PinEvent.PRESSED:
|
|
||||||
return self._set_event(KeyEvent.PRESSED_TOGGLED_ON)
|
|
||||||
return self._set_event(KeyEvent.NO_EVENT)
|
|
||||||
if current_key_event == KeyEvent.PRESSED_TOGGLED_ON:
|
|
||||||
if next_pin_event == PinEvent.RELEASED:
|
|
||||||
return self._set_event(KeyEvent.RELEASED_TOGGLED_ON)
|
|
||||||
return self._set_event(KeyEvent.NO_EVENT)
|
|
||||||
if current_key_event == KeyEvent.RELEASED_TOGGLED_ON:
|
|
||||||
if next_pin_event == PinEvent.PRESSED:
|
|
||||||
return self._set_event(KeyEvent.PRESSED_TOGGLED_OFF)
|
|
||||||
return self._set_event(KeyEvent.NO_EVENT)
|
|
||||||
if current_key_event == KeyEvent.PRESSED_TOGGLED_OFF:
|
|
||||||
if next_pin_event == PinEvent.RELEASED:
|
|
||||||
return self._set_event(KeyEvent.RELEASED)
|
|
||||||
return self._set_event(KeyEvent.NO_EVENT)
|
|
||||||
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def _handle_event(self, keyboard, pin):
|
|
||||||
current = self.events.current
|
|
||||||
if self.events.soft_release.is_queued():
|
|
||||||
pass
|
|
||||||
if self.events.soft_release.is_handled():
|
|
||||||
pass
|
|
||||||
|
|
||||||
if current == KeyEvent.PRESSED_TOGGLED_ON:
|
|
||||||
keyboard.layer.toggle(self.layer_to_switch_to)
|
|
||||||
self._release_all_other_keys(keyboard)
|
|
||||||
return
|
|
||||||
if current == KeyEvent.RELEASED_TOGGLED_ON:
|
|
||||||
return
|
|
||||||
if current == KeyEvent.PRESSED_TOGGLED_OFF:
|
|
||||||
keyboard.layer.release_toggled()
|
|
||||||
self._release_all_other_keys(keyboard)
|
|
||||||
return
|
|
||||||
if current == KeyEvent.RELEASED:
|
|
||||||
return
|
|
||||||
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def handle(self, keyboard, pin: Pin, is_active: bool = False):
|
|
||||||
if self.events.soft_release.needs_attention():
|
|
||||||
self._handle_soft_release(keyboard)
|
|
||||||
return
|
|
||||||
if self._consume_next_event(pin):
|
|
||||||
self._handle_event(keyboard, pin)
|
|
||||||
|
|
||||||
|
|
||||||
class Hold(LayerKeyBase):
|
|
||||||
currently_pressed: bool
|
|
||||||
|
|
||||||
def __init__(self, layer: int):
|
|
||||||
allowed_events = [
|
|
||||||
KeyEvent.NO_EVENT,
|
|
||||||
KeyEvent.PRESSED_,
|
|
||||||
KeyEvent.RELEASED
|
|
||||||
]
|
|
||||||
super().__init__(layer, allowed_events)
|
|
||||||
self.currently_pressed = False
|
|
||||||
|
|
||||||
def soft_release(self, keyboard):
|
|
||||||
if self.currently_pressed:
|
|
||||||
self.events.soft_release.queue()
|
|
||||||
|
|
||||||
def _handle_soft_release(self, keyboard):
|
|
||||||
if self.events.soft_release.is_queued():
|
|
||||||
if self.currently_pressed:
|
|
||||||
self._release(keyboard)
|
|
||||||
self._set_event(KeyEvent.RELEASED)
|
|
||||||
self.events.soft_release.reset()
|
|
||||||
return
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def handle(self, keyboard, pin, is_active: bool = False):
|
|
||||||
new_event_available = self._consume_next_event(pin)
|
|
||||||
if self.events.soft_release.needs_attention():
|
|
||||||
self._handle_soft_release(keyboard)
|
|
||||||
return
|
|
||||||
if new_event_available and is_active:
|
|
||||||
self._handle_event(keyboard)
|
|
||||||
|
|
||||||
def _consume_next_event(self, pin: Pin) -> bool:
|
|
||||||
next_pin_event = pin.read_event()
|
|
||||||
|
|
||||||
if self.events.current == KeyEvent.RELEASED:
|
|
||||||
if next_pin_event == PinEvent.PRESSED:
|
|
||||||
return self._set_event(KeyEvent.PRESSED_)
|
|
||||||
return self._set_event(KeyEvent.NO_EVENT)
|
|
||||||
if self.events.current == KeyEvent.PRESSED_:
|
|
||||||
if next_pin_event == PinEvent.RELEASED:
|
|
||||||
return self._set_event(KeyEvent.RELEASED)
|
|
||||||
return self._set_event(KeyEvent.NO_EVENT)
|
|
||||||
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def _handle_event(self, keyboard):
|
|
||||||
current = self.events.current
|
|
||||||
|
|
||||||
if current == KeyEvent.PRESSED_:
|
|
||||||
self._press(keyboard)
|
|
||||||
return
|
|
||||||
if current == KeyEvent.RELEASED:
|
|
||||||
self._release(keyboard)
|
|
||||||
return
|
|
||||||
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def _press(self, keyboard):
|
|
||||||
self.currently_pressed = True
|
|
||||||
keyboard.layer.hold(self.layer_to_switch_to)
|
|
||||||
self._release_all_other_non_toggle_keys(keyboard)
|
|
||||||
|
|
||||||
def _release(self, keyboard):
|
|
||||||
self.currently_pressed = False
|
|
||||||
keyboard.layer.release_held()
|
|
||||||
self._release_all_other_non_toggle_keys(keyboard)
|
|
||||||
|
|
||||||
def get_delta(self, pin: PinState) -> KeyDelta:
|
|
||||||
changes = KeyDelta()
|
|
||||||
if pin.is_pressed():
|
if pin.is_pressed():
|
||||||
changes.layer_delta.apply_layer(
|
changes.layer_delta.apply_layer(
|
||||||
LayerChange(self.layer_to_switch_to))
|
LayerChange(self.layer_to_switch_to))
|
||||||
|
|||||||
@@ -223,12 +223,11 @@ class Keyboard:
|
|||||||
def remove_keycode_from_report(self, keycode: int):
|
def remove_keycode_from_report(self, keycode: int):
|
||||||
try:
|
try:
|
||||||
self.pressed_keys.remove(keycode)
|
self.pressed_keys.remove(keycode)
|
||||||
except KeyError:
|
except ValueError:
|
||||||
# silently pass errors when same keycode has been issued twice
|
# silently pass errors when same keycode has been issued twice
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def _update_layer_led(self) -> None:
|
def _update_layer_led(self, layer: int) -> None:
|
||||||
layer = self.layer.get()
|
|
||||||
if len(self.layer_colors) >= (layer + 1):
|
if len(self.layer_colors) >= (layer + 1):
|
||||||
self.led.set(self.layer_colors[layer])
|
self.led.set(self.layer_colors[layer])
|
||||||
else:
|
else:
|
||||||
@@ -260,15 +259,15 @@ class Keyboard:
|
|||||||
print("Keyboard started")
|
print("Keyboard started")
|
||||||
m = KeymapManager(self.keymap, self.pins)
|
m = KeymapManager(self.keymap, self.pins)
|
||||||
while True:
|
while True:
|
||||||
keymap_delta = m.step()
|
keymap_report = m.step()
|
||||||
if keymap_delta.contains_keycode_changes():
|
if keymap_report.keymap_changes.contains_keycode_changes():
|
||||||
for keycode in keymap_delta.get_to_press():
|
for keycode in keymap_report.keymap_changes.get_to_press():
|
||||||
self.add_keycode_to_report(keycode)
|
self.add_keycode_to_report(keycode)
|
||||||
for keycode in keymap_delta.get_to_unpress():
|
for keycode in keymap_report.keymap_changes.get_to_unpress():
|
||||||
self.remove_keycode_from_report(keycode)
|
self.remove_keycode_from_report(keycode)
|
||||||
self.send_nkro_report()
|
self.send_nkro_report()
|
||||||
|
|
||||||
self._update_layer_led()
|
self._update_layer_led(keymap_report.current_layer)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if self.debug_repl:
|
if self.debug_repl:
|
||||||
raise e
|
raise e
|
||||||
|
|||||||
45
nmlkpy/keymap_delta.py
Normal file → Executable file
45
nmlkpy/keymap_delta.py
Normal file → Executable file
@@ -23,6 +23,9 @@ class LayerChange:
|
|||||||
def get_layer(self) -> int:
|
def get_layer(self) -> int:
|
||||||
return self._to_layer
|
return self._to_layer
|
||||||
|
|
||||||
|
def is_toggle_layer_change(self) -> bool:
|
||||||
|
return self._is_toggle
|
||||||
|
|
||||||
|
|
||||||
class LayerDelta:
|
class LayerDelta:
|
||||||
_layers_to_apply: list[LayerChange]
|
_layers_to_apply: list[LayerChange]
|
||||||
@@ -35,6 +38,24 @@ class LayerDelta:
|
|||||||
def contains_layer_change(self) -> bool:
|
def contains_layer_change(self) -> bool:
|
||||||
return len(self._layers_to_apply) + len(self._layers_to_remove) > 0
|
return len(self._layers_to_apply) + len(self._layers_to_remove) > 0
|
||||||
|
|
||||||
|
def contains_layers_to_apply(self) -> bool:
|
||||||
|
return len(self.get_layers_to_apply()) > 0
|
||||||
|
|
||||||
|
def contains_layers_to_remove(self) -> bool:
|
||||||
|
return len(self.get_layers_to_remove()) > 0
|
||||||
|
|
||||||
|
def get_hold_layers_to_apply(self) -> list[LayerChange]:
|
||||||
|
return filter(lambda delta: not delta.is_toggle_layer_change(), self._layers_to_apply)
|
||||||
|
|
||||||
|
def get_hold_layers_to_remove(self) -> list[LayerChange]:
|
||||||
|
return filter(lambda delta: not delta.is_toggle_layer_change(), self._layers_to_remove)
|
||||||
|
|
||||||
|
def get_toggle_layers_to_apply(self) -> list[LayerChange]:
|
||||||
|
return filter(lambda delta: delta.is_toggle_layer_change(), self._layers_to_apply)
|
||||||
|
|
||||||
|
def get_toggle_layers_to_remove(self) -> list[LayerChange]:
|
||||||
|
return filter(lambda delta: delta.is_toggle_layer_change(), self._layers_to_remove)
|
||||||
|
|
||||||
def get_layers_to_apply(self) -> list[LayerChange]:
|
def get_layers_to_apply(self) -> list[LayerChange]:
|
||||||
return self._layers_to_apply
|
return self._layers_to_apply
|
||||||
|
|
||||||
@@ -57,27 +78,35 @@ class LayerDelta:
|
|||||||
|
|
||||||
|
|
||||||
class KeymapDelta:
|
class KeymapDelta:
|
||||||
_keycodes_to_press: list[int]
|
_keycodes_to_press: set[int]
|
||||||
_keycodes_to_unpress: list[int]
|
_keycodes_to_unpress: set[int]
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self._keycodes_to_press = []
|
self._keycodes_to_press = set()
|
||||||
self._keycodes_to_unpress = []
|
self._keycodes_to_unpress = set()
|
||||||
|
|
||||||
def get_to_press(self) -> list[int]:
|
def get_to_press(self) -> set[int]:
|
||||||
return self._keycodes_to_press
|
return self._keycodes_to_press
|
||||||
|
|
||||||
def get_to_unpress(self) -> list[int]:
|
def get_to_unpress(self) -> set[int]:
|
||||||
return self._keycodes_to_unpress
|
return self._keycodes_to_unpress
|
||||||
|
|
||||||
def contains_keycode_changes(self) -> bool:
|
def contains_keycode_changes(self) -> bool:
|
||||||
return len(self._keycodes_to_unpress) + len(self._keycodes_to_press) > 0
|
return len(self._keycodes_to_unpress) + len(self._keycodes_to_press) > 0
|
||||||
|
|
||||||
def press(self, keycode: int) -> None:
|
def press(self, keycode: int) -> None:
|
||||||
self._keycodes_to_press.append(keycode)
|
self._keycodes_to_press.add(keycode)
|
||||||
|
try:
|
||||||
|
self._keycodes_to_unpress.remove(keycode)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
def unpress(self, keycode: int) -> None:
|
def unpress(self, keycode: int) -> None:
|
||||||
self._keycodes_to_unpress.append(keycode)
|
self._keycodes_to_unpress.add(keycode)
|
||||||
|
try:
|
||||||
|
self._keycodes_to_press.remove(keycode)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
def merge_deltas(self, other_delta) -> None:
|
def merge_deltas(self, other_delta) -> None:
|
||||||
for keycode in other_delta.get_to_press():
|
for keycode in other_delta.get_to_press():
|
||||||
|
|||||||
99
nmlkpy/keymap_manager.py
Normal file → Executable file
99
nmlkpy/keymap_manager.py
Normal file → Executable file
@@ -1,12 +1,8 @@
|
|||||||
from nmlkpy.key_types.layer import Hold
|
|
||||||
from .keymap_delta import KeymapDelta, LayerChange, LayerDelta
|
|
||||||
from .pinstate_manager import PinState, PinManager
|
|
||||||
from .key_types.base import KeyBase
|
|
||||||
from .key_types.keycode import KeycodeBase
|
|
||||||
from .pin import Pin
|
|
||||||
from micropython import const
|
|
||||||
|
|
||||||
from nmlkpy import keymap_delta
|
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
|
# Needs to be built in a way where every "key type" only has to know how to modify it's own state
|
||||||
@@ -35,51 +31,110 @@ from nmlkpy import keymap_delta
|
|||||||
# Pins
|
# Pins
|
||||||
# Can be asked for their current event
|
# 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
|
# Pins state is a class that holds current and last, can give you the delta to figure out what to change
|
||||||
_LAYERS_NONE = 0
|
_LAYERS_HOLD_NONE = None
|
||||||
|
_LAYERS_TOGGLE_NONE = 0
|
||||||
|
|
||||||
|
|
||||||
class LayerManager:
|
class LayerManager:
|
||||||
number_of_layers: int = None
|
number_of_layers: int = None
|
||||||
current_layer: int = None
|
current_hold_layer: int = None
|
||||||
|
current_toggle_layer: int = None
|
||||||
|
|
||||||
def __init__(self, number_of_layers: int) -> None:
|
def __init__(self, number_of_layers: int) -> None:
|
||||||
assert number_of_layers is not None
|
assert number_of_layers is not None
|
||||||
assert number_of_layers > 0
|
assert number_of_layers > 0
|
||||||
self.number_of_layers = number_of_layers
|
self.number_of_layers = number_of_layers
|
||||||
self.current_layer = _LAYERS_NONE
|
self.current_hold_layer = _LAYERS_HOLD_NONE
|
||||||
|
self.current_toggle_layer = _LAYERS_TOGGLE_NONE
|
||||||
|
|
||||||
def get_current_layer(self) -> int:
|
def get_current_layer(self) -> int:
|
||||||
return self.current_layer
|
return self.current_hold_layer if self.current_hold_layer is not _LAYERS_HOLD_NONE else self.current_toggle_layer
|
||||||
|
|
||||||
def handle_layer_changes(self, layer):
|
def step(self, changes: LayerDelta):
|
||||||
pass
|
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:
|
class KeymapManager:
|
||||||
keymap: list[list[KeyBase]] = None
|
keymap: list[list[KeyBase]] = None
|
||||||
pin_manager: PinManager = None
|
pin_manager: PinManager = None
|
||||||
layer_manager: LayerManager = 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]):
|
def __init__(self, keymap: list[list[KeyBase]], pins: list[Pin]):
|
||||||
self.keymap = keymap
|
self.keymap = keymap
|
||||||
self.pin_manager = PinManager(pins)
|
self.pin_manager = PinManager(pins)
|
||||||
self.layer_manager = LayerManager(len(keymap))
|
self.layer_manager = LayerManager(len(keymap))
|
||||||
|
self.keys_state = [None for key in keymap[0]]
|
||||||
|
self.keycodes_currently_pressed = set()
|
||||||
|
|
||||||
def step(self) -> KeymapDelta:
|
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()
|
self.pin_manager.step()
|
||||||
pin_changes = self.pin_manager.get_state_delta()
|
pin_changes = self.pin_manager.get_state_delta()
|
||||||
keymap_changes = KeymapDelta()
|
keymap_changes = KeymapDelta()
|
||||||
layer_changes = LayerDelta()
|
layer_changes = LayerDelta()
|
||||||
layer_index = self.layer_manager.get_current_layer()
|
current_layer_index = self.layer_manager.get_current_layer()
|
||||||
for pin_index in range(len(pin_changes)):
|
for pin_index in range(len(pin_changes)):
|
||||||
pin_state = pin_changes[pin_index]
|
pin_state = pin_changes[pin_index]
|
||||||
if pin_state is None:
|
if pin_state is None:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
key = self.keymap[layer_index][pin_index]
|
key = self.keymap[current_layer_index][pin_index]
|
||||||
if isinstance(key, KeycodeBase) or isinstance(key, Hold):
|
custom_state = self.keys_state[pin_index]
|
||||||
key_changes = key.get_delta(pin_state)
|
key_changes = key.calculate_changes(pin_state, custom_state)
|
||||||
keymap_changes.merge_deltas(key_changes.keymap_delta)
|
keymap_changes.merge_deltas(key_changes.keymap_delta)
|
||||||
layer_changes.merge_deltas(key_changes.layer_delta)
|
layer_changes.merge_deltas(key_changes.layer_delta)
|
||||||
|
self.keys_state[pin_index] = key_changes.special_state
|
||||||
|
|
||||||
return keymap_changes
|
return self.createReport(keymap_changes, layer_changes)
|
||||||
|
|||||||
Reference in New Issue
Block a user