# -*- 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