import subprocess import time from dataclasses import dataclass from typing import Optional, Tuple from wechat_auto.utils.logger import logger from wechat_auto.config import settings @dataclass class WindowPosition: x: int y: int width: int height: int @property def center(self) -> Tuple[int, int]: return (self.x + self.width // 2, self.y + self.height // 2) def relative_to(self, rel_x: int, rel_y: int) -> Tuple[int, int]: return (self.x + rel_x, self.y + rel_y) class WindowManager: def __init__(self, window_name: str = None): self.window_name = window_name or settings.wechat_window_name def find_window(self, timeout: float = 10.0) -> Optional[str]: start_time = time.time() search_methods = [ ['xdotool', 'search', '--name', self.window_name], ['xdotool', 'search', '--class', 'wechat'], ['xdotool', 'search', '--classname', 'wechat'], ] while time.time() - start_time < timeout: for cmd in search_methods: try: result = subprocess.run( cmd, capture_output=True, text=True, timeout=5 ) window_id = result.stdout.strip().split('\n')[0] if window_id: logger.info(f"找到窗口: {self.window_name}, ID: {window_id}") return window_id except Exception as e: logger.debug(f"搜索方式 {cmd} 失败: {e}") time.sleep(0.5) logger.error(f"未找到窗口: {self.window_name}") return None def get_window_position(self, window_id: str = None) -> Optional[WindowPosition]: if not window_id: window_id = self.find_window() if not window_id: return None try: result = subprocess.run( ['xdotool', 'getwindowgeometry', window_id], capture_output=True, text=True, timeout=5 ) output = result.stdout x = y = width = height = 0 for line in output.split('\n'): line = line.strip() if line.startswith('Position:'): parts = line.split(':')[1].strip().split(',') x = int(parts[0]) y = int(parts[1]) elif line.startswith('Geometry:'): parts = line.split(':')[1].strip().split('x') width = int(parts[0]) height = int(parts[1]) if x or y or width or height: pos = WindowPosition(x=x, y=y, width=width, height=height) logger.info(f"窗口位置: {pos}") return pos except Exception as e: logger.error(f"获取窗口位置失败: {e}") return None def activate_window(self, window_id: str = None) -> bool: if not window_id: window_id = self.find_window() if not window_id: return False try: subprocess.run( ['xdotool', 'windowactivate', window_id], capture_output=True, timeout=5 ) time.sleep(0.5) logger.info(f"窗口已激活: {window_id}") return True except Exception as e: logger.error(f"激活窗口失败: {e}") return False def is_window_visible(self, window_id: str = None) -> bool: if not window_id: window_id = self.find_window() if not window_id: return False try: result = subprocess.run( ['xdotool', 'getwindowname', window_id], capture_output=True, text=True, timeout=5 ) return bool(result.stdout.strip()) except Exception: return False