diff --git a/tests/test_devices.py b/tests/test_devices.py index 10d77e2..4a44b9b 100644 --- a/tests/test_devices.py +++ b/tests/test_devices.py @@ -284,6 +284,9 @@ class Registry(Base): self.assertEqual(self.reg.update_device(b["id"], name="Desk", user="deck", port="2222")["port"], 2222) with self.assertRaises(fd.DeviceError): self.reg.update_device(b["id"], user="bad user") + with self.assertRaises(fd.DeviceError): + self.reg.update_device(b["id"], user="steam", port="bad") + self.assertEqual(self.reg.get(b["id"])["user"], "deck") # a rejected edit changes nothing self.reg.remove_device(b["id"]) self.assertEqual(self.reg.active(), a["id"]) with self.assertRaises(fd.DeviceError): diff --git a/ui/frame_devices.py b/ui/frame_devices.py index 911f006..a78470f 100644 --- a/ui/frame_devices.py +++ b/ui/frame_devices.py @@ -519,12 +519,11 @@ class Registry: """-> the device after the change. The caller mirrors user and port into ~/.ssh/config.""" with self.lock: d = self._find(device_id) - if name is not None: - d["name"] = check_text(name, "name") or d["alias"] - if user is not None: - d["user"] = check_user(user) - if port is not None: - d["port"] = check_port(port) + # Check everything first: a rejected edit changes nothing. + name = None if name is None else (check_text(name, "name") or d["alias"]) + user = None if user is None else check_user(user) + port = None if port is None else check_port(port) + d.update({k: v for k, v in (("name", name), ("user", user), ("port", port)) if v is not None}) self.save() return copy.deepcopy(d) diff --git a/ui/frame_link.py b/ui/frame_link.py index cdb5ac9..96c73bc 100644 --- a/ui/frame_link.py +++ b/ui/frame_link.py @@ -647,7 +647,11 @@ class Link: def handshake(self, device, a, found, user): """SSH to one address, following ssh -v through stages 3-5. -> "ok", "next" (try another address) or "stop".""" - opts = self.host_opts(device, a["host"]) + # An IPv4 address that answered is used as is, so ssh doesn't look the name up + # again and try an address that didn't answer (a dead IPv6 route, say). IPv6 + # answers keep the name: a link-local one needs its zone, which ssh adds itself. + ip = found.get("ip") or "" + opts = self.host_opts(device, ip if re.fullmatch(r"\d{1,3}(\.\d{1,3}){3}", ip) else a["host"]) alias = device["alias"] with self.route_lock: if self.attempt_gen != self.gen: diff --git a/ui/index.html b/ui/index.html index dd584c4..490d7cf 100644 --- a/ui/index.html +++ b/ui/index.html @@ -2539,6 +2539,13 @@ function onConnection(s) { $("canvas").hidden = true; $("viewerEmpty").hidden = false; $("zoombar").hidden = true; ["stamp", "srcBadge", "asleep"].forEach(id => $(id).hidden = true); $("saveBtn").disabled = true; + // Lists and their buttons (Remove, Launch…) were the other headset's: clear them + // before anyone clicks one, until the new headset's arrive. + ["games", "titleList", "andApps", "flatpaks", "shotGrid", "gmGrid"].forEach(id => { + if ($(id)) $(id).innerHTML = `