# window_info_crossplatform.py """ Récupération des informations sur la fenêtre active - CROSS-PLATFORM Supporte: - Linux (X11 via xdotool) - Windows (via pywin32) - macOS (via pyobjc) Installation des dépendances: pip install pywin32 # Windows pip install pyobjc-framework-Cocoa # macOS pip install psutil # Tous OS """ from __future__ import annotations import platform import subprocess from typing import Dict, Optional def _run_cmd(cmd: list[str]) -> Optional[str]: """Exécute une commande et renvoie la sortie texte (strippée), ou None en cas d'erreur.""" try: out = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) return out.decode("utf-8", errors="ignore").strip() except Exception: return None def get_active_window_info() -> Dict[str, str]: """ Renvoie un dict : { "title": "...", "app_name": "..." } Détecte automatiquement l'OS et utilise la méthode appropriée. """ system = platform.system() if system == "Linux": return _get_window_info_linux() elif system == "Windows": return _get_window_info_windows() elif system == "Darwin": # macOS return _get_window_info_macos() else: return {"title": "unknown_window", "app_name": "unknown_app"} def _get_window_info_linux() -> Dict[str, str]: """ Linux: utilise xdotool (X11) Nécessite: sudo apt-get install xdotool """ title = _run_cmd(["xdotool", "getactivewindow", "getwindowname"]) pid_str = _run_cmd(["xdotool", "getactivewindow", "getwindowpid"]) app_name: Optional[str] = None if pid_str: pid_str = pid_str.strip() # On récupère le nom du binaire via ps app_name = _run_cmd(["ps", "-p", pid_str, "-o", "comm="]) if not title: title = "unknown_window" if not app_name: app_name = "unknown_app" return { "title": title, "app_name": app_name, } def _get_window_info_windows() -> Dict[str, str]: """ Windows: utilise pywin32 + psutil Nécessite: pip install pywin32 psutil """ try: import win32gui import win32process import psutil # Fenêtre au premier plan hwnd = win32gui.GetForegroundWindow() # Titre de la fenêtre title = win32gui.GetWindowText(hwnd) if not title: title = "unknown_window" # PID du processus _, pid = win32process.GetWindowThreadProcessId(hwnd) # Nom du processus try: process = psutil.Process(pid) app_name = process.name() except (psutil.NoSuchProcess, psutil.AccessDenied): app_name = "unknown_app" return { "title": title, "app_name": app_name, } except ImportError: # pywin32 ou psutil non installé return { "title": "unknown_window (pywin32 missing)", "app_name": "unknown_app (pywin32 missing)", } except Exception as e: return { "title": f"error: {e}", "app_name": "unknown_app", } def _get_window_info_macos() -> Dict[str, str]: """ macOS: utilise pyobjc (AppKit) Nécessite: pip install pyobjc-framework-Cocoa Note: Nécessite les permissions "Accessibility" dans System Preferences """ try: from AppKit import NSWorkspace from Quartz import ( CGWindowListCopyWindowInfo, kCGWindowListOptionOnScreenOnly, kCGNullWindowID ) # Application active active_app = NSWorkspace.sharedWorkspace().activeApplication() app_name = active_app.get('NSApplicationName', 'unknown_app') # Titre de la fenêtre (via Quartz) # On cherche la fenêtre de l'app active qui est au premier plan window_list = CGWindowListCopyWindowInfo( kCGWindowListOptionOnScreenOnly, kCGNullWindowID ) title = "unknown_window" for window in window_list: owner_name = window.get('kCGWindowOwnerName', '') if owner_name == app_name: window_title = window.get('kCGWindowName', '') if window_title: title = window_title break return { "title": title, "app_name": app_name, } except ImportError: # pyobjc non installé return { "title": "unknown_window (pyobjc missing)", "app_name": "unknown_app (pyobjc missing)", } except Exception as e: return { "title": f"error: {e}", "app_name": "unknown_app", } # Test rapide if __name__ == "__main__": import time print(f"OS détecté: {platform.system()}") print("\nTest de capture fenêtre active (5 secondes)...") print("Changez de fenêtre pour tester!\n") for i in range(5): info = get_active_window_info() print(f"[{i+1}] App: {info['app_name']:20s} | Title: {info['title']}") time.sleep(1)