"""L1 Backend 抽象层基类:定义 BackendProtocol 与 WindowGeometry。 所有具体后端(xdotool / OpenCV / CDP)均实现 BackendProtocol, 上层 Actions / Flow 通过 Protocol 解耦具体实现,便于切换或回滚。 """ from __future__ import annotations from abc import ABC, abstractmethod from dataclasses import dataclass @dataclass class WindowGeometry: """窗口几何信息(绝对坐标 + 宽高)。 所有字段默认值 0,避免构造时必填字段顺序问题。 """ x: int = 0 y: int = 0 width: int = 0 height: int = 0 class BackendProtocol(ABC): """UI 自动化后端协议(L1 抽象)。 子类必须实现所有 abstract 方法;capabilities 属性提供该后端 支持的能力集合(如 click / type / key / screenshot / window)。 """ @abstractmethod async def click(self, x: int, y: int) -> None: """在绝对坐标 (x, y) 处左键单击。""" @abstractmethod async def type_text(self, text: str) -> None: """输入文本(不解析修饰键组合,原样输入)。""" @abstractmethod async def key_press(self, key: str, repeat: int = 1) -> None: """按键,支持 repeat 重复。""" @abstractmethod async def screenshot(self) -> bytes: """截图并返回 PNG 字节流(不落盘)。""" @abstractmethod async def get_window_geometry(self, window_title: str) -> WindowGeometry: """按窗口标题查找窗口并返回几何信息。""" @abstractmethod async def activate_window(self, window_title: str) -> None: """激活指定窗口(使其获得焦点)。""" async def is_window_active(self, window_title: str) -> bool: """校验指定标题的窗口是否是当前活动窗口(拥有焦点)。 默认实现返回 True(信任 activate_window 的结果)。 XdotoolBackend 覆盖为真正调用 getactivewindow 对比。 用于纯坐标模式下 _verify_session_still_open 校验窗口焦点, 避免 cache_hit 分支命中已失焦的会话。 """ return True @property def capabilities(self) -> set[str]: """返回后端支持的能力集合。子类可覆盖。""" return {"click", "type", "key", "screenshot", "window"}