108 lines
3.2 KiB
Python
108 lines
3.2 KiB
Python
"""
|
|
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)
|
|
return pid == self._process_id
|
|
else:
|
|
# Check window title
|
|
title = win32gui.GetWindowText(hwnd)
|
|
return 'entropia' in title.lower()
|
|
|
|
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}")
|