From c69ea6266ff6e2fe718f874afebf07d4df1a8199 Mon Sep 17 00:00:00 2001 From: saphid <4596216+saphid@users.noreply.github.com> Date: Mon, 28 Sep 2026 23:03:27 +1000 Subject: [PATCH] Devices: fixes from review round 10 - Removing or moving the active headset's address waits for running installs, like switching. - A volume change still waiting to be sent goes to the headset whose slider it was, and a switch cancels it. Co-Authored-By: Claude Opus 5.5 (1M context) --- tests/test_link.py | 4 +++- ui/frame_link.py | 6 ++++-- ui/index.html | 8 +++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/test_link.py b/tests/test_link.py index 04155a9..1a8589e 100644 --- a/tests/test_link.py +++ b/tests/test_link.py @@ -349,7 +349,9 @@ class Connecting(unittest.TestCase): d = self.device("localhost") other = self.reg.add_device("frame-other") for body in ({"action": "use", "id": other["id"]}, {"action": "remove", "id": d["id"]}, - {"action": "update", "id": d["id"], "port": 2222}): + {"action": "update", "id": d["id"], "port": 2222}, + {"action": "address-remove", "id": d["id"], "host": "localhost"}, + {"action": "address-update", "id": d["id"], "host": "localhost", "newHost": "127.0.0.1"}): with self.assertRaises(fd.DeviceError, msg=body): fl.devices_action(self.link, body, open_setup=None, busy=lambda: 1) # Renaming, or changing another headset, is fine. diff --git a/ui/frame_link.py b/ui/frame_link.py index a1b61f6..90eb0d5 100644 --- a/ui/frame_link.py +++ b/ui/frame_link.py @@ -922,8 +922,10 @@ def devices_action(link, body, open_setup, busy=lambda: 0): did = body.get("id") active = link.active_device() is_active = did == active["id"] - moves = action == "use" or (is_active and (action == "remove" or ( - action == "update" and (body.get("user") is not None or body.get("port") is not None)))) + moves = action == "use" or (is_active and ( + action in ("remove", "address-remove") + or (action == "update" and (body.get("user") is not None or body.get("port") is not None)) + or (action == "address-update" and body.get("newHost") not in (None, body.get("host"))))) if moves and busy(): raise frame_devices.DeviceError( f"Wait for what's running on {active['name']} to finish (see the activity bar), then try again") diff --git a/ui/index.html b/ui/index.html index b14f94f..0cebcb0 100644 --- a/ui/index.html +++ b/ui/index.html @@ -925,11 +925,11 @@ const savesToDevice = () => !!(window.frameApp && window.frameApp.saveImages); let devGen = 0; // Answers that don't depend on the headset (this computer's data, the catalogue, jobs). const HEADSET_FREE = /^\/api\/(devices|connection|host|job|titles\/job|webinstall\/job|apk-versions|android\/(catalog|reports)|steam\/search)\b/; -async function api(path, body) { +async function api(path, body, device = window.connDevice) { const gen = devGen; // Changes name the headset the page was showing, so the server refuses one meant // for a headset it has since switched away from. - const dev = window.connDevice ? { "X-Frame-Device": window.connDevice } : {}; + const dev = device ? { "X-Frame-Device": device } : {}; const opts = body === undefined ? { headers: {"X-Frame-UI": UI_KEY} } : { method: "POST", headers: {"Content-Type": "application/json", "X-Frame-UI": UI_KEY, ...dev}, body: JSON.stringify(body) }; let r; @@ -1485,8 +1485,9 @@ function setVolume(pct, muted) { $("vol").oninput = () => { setVolume(+$("vol").value); clearTimeout(volTimer); + const dev = window.connDevice; // the headset whose slider this was, even 250 ms later volTimer = setTimeout(async () => { - try { await api("/api/volume", { level: $("vol").value / 100 }); } catch (e) { toast(e.message, true); } + try { await api("/api/volume", { level: $("vol").value / 100 }, dev); } catch (e) { toast(e.message, true); } volTimer = null; }, 250); }; @@ -2551,6 +2552,7 @@ function onConnection(s) { devGen++; // answers still on their way are about the other headset refreshing = null; // and the next status check must be a new one if (live) toggleLive(false); // the other headset's video or captures + clearTimeout(volTimer); volTimer = null; // a volume change still waiting to be sent viewGen++; // a capture still on its way is the other headset's too lastImg = null; lastShot = null; $("canvas").hidden = true; $("viewerEmpty").hidden = false; $("zoombar").hidden = true;