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:
100
inputremapper/bin/input_remapper_focus_service.py
Normal file
100
inputremapper/bin/input_remapper_focus_service.py
Normal file
@ -0,0 +1,100 @@
|
||||
# -*- 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/>.
|
||||
|
||||
"""Starts the user-level focus-service for focus-driven preset binding."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from argparse import ArgumentParser
|
||||
from typing import Optional
|
||||
|
||||
from inputremapper.configs.global_config import GlobalConfig
|
||||
from inputremapper.daemon import Daemon, DaemonProxy
|
||||
from inputremapper.logging.logger import logger
|
||||
|
||||
|
||||
class InputRemapperFocusServiceBin:
|
||||
@staticmethod
|
||||
def _connect_daemon(retries: int = 10, delay: float = 1.0) -> Optional[DaemonProxy]:
|
||||
"""Connect to the root daemon over the system bus, without pkexec.
|
||||
|
||||
The focus-service runs as a background user service and must never
|
||||
trigger a polkit prompt, so it connects with fallback=False and simply
|
||||
retries until the daemon (started separately, e.g. via its systemd
|
||||
unit) becomes available.
|
||||
"""
|
||||
for attempt in range(retries):
|
||||
daemon = Daemon.connect(fallback=False)
|
||||
if daemon is not None:
|
||||
return daemon
|
||||
logger.info(
|
||||
"Daemon not available yet (attempt %d/%d), retrying in %.1fs",
|
||||
attempt + 1,
|
||||
retries,
|
||||
delay,
|
||||
)
|
||||
time.sleep(delay)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def main() -> None:
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--debug",
|
||||
action="store_true",
|
||||
dest="debug",
|
||||
help="Displays additional debug information",
|
||||
default=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--hide-info",
|
||||
action="store_true",
|
||||
dest="hide_info",
|
||||
help="Don't display version information",
|
||||
default=False,
|
||||
)
|
||||
|
||||
options = parser.parse_args(sys.argv[1:])
|
||||
|
||||
logger.update_verbosity(options.debug)
|
||||
|
||||
if not options.hide_info:
|
||||
logger.log_info("input-remapper-focus-service")
|
||||
|
||||
if os.getuid() == 0:
|
||||
logger.warning(
|
||||
"The focus-service is meant to run as the logged-in user, not root"
|
||||
)
|
||||
|
||||
# Imported here so log verbosity is configured first.
|
||||
from inputremapper.focus.focus_service import FocusService
|
||||
from inputremapper.focus.focus_watcher import select_backend
|
||||
|
||||
global_config = GlobalConfig()
|
||||
backend = select_backend()
|
||||
|
||||
daemon = InputRemapperFocusServiceBin._connect_daemon()
|
||||
if daemon is None:
|
||||
logger.error("Could not connect to the input-remapper daemon, exiting")
|
||||
sys.exit(1)
|
||||
|
||||
focus_service = FocusService(global_config, backend, daemon)
|
||||
focus_service.run()
|
||||
@ -87,6 +87,12 @@ class InputRemapperGtkBin:
|
||||
message_broker = MessageBroker()
|
||||
|
||||
global_config = GlobalConfig()
|
||||
global_config.load_config()
|
||||
|
||||
# Start the user-level focus-service if focus-driven application binding is
|
||||
# enabled, mirroring how the reader-service is launched below.
|
||||
if global_config.get_app_binding_enabled():
|
||||
InputRemapperGtkBin.start_focus_service()
|
||||
|
||||
# Create the ReaderClient before we start the reader-service, otherwise the
|
||||
# privileged service creates and owns those pipes, and then they cannot be accessed
|
||||
@ -143,6 +149,14 @@ class InputRemapperGtkBin:
|
||||
logger.error(e)
|
||||
sys.exit(11)
|
||||
|
||||
@staticmethod
|
||||
def start_focus_service():
|
||||
from inputremapper.gui.focus_service_client import (
|
||||
ensure_focus_service_running,
|
||||
)
|
||||
|
||||
ensure_focus_service_running()
|
||||
|
||||
@staticmethod
|
||||
def stop(daemon, controller):
|
||||
if isinstance(daemon, Daemon):
|
||||
|
||||
Reference in New Issue
Block a user