Support a simple macro of a sequence of keycodes (basis for SEND_STRING)
This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
									
								
							
							
						
						
									
										10
									
								
								kmk/macros/__init__.py
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										14
									
								
								kmk/macros/simple.py
									
									
									
									
									
										Normal 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)
 | 
			
		||||
@@ -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()
 | 
			
		||||
 
 | 
			
		||||
@@ -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],
 | 
			
		||||
    ],
 | 
			
		||||
]
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user