From c8eb510bc6e1445905039c19802ae69380483100 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Thu, 23 Jul 2026 10:09:15 +0100 Subject: [PATCH 1/2] web: suppress the browser default on held-key auto-repeats The keydown handler bailed out on e.repeat before reaching preventDefault, so while the initial press of a captured key was consumed, every auto-repeat fell through to the browser and its default action fired - holding a cursor key scrolled the page under the game. Skipping repeats is still right for the emulator side (the Amiga keyboard sends one down code per press; the guest OS does its own key repeat), so track which codes the first keydown consumed and call preventDefault on their repeats without re-forwarding them. The keyup handler clears the entry, which also covers a hold that survives a focus loss. --- crates/copperline-web/www/try.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/crates/copperline-web/www/try.js b/crates/copperline-web/www/try.js index 1b5cac3..bed6198 100644 --- a/crates/copperline-web/www/try.js +++ b/crates/copperline-web/www/try.js @@ -996,13 +996,26 @@ function updatePadStatus(releasedPort1) { // --- keyboard ------------------------------------------------------------ +// Auto-repeat keydowns must not reach the emulator (the Amiga keyboard +// sends one down code; the guest OS does its own repeat), but the browser +// default still has to be suppressed on every repeat or holding a cursor +// key scrolls the page. Track which codes the first keydown consumed. +const consumedKeys = new Set(); window.addEventListener('keydown', (e) => { - if (!emu || !running || e.repeat) return; - if (joystickKey(e.code, true) || emu.key_event(e.code, true)) e.preventDefault(); + if (!emu || !running) return; + if (e.repeat) { + if (consumedKeys.has(e.code)) e.preventDefault(); + return; + } + if (joystickKey(e.code, true) || emu.key_event(e.code, true)) { + consumedKeys.add(e.code); + e.preventDefault(); + } }); window.addEventListener('keyup', (e) => { if (!emu || !running) return; - if (joystickKey(e.code, false) || emu.key_event(e.code, false)) e.preventDefault(); + const consumed = joystickKey(e.code, false) || emu.key_event(e.code, false); + if (consumedKeys.delete(e.code) || consumed) e.preventDefault(); }); // --- mouse --------------------------------------------------------------- From ed8183b726088ecf184f99c365b45085aea68889 Mon Sep 17 00:00:00 2001 From: Andrew Hutchings Date: Thu, 23 Jul 2026 10:21:22 +0100 Subject: [PATCH 2/2] web: keep the consumed-key set from going stale across stop or blur A hold that spans an emulator stop skipped the keyup delete behind the running check, and a hold broken by a focus loss gets no keyup at all. Delete before the running check on keyup, and let a non-repeat keydown that consumes nothing clear any leftover entry for its code, so the set corrects itself no matter how a hold ends. --- crates/copperline-web/www/try.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/copperline-web/www/try.js b/crates/copperline-web/www/try.js index bed6198..ea6c602 100644 --- a/crates/copperline-web/www/try.js +++ b/crates/copperline-web/www/try.js @@ -1010,12 +1010,18 @@ window.addEventListener('keydown', (e) => { if (joystickKey(e.code, true) || emu.key_event(e.code, true)) { consumedKeys.add(e.code); e.preventDefault(); + } else { + consumedKeys.delete(e.code); } }); window.addEventListener('keyup', (e) => { + // Delete before the running check: a hold that spans an emulator stop + // or a focus loss must not leave a stale entry behind. + const consumed = consumedKeys.delete(e.code); if (!emu || !running) return; - const consumed = joystickKey(e.code, false) || emu.key_event(e.code, false); - if (consumedKeys.delete(e.code) || consumed) e.preventDefault(); + if (joystickKey(e.code, false) || emu.key_event(e.code, false) || consumed) { + e.preventDefault(); + } }); // --- mouse ---------------------------------------------------------------