Support a simple macro of a sequence of keycodes (basis for SEND_STRING)

This commit is contained in:
Josh Klar 2018-09-30 18:03:43 -07:00
parent 99573de217
commit bdd4f86472
No known key found for this signature in database
GPG Key ID: 220F99BD7DB7A99E
6 changed files with 134 additions and 10 deletions

View File

@ -9,6 +9,9 @@ KEY_DOWN_EVENT = const(2)
INIT_FIRMWARE_EVENT = const(3)
NEW_MATRIX_EVENT = const(4)
HID_REPORT_EVENT = const(5)
KEYCODE_UP_EVENT = const(6)
KEYCODE_DOWN_EVENT = const(7)
MACRO_COMPLETE_EVENT = const(8)
logger = logging.getLogger(__name__)
@ -39,6 +42,28 @@ def key_down_event(row, col):
}
def keycode_up_event(keycode):
'''
Press a key by Keycode object, bypassing the keymap. Used mostly for
macros.
'''
return {
'type': KEYCODE_UP_EVENT,
'keycode': keycode,
}
def keycode_down_event(keycode):
'''
Release a key by Keycode object, bypassing the keymap. Used mostly for
macros.
'''
return {
'type': KEYCODE_DOWN_EVENT,
'keycode': keycode,
}
def new_matrix_event(matrix):
return {
'type': NEW_MATRIX_EVENT,
@ -52,6 +77,13 @@ def hid_report_event():
}
def macro_complete_event(macro):
return {
'type': MACRO_COMPLETE_EVENT,
'macro': macro,
}
def matrix_changed(new_matrix):
def _key_pressed(dispatch, get_state):
state = get_state()
@ -94,4 +126,12 @@ def matrix_changed(new_matrix):
except ImportError:
logger.warning('Tried to reset to bootloader, but not supported on this chip?')
while get_state().macros_pending:
macro = get_state().macros_pending[0]
for event in macro():
dispatch(event)
dispatch(macro_complete_event(macro))
return _key_pressed

View File

@ -4,9 +4,11 @@ import sys
from kmk.common.consts import DiodeOrientation
from kmk.common.event_defs import (HID_REPORT_EVENT, INIT_FIRMWARE_EVENT,
KEY_DOWN_EVENT, KEY_UP_EVENT,
NEW_MATRIX_EVENT)
KEYCODE_DOWN_EVENT, KEYCODE_UP_EVENT,
MACRO_COMPLETE_EVENT, NEW_MATRIX_EVENT)
from kmk.common.internal_keycodes import process_internal_key_event
from kmk.common.keycodes import FIRST_KMK_INTERNAL_KEYCODE, Keycodes
from kmk.macros import KMKMacro
class ReduxStore:
@ -52,6 +54,7 @@ class ReduxStore:
class InternalState:
modifiers_pressed = frozenset()
keys_pressed = frozenset()
macros_pending = []
keymap = []
row_pins = []
col_pins = []
@ -133,6 +136,20 @@ def kmk_reducer(state=None, action=None, logger=None):
matrix=action['matrix'],
)
if action['type'] == KEYCODE_UP_EVENT:
return state.update(
keys_pressed=frozenset(
key for key in state.keys_pressed if key != action['keycode']
),
)
if action['type'] == KEYCODE_DOWN_EVENT:
return state.update(
keys_pressed=(
state.keys_pressed | {action['keycode']}
),
)
if action['type'] == KEY_UP_EVENT:
row = action['row']
col = action['col']
@ -144,6 +161,14 @@ def kmk_reducer(state=None, action=None, logger=None):
if not changed_key:
return state
if isinstance(changed_key, KMKMacro):
if changed_key.keyup:
return state.update(
macros_pending=state.macros_pending + [changed_key.keyup],
)
return state
newstate = state.update(
keys_pressed=frozenset(
key for key in state.keys_pressed if key != changed_key
@ -166,6 +191,14 @@ def kmk_reducer(state=None, action=None, logger=None):
if not changed_key:
return state
if isinstance(changed_key, KMKMacro):
if changed_key.keydown:
return state.update(
macros_pending=state.macros_pending + [changed_key.keydown],
)
return state
newstate = state.update(
keys_pressed=(
state.keys_pressed | {changed_key}
@ -196,6 +229,14 @@ def kmk_reducer(state=None, action=None, logger=None):
if action['type'] == HID_REPORT_EVENT:
return state
if action['type'] == MACRO_COMPLETE_EVENT:
return state.update(
macros_pending=[
m for m in state.macros_pending
if m != action['macro']
],
)
# On unhandled events, log and do not mutate state
logger.warning('Unhandled event! Returning state unmodified.')
return state

10
kmk/macros/__init__.py Normal file
View File

@ -0,0 +1,10 @@
class KMKMacro:
def __init__(self, keydown=None, keyup=None):
self.keydown = keydown
self.keyup = keyup
def on_keydown(self):
return self.keydown() if self.keydown else None
def on_keyup(self):
return self.keyup() if self.keyup else None

14
kmk/macros/simple.py Normal file
View File

@ -0,0 +1,14 @@
from kmk.common.event_defs import (hid_report_event, keycode_down_event,
keycode_up_event)
from kmk.macros import KMKMacro
def simple_key_sequence(seq):
def _simple_key_sequence():
for key in seq:
yield keycode_down_event(key)
yield hid_report_event()
yield keycode_up_event(key)
yield hid_report_event()
return KMKMacro(keydown=_simple_key_sequence)

View File

@ -73,7 +73,6 @@ class HIDHelper:
# device, or keys will get stuck (mostly when releasing
# media/consumer keys)
self.send()
delay(10)
self.report_device[0] = needed_reporting_device
@ -99,6 +98,17 @@ class HIDHelper:
self.logger.debug('Sending HID report: {}'.format(self._evt))
self._hid.send(self._evt)
# Without this delay, events get clobbered and you'll likely end up with
# a string like `heloooooooooooooooo` rather than `hello`. This number
# may be able to be shrunken down. It may also make sense to use
# time.sleep_us or time.sleep_ms or time.sleep (platform dependent)
# on non-Pyboards.
#
# It'd be real awesome if pyb.USB_HID.send/recv would support
# uselect.poll or uselect.select to more safely determine when
# it is safe to write to the host again...
delay(10)
return self
def send_string(self, message):
@ -128,13 +138,6 @@ class HIDHelper:
self.add_key(kc)
self.send()
# Without this delay, events get clobbered and you'll likely end up with
# a string like `heloooooooooooooooo` rather than `hello`. This number
# may be able to be shrunken down. It may also make sense to use
# time.sleep_us or time.sleep_ms or time.sleep (platform dependent)
# on non-Pyboards.
delay(10)
# Release all keys or we'll forever hold whatever the last keyadd was
self.clear_all()
self.send()

View File

@ -3,6 +3,7 @@ import machine
from kmk.common.consts import DiodeOrientation
from kmk.common.keycodes import KC
from kmk.entrypoints.handwire.pyboard import main
from kmk.macros.simple import simple_key_sequence
p = machine.Pin.board
cols = (p.X10, p.X11, p.X12)
@ -10,6 +11,21 @@ rows = (p.X1, p.X2, p.X3)
diode_orientation = DiodeOrientation.COLUMNS
MACRO_TEST_STRING = simple_key_sequence([
KC.LSHIFT(KC.H),
KC.E,
KC.L,
KC.L,
KC.O,
KC.SPACE,
KC.LSHIFT(KC.K),
KC.LSHIFT(KC.M),
KC.LSHIFT(KC.K),
KC.EXCLAIM,
])
keymap = [
[
[KC.MO(1), KC.GESC, KC.RESET],
@ -24,6 +40,6 @@ keymap = [
[
[KC.VOLU, KC.MUTE, KC.Z],
[KC.TRNS, KC.PIPE, KC.MEDIA_PLAY_PAUSE],
[KC.VOLD, KC.P, KC.Q],
[KC.VOLD, KC.P, MACRO_TEST_STRING],
],
]