feat(app-binding): lier les presets aux applications

Ajoute la configuration des associations application/preset, le service de suivi du focus, l'intégration GUI et les fichiers d'installation nécessaires.

Ajoute les tests unitaires ciblés des bindings, du service de focus, des migrations et des composants GUI associés.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-19 23:52:34 +02:00
parent 0ab77d2a64
commit 9ef2c1439c
42 changed files with 4353 additions and 12 deletions

View File

@ -0,0 +1,88 @@
# -*- 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/>.
"""The base class and event for all focus backends.
This lives in its own module to avoid a circular import between the backend
registry (``focus_watcher``) and the concrete backends.
"""
from __future__ import annotations
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Callable, Optional
@dataclass(frozen=True)
class FocusEvent:
"""A normalized description of the currently focused window.
Attributes
----------
app_id
The canonical application identifier (WM_CLASS on X11, app_id on
Wayland). This is the value bindings are matched against. May be empty
for transient focus changes (menus, popups, no focus).
title
The human readable window title (_NET_WM_NAME / title).
backend
The name of the backend that produced this event.
"""
app_id: str
title: str
backend: str
# Type alias for the focus callback passed to FocusBackend.start.
OnFocus = Callable[[FocusEvent], None]
class FocusBackend(ABC):
"""Detects the focused window for a specific compositor / display server.
Backends integrate into the GLib main loop (e.g. via ``GLib.io_add_watch``
on a file descriptor) and call the ``on_focus`` callback whenever the
focused window changes.
"""
# A short, human readable identifier, also used as FocusEvent.backend.
name: str = "base"
@staticmethod
@abstractmethod
def is_available() -> bool:
"""Whether this backend can run in the current session."""
raise NotImplementedError
@abstractmethod
def start(self, on_focus: OnFocus) -> None:
"""Begin watching for focus changes, invoking on_focus on each change."""
raise NotImplementedError
@abstractmethod
def stop(self) -> None:
"""Stop watching and release all resources."""
raise NotImplementedError
@abstractmethod
def get_current(self) -> Optional[FocusEvent]:
"""Return the currently focused window, or None if unavailable."""
raise NotImplementedError