from typing import Optional
from maa.define import MaaWin32InputMethodEnum, MaaWin32ScreencapMethodEnum
from maa.toolkit import DesktopWindow, Toolkit
from maa.controller import Win32Controller
from maa_mcp.core import (
mcp,
object_registry,
controller_info_registry,
ControllerInfo,
ControllerType,
)
# 截图/鼠标/键盘方法名称到枚举值的映射
_SCREENCAP_METHOD_MAP = {
"FramePool": MaaWin32ScreencapMethodEnum.FramePool,
"PrintWindow": MaaWin32ScreencapMethodEnum.PrintWindow,
"GDI": MaaWin32ScreencapMethodEnum.GDI,
"DXGI_DesktopDup_Window": MaaWin32ScreencapMethodEnum.DXGI_DesktopDup_Window,
"ScreenDC": MaaWin32ScreencapMethodEnum.ScreenDC,
"DXGI_DesktopDup": MaaWin32ScreencapMethodEnum.DXGI_DesktopDup,
}
_MOUSE_METHOD_MAP = {
"PostMessage": MaaWin32InputMethodEnum.PostMessage,
"PostMessageWithCursorPos": MaaWin32InputMethodEnum.PostMessageWithCursorPos,
"Seize": MaaWin32InputMethodEnum.Seize,
}
_KEYBOARD_METHOD_MAP = {
"PostMessage": MaaWin32InputMethodEnum.PostMessage,
"Seize": MaaWin32InputMethodEnum.Seize,
}
@mcp.tool(
name="find_window_list",
description="""
扫描并枚举当前系统中所有可用的窗口。
返回值类型:
- 窗口名称列表
重要约束:
当返回多个窗口时,必须立即暂停执行流程,向用户展示窗口列表并等待用户明确选择。
严禁在未获得用户确认的情况下自动选择窗口。
""",
)
def find_window_list() -> list[str]:
window_list = Toolkit.find_desktop_windows()
for window in window_list:
object_registry.register_by_name(window.window_name, window)
return [window.window_name for window in window_list if window.window_name]
@mcp.tool(
name="connect_window",
description="""
建立与指定窗口的连接,获取窗口控制器实例。
参数:
- window_name: 窗口名称,需通过 find_window_list() 获取
- screencap_method: 截图方式,默认 "FramePool"(一般无需修改)
- mouse_method: 鼠标输入方式,默认 "PostMessage"(一般无需修改)
- keyboard_method: 键盘输入方式,默认 "PostMessage"(一般无需修改)
返回值:
- 成功:返回窗口控制器 ID(字符串),用于后续所有窗口操作
- 失败:返回 None
说明:
窗口控制器 ID 将用于后续的点击、滑动、截图等操作,请妥善保存。
截图/输入方式选择(仅当默认方式不工作时尝试切换):
截图方式优先级(从高到低):
- "FramePool"(默认,可后台)
- "PrintWindow"(可后台)
- "GDI"(可后台)
- "DXGI_DesktopDup_Window"(只能前台)
- "ScreenDC"(只能前台)
- "DXGI_DesktopDup"(仅作最后手段!截取整个桌面而非单窗口,触控坐标会不正确)
鼠标方式优先级(从高到低):
- "PostMessage"(默认,可后台)
- "PostMessageWithCursorPos"(可后台,但偶尔会抢鼠标)
- "Seize"(只能前台,会抢占鼠标键盘)
键盘方式优先级(从高到低):
- "PostMessage"(默认,可后台)
- "Seize"(只能前台,会抢占鼠标键盘)
""",
)
def connect_window(
window_name: str,
screencap_method: str = "FramePool",
mouse_method: str = "PostMessage",
keyboard_method: str = "PostMessage",
) -> Optional[str]:
window: DesktopWindow | None = object_registry.get(window_name)
if not window:
return None
screencap_enum = _SCREENCAP_METHOD_MAP.get(
screencap_method, MaaWin32ScreencapMethodEnum.FramePool
)
mouse_enum = _MOUSE_METHOD_MAP.get(
mouse_method, MaaWin32InputMethodEnum.PostMessage
)
keyboard_enum = _KEYBOARD_METHOD_MAP.get(
keyboard_method, MaaWin32InputMethodEnum.PostMessage
)
window_controller = Win32Controller(
window.hwnd,
screencap_method=screencap_enum,
mouse_method=mouse_enum,
keyboard_method=keyboard_enum,
)
# 设置默认截图短边为 1080p
# 电脑屏幕通常较大,使用更高清的截图
window_controller.set_screenshot_target_short_side(1080)
if not window_controller.post_connection().wait().succeeded:
return None
controller_id = object_registry.register(window_controller)
controller_info_registry[controller_id] = ControllerInfo(
controller_type=ControllerType.WIN32,
keyboard_method=keyboard_method,
)
return controller_id