diff --git a/mobile/index.html b/mobile/index.html
index 361e51f..74a12e5 100644
--- a/mobile/index.html
+++ b/mobile/index.html
@@ -740,24 +740,35 @@ function ownComputer() { const n = botName(S.open); return n ? n + " 的電腦"
$("b-screen").onclick = $("b-screen2").onclick = () => { S.back = S.view; show("screen"); $("seg").children[0].textContent = ownComputer(); $("seg").children[1].style.display = S.mac ? "" : "none"; startFrames(); };
$("b-back2").onclick = () => show(S.back === "chat" ? "chat" : "list");
document.querySelectorAll("#seg button").forEach((b) => b.onclick = () => { document.querySelectorAll("#seg button").forEach((x) => x.classList.toggle("on", x === b)); S.screen = b.dataset.w; $("k-wake").style.display = S.screen === "box" ? "" : "none"; startFrames(); });
-let busyFrame = false, lastUrl = null;
-function startFrames() { stopFrames(); frame(); S.frameTimer = setInterval(frame, 1000); }
-function stopFrames() { clearInterval(S.frameTimer); S.frameTimer = null; }
-async function frame() {
- if (busyFrame || document.hidden) return; busyFrame = true;
- try {
- const r = await api(base() + "api/screen/" + S.screen, {raw: true});
- if (!r.ok) { const v = await r.json().catch(() => ({})); $("stage").innerHTML = `
${esc(v.error || "看不到畫面")}
`; $("screen-live").textContent = ""; }
- else {
- const sz = (r.headers.get("X-Size") || "0x0").split("x").map(Number); S.size = sz;
+// The bots' computer sends a picture the moment its screen changes: ask for the one after the
+// picture shown (X-Seq), again as soon as it arrives. (It was one picture a second, so a tap
+// showed its result a second or two later.) This computer's own screen: a picture at a time.
+let lastUrl = null;
+const nap = (ms) => new Promise((r) => setTimeout(r, ms));
+function startFrames() { stopFrames(); const run = S.frameRun = {}; frames(run); }
+function stopFrames() { S.frameRun = null; }
+async function frames(run) {
+ let seq = 0;
+ while (S.frameRun === run) {
+ if (document.hidden) { await nap(800); continue; }
+ try {
+ const live = S.screen === "box";
+ const r = await api(base() + "api/screen/" + S.screen + (live ? "?after=" + seq : ""), {raw: true});
+ if (S.frameRun !== run) return;
+ if (!r.ok) { const v = await r.json().catch(() => ({})); $("stage").innerHTML = `${esc(v.error || "看不到畫面")}
`; $("screen-live").textContent = ""; await nap(1500); continue; }
+ const n = +(r.headers.get("X-Seq") || 0);
+ if (live && n && n === seq) continue;
+ seq = n;
+ S.size = (r.headers.get("X-Size") || "0x0").split("x").map(Number);
const url = URL.createObjectURL(await r.blob());
let img = $("stage").querySelector("img");
if (!img) { $("stage").innerHTML = ""; img = document.createElement("img"); img.draggable = false; $("stage").appendChild(img); bindStage(img); }
img.src = url; if (lastUrl) URL.revokeObjectURL(lastUrl); lastUrl = url;
$("screen-live").innerHTML = `即時`;
- }
- } catch (e) { $("screen-live").textContent = ""; }
- busyFrame = false;
+ // At most about 8 pictures a second (an animated page would send all it can).
+ await nap(!live || !n ? 700 : 120);
+ } catch (e) { $("screen-live").textContent = ""; await nap(1500); }
+ }
}
function point(img, e) {
const r = img.getBoundingClientRect();
@@ -777,7 +788,7 @@ function act(img, e, button) {
input2({kind: "click", x: p.x, y: p.y, button});
}
async function input2(body) {
- try { await api(base() + "api/screen/" + S.screen, {json: body}); setTimeout(frame, 250); } catch (e) { toast(e.message); }
+ try { await api(base() + "api/screen/" + S.screen, {json: body}); } catch (e) { toast(e.message); }
}
document.querySelectorAll("[data-key]").forEach((b) => b.onclick = () => input2({kind: "key", key: b.dataset.key}));
document.querySelectorAll("[data-scroll]").forEach((b) => b.onclick = () => input2({kind: "scroll", x: Math.round(S.size[0] / 2), y: Math.round(S.size[1] / 2), direction: b.dataset.scroll, amount: 5}));
@@ -861,7 +872,7 @@ if ("serviceWorker" in navigator) navigator.serviceWorker.addEventListener("mess
if (d.host && d.host !== S.host && S.relay) { S.host = d.host; localStorage.setItem("anybot.host", S.host); S.version = 0; }
if (d.bot) openChat(+d.bot);
});
-document.addEventListener("visibilitychange", () => { if (!document.hidden && S.view === "screen") frame(); });
+document.addEventListener("visibilitychange", () => { if (!document.hidden && S.view === "screen") startFrames(); });
hello();