""" foreground_fix.py Wartet auf den Start von CrimsonDesert.exe und bringt das Spielfenster automatisch in den Vordergrund. Verwendung: pythonw foreground_fix.py [--timeout 120] [--process CrimsonDesert] Abhängigkeiten (im .venv): pip install psutil pywin32 """ import argparse import ctypes import sys import time import psutil import win32api import win32con import win32gui import win32process # Minimale Fensterbreite in Pixeln, um den Splashscreen auszuschließen. MIN_WINDOW_WIDTH = 800 # Anzahl der Fokus-Versuche und Pause dazwischen FOCUS_RETRIES = 5 FOCUS_RETRY_DELAY = 1.0 def find_process_pids(name: str) -> set[int]: pids = set() for proc in psutil.process_iter(["pid", "name"]): try: if name.lower() in proc.info["name"].lower(): pids.add(proc.info["pid"]) except (psutil.NoSuchProcess, psutil.AccessDenied): pass return pids def find_game_window(pids: set[int]) -> int | None: found = [] def callback(hwnd, _): if not win32gui.IsWindowVisible(hwnd): return True try: _, pid = win32process.GetWindowThreadProcessId(hwnd) except Exception: return True if pid not in pids: return True try: rect = win32gui.GetWindowRect(hwnd) except Exception: return True width = rect[2] - rect[0] height = rect[3] - rect[1] if width >= MIN_WINDOW_WIDTH: title = win32gui.GetWindowText(hwnd) found.append((hwnd, title, width, height)) return True win32gui.EnumWindows(callback, None) if not found: return None found.sort(key=lambda x: x[2] * x[3], reverse=True) return found[0][0] def is_foreground(hwnd: int) -> bool: """Prüft ob das angegebene Fenster aktuell das Vordergrundfenster ist.""" return win32gui.GetForegroundWindow() == hwnd def bring_to_front(hwnd: int) -> bool: """ Bringt das Fenster in den Vordergrund. Gibt True zurück wenn erfolgreich. Verwendet Alt-Key + AttachThreadInput als Workaround. """ try: # Alt-Taste simulieren — hebt die SetForegroundWindow-Sperre auf win32api.keybd_event(win32con.VK_MENU, 0, 0, 0) time.sleep(0.05) fg_hwnd = win32gui.GetForegroundWindow() fg_tid, _ = win32process.GetWindowThreadProcessId(fg_hwnd) tgt_tid, _ = win32process.GetWindowThreadProcessId(hwnd) attached = False if fg_tid != tgt_tid: ctypes.windll.user32.AttachThreadInput(tgt_tid, fg_tid, True) attached = True win32gui.ShowWindow(hwnd, win32con.SW_SHOW) win32gui.BringWindowToTop(hwnd) win32gui.SetForegroundWindow(hwnd) ctypes.windll.user32.SetFocus(hwnd) ctypes.windll.user32.SetActiveWindow(hwnd) if attached: ctypes.windll.user32.AttachThreadInput(tgt_tid, fg_tid, False) win32api.keybd_event(win32con.VK_MENU, 0, win32con.KEYEVENTF_KEYUP, 0) return is_foreground(hwnd) except Exception: return False def minimize_other_windows(game_hwnd: int, game_pids: set[int]) -> None: """Minimiert Steam- und andere störende Fenster.""" def callback(hwnd, _): if hwnd == game_hwnd: return True if not win32gui.IsWindowVisible(hwnd): return True try: _, pid = win32process.GetWindowThreadProcessId(hwnd) except Exception: return True try: proc = psutil.Process(pid) name = proc.name().lower() except (psutil.NoSuchProcess, psutil.AccessDenied): return True # Steam-Fenster minimieren if "steam" in name: try: win32gui.ShowWindow(hwnd, win32con.SW_MINIMIZE) except Exception: pass return True win32gui.EnumWindows(callback, None) def main() -> None: parser = argparse.ArgumentParser() parser.add_argument("--timeout", type=int, default=120) parser.add_argument("--process", type=str, default="CrimsonDesert") args = parser.parse_args() deadline = time.time() + args.timeout process_name = args.process # --- Phase 1: Warten auf den Prozess --- pids: set[int] = set() while time.time() < deadline: pids = find_process_pids(process_name) if pids: break time.sleep(0.5) else: sys.exit(1) # --- Phase 2: Warten auf das Hauptfenster --- hwnd: int | None = None while time.time() < deadline: pids = find_process_pids(process_name) hwnd = find_game_window(pids) if hwnd: # Warten bis das Spiel sein Fenster vollständig aufgebaut hat time.sleep(2) break time.sleep(0.5) else: sys.exit(1) # --- Phase 3: Steam minimieren + Spielfenster fokussieren (mit Retry) --- minimize_other_windows(hwnd, pids) time.sleep(0.3) for attempt in range(FOCUS_RETRIES): success = bring_to_front(hwnd) if success and is_foreground(hwnd): break time.sleep(FOCUS_RETRY_DELAY) if __name__ == "__main__": main()