From 8625bb0188df3e40e5219833fb21eb6d3e298fd7 Mon Sep 17 00:00:00 2001 From: Andre <200930841+Andre-Bzk@users.noreply.github.com> Date: Sat, 4 Apr 2026 11:19:45 +0200 Subject: [PATCH] =?UTF-8?q?Logging,=20Fensterwechsel-Erkennung=20und=20sta?= =?UTF-8?q?bile=20Fenstergr=C3=B6=C3=9Fe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Datei-Logging nach foreground_fix.log für Debugging hinzugefügt - Fensterwechsel (Splash -> Hauptfenster) erkennen und automatisch neu fokussieren - Vor dem Fokussieren warten bis die Fenstergröße stabil ist - minimize_other_windows entfernt (unnötig) - Retries erhöht (5->15) und Delay (1s->2s) für mehr Zuverlässigkeit --- foreground_fix.py | 122 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 85 insertions(+), 37 deletions(-) diff --git a/foreground_fix.py b/foreground_fix.py index b52b25b..8af89ea 100644 --- a/foreground_fix.py +++ b/foreground_fix.py @@ -12,6 +12,7 @@ Abhängigkeiten (im .venv): import argparse import ctypes +import logging import sys import time @@ -21,12 +22,21 @@ import win32con import win32gui import win32process +from pathlib import Path + +_LOG_PATH = Path(__file__).resolve().parent / "foreground_fix.log" +logging.basicConfig( + filename=str(_LOG_PATH), + level=logging.DEBUG, + format="%(asctime)s %(levelname)s %(message)s", +) + # 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 +FOCUS_RETRIES = 15 +FOCUS_RETRY_DELAY = 2.0 def find_process_pids(name: str) -> set[int]: @@ -60,6 +70,7 @@ def find_game_window(pids: set[int]) -> int | None: height = rect[3] - rect[1] if width >= MIN_WINDOW_WIDTH: title = win32gui.GetWindowText(hwnd) + logging.debug("Candidate window: hwnd=%s title=%r size=%dx%d pid=%d", hwnd, title, width, height, pid) found.append((hwnd, title, width, height)) return True @@ -70,6 +81,32 @@ def find_game_window(pids: set[int]) -> int | None: return found[0][0] +def wait_for_stable_size(hwnd: int, polls: int = 3, interval: float = 0.5, + timeout: float = 5.0) -> tuple[int, int]: + """Wartet bis die Fenstergröße sich stabilisiert hat (kein Resize mehr). + Gibt die finale (width, height) zurück.""" + last_size = (0, 0) + stable_count = 0 + deadline = time.time() + timeout + while time.time() < deadline: + try: + rect = win32gui.GetWindowRect(hwnd) + size = (rect[2] - rect[0], rect[3] - rect[1]) + except Exception: + break + if size == last_size: + stable_count += 1 + if stable_count >= polls: + break + else: + stable_count = 0 + last_size = size + time.sleep(interval) + logging.debug("Stable size for hwnd=%s: %dx%d (after %d stable polls)", + hwnd, last_size[0], last_size[1], stable_count) + return last_size + + def is_foreground(hwnd: int) -> bool: """Prüft ob das angegebene Fenster aktuell das Vordergrundfenster ist.""" return win32gui.GetForegroundWindow() == hwnd @@ -105,37 +142,15 @@ def bring_to_front(hwnd: int) -> bool: win32api.keybd_event(win32con.VK_MENU, 0, win32con.KEYEVENTF_KEYUP, 0) - return is_foreground(hwnd) - except Exception: + result = is_foreground(hwnd) + logging.debug("bring_to_front result: %s (foreground hwnd=%s, target hwnd=%s)", + result, win32gui.GetForegroundWindow(), hwnd) + return result + except Exception as e: + logging.exception("bring_to_front failed: %s", e) 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() @@ -162,23 +177,56 @@ def main() -> None: pids = find_process_pids(process_name) hwnd = find_game_window(pids) if hwnd: + logging.info("Initial window found: hwnd=%s title=%r", hwnd, win32gui.GetWindowText(hwnd)) # Warten bis das Spiel sein Fenster vollständig aufgebaut hat - time.sleep(2) + time.sleep(3) break time.sleep(0.5) else: + logging.error("Timeout waiting for game window") sys.exit(1) - # --- Phase 3: Steam minimieren + Spielfenster fokussieren (mit Retry) --- - minimize_other_windows(hwnd, pids) - time.sleep(0.3) - + # --- Phase 3: Spielfenster fokussieren und auf Fensterwechsel reagieren --- + # Beobachtet über die gesamte Retry-Dauer, ob sich das Fenster ändert + # (Splash -> Hauptfenster), und fokussiert jedes neue Fenster erneut. + focused_hwnd: int | None = None for attempt in range(FOCUS_RETRIES): - success = bring_to_front(hwnd) - if success and is_foreground(hwnd): + pids = find_process_pids(process_name) + if not pids: + logging.warning("Game process gone, stopping") break + + current_hwnd = find_game_window(pids) + + if current_hwnd and current_hwnd != hwnd: + logging.info("Window changed: old=%s new=%s title=%r", + hwnd, current_hwnd, win32gui.GetWindowText(current_hwnd)) + hwnd = current_hwnd + focused_hwnd = None # neues Fenster muss neu fokussiert werden + time.sleep(1) + + if not hwnd: + time.sleep(FOCUS_RETRY_DELAY) + continue + + # Bereits erfolgreich fokussiert — weiter beobachten ob Fenster wechselt + if focused_hwnd == hwnd and is_foreground(hwnd): + time.sleep(FOCUS_RETRY_DELAY) + continue + + # Warten bis Fenstergröße stabil ist (Resize abgeschlossen) + wait_for_stable_size(hwnd) + + logging.info("Focus attempt %d/%d for hwnd=%s title=%r", + attempt + 1, FOCUS_RETRIES, hwnd, win32gui.GetWindowText(hwnd)) + success = bring_to_front(hwnd) + if success: + logging.info("Focus succeeded on attempt %d", attempt + 1) + focused_hwnd = hwnd time.sleep(FOCUS_RETRY_DELAY) + logging.info("Done. Final focused hwnd=%s", focused_hwnd) + if __name__ == "__main__": main()