# FocusFix - Game Foreground Fix for Windows Windows-Hilfsprogramm, das ein Spielfenster automatisch in den Vordergrund bringt. Gedacht für Spiele, die nach dem Start im Hintergrund landen oder von Steam, Taskleiste, Splashscreens oder Overlay-Fenstern verdeckt werden. Funktioniert mit **Steam** (direkt über `%command%`) und **Sunshine/Moonlight** (manueller Prozessname). --- ## Inhaltsverzeichnis - [Problembeschreibung](#problembeschreibung) - [Lösungsansatz](#lösungsansatz) - [Architektur](#architektur) - [Voraussetzungen](#voraussetzungen) - [Installation](#installation) - [Konfiguration](#konfiguration) - [Steam](#steam) - [Sunshine / Moonlight](#sunshine--moonlight) - [Ablauf zur Laufzeit](#ablauf-zur-laufzeit) - [Dateireferenz](#dateireferenz) - [focusfix.py](#focusfixpy) - [launch.vbs](#launchvbs) - [Konfigurationsparameter](#konfigurationsparameter) - [Windows API — Technischer Hintergrund](#windows-api--technischer-hintergrund) - [Das SetForegroundWindow-Problem](#das-setforegroundwindow-problem) - [Workaround-Strategie](#workaround-strategie) - [Splashscreen-Erkennung](#splashscreen-erkennung) - [Manuelles Testen](#manuelles-testen) - [Debugging](#debugging) - [Bekannte Einschränkungen](#bekannte-einschränkungen) --- ## Problembeschreibung Beim Start eines Spiels über Steam oder Sunshine/Moonlight öffnet sich das Spielfenster im **Hintergrund**. Die Steam-Oberfläche, die Windows-Taskleiste oder andere Fenster verdecken es, und der Spieler muss manuell per Alt+Tab wechseln. Ursache: Windows vergibt den Vordergrundfokus nur eingeschränkt an Prozesse, die das System als berechtigt ansieht. Zusätzlich erzeugen manche Spiele beim Start mehrere Fenster wie Splashscreens, Launcher oder titellose Overlay-Fenster, die den Fokus stören können. ## Lösungsansatz `focusfix.py` kann in zwei Modi arbeiten: 1. **Steam-Wrapper-Modus:** Das Script erhält `%command%`, leitet daraus den Prozessnamen ab, startet das Spiel selbst und überwacht anschließend den Fensterfokus. 2. **Direktmodus:** Das Script wird mit `--process Name` gestartet und überwacht einen bekannten Spielprozess. Das Tool ist nicht auf ein bestimmtes Spiel festgelegt, sondern für viele ähnliche Fokus-Probleme mit vergleichbarem Fensterverhalten gedacht. ## Architektur ``` Steam Launch Options / Sunshine Befehlsvorbereitung │ ▼ ┌────────────────────┐ │ pythonw.exe │ │ focusfix.py │ │ │ │ Steam: %command% │ │ Sunshine: │ │ --process Name │ └────────┬───────────┘ │ Steam-Modus: startet das Spiel selbst │ Prozess/Fenster gefunden │ ┌────────▼───────────┐ │ 1. Prozess finden │ │ 2. Fenster erkennen│ │ 3. Größe abwarten │ │ 4. Spiel fokussieren│ │ 5. Wechsel beobachten│ │ 6. Script beendet │ └────────────────────┘ ``` **Zwei Dateien**, davon eine nur noch optional: | Datei | Sprache | Aufgabe | |---|---|---| | `focusfix.py` | Python 3 | Steam-Wrapper-Modus, Direktmodus per `--process`, Prozess-Monitoring, Fenstererkennung, Fensterwechsel-Erkennung, Fokus-Erzwingung, Logging | | `launch.vbs` | VBScript | Legacy-Wrapper für ältere Steam-Konfigurationen; für den aktuellen Ablauf nicht mehr erforderlich | ## Voraussetzungen | Komponente | Version | Hinweis | |---|---|---| | Windows | 10 / 11 | Benötigt Win32 API (user32.dll) | | Python | 3.10+ | `py` Launcher muss verfügbar sein | | pip-Pakete | `psutil`, `pywin32` | Werden im `.venv` installiert | | Steam | Beliebig | Startoptionen müssen konfigurierbar sein | ## Installation ```bash cd C:\Path\To\FocusFix # Virtual Environment erstellen py -m venv .venv # Aktivieren .venv\Scripts\activate # Abhängigkeiten installieren pip install psutil pywin32 ``` ### Dateistruktur nach Installation ``` C:\Path\To\FocusFix\ ├── .venv\ # Python Virtual Environment ├── focusfix.py # Hauptscript ├── focusfix.log # Logdatei (wird automatisch erstellt) ├── launch.vbs # Legacy-Wrapper, optional ├── CLAUDE.md # Kontext für Claude Code └── README.md # Diese Dokumentation ``` ## Konfiguration ### Steam 1. Steam öffnen → **Bibliothek** 2. Rechtsklick auf das Spiel → **Eigenschaften** 3. Unter **Allgemein → Startoptionen** eine der folgenden Varianten eintragen: #### Variante A — Empfohlen: direkt mit `focusfix.py` ```text "C:\Path\To\FocusFix\.venv\Scripts\pythonw.exe" "C:\Path\To\FocusFix\focusfix.py" %command% ``` `focusfix.py` extrahiert den Prozessnamen automatisch aus `%command%`, startet das eigentliche Spiel und überwacht danach den Fensterfokus. Die gleiche Startoption kann für beliebige Steam-Spiele mit vergleichbarem Fokus-Problem verwendet werden. Die Pfade im Beispiel sind Platzhalter und müssen durch deinen tatsächlichen Installationspfad ersetzt werden. **Erklärung:** - `pythonw.exe` — startet Python ohne Konsolenfenster - `"...\focusfix.py"` — Python-Script im Steam-Wrapper-Modus - `%command%` — Steam-Platzhalter, wird zur Laufzeit durch den vollständigen Pfad zur Spiel-Executable ersetzt, z.B. `"C:\SteamLibrary\...\Game.exe"` #### Variante B — Legacy: über `launch.vbs` Wenn du den alten VBScript-Wrapper weiterhin verwenden willst, kannst du in Steam Folgendes eintragen: ```text wscript "C:\Path\To\FocusFix\launch.vbs" %command% ``` `launch.vbs` extrahiert den Prozessnamen aus `%command%`, startet `focusfix.py` unsichtbar mit `--process` und startet danach das eigentliche Spiel. Diese Variante funktioniert weiterhin, ist aber nur noch als Legacy-/Fallback-Weg gedacht, weil `focusfix.py` den Wrapper-Job inzwischen selbst übernehmen kann. Die Pfade im Beispiel sind Platzhalter und müssen durch deinen tatsächlichen Installationspfad ersetzt werden. **Erklärung:** - `wscript` — startet VBScript ohne sichtbares Konsolenfenster - `"...\launch.vbs"` — Legacy-Wrapper, der `focusfix.py` indirekt startet - `%command%` — Steam-Platzhalter mit dem ursprünglichen Spielstartbefehl #### Variante C — Release-Exe Wenn du eine gebaute Release-Datei verwenden willst, kannst du statt Python direkt die `.exe` in Steam eintragen. In diesem Repository liegt aktuell eine Build-Datei unter `dist\nuinstall\FocusFix.exe`: ```text "C:\Path\To\FocusFix\dist\nuinstall\FocusFix.exe" %command% ``` Die `.exe` übernimmt denselben Wrapper-Modus wie `focusfix.py`: Sie erhält `%command%`, startet das Spiel und überwacht danach den Fokus. Diese Variante ist praktisch, wenn du keine sichtbare Python-Installation oder kein venv in den Steam-Startoptionen referenzieren willst. **Wichtig:** Der Dateiname der Release-Exe hängt vom Build ab. Wenn du später eine neu benannte Exe wie `FocusFix.exe` baust, muss genau dieser Pfad in Steam eingetragen werden. Auch hier sind die Pfade im Beispiel Platzhalter und müssen durch deinen tatsächlichen Installationspfad ersetzt werden. ### Sunshine / Moonlight In Sunshine als **Befehlsvorbereitung** (Command Preparation) pro Spiel eintragen: ```text "C:\Path\To\FocusFix\.venv\Scripts\pythonw.exe" "C:\Path\To\FocusFix\focusfix.py" --process "Prozessname" ``` Den Prozessnamen im Task-Manager ablesen, ohne `.exe`. Beispiele: | Spiel | Prozessname | |---|---| | Crimson Desert | `CrimsonDesert` | | Wuthering Waves | `Client-Win64-Shipping` | **Wichtig:** Der vollständige Pfad zu `pythonw.exe` im `.venv` ist nötig, damit die Abhängigkeiten (`psutil`, `pywin32`) gefunden werden. `py` oder `python` nutzt sonst möglicherweise eine andere Python-Installation. ## Ablauf zur Laufzeit ```text Zeitachse ───────────────────────────────────────────────────────────────────── t=0s Steam startet das Script mit %command% oder Sunshine mit --process "GameName" t=0.1s focusfix.py beginnt zu arbeiten → Steam-Modus: startet zuerst das Spiel und leitet den Prozessnamen ab → Danach: sucht nach Prozess "GameName" t=~3s Spielprozess gestartet, ggf. Splashscreen erscheint → Script findet den Prozess (PID) → Sucht nach Fenster mit Breite >= 800px → Kleine Splashscreens werden ignoriert t=~10s Hauptfenster des Spiels erscheint → Script findet das Fenster → Bevorzugt Fenster mit Titel → Wartet 3 Sekunden → Wartet bis Fenstergröße stabil ist t=~13s Phase 3: Fokus-Erzwingung → Spielfenster wird in den Vordergrund gebracht → Bei Misserfolg: bis zu 15 Wiederholungen mit je 2s Pause → Beobachtet Fensterwechsel (z.B. Splash → Hauptfenster) → Ignoriert problematische titellose Overlay-Fenster t=~15s Erfolg → focusfix.py beendet sich → Logdatei enthält den vollständigen Ablauf ``` ## Dateireferenz ### focusfix.py #### Funktionsübersicht | Funktion | Beschreibung | |---|---| | `derive_process_name(command)` | Leitet den Prozessnamen aus dem ersten Element eines Spielbefehls ab | | `launch_game(command)` | Startet das eigentliche Spiel im Steam-Wrapper-Modus | | `find_process_pids(name)` | Sucht laufende Prozesse nach Name (Teilstring, case-insensitiv), gibt PIDs zurück | | `find_game_window(pids)` | Findet das Hauptfenster der gegebenen PIDs, bevorzugt Fenster mit Titel und dann nach Größe | | `wait_for_stable_size(hwnd)` | Wartet bis die Fenstergröße sich stabilisiert hat | | `is_foreground(hwnd)` | Prüft ob ein Fenster aktuell im Vordergrund ist | | `bring_to_front(hwnd)` | Erzwingt den Vordergrundfokus für ein Fenster | | `main()` | Orchestriert Startmodus, Prozesssuche, Fenstersuche und Fokus-Erzwingung | #### `main()` — Ablauf 1. **Startphase:** Entweder wird der Prozessname direkt über `--process` übergeben oder im Steam-Modus aus dem Spielbefehl abgeleitet. 2. **Steam-Wrapper-Modus:** Falls ein Spielbefehl übergeben wurde, startet das Script das Spiel selbst. 3. **Phase 1 — Prozess-Suche:** Pollt alle 0,5 Sekunden nach dem Prozessnamen. Bricht nach `--timeout` Sekunden ab. 4. **Phase 2 — Fenster-Suche:** Pollt alle 0,5 Sekunden nach einem sichtbaren Fenster mit ausreichender Größe. Nach Fund: 3 Sekunden Wartezeit für die vollständige Initialisierung. 5. **Phase 3 — Fokus mit Fensterwechsel-Erkennung:** Bis zu 15 Versuche den Spielfokus zu erzwingen, mit je 2 Sekunden Pause. Beobachtet dabei, ob sich das Fenster ändert und fokussiert das neue Fenster erneut. ### launch.vbs `launch.vbs` ist nur noch ein Legacy-Wrapper. Die aktuelle empfohlene Steam-Konfiguration nutzt `focusfix.py` direkt. Die Datei kann als Fallback im Repository bleiben, wird für den normalen Ablauf aber nicht mehr benötigt. ## Konfigurationsparameter ### Kommandozeilenargumente | Argument | Pflicht | Standard | Beschreibung | |---|---|---|---| | `--process` | Nein | — | Teilstring des Prozessnamens (case-insensitiv, ohne `.exe`) | | `--timeout` | Nein | `120` | Maximale Wartezeit in Sekunden für Prozess- und Fensterfund | | `command` | Nein | — | Steam-Spielbefehl, z.B. `%command%`; startet das Spiel und leitet den Prozessnamen automatisch ab | Es muss entweder `--process` oder ein Spielbefehl übergeben werden. ### Konstanten in `focusfix.py` | Konstante | Wert | Beschreibung | |---|---|---| | `MIN_WINDOW_WIDTH` | `800` | Mindestbreite in Pixeln, um Splashscreens auszufiltern | | `FOCUS_RETRIES` | `15` | Maximale Anzahl der Fokus-Versuche | | `FOCUS_RETRY_DELAY` | `2.0` | Sekunden zwischen Fokus-Versuchen | ### Relevante Pfade | Pfad | Zweck | |---|---| | `C:\Path\To\FocusFix\.venv\Scripts\pythonw.exe` | Beispielpfad zum Python-Interpreter ohne Konsolenfenster, für Steam empfohlen | | `C:\Path\To\FocusFix\focusfix.py` | Beispielpfad zum Hauptscript | Bei einer Änderung des Installationsverzeichnisses müssen diese Pfade in den Steam-Startoptionen oder in Sunshine angepasst werden. ## Windows API — Technischer Hintergrund ### Das SetForegroundWindow-Problem Seit Windows 2000 verhindert das Betriebssystem, dass beliebige Prozesse den Vordergrundfokus "stehlen" können. `SetForegroundWindow(hwnd)` funktioniert nur eingeschränkt. Andernfalls blinkt das Fenster lediglich in der Taskleiste. Da `focusfix.py` als eigenständiger Prozess läuft und nicht Teil der normalen Spiel-UI ist, erfüllt es diese Bedingungen oft nicht direkt. ### Workaround-Strategie Das Script kombiniert drei Techniken: **1. Alt-Key-Simulation (`keybd_event`)** Simuliert einen Tastendruck der Alt-Taste. Windows behandelt den aufrufenden Prozess danach eher wie einen Prozess mit Benutzerinteraktion. **2. Thread-Input-Verbindung (`AttachThreadInput`)** Verbindet die Eingabewarteschlangen zweier Threads. Dadurch kann das Ziel-Fenster zuverlässiger in den Vordergrund gebracht werden. **3. Mehrfache Fokus-Befehle** `ShowWindow`, `BringWindowToTop`, `SetForegroundWindow`, `SetFocus` und `SetActiveWindow` adressieren jeweils einen leicht anderen Aspekt des Fenster-Fokus unter Windows. ### Splashscreen-Erkennung Viele Spiele zeigen beim Start einen Splashscreen, bevor das Hauptfenster erscheint. Beide gehören zum selben Prozess. Das wichtigste Unterscheidungsmerkmal ist hier die Fenstergröße. Kleine Startfenster werden über `MIN_WINDOW_WIDTH = 800` aussortiert. Zusätzlich werden Fenster mit Titel bevorzugt, um titellose Overlay-Fenster nicht fälschlich als Hauptfenster zu wählen. ## Manuelles Testen ### Direktmodus testen ```bash cd C:\Path\To\FocusFix .venv\Scripts\python focusfix.py --process "CrimsonDesert" --timeout 5 ``` ### Steam-Wrapper-Modus testen ```bash .venv\Scripts\python focusfix.py "C:\Path\To\Game\TestGame.exe" ``` Die Laufzeit-Logs landen in `focusfix.log`. Bei Ausführung über `pythonw.exe` gibt es keine sichtbare Ausgabe. Bei manuellen Starts mit `python.exe` können Argument- oder Interpreter-Fehler weiterhin in der Konsole erscheinen. ### Gesamtablauf testen 1. Steam-Startoptionen wie oben konfigurieren. 2. Spiel über Steam starten. 3. Erwartung: Spielfenster erscheint nach dem Ladescreen im Vordergrund, ohne zusätzliches Konsolenfenster. 4. `focusfix.log` prüfen. ## Debugging Alle Informationen werden in `focusfix.log` protokolliert: - Gefundene Kandidaten-Fenster mit Handle, Titel, Größe und PID - Fensterwechsel und ignorierte titellose Fenster - Fokus-Versuche mit Ergebnis - Fehler mit Traceback ### Häufige Probleme | Symptom | Ursache | Lösung | |---|---|---| | Spiel startet nicht | Steam-Startoptionen fehlerhaft | Prüfen ob `%command%` am Ende steht und `pythonw.exe`/`focusfix.py` korrekt referenziert werden | | `ModuleNotFoundError: psutil` | Falsche Python-Installation | Vollständigen Pfad zu `.venv\Scripts\pythonw.exe` verwenden | | Script findet Prozess nicht | Prozessname stimmt nicht oder Launcher-Kette ist ungewöhnlich | Mit `--process` testen oder Log prüfen | | Fenster wird nicht fokussiert | Windows-Restriktion zu streng | Retry-Anzahl/Delay in Konstanten erhöhen | | Alt-Taste bleibt hängen | Spiel mit Admin-Rechten, Script ohne | Script ebenfalls als Admin starten | | CMD-Fenster sichtbar | `python.exe` statt `pythonw.exe` | Steam-/Sunshine-Konfiguration prüfen | | Verdunkeltes/blockiertes Spiel | Titelloses Overlay-Fenster | Log prüfen; sollte meist durch Fenster-mit-Titel-Bevorzugung abgefangen werden | ### Fenster-Inspektion per PowerShell ```powershell Get-Process -Name GameName | Select-Object Id, Name, MainWindowTitle, MainWindowHandle ``` ## Bekannte Einschränkungen - **Kein Admin erforderlich**, aber manche Spiele starten mit erhöhten Rechten. Dann muss das Script ebenfalls als Admin laufen. - **Exklusiver Vollbildmodus:** Spiele im exklusiven Vollbild können sich anders verhalten. - **Anti-Cheat:** Manche Anti-Cheat-Systeme könnten Eingabesimulation oder Fokus-Workarounds blockieren. - **Mehrere Monitore:** `find_game_window` wählt das Fenster unabhängig vom Monitor. - **Nicht universell für jedes Spiel:** Das Tool ist generisch für viele ähnliche Fälle, aber nicht garantiert für jeden Launcher, jedes Anti-Cheat-System oder jedes ungewöhnliche Fenster-/Startverhalten.