# -*- 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 . """Abstractions to detect the currently focused window across compositors. Focus detection is implemented as a registry of backends. To add support for a new compositor, implement a new ``FocusBackend`` and append its class to ``focus_backend_classes`` (ordered by priority). ``select_backend`` returns the first backend that reports itself as available, so the most specific backend for the current session wins. """ from __future__ import annotations from typing import List, Type from inputremapper.focus.backends.hyprland import HyprlandFocusBackend from inputremapper.focus.backends.null import NullBackend from inputremapper.focus.backends.sway import SwayFocusBackend from inputremapper.focus.backends.wlr_foreign_toplevel import ( WlrForeignToplevelBackend, ) from inputremapper.focus.backends.xorg import XorgFocusBackend from inputremapper.focus.focus_backend import FocusBackend, FocusEvent from inputremapper.logging.logger import logger # Re-exported for convenience / backwards compatible imports. __all__ = [ "FocusEvent", "FocusBackend", "focus_backend_classes", "select_backend", ] # Ordered by priority. The first one that reports is_available() wins. # SWAYSOCK -> HYPRLAND_INSTANCE_SIGNATURE -> wlr-foreign-toplevel -> DISPLAY -> Null focus_backend_classes: List[Type[FocusBackend]] = [ SwayFocusBackend, HyprlandFocusBackend, WlrForeignToplevelBackend, XorgFocusBackend, NullBackend, ] def select_backend() -> FocusBackend: """Instantiate the first available focus backend by priority.""" for backend_class in focus_backend_classes: try: available = backend_class.is_available() except Exception as error: # pragma: no cover - defensive logger.debug( "Backend %s failed its availability check: %s", backend_class.__name__, error, ) available = False if available: logger.info("Using focus backend %s", backend_class.name) return backend_class() # NullBackend.is_available() always returns True, so this is unreachable # as long as it stays in the registry. logger.warning("No focus backend available, falling back to NullBackend") return NullBackend()