mirror of
https://github.com/Andre-Bzk/FocusFix.git
synced 2026-11-15 15:43:12 +01:00
Logging, Fensterwechsel-Erkennung und stabile Fenstergröße
- 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
This commit is contained in:
+85
-37
@@ -12,6 +12,7 @@ Abhängigkeiten (im .venv):
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import ctypes
|
import ctypes
|
||||||
|
import logging
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@@ -21,12 +22,21 @@ import win32con
|
|||||||
import win32gui
|
import win32gui
|
||||||
import win32process
|
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.
|
# Minimale Fensterbreite in Pixeln, um den Splashscreen auszuschließen.
|
||||||
MIN_WINDOW_WIDTH = 800
|
MIN_WINDOW_WIDTH = 800
|
||||||
|
|
||||||
# Anzahl der Fokus-Versuche und Pause dazwischen
|
# Anzahl der Fokus-Versuche und Pause dazwischen
|
||||||
FOCUS_RETRIES = 5
|
FOCUS_RETRIES = 15
|
||||||
FOCUS_RETRY_DELAY = 1.0
|
FOCUS_RETRY_DELAY = 2.0
|
||||||
|
|
||||||
|
|
||||||
def find_process_pids(name: str) -> set[int]:
|
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]
|
height = rect[3] - rect[1]
|
||||||
if width >= MIN_WINDOW_WIDTH:
|
if width >= MIN_WINDOW_WIDTH:
|
||||||
title = win32gui.GetWindowText(hwnd)
|
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))
|
found.append((hwnd, title, width, height))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -70,6 +81,32 @@ def find_game_window(pids: set[int]) -> int | None:
|
|||||||
return found[0][0]
|
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:
|
def is_foreground(hwnd: int) -> bool:
|
||||||
"""Prüft ob das angegebene Fenster aktuell das Vordergrundfenster ist."""
|
"""Prüft ob das angegebene Fenster aktuell das Vordergrundfenster ist."""
|
||||||
return win32gui.GetForegroundWindow() == hwnd
|
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)
|
win32api.keybd_event(win32con.VK_MENU, 0, win32con.KEYEVENTF_KEYUP, 0)
|
||||||
|
|
||||||
return is_foreground(hwnd)
|
result = is_foreground(hwnd)
|
||||||
except Exception:
|
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
|
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:
|
def main() -> None:
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
@@ -162,22 +177,55 @@ def main() -> None:
|
|||||||
pids = find_process_pids(process_name)
|
pids = find_process_pids(process_name)
|
||||||
hwnd = find_game_window(pids)
|
hwnd = find_game_window(pids)
|
||||||
if hwnd:
|
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
|
# Warten bis das Spiel sein Fenster vollständig aufgebaut hat
|
||||||
time.sleep(2)
|
time.sleep(3)
|
||||||
break
|
break
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
else:
|
else:
|
||||||
|
logging.error("Timeout waiting for game window")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# --- Phase 3: Steam minimieren + Spielfenster fokussieren (mit Retry) ---
|
# --- Phase 3: Spielfenster fokussieren und auf Fensterwechsel reagieren ---
|
||||||
minimize_other_windows(hwnd, pids)
|
# Beobachtet über die gesamte Retry-Dauer, ob sich das Fenster ändert
|
||||||
time.sleep(0.3)
|
# (Splash -> Hauptfenster), und fokussiert jedes neue Fenster erneut.
|
||||||
|
focused_hwnd: int | None = None
|
||||||
for attempt in range(FOCUS_RETRIES):
|
for attempt in range(FOCUS_RETRIES):
|
||||||
success = bring_to_front(hwnd)
|
pids = find_process_pids(process_name)
|
||||||
if success and is_foreground(hwnd):
|
if not pids:
|
||||||
|
logging.warning("Game process gone, stopping")
|
||||||
break
|
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)
|
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__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user