""" Window tracker for monitoring Entropia Universe window state. """ import time import logging import threading from typing import Optional, Callable, List class WindowTracker: """Tracks the EU game window state. Monitors window focus, visibility, and position without interfering with the game. """ def __init__(self, poll_interval: float = 0.5): """Initialize window tracker. Args: poll_interval: Seconds between window checks """ self.poll_interval = poll_interval self._running = False self._thread: Optional[threading.Thread] = None self._callbacks: List[Callable] = [] self._logger = logging.getLogger("WindowTracker") self._is_focused = False self._window_handle: Optional[int] = None self._process_id: Optional[int] = None def start(self, process_id: Optional[int] = None) -> bool: """Start tracking window.""" if self._running: return True self._process_id = process_id self._running = True self._thread = threading.Thread(target=self._track_loop, daemon=True) self._thread.start() return True def stop(self) -> None: """Stop tracking window.""" self._running = False if self._thread: self._thread.join(timeout=1.0) def on_change(self, callback: Callable[[bool], None]) -> Callable: """Register focus change callback.""" self._callbacks.append(callback) return callback def is_focused(self) -> bool: """Check if game window is focused.""" return self._is_focused def _track_loop(self) -> None: """Main tracking loop.""" while self._running: try: focused = self._check_focus() if focused != self._is_focused: self._is_focused = focused self._notify_change(focused) time.sleep(self.poll_interval) except Exception as e: self._logger.error(f"Tracking error: {e}") time.sleep(self.poll_interval) def _check_focus(self) -> bool: """Check if game window is focused.""" try: import win32gui import win32process hwnd = win32gui.GetForegroundWindow() if self._process_id: _, pid = win32process.GetWindowThreadProcessId(hwnd) if pid == self._process_id: return True # Check window title - supports multiple patterns title = win32gui.GetWindowText(hwnd) title_lower = title.lower() # Match various EU window titles: # - "Entropia Universe Client (64 bit)" # - "Entropia Universe Client (64 bit) - Planet Calypso" # - "Entropia.exe" patterns = [ 'entropia universe client', 'entropia.exe', 'entropia universe' ] return any(pattern in title_lower for pattern in patterns) except ImportError: pass return False def _notify_change(self, focused: bool) -> None: """Notify callbacks of focus change.""" for callback in self._callbacks: try: callback(focused) except Exception as e: self._logger.error(f"Callback error: {e}")