# -*- coding: utf-8 -*- # input-remapper - GUI for device specific keyboard mappings # Copyright (C) 2025 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 . """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()