commit 7e61ee3c5c214a84ee8e33f77660272085e42add Author: Andre <200930841+Andre-Bzk@users.noreply.github.com> Date: Fri Apr 3 13:16:43 2026 +0200 Prototyp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4603a1b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.venv/ +README.md +CLAUDE.md diff --git a/foreground_fix.py b/foreground_fix.py new file mode 100644 index 0000000..b52b25b --- /dev/null +++ b/foreground_fix.py @@ -0,0 +1,184 @@ +""" +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() diff --git a/launch.vbs b/launch.vbs new file mode 100644 index 0000000..38bb6f8 --- /dev/null +++ b/launch.vbs @@ -0,0 +1,15 @@ +Set WshShell = CreateObject("WScript.Shell") + +' Foreground-Fix Script unsichtbar starten (pythonw = kein Konsolenfenster) +WshShell.Run """C:\Projekte\Steam_backgroud_fix\.venv\Scripts\pythonw.exe"" ""C:\Projekte\Steam_backgroud_fix\foreground_fix.py""", 0, False + +' Spiel starten: alle Argumente (= Steam %command%) zusammensetzen und ausführen +Dim gameCmd, i +gameCmd = "" +For i = 0 To WScript.Arguments.Count - 1 + gameCmd = gameCmd & """" & WScript.Arguments(i) & """ " +Next + +If gameCmd <> "" Then + WshShell.Run gameCmd, 1, False +End If