← Commit history

Ship 0.3.1 client-side idle auto-disconnect safeguard (GetLastInputInfo); host untouched

John Lauer ·6f05b6b3f9 ·4mo ago ·parent 584ee0a
2 files changed +50−4
BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.3.0+0.3.1
server.py+49−3
@@ -23,10 +23,11 @@ import sys import threading import time import urllib.request+import ctypes from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer  BRIDGE_NAME = "rustdesk"-BRIDGE_VERSION = "0.3.0"+BRIDGE_VERSION = "0.3.1" DEFAULT_PORT = 8810 RUSTDESK_DL_URL = "https://github.com/rustdesk/rustdesk/releases/download/1.4.8/rustdesk-1.4.8-x86_64.exe" @@ -38,6 +39,13 @@ _START = time.time() _dl_lock = threading.Lock() _dl = {"state": "idle", "pct": 0, "path": None, "error": None} +# ---- safeguard: auto-disconnect a session when THIS (client) machine goes idle ----+# A live RustDesk client session keeps the local display awake (like media playback).+# So if the user walks away (no input on this laptop) we drop the session -> the screen+# can sleep/screensaver normally. Active use resets the timer (mouse/keys into the+# session ARE local input). The HOST is untouched. 0 disables. Default 10 min.+IDLE_DISCONNECT_SECS = int(os.environ.get("RUSTDESK_IDLE_DISCONNECT_SECS", "600"))+  # ---------------------------------------------------------------- helpers def _candidate_exes():@@ -114,6 +122,42 @@ def _download_client():             _dl.update(state="error", error=str(e))  +def _user_idle_secs():+    """Seconds since the last user input on THIS (client) machine. 0 on non-Windows/error."""+    try:+        class _LII(ctypes.Structure):+            _fields_ = [("cbSize", ctypes.c_uint), ("dwTime", ctypes.c_uint)]+        lii = _LII()+        lii.cbSize = ctypes.sizeof(lii)+        if ctypes.windll.user32.GetLastInputInfo(ctypes.byref(lii)):+            tick = ctypes.windll.kernel32.GetTickCount()+            return max(0, (int(tick) - int(lii.dwTime)) // 1000)+    except Exception:+        pass+    return 0+++def _idle_monitor():+    """Drop active sessions once the local user has been idle past IDLE_DISCONNECT_SECS,+    so the client's screen can sleep. Never touches the host side."""+    while True:+        time.sleep(30)+        limit = IDLE_DISCONNECT_SECS+        if limit <= 0:+            continue+        sess = _sessions()+        if sess and _user_idle_secs() >= limit:+            for s in sess:+                try:+                    subprocess.run(["taskkill", "/PID", str(s["pid"]), "/F"],+                                   capture_output=True, timeout=10, creationflags=_NO_WINDOW)+                except Exception:+                    pass+            sys.stderr.write("rustdesk bridge: auto-disconnected %d idle session(s) after %ds idle\n"+                             % (len(sess), limit))+            sys.stderr.flush()++ # ---------------------------------------------------------------- verbs def v_connect(args):     to = (args.get("to") or args.get("id") or args.get("ip") or "").strip()@@ -136,7 +180,7 @@ def v_connect(args):                 "_hint": "The client exe was found but wouldn't launch. Check rustdesk_client_status; the exe path may be stale."}     return {"success": True, "output": "connecting to %s" % to,             "to": to, "pid": p.pid, "withPassword": bool(pw), "exe": exe,-            "_hint": "Session window opens as '%s - Remote Desktop - RustDesk'. Confirm with rustdesk_client_status; close with rustdesk_disconnect. Without a password it waits at a prompt on the controlling machine." % to}+            "_hint": "Session window opens as '%s - Remote Desktop - RustDesk'. NOTE: while open this keeps the CONTROLLING machine's display awake (RustDesk treats it like live playback) - the bridge auto-disconnects after %ds of local idle so the screen can still sleep; close sooner with rustdesk_disconnect. Confirm with rustdesk_client_status; without a password it waits at a prompt on the controlling machine." % (to, IDLE_DISCONNECT_SECS)}   def v_disconnect(args):@@ -366,8 +410,10 @@ def _parse_port(argv): def main():     port = _parse_port(sys.argv[1:])     srv = ThreadingHTTPServer(("127.0.0.1", port), Handler)-    sys.stderr.write("rustdesk bridge v%s listening on 127.0.0.1:%d\n" % (BRIDGE_VERSION, port))+    sys.stderr.write("rustdesk bridge v%s listening on 127.0.0.1:%d (idle-disconnect %ds)\n"+                     % (BRIDGE_VERSION, port, IDLE_DISCONNECT_SECS))     sys.stderr.flush()+    threading.Thread(target=_idle_monitor, daemon=True).start()     srv.serve_forever()