fix: forțează SetForegroundWindow prin ALT-key pentru anti focus-stealing

pygetwindow.activate() raporta succes dar Windows refuza silent ridicarea
ferestrei TS (confirmat în audit: window_focused cu titlul corect, dar
screenshot-ul manual tot prindea app-ul de deasupra). Trucul standard:
keybd_event(ALT) resetează focus-lock-ul, apoi SetForegroundWindow via
ctypes pe hwnd direct. Fallback la win.activate() dacă lipsește _hWnd.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-21 16:22:47 +03:00
parent 9e0202c9ee
commit 2c1dae14fc

View File

@@ -202,6 +202,12 @@ def _focus_window_by_title(needle: str) -> str | None:
Returnează titlul găsit sau None (niciun match, Win32 a refuzat activate, Returnează titlul găsit sau None (niciun match, Win32 a refuzat activate,
sau `pygetwindow` lipsește — caz normal pe Linux/WSL unde rulează doar testele). sau `pygetwindow` lipsește — caz normal pe Linux/WSL unde rulează doar testele).
pygetwindow.activate() folosește SetForegroundWindow, care Windows îl
refuză silent când procesul apelant nu e cel care deține focus-ul
(protecția anti focus-stealing introdusă în Win2000). Trucul standard:
trimiți un ALT press/release ca să resetezi lock-ul, apoi restore +
SetForegroundWindow prin ctypes pe hwnd direct.
""" """
try: try:
import pygetwindow as gw # type: ignore[import-untyped] import pygetwindow as gw # type: ignore[import-untyped]
@@ -213,13 +219,25 @@ def _focus_window_by_title(needle: str) -> str | None:
if not matches: if not matches:
return None return None
win = matches[0] win = matches[0]
hwnd = getattr(win, "_hWnd", None)
if hwnd is None:
try: try:
if win.isMinimized:
win.restore()
win.activate() win.activate()
return win.title return win.title
except Exception: except Exception:
return None return None
try:
import ctypes
user32 = ctypes.windll.user32
# VK_MENU=0x12 (ALT), KEYEVENTF_KEYUP=0x02 — unlocks focus protection.
user32.keybd_event(0x12, 0, 0, 0)
user32.keybd_event(0x12, 0, 0x02, 0)
user32.ShowWindow(hwnd, 9) # SW_RESTORE
user32.BringWindowToTop(hwnd)
user32.SetForegroundWindow(hwnd)
return win.title
except Exception:
return None
def _cmd_run(args) -> None: def _cmd_run(args) -> None: