mirror of
https://github.com/Andre-Bzk/FocusFix.git
synced 2026-11-15 15:43:12 +01:00
Prototyp
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
.venv/
|
||||||
|
README.md
|
||||||
|
CLAUDE.md
|
||||||
@@ -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()
|
||||||
+15
@@ -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
|
||||||
Reference in New Issue
Block a user