From 2cf10cf0a675ea7bb6d944d261e56961c5e690cf Mon Sep 17 00:00:00 2001 From: Nicolas Adenis-Lamarre Date: Sat, 23 Aug 2025 18:08:27 +0200 Subject: [PATCH] keyboardtopads: add combination of keys to generate hotkeys Signed-off-by: Nicolas Adenis-Lamarre --- .../pads/keyboardtopads/keyboardToPads.py | 101 +++++++++++++----- .../keyboardtopads/keyboardToPadsLauncher.sh | 1 + 2 files changed, 74 insertions(+), 28 deletions(-) diff --git a/package/batocera/controllers/pads/keyboardtopads/keyboardToPads.py b/package/batocera/controllers/pads/keyboardtopads/keyboardToPads.py index f64241449ac..53bf965b342 100755 --- a/package/batocera/controllers/pads/keyboardtopads/keyboardToPads.py +++ b/package/batocera/controllers/pads/keyboardtopads/keyboardToPads.py @@ -11,13 +11,31 @@ import re from pathlib import Path +class Loader(yaml.SafeLoader): + pass + +def construct_mapping(self, node): + pairs = self.construct_pairs(node, deep=True) + try: + return dict(pairs) + except TypeError: + rv = {} + for key, value in pairs: + if isinstance(key, list): + key = tuple(key) + rv[key] = value + return rv + +Loader.construct_mapping = construct_mapping +Loader.add_constructor('tag:yaml.org,2002:map', Loader.construct_mapping) + class KeyboardController: def generateRules(self, configFile) -> str: rules = "" config = {} with open(configFile, 'r') as file: - config = yaml.safe_load(file) + config = yaml.load(file, Loader=Loader) if "target_devices" not in config: raise Exception("missing target_devices entry in config file") @@ -38,10 +56,24 @@ def generateRules(self, configFile) -> str: rules += "ENV{ID_INPUT_JOYSTICK}=\"1\", ENV{ID_INPUT_KEYBOARD}=\"0\", ENV{ID_INPUT_KEY}=\"0\"\n" return rules + def checkMapping(self, mapping): + mappingParts = mapping.split(':') + if len(mappingParts) not in [2, 3]: + raise Exception("invalid key "+mapping) + if mappingParts[0] not in ["key", "abs", "btn"]: + raise Exception("invalid key type "+mapping) + + def checkValue(self, value): + valueParts = value.split(':') + if len(valueParts) not in [2, 3]: + raise Exception("invalid key "+value) + if valueParts[0] not in ["key", "abs", "btn"]: + raise Exception("invalid key type "+value) + def generateCommand(self, configFile, inputFile) -> list[str]: config = {} with open(configFile, 'r') as file: - config = yaml.safe_load(file) + config = yaml.load(file, Loader=Loader) # specific load for mapping as array (combination of keys ["key:a", "key:b"] : "key:exit") cmd = ["evsieve", "--input", inputFile, "persist=exit" ] @@ -59,33 +91,46 @@ def generateCommand(self, configFile, inputFile) -> list[str]: # check mappings and values for mapping, value in target_device["mapping"].items(): - mappingParts = mapping.split(':') - valueParts = value.split(':') - if len(mappingParts) not in [2, 3]: - raise Exception("invalid key "+mapping) - if len(valueParts) not in [2, 3]: - raise Exception("invalid key "+value) - if mappingParts[0] not in ["key", "abs", "btn"]: - raise Exception("invalid key type "+mapping) - if valueParts[0] not in ["key", "abs", "btn"]: - raise Exception("invalid key type "+value) - - # add mappings + if type(mapping) is tuple: + for t in mapping: + self.checkMapping(t) + else: + self.checkMapping(mapping) + self.checkValue(value) + + # add key combinations first, to not block them (do, doing a first loop on all device for key combinations) + for target_device in config["target_devices"]: + for mapping_tuple, value in target_device["mapping"].items(): + if type(mapping_tuple) is tuple: + cmd.extend(["--hook"]) + for mapping in mapping_tuple: + mappingParts = mapping.split(':') + mappingtypename = mappingParts[0] + ":" + mappingParts[1] + valueParts = value.split(':') + valuetypename = valueParts[0] + ":" + valueParts[1] + cmd.extend(["{}".format(mappingtypename)]) + cmd.extend(["send-key={}".format(valuetypename)]) # should be hotkeys only + + # redo the loop to add the keys, without the checks this time + for target_device in config["target_devices"]: + # simple keys for mapping, value in target_device["mapping"].items(): - mappingParts = mapping.split(':') - mappingtypename = mappingParts[0] + ":" + mappingParts[1] - valueParts = value.split(':') - valuetypename = valueParts[0] + ":" + valueParts[1] - cmd.extend(["--block", "{}:2".format(mappingtypename)]) # block the repeat keys - - if len(mappingParts) == 2 and len(valueParts) == 2: - cmd.extend(["--map", "yield", mapping, value]) - elif len(mappingParts) == 2 and len(valueParts) == 3: - cmd.extend(["--map", "yield", "{}:1".format(mapping), value]) - cmd.extend(["--map", "yield", "{}:0".format(mapping), "{}:0".format(valuetypename)]) - elif len(mappingParts) == 3 and len(valueParts) == 2: - cmd.extend(["--map", "yield", mapping, "{}:1".format(value)]) - cmd.extend(["--map", "yield", "{}:0".format(mappingtypename), "{}:0".format(value)]) + if type(mapping) is not tuple: + # simple keys + mappingParts = mapping.split(':') + mappingtypename = mappingParts[0] + ":" + mappingParts[1] + valueParts = value.split(':') + valuetypename = valueParts[0] + ":" + valueParts[1] + cmd.extend(["--block", "{}:2".format(mappingtypename)]) # block the repeat keys + + if len(mappingParts) == 2 and len(valueParts) == 2: + cmd.extend(["--map", "yield", mapping, value]) + elif len(mappingParts) == 2 and len(valueParts) == 3: + cmd.extend(["--map", "yield", "{}:1".format(mapping), value]) + cmd.extend(["--map", "yield", "{}:0".format(mapping), "{}:0".format(valuetypename)]) + elif len(mappingParts) == 3 and len(valueParts) == 2: + cmd.extend(["--map", "yield", mapping, "{}:1".format(value)]) + cmd.extend(["--map", "yield", "{}:0".format(mappingtypename), "{}:0".format(value)]) # output device cmd.extend(["--output", "name={}".format(target_device["name"]), "device-id=ba10:ce8a"]) diff --git a/package/batocera/controllers/pads/keyboardtopads/keyboardToPadsLauncher.sh b/package/batocera/controllers/pads/keyboardtopads/keyboardToPadsLauncher.sh index 5480fd53f91..d7004c0db99 100755 --- a/package/batocera/controllers/pads/keyboardtopads/keyboardToPadsLauncher.sh +++ b/package/batocera/controllers/pads/keyboardtopads/keyboardToPadsLauncher.sh @@ -91,6 +91,7 @@ else else # no config file found, do 1 to 1 mapping to enable keyboard # map the same, but this one will have ID_INPUT_KEYBOARD set to 1 + echo "no config file found" >&2 if test -t 1 # check output is a tty then evsieve --input "${1}" --output name="Default virtual keyboard"