repeating macros while holding keys

xkb
sezanzeb 4 years ago committed by sezanzeb
parent 8cc63244f5
commit cb7cc67e7f

@ -117,11 +117,11 @@ cd key-mapper && sudo python3 setup.py install
- [x] support timed macros, maybe using some sort of syntax - [x] support timed macros, maybe using some sort of syntax
- [x] add to the AUR, provide .deb file - [x] add to the AUR, provide .deb file
- [x] basic support for gamepads as keyboard and mouse combi - [x] basic support for gamepads as keyboard and mouse combi
- [x] executing a macro forever while holding down the key
- [ ] map D-Pad and Joystick directions as buttons, joystick purpose via config - [ ] map D-Pad and Joystick directions as buttons, joystick purpose via config
- [ ] automatically load presets when devices get plugged in after login - [ ] automatically load presets when devices get plugged in after login
- [ ] option to write hwdb configs for lower level mappings ([Remapping keys using hwdb files](https://www.reddit.com/r/linux_gaming/comments/k3h9qv/remapping_keys_using_hwdb_files/)) - [ ] option to write hwdb configs for lower level mappings ([Remapping keys using hwdb files](https://www.reddit.com/r/linux_gaming/comments/k3h9qv/remapping_keys_using_hwdb_files/))
- [ ] mapping a combined button press to a key - [ ] mapping a combined button press to a key
- [ ] executing a macro while holding down the key
## Tests ## Tests

@ -65,20 +65,34 @@ def handle_keycode(code_to_code, macros, event, uinput):
mapping of linux-keycode to linux-keycode mapping of linux-keycode to linux-keycode
macros : dict macros : dict
mapping of linux-keycode to _Macro objects mapping of linux-keycode to _Macro objects
event : evdev.InputEvent
""" """
if event.value == 2: if event.value == 2:
# button-hold event # button-hold event. Linux seems to create them on its own, no need
# to inject them.
return return
input_keycode = event.code input_keycode = event.code
input_type = event.type input_type = event.type
if input_keycode in macros: if input_keycode in macros:
if event.value == 0:
# key-release event. Tell the macro for that keycode
# that the key is released and let it decide what to with that
# information.
macro = active_macros.get(input_keycode)
# TODO test
if macro is not None:
macro.release_key()
if event.value != 1: if event.value != 1:
# only key-down events trigger macros # only key-down events trigger macros
return return
macro = macros[input_keycode] macro = macros[input_keycode]
active_macros[input_keycode] = macro
# TODO test that holding is true
macro.holding = True
logger.spam( logger.spam(
'got code:%s value:%s, maps to macro %s', 'got code:%s value:%s, maps to macro %s',
input_keycode, input_keycode,

@ -80,6 +80,8 @@ class _Macro:
# all required capabilities, without those of child macros # all required capabilities, without those of child macros
self.capabilities = set() self.capabilities = set()
# TODO test that child_macros is properly populated
self.child_macros = [] self.child_macros = []
def get_capabilities(self): def get_capabilities(self):
@ -100,9 +102,8 @@ class _Macro:
macro will write to this function once executed with `.run()`. macro will write to this function once executed with `.run()`.
""" """
self.handler = handler self.handler = handler
for task_type, task in self.tasks: for macro in self.child_macros:
if task_type == CHILD_MACRO: macro.set_handler(handler)
task.set_handler(handler)
async def run(self): async def run(self):
"""Run the macro.""" """Run the macro."""
@ -113,9 +114,6 @@ class _Macro:
logger.debug('Macro execution stopped') logger.debug('Macro execution stopped')
break break
if task_type == CHILD_MACRO:
task = task.run
coroutine = task() coroutine = task()
if asyncio.iscoroutine(coroutine): if asyncio.iscoroutine(coroutine):
await coroutine await coroutine
@ -125,7 +123,13 @@ class _Macro:
# TODO test # TODO test
self.running = False self.running = False
async def hold(self, macro): def release_key(self):
"""Tell all child macros that the key was released."""
self.holding = False
for macro in self.child_macros:
macro.release_key()
def hold(self, macro):
"""Loops the execution until key release.""" """Loops the execution until key release."""
if not isinstance(macro, _Macro): if not isinstance(macro, _Macro):
raise ValueError( raise ValueError(
@ -134,12 +138,15 @@ class _Macro:
) )
# TODO test # TODO test
def task():
async def task():
while self.holding and self.running: while self.holding and self.running:
await self.run() await macro.run()
self.tasks.append((REPEAT, task)) self.tasks.append((REPEAT, task))
self.child_macros.append(macro)
return self return self
def modify(self, modifier, macro): def modify(self, modifier, macro):
@ -164,9 +171,11 @@ class _Macro:
self.capabilities.add(code) self.capabilities.add(code)
self.child_macros.append(macro)
self.tasks.append((MODIFIER, lambda: self.handler(code, 1))) self.tasks.append((MODIFIER, lambda: self.handler(code, 1)))
self.add_keycode_pause() self.add_keycode_pause()
self.tasks.append((CHILD_MACRO, macro)) self.tasks.append((CHILD_MACRO, macro.run))
self.add_keycode_pause() self.add_keycode_pause()
self.tasks.append((MODIFIER, lambda: self.handler(code, 0))) self.tasks.append((MODIFIER, lambda: self.handler(code, 0)))
self.add_keycode_pause() self.add_keycode_pause()
@ -195,7 +204,10 @@ class _Macro:
) )
for _ in range(repeats): for _ in range(repeats):
self.tasks.append((CHILD_MACRO, macro)) self.tasks.append((CHILD_MACRO, macro.run))
self.child_macros.append(macro)
return self return self
def add_keycode_pause(self): def add_keycode_pause(self):

@ -154,7 +154,10 @@ def push_event(device, event):
class Event: class Event:
"""Event to put into the injector for tests.""" """Event to put into the injector for tests.
fakes evdev.InputEvent
"""
def __init__(self, type, code, value): def __init__(self, type, code, value):
""" """
Paramaters Paramaters

@ -33,6 +33,7 @@ class TestKeycodeMapper(unittest.TestCase):
self.assertFalse(should_map_event_as_btn(EV_ABS, ABS_X)) self.assertFalse(should_map_event_as_btn(EV_ABS, ABS_X))
self.assertFalse(should_map_event_as_btn(EV_REL, REL_X)) self.assertFalse(should_map_event_as_btn(EV_REL, REL_X))
# TODO test for macro holding
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

Loading…
Cancel
Save