from abc import ABC, abstractmethod from .config import OutputConfig class AbstractWorkspaceBackend(ABC): """ Abstract class for workspace backends (i.e. an IPC interface to a compositor / window manager) """ _instance: "AbstractWorkspaceBackend" = None def __init__(self): super().__init__() if self._instance is not None: raise RuntimeError( "Cannot instantiate more than one WorkspaceBackend at a time. Use getBackend() to get the current backend." ) self._instance = self def _scaleToString(self, scale: int) -> str: """Convert the fixed-point scale to a string""" # We do it this way to not lose precision when converting to a float return f"{scale // 100}.{scale % 100}" @abstractmethod async def focusWorkspace(self, index: int): """Focus a workspace in the compositor based off of its compositor index""" pass @abstractmethod async def moveWorkspace(self, index: int, output: str): """Move a workspace to a given output""" pass @abstractmethod async def focusOutput(self, output: str): """Focus an output in the compositor""" pass @abstractmethod async def moveContainer(self, workspace: int): """Move the focused container to a given workspace compositor index""" pass @abstractmethod async def configureOutput(self, output: str, config: OutputConfig): """Configure an output""" pass @abstractmethod async def disableOutput(self, output: str): """Disable an output""" pass @abstractmethod def onWorkspaceChange(self, callback): """Register a callback to be called when the workspace changes""" pass @abstractmethod def onOutputChange(self, callback): """Register a callback to be called when the output configuration changes""" pass @abstractmethod def onModeChange(self, callback): """Register a callback to be called when the binding mode changes, in compositors that support it""" pass @abstractmethod async def getOutputs(self) -> list[OutputAdapter]: """Get the currently connected outputs""" @abstractmethod @property def name(self) -> str: """The name of the backend""" pass def getBackend() -> AbstractWorkspaceBackend: """ Get the currently-active workspace backend """ if AbstractWorkspaceBackend._instance is None: raise RuntimeError( "No workspace backend is currently active. Please instantiate one." ) return AbstractWorkspaceBackend._instance class WorkspaceAdapter: """ Common interface for workspace backends that represents the result of a workspace query. Only contains information that the workspace manager needs to know. """ def __init__( self, index: int, output: str, focused: bool, visible: bool, urgent: bool ): self.index = index self.output = output self.focused = focused self.visible = visible self.urgent = urgent class RectAdapter: """ Common interface for workspace backends that represents a rectangle. """ def __init__(self, x: int, y: int, width: int, height: int): self.x = x self.y = y self.width = width self.height = height class OutputAdapter: """ Common interface for workspace backends that represents the result of an output query. Only contains information that the workspace manager needs to know. """ def __init__( self, name: str, active: bool, rect: RectAdapter, scale: int, transform: str, focused: bool, current_workspace: int, make: str, model: str, serial: str, ): self.name = name self.active = active self.rect = rect self.scale = scale self.transform = transform self.focused = focused self.current_workspace = current_workspace self.make = make self.model = model self.serial = serial