hopefully does not lose keyboard control any more for users

This commit is contained in:
2026-07-26 14:09:55 +01:00
parent a7b4f04118
commit d3baa7e050
2 changed files with 58 additions and 17 deletions
+31 -17
View File
@@ -35,7 +35,7 @@ from gi.repository import Gst, GstWebRTC, GstSdp, GLib
import requests
import websockets
logging.basicConfig(level=logging.INFO, format='[gst_agent] %(message)s', stream=sys.stderr)
logging.basicConfig(level=logging.INFO, format='%(asctime)s [gst_agent] %(message)s', stream=sys.stderr)
log = logging.getLogger(__name__)
# ── Configuration ─────────────────────────────────────────────────────────────
@@ -345,16 +345,33 @@ class CSSStreamAgent:
def css_watchdog():
# If cstrike_win64.exe crashes (e.g. Wine c0000409 stack overrun), the
# stream keeps running but shows a black screen forever with no recovery.
# This watchdog detects the crash and relaunches CSS directly via revLoader,
# the same way start.sh does on initial boot. gst_agent keeps running and
# will simply start capturing the relaunched game once it renders.
# This watchdog detects the crash and triggers a restart.
#
# IMPORTANT: this must NOT relaunch CSS directly itself. Doing so
# (a bare `wine revLoader.exe` Popen call) created a second, uncoordinated
# restart authority alongside the signaling server's own kill+relaunch
# logic (manual button / scheduled 5h restart). If both fired close
# together, two separate Wine processes could end up racing to launch
# CSS, producing duplicate/orphaned windows — xdotool's window search
# would then target an arbitrary one, causing keyboard input to land on
# a stale/backgrounded window while video (which captures the whole X
# display, not a specific window) kept working fine. That mismatch is
# exactly the "mouse/video fine, keyboard dead" symptom that was
# reported after a watchdog relaunch overlapped with a manual restart.
#
# So instead we just notify the signaling server, which has the single
# authoritative kill+relaunch implementation (performCssRestart) shared
# by the manual restart button, the scheduled 5-hour restart, and this
# watchdog — never more than one restart in flight at a time.
#
# We only start watching once CSS has been seen alive at least once, to
# avoid racing its initial startup. Require several consecutive misses to
# avoid false positives during a normal controller-triggered relaunch
# (the signaling server kills+relaunches CSS on controller change).
home = os.path.expanduser('~')
revloader = os.path.join(home, 'Counter-Strike Source', 'revLoader.exe')
# avoid false positives during a normal controller-triggered handoff.
restart_url = SIG_URL.replace('ws://', 'http://').replace('wss://', 'https://')
if not restart_url.endswith('/'):
restart_url += '/'
restart_url += 'webclient-force-restart'
seen_alive = False
missing_count = 0
while True:
@@ -381,17 +398,14 @@ def css_watchdog():
# 4 consecutive misses (~20s) — long enough to not collide with the
# signaling server's own kill+relaunch cycle on controller change.
if missing_count >= 4:
log.error('CSS appears crashed — relaunching revLoader')
log.error('CSS appears crashed — requesting restart from signaling server')
try:
env = dict(os.environ, DISPLAY=DISPLAY)
subprocess.Popen(['wine', revloader], env=env,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
log.info('revLoader relaunched by watchdog')
resp = requests.post(restart_url, timeout=5)
log.info(f'restart request sent to {restart_url}, status={resp.status_code}')
except Exception as e:
log.error(f'watchdog relaunch failed: {e}')
# Reset so we give the relaunched game time to come up before
# counting misses again.
log.error(f'watchdog restart request failed: {e}')
# Reset so we give the restart time to complete before counting
# misses again.
seen_alive = False
missing_count = 0