Files
jiangchang-platform-kit/src/jiangchang_desktop_sdk/window_win32.py
chendelian 6e3f79dde9 feat(sdk): 抽出 window_win32 共享模块,client 激活方法薄壳化
把 _activate_and_maximize_windows 内嵌的 50+ 行 ctypes 实现抽到独立的 SDK 子模块
src/jiangchang_desktop_sdk/window_win32.py,暴露公开函数
activate_and_maximize_jiangchang_window,给 screencast 工具也复用(见后续 commit)。

client.py 的 _activate_and_maximize_windows 退化为 6 行薄壳,调用新模块。

设计:
- window_win32 是 SDK 子模块(放 src/jiangchang_desktop_sdk/ 下),不放 tools/,
  避免 SDK 反向依赖工具层
- ctypes EnumWindows + ShowWindow(SW_MAXIMIZE) + SetForegroundWindow 逻辑不变
- 跨平台行为不变:Windows 真执行,其他系统返回 False
- bring_to_front 调用方完全不感知变化(向后兼容)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-25 11:13:27 +08:00

86 lines
2.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""
Win32 窗口工具:跨 SDK / screencast 共享的"激活并最大化匠厂主窗口"实现。
为什么单独抽出来:
- SDK 的 bring_to_front 需要 OS 级窗口前置page.bring_to_front 只切 tab
- screencast 的 run_screencast 录屏前也要把匠厂浮到前面并铺满屏幕
- 两者用同一份 EnumWindows + ShowWindow(SW_MAXIMIZE) + SetForegroundWindow 实现,
避免代码重复维护
跨平台行为:
- Windows: 真正执行
- 其他系统: 直接返回 False调用方按需 fallback 到协议激活)
"""
from __future__ import annotations
import logging
import os
from typing import Optional, Tuple
_logger = logging.getLogger("jiangchang_desktop_sdk.window_win32")
_DEFAULT_KEYWORDS: Tuple[str, ...] = ("匠厂", "Jiangchang", "ClawX", "OpenClaw")
SW_MAXIMIZE = 3
def activate_and_maximize_jiangchang_window(
keywords: Optional[Tuple[str, ...]] = None,
) -> bool:
"""Windows: EnumWindows 找匠厂主窗口 → ShowWindow(SW_MAXIMIZE) + SetForegroundWindow。
Args:
keywords: 标题模糊匹配的关键词;不传则用默认
("匠厂", "Jiangchang", "ClawX", "OpenClaw")
Returns:
True: 找到窗口并执行了 ShowWindow + SetForegroundWindow
False: 非 Windows / ctypes 不可用 / 找不到匹配窗口 / 任何异常
"""
if os.name != "nt":
return False
try:
import ctypes
from ctypes import wintypes
except Exception as exc:
_logger.debug("ctypes 不可用:%s", exc)
return False
user32 = ctypes.windll.user32
kw = keywords or _DEFAULT_KEYWORDS
matching: list = []
@ctypes.WINFUNCTYPE(ctypes.c_bool, wintypes.HWND, wintypes.LPARAM)
def _enum_proc(hwnd, _lparam):
if not user32.IsWindowVisible(hwnd):
return True
length = user32.GetWindowTextLengthW(hwnd)
if length == 0:
return True
buf = ctypes.create_unicode_buffer(length + 1)
user32.GetWindowTextW(hwnd, buf, length + 1)
if any(k in buf.value for k in kw):
matching.append((hwnd, buf.value))
return True
try:
user32.EnumWindows(_enum_proc, 0)
except Exception as exc:
_logger.debug("EnumWindows 失败:%s", exc)
return False
if not matching:
_logger.debug("未找到匠厂主窗口keywords=%s", kw)
return False
hwnd, title = matching[0]
try:
user32.ShowWindow(hwnd, SW_MAXIMIZE)
user32.SetForegroundWindow(hwnd)
_logger.debug("已激活+最大化 hwnd=%s title=%s", hwnd, title)
return True
except Exception as exc:
_logger.debug("ShowWindow/SetForegroundWindow 失败:%s", exc)
return False