chore(repo): baseline du fork input-remapper (upstream v2.2.1)
Le dépôt ne contenait que README.md ; ajout de l'intégralité du code fonctionnel du fork comme socle stable de la branche de release. L'état runtime d'orchestration (.ideai/) est exclu du suivi. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
213
inputremapper/injection/mapping_handlers/mapping_handler.py
Normal file
213
inputremapper/injection/mapping_handlers/mapping_handler.py
Normal file
@ -0,0 +1,213 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# input-remapper - GUI for device specific keyboard mappings
|
||||
# Copyright (C) 2025 sezanzeb <b8x45ygc9@mozmail.com>
|
||||
#
|
||||
# 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 <https://www.gnu.org/licenses/>.
|
||||
"""Provides protocols for mapping handlers
|
||||
|
||||
*** The architecture behind mapping handlers ***
|
||||
|
||||
Handling an InputEvent is done in 3 steps:
|
||||
1. Input Event Handling
|
||||
A MappingHandler that does Input event handling receives Input Events directly
|
||||
from the EventReader.
|
||||
To do so it must implement the MappingHandler protocol.
|
||||
An MappingHandler may handle multiple events (InputEvent.type_and_code)
|
||||
|
||||
2. Event Transformation
|
||||
The event gets transformed as described by the mapping.
|
||||
e.g.: combining multiple events to a single one
|
||||
transforming EV_ABS to EV_REL
|
||||
macros
|
||||
...
|
||||
Multiple transformations may get chained
|
||||
|
||||
3. Event Injection
|
||||
The transformed event gets injected to a global_uinput
|
||||
|
||||
MappingHandlers can implement one or more of these steps.
|
||||
|
||||
Overview of implemented handlers and the steps they implement:
|
||||
|
||||
Step 1:
|
||||
- HierarchyHandler
|
||||
|
||||
Step 1 and 2:
|
||||
- CombinationHandler
|
||||
- AbsToBtnHandler
|
||||
- RelToBtnHandler
|
||||
|
||||
Step 1, 2 and 3:
|
||||
- AbsToRelHandler
|
||||
- NullHandler
|
||||
|
||||
Step 2 and 3:
|
||||
- KeyHandler
|
||||
- MacroHandler
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import enum
|
||||
from typing import Dict, Protocol, Set, Optional, List
|
||||
|
||||
import evdev
|
||||
|
||||
from inputremapper.configs.input_config import InputCombination, InputConfig
|
||||
from inputremapper.configs.mapping import Mapping
|
||||
from inputremapper.exceptions import MappingParsingError
|
||||
from inputremapper.injection.global_uinputs import GlobalUInputs
|
||||
from inputremapper.input_event import InputEvent
|
||||
from inputremapper.logging.logger import logger
|
||||
|
||||
|
||||
class EventListener(Protocol):
|
||||
async def __call__(self, event: evdev.InputEvent) -> None: ...
|
||||
|
||||
|
||||
class ContextProtocol(Protocol):
|
||||
"""The parts from context needed for handlers."""
|
||||
|
||||
listeners: Set[EventListener]
|
||||
|
||||
def get_forward_uinput(self, origin_hash) -> evdev.UInput:
|
||||
pass
|
||||
|
||||
|
||||
class NotifyCallback(Protocol):
|
||||
"""Type signature of MappingHandler.notify
|
||||
|
||||
return True if the event was actually taken care of
|
||||
"""
|
||||
|
||||
def __call__(
|
||||
self,
|
||||
event: InputEvent,
|
||||
source: evdev.InputDevice,
|
||||
suppress: bool = False,
|
||||
) -> bool: ...
|
||||
|
||||
|
||||
class HandlerEnums(enum.Enum):
|
||||
# converting to btn
|
||||
abs2btn = enum.auto()
|
||||
rel2btn = enum.auto()
|
||||
|
||||
macro = enum.auto()
|
||||
key = enum.auto()
|
||||
|
||||
# converting to "analog"
|
||||
btn2rel = enum.auto()
|
||||
rel2rel = enum.auto()
|
||||
abs2rel = enum.auto()
|
||||
|
||||
btn2abs = enum.auto()
|
||||
rel2abs = enum.auto()
|
||||
abs2abs = enum.auto()
|
||||
|
||||
# special handlers
|
||||
combination = enum.auto()
|
||||
hierarchy = enum.auto()
|
||||
axisswitch = enum.auto()
|
||||
disable = enum.auto()
|
||||
|
||||
|
||||
class MappingHandler:
|
||||
mapping: Mapping
|
||||
# all input events this handler cares about
|
||||
# should always be a subset of mapping.input_combination
|
||||
input_configs: List[InputConfig]
|
||||
_sub_handler: Optional[MappingHandler]
|
||||
|
||||
# https://bugs.python.org/issue44807
|
||||
def __init__(
|
||||
self,
|
||||
combination: InputCombination,
|
||||
mapping: Mapping,
|
||||
global_uinputs: GlobalUInputs,
|
||||
**_,
|
||||
) -> None:
|
||||
"""Initialize the handler
|
||||
|
||||
Parameters
|
||||
----------
|
||||
combination
|
||||
the combination from sub_handler.wrap_with()
|
||||
mapping
|
||||
"""
|
||||
self.mapping = mapping
|
||||
self.input_configs = list(combination)
|
||||
self._sub_handler = None
|
||||
self.global_uinputs = global_uinputs
|
||||
|
||||
def notify(
|
||||
self,
|
||||
event: InputEvent,
|
||||
source: evdev.InputDevice,
|
||||
suppress: bool = False,
|
||||
) -> bool:
|
||||
"""Notify this handler about an incoming event.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
event
|
||||
The newest event that came from `source`, and that should be mapped to
|
||||
something else
|
||||
source
|
||||
Where `event` comes from
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def reset(self) -> None:
|
||||
"""Reset the state of the handler e.g. release any buttons."""
|
||||
raise NotImplementedError
|
||||
|
||||
def needs_wrapping(self) -> bool:
|
||||
"""If this handler needs to be wrapped in another MappingHandler."""
|
||||
return len(self.wrap_with()) > 0
|
||||
|
||||
def needs_ranking(self) -> bool:
|
||||
"""If this handler needs ranking and wrapping with a HierarchyHandler."""
|
||||
return False
|
||||
|
||||
def rank_by(self) -> Optional[InputCombination]:
|
||||
"""The combination for which this handler needs ranking."""
|
||||
|
||||
def wrap_with(self) -> Dict[InputCombination, HandlerEnums]:
|
||||
"""A dict of InputCombination -> HandlerEnums.
|
||||
|
||||
for each InputCombination this handler should be wrapped
|
||||
with the given MappingHandler.
|
||||
"""
|
||||
return {}
|
||||
|
||||
def set_sub_handler(self, handler: MappingHandler) -> None:
|
||||
"""Give this handler a sub_handler."""
|
||||
self._sub_handler = handler
|
||||
|
||||
def occlude_input_event(self, input_config: InputConfig) -> None:
|
||||
"""Remove the config from self.input_configs."""
|
||||
if not self.input_configs:
|
||||
logger.debug_mapping_handler(self)
|
||||
raise MappingParsingError(
|
||||
"Cannot remove a non existing config", mapping_handler=self
|
||||
)
|
||||
|
||||
# should be called for each event a wrapping-handler
|
||||
# has in its input_configs InputCombination
|
||||
self.input_configs.remove(input_config)
|
||||
|
||||
def get_children(self) -> List[MappingHandler]:
|
||||
return []
|
||||
Reference in New Issue
Block a user