mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-06 04:04:21 +02:00
Devices: the app waits for its old server before starting the new one; clipboard sends keep their headset (review round 31)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
cb6f294299
commit
a9740ad6f2
4 files changed
+13
-5
No files matched your search
+8
-2
@@ -154,10 +154,16 @@ async function startServer() {
|
||||
|
||||
// Closing stdin lets server.py close its SSH connections and exit (the only clean
|
||||
// way on Windows); SIGTERM does the same elsewhere.
|
||||
// Resolves once it has exited (or after 20 s), so a replacement can take the server
|
||||
// lock: server.py allows one per user.
|
||||
function endServer(child) {
|
||||
const gone = child.exitCode !== null || child.signalCode !== null ? Promise.resolve()
|
||||
: new Promise((resolve) => child.once("exit", resolve));
|
||||
try { child.stdin.end(); } catch {}
|
||||
if (!IS_WIN) child.kill("SIGTERM");
|
||||
setTimeout(() => { if (child.exitCode === null && child.signalCode === null) child.kill(); }, 5000).unref();
|
||||
// server.py ignores a second SIGTERM while it shuts down, so the fallback is a hard kill.
|
||||
setTimeout(() => { if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL"); }, 12000).unref();
|
||||
return Promise.race([gone, new Promise((resolve) => setTimeout(resolve, 20000).unref())]);
|
||||
}
|
||||
|
||||
function stopServer() {
|
||||
@@ -182,7 +188,7 @@ async function restartServer() {
|
||||
const old = server;
|
||||
server = null;
|
||||
url = null;
|
||||
if (old) endServer(old);
|
||||
if (old) await endServer(old);
|
||||
await load();
|
||||
}
|
||||
|
||||
|
||||
@@ -248,7 +248,8 @@ class OneServer(unittest.TestCase):
|
||||
|
||||
def test_a_second_server_is_refused_until_the_first_exits(self):
|
||||
data = tempfile.mkdtemp(prefix="frame-one-server-")
|
||||
env = {**os.environ, "FRAME_CONTROL_DATA_DIR": data, "FRAME_ALIAS": "frame-control-test.invalid"}
|
||||
env = {**os.environ, "FRAME_CONTROL_DATA_DIR": data, "FRAME_ALIAS": "frame-control-test.invalid",
|
||||
"FRAME_CONTROL_SERVER_WAIT": "1"}
|
||||
first = self.start(env)
|
||||
self.assertIn("Frame Control on", first.stdout.readline())
|
||||
second = subprocess.run([sys.executable, str(ROOT / "ui" / "server.py"), "--port", "0"], env=env,
|
||||
|
||||
+2
-1
@@ -1503,9 +1503,10 @@ $("clipSend").onclick = () => {
|
||||
// In the app, Electron reads the clipboard; in a browser, the server does.
|
||||
async function sendComputerClipboard() {
|
||||
if (!window.frameApp) return api("/api/clipboard", { fromComputer: true });
|
||||
const device = window.connDevice; // the headset it was sent to, not one switched to while reading
|
||||
const text = await window.frameApp.readClipboard();
|
||||
if (!text) throw new Error("The clipboard is empty (or holds something other than text)");
|
||||
return api("/api/clipboard", { text });
|
||||
return api("/api/clipboard", { text }, device);
|
||||
}
|
||||
$("clipMac").onclick = () => act($("clipMac").textContent, sendComputerClipboard, $("clipMac"));
|
||||
|
||||
|
||||
+1
-1
@@ -1662,7 +1662,7 @@ def one_server():
|
||||
edit the headsets on their own, and could move each other's installs to another
|
||||
headset. Held until this process exits. (FRAME_CONTROL_DATA_DIR gives a second,
|
||||
separate one, as the tests do.)"""
|
||||
lock = frame_devices.file_lock(frame_host.data_dir("server.lock"), timeout=8) # the app restarting its server
|
||||
lock = frame_devices.file_lock(frame_host.data_dir("server.lock"), timeout=float(os.environ.get("FRAME_CONTROL_SERVER_WAIT") or 20)) # while the app restarts it
|
||||
try:
|
||||
lock.__enter__()
|
||||
except OSError:
|
||||
|
||||
Reference in new issue
Block a user