Send one request per keypress carrying the measured duration

The browser now times the press with performance.now() and posts hold_ms once,
instead of sending down and up as two requests. Halves the round trips per key and
makes press duration independent of the link.

Deletes test_sends_down_and_up_not_just_tap: it asserted the behaviour being
replaced, so keeping it would have meant asserting the bug.

MIN_HOLD_MS is injected into the page from webui.py so the two agree on the floor,
which exists because anything under the firmware's 20 ms debounce does not register
at all -- a very fast click still has to ask for 60 ms.

Verified over a simulated 400 ms link: three keys in 1.58 s where the old design
needed ~2.45 s, and a 120 ms press opens the menu (gScreenToDisplay 0 -> 1) with
DOWN then moving the cursor. The generated page also passes node --check.
This commit is contained in:
mckero committed 2026-08-28 05:41:59 +01:00
1 parent e7f3f9da76
commit 7fe605eeaf
2 files changed
+40 -22

No files matched your search

+17 -12
View File
@@ -218,21 +218,23 @@ def render_index(scale: int) -> str:
<div class="pad">{grid}</div>
</div>
<div id="status">connecting...</div>
<p class="hint">Hold a key to send a long press: over 400 ms the firmware
<p class="hint">How long you hold a key is measured here and sent as a number,
so a slow link cannot turn a tap into a long press. Over 400 ms the firmware
treats it as held, which is a different event. Arrows move, Enter is MENU,
Esc is EXIT, digits map straight through. No PTT button -- the keypad model
has no PTT line.</p>
</div>
<script>
const BINDINGS = {json.dumps(KEY_BINDINGS)};
const held = new Set();
const MIN_HOLD_MS = {MIN_HOLD_MS};
const pressedAt = new Map();
async function send(key, action) {{
async function sendKey(key, holdMs) {{
try {{
await fetch('/api/key', {{
method: 'POST',
headers: {{'Content-Type': 'application/json'}},
body: JSON.stringify({{key: key, action: action}})
body: JSON.stringify({{key: key, hold_ms: Math.round(holdMs)}})
}});
}} catch (err) {{
document.getElementById('status').textContent = 'send failed: ' + err;
@@ -244,18 +246,21 @@ function mark(key, on) {{
.forEach(el => el.classList.toggle('active', on));
}}
// Press duration is decided by the browser, not the server.
// One request per key, carrying the duration as a number. Sending 'down' and
// 'up' as two requests would put the network round trip inside the press: at
// 400 ms RTT every tap arrived as a ~407 ms hold, which the firmware dispatches
// as a held key and MAIN_Key_MENU ignores.
function down(key) {{
if (held.has(key)) return;
held.add(key);
if (pressedAt.has(key)) return;
pressedAt.set(key, performance.now());
mark(key, true);
send(key, 'down');
}}
function up(key) {{
if (!held.has(key)) return;
held.delete(key);
const started = pressedAt.get(key);
if (started === undefined) return;
pressedAt.delete(key);
mark(key, false);
send(key, 'up');
sendKey(key, Math.max(performance.now() - started, MIN_HOLD_MS));
}}
document.querySelectorAll('.key').forEach(btn => {{
@@ -281,7 +286,7 @@ addEventListener('keyup', ev => {{
}});
// Release on blur, so losing focus mid-press cannot leave a key stuck down.
addEventListener('blur', () => {{
[...held].forEach(up);
[...pressedAt.keys()].forEach(up);
fetch('/api/release-all', {{method: 'POST'}}).catch(() => {{}});
}});