#!/usr/bin/python3 # -*- coding: utf-8 -*- # input-remapper - GUI for device specific keyboard mappings # Copyright (C) 2022 sezanzeb # # This file is part of input-remapper. # # input-remapper is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # input-remapper is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with input-remapper. If not, see . import evdev import inputremapper.utils import inputremapper.exceptions from inputremapper.logger import logger DEV_NAME = "input-remapper" DEFAULT_UINPUTS = { # for event codes see linux/input-event-codes.h "keyboard": { evdev.ecodes.EV_KEY: list(evdev.ecodes.KEY.keys() & evdev.ecodes.keys.keys()) }, "gamepad": { evdev.ecodes.EV_KEY: [*range(0x130, 0x13F)], # BTN_SOUTH - BTN_THUMBR evdev.ecodes.EV_ABS: [ *range(0x00, 0x06), *range(0x10, 0x12), ], # 6-axis and 1 hat switch }, "mouse": { evdev.ecodes.EV_KEY: [*range(0x110, 0x118)], # BTN_LEFT - BTN_TASK evdev.ecodes.EV_REL: [*range(0x00, 0x0A)], # all REL axis }, } class UInput(evdev.UInput): def __init__(self, *args, **kwargs): logger.debug(f"creating UInput device: '{kwargs['name']}'") super().__init__(*args, **kwargs) def can_emit(self, event): """check if an event can be emitted by the uinput Wrong events might be injected if the group mappings are wrong """ # TODO check for event value especially for EV_ABS return event[1] in self.capabilities().get(event[0], []) class FrontendUInput: """Uinput which can not actually send events, for use in the frontend""" def __init__(self, *args, events=None, name="py-evdev-uinput", **kwargs): # see https://python-evdev.readthedocs.io/en/latest/apidoc.html#module-evdev.uinput self.events = events self.name = name logger.debug(f"creating fake UInput device: '{self.name}'") def capabilities(self): return self.events class GlobalUInputs: """Manages all uinputs that are shared between all injection processes.""" def __init__(self): self.devices = {} self._uinput_factory = None def __iter__(self): return iter(uinput for _, uinput in self.devices.items()) def prepare(self): """Generate uinputs. This has to be done in the main process before injections start. """ if inputremapper.utils.is_service(): self._uinput_factory = UInput else: self._uinput_factory = FrontendUInput for name, events in DEFAULT_UINPUTS.items(): if name in self.devices.keys(): continue self.devices[name] = self._uinput_factory( name=f"{DEV_NAME} {name}", phys=DEV_NAME, events=events, ) def write(self, event, target_uinput): """write event to target uinput""" uinput = self.get_uinput(target_uinput) if not uinput: raise inputremapper.exceptions.UinputNotAvailable(target_uinput) if not uinput.can_emit(event): raise inputremapper.exceptions.EventNotHandled(event) uinput.write(*event) uinput.syn() def get_uinput(self, name): """UInput with name Or None if there is no uinput with this name. Parameters ---------- name : uniqe name of the uinput device """ if name in self.devices.keys(): return self.devices[name] return None global_uinputs = GlobalUInputs()