# FocusFix - Game Foreground Fix for Windows Windows-Hilfsprogramm, das ein Spielfenster automatisch in den Vordergrund bringt. Gedacht für Spiele (z.b. Crimson Desert), 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 - [Schnellstart](#schnellstart) - [Für Nutzer](#fur-nutzer) - [Für Entwickler](#fur-entwickler) - [Problembeschreibung](#problembeschreibung) - [Lösungsansatz](#lösungsansatz) - [Architektur](#architektur) - [Voraussetzungen](#voraussetzungen) - [Direkte Verwendung](#direkte-verwendung) - [Aus Quellcode ausführen](#aus-quellcode-ausfuhren) - [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) --- ## Schnellstart ### Für Nutzer Wenn du FocusFix direkt verwenden willst, ist eine fertige `FocusFix.exe` der empfohlene Weg. Du brauchst dann weder ein Virtual Environment noch ein manuelles `pip install`. Steam-Beispiel: ```text "C:\Path\To\FocusFix\FocusFix.exe" %command% ``` Mit Debug-Logging: ```text "C:\Path\To\FocusFix\FocusFix.exe" -debug %command% ``` ### Für Entwickler Wenn du FocusFix aus dem Repository starten oder weiterentwickeln willst: ```bash cd C:\Path\To\FocusFix py -m pip install -r requirements.txt ``` Danach kannst du `focusfix.py` direkt mit deinem Python-Interpreter oder über `pythonw.exe` verwenden. Steam-Beispiel für die Quellcode-Nutzung mit lokalem `.venv`: ```text "C:\Projekte\FocusFix\.venv\Scripts\pythonw.exe" "C:\Projekte\FocusFix\focusfix.py" %command% ``` Ohne lokales `.venv` und mit globalem Python im `PATH`: ```text "pythonw.exe" "C:\Projekte\FocusFix\focusfix.py" %command% ``` ## 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) | | Steam | Beliebig | Startoptionen müssen konfigurierbar sein | | Python | 3.10+ | Nur für Quellcode-Nutzung erforderlich | | pip-Pakete | `psutil`, `pywin32` | Nur für Quellcode-Nutzung erforderlich | ## Direkte Verwendung Für normale Nutzer ist eine fertige `.exe` der einfachste Weg. ## Aus Quellcode ausführen ```bash cd C:\Path\To\FocusFix py -m pip install -r requirements.txt ``` Optional kannst du dafür auch ein Virtual Environment verwenden. Für Entwickler ist das sinnvoll, für Endanwender aber nicht der Standardweg. ### Dateistruktur im Repository ``` C:\Path\To\FocusFix\ ├── focusfix.py # Hauptscript ├── focusfix.log # Logdatei (nur bei --debug / -debug) ├── launch.vbs # Legacy-Wrapper, optional ├── requirements.txt # Python-Abhängigkeiten für Quellcode-Nutzung ├── 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 für Nutzer: `FocusFix.exe` ```text "C:\Path\To\FocusFix\FocusFix.exe" %command% ``` Mit Debug-Logging: ```text "C:\Path\To\FocusFix\FocusFix.exe" -debug %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 für Endanwender gedacht. Die Pfade im Beispiel sind Platzhalter und müssen durch deinen tatsächlichen Installationspfad ersetzt werden. #### Variante B — Quellcode direkt mit `focusfix.py` ```text "C:\Path\To\Python\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 C — 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% Beispiel: "C:\Projekte\FocusFix\launch.vbs" %command% ``` Mit Debug-Logging: ```text wscript "C:\Path\To\FocusFix\launch.vbs" -debug %command% wscript "C:\Projekte\FocusFix\launch.vbs" -debug %command% ``` `launch.vbs` extrahiert den Prozessnamen aus `%command%`, startet `focusfix.py` unsichtbar mit `--process` und startet danach das eigentliche Spiel. `-d`, `-debug` und `--debug` werden an `focusfix.py` weitergereicht und nicht an das Spiel übergeben. Diese Variante funktioniert weiterhin, ist aber nur noch als Legacy-/Fallback-Weg gedacht, weil `focusfix.py` den Wrapper-Job inzwischen selbst übernehmen kann. Der Wrapper erwartet `focusfix.py` relativ zu seinem eigenen Speicherort. Für Python nutzt er zuerst `.venv\Scripts\pythonw.exe` im selben Ordner und fällt, falls nicht vorhanden, auf `pythonw.exe` aus der globalen Installation beziehungsweise dem `PATH` zurück. **Erklärung:** - `wscript` — startet VBScript ohne sichtbares Konsolenfenster - `"...\launch.vbs"` — Legacy-Wrapper, der `focusfix.py` indirekt startet - `-debug` — optional, aktiviert `focusfix.log` für `focusfix.py` - `%command%` — Steam-Platzhalter mit dem ursprünglichen Spielstartbefehl ### Sunshine / Moonlight Für Nutzer mit `.exe`: ```text "C:\Path\To\FocusFix\FocusFix.exe" --process "Prozessname" ``` Mit Debug-Logging: ```text "C:\Path\To\FocusFix\FocusFix.exe" --debug --process "Prozessname" ``` Für Quellcode-Nutzung in Sunshine als **Befehlsvorbereitung** (Command Preparation) pro Spiel: ```text "C:\Path\To\Python\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:** Wenn du den Quellcode-Weg nutzt, muss der verwendete Python-Interpreter Zugriff auf die installierten Abhängigkeiten aus `requirements.txt` haben. `pythonw.exe` ist für unsichtbare Hintergrundausführung sinnvoller als `python.exe`. ## 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 Nutzung für Endanwender erfolgt über `FocusFix.exe`. Die Datei kann als Fallback im Repository bleiben, wird für den normalen Ablauf aber nicht mehr benötigt. Der Wrapper löst `focusfix.py` relativ zu seinem eigenen Ordner auf, verwendet bevorzugt `.venv\Scripts\pythonw.exe` daneben und fällt sonst auf `pythonw.exe` aus der globalen Python-Installation beziehungsweise dem `PATH` zurück. `-d`/`-debug`/`--debug` kann er an `focusfix.py` weitergeben. ## 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\FocusFix.exe` | Empfohlener Pfad für Endanwender | | `C:\Path\To\Python\pythonw.exe` | Beispielpfad zum Python-Interpreter ohne Konsolenfenster, für Quellcode-Nutzung | | `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 python focusfix.py --process "CrimsonDesert" --timeout 5 ``` ### Steam-Wrapper-Modus testen ```bash python focusfix.py "C:\Path\To\Game\TestGame.exe" ``` Standardmäßig erzeugt FocusFix keine Logdatei. Mit `--debug` oder `-debug` landen die Laufzeit-Logs in `focusfix.log`; bei jedem neuen Scriptstart wird die Datei dabei überschrieben. 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. Falls mit `--debug` gestartet wurde, `focusfix.log` prüfen. ## Debugging Für die Fehlersuche FocusFix mit `--debug` oder `-debug` starten. Dann werden alle Informationen in `focusfix.log` protokolliert; ein vorhandenes Logfile aus einem früheren Lauf wird dabei ersetzt: - 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 `.exe` oder `pythonw.exe`/`focusfix.py` korrekt referenziert werden | | `ModuleNotFoundError: psutil` | Falsche Python-Installation | `py -m pip install -r requirements.txt` ausführen oder den richtigen Python-Interpreter 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 oder `.exe` verwenden | | 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.