From 2245dbe0e0f5fda2f968b65d9aaf15415002fd5f Mon Sep 17 00:00:00 2001 From: Steven Hugg Date: Fri, 25 Sep 2026 21:52:40 +0200 Subject: [PATCH] fixed input for headless --- src/tools/emutarget.ts | 11 +++++++++-- test/cli/testcli.js | 5 +++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/tools/emutarget.ts b/src/tools/emutarget.ts index b9d9e096..d48126b9 100644 --- a/src/tools/emutarget.ts +++ b/src/tools/emutarget.ts @@ -89,6 +89,10 @@ function installHeadlessVideo() { let pixels: Uint32Array | null = null; let params: { width: number, height: number, rotate?: number, aspect?: number } | null = null; let frameRate = 60; + // the handler a platform registers on its canvas; it's how the IDE + // delivers keys, whether or not there's a Machine behind the platform + let keyHandler: ((key: number, code: number, flags: number) => void) | null = null; + const setKeyboardEvents = function (callback) { keyHandler ??= callback; }; const RasterVideo: any = function (_el: any, width: number, height: number, options?: { rotate?: number, aspect?: number }) { const buffer = new ArrayBuffer(width * height * 4); const datau8 = new Uint8Array(buffer); @@ -99,7 +103,7 @@ function installHeadlessVideo() { pixels = datau32; } this.create = function () { this.width = width; this.height = height; }; - this.setKeyboardEvents = function () { }; + this.setKeyboardEvents = setKeyboardEvents; this.getFrameData = function () { return datau32; }; this.getImageData = function () { return { data: datau8, width, height }; }; this.updateFrame = function () { }; @@ -114,7 +118,7 @@ function installHeadlessVideo() { }; const VectorVideo: any = function () { this.create = function () { this.drawops = 0; }; - this.setKeyboardEvents = function () { }; + this.setKeyboardEvents = setKeyboardEvents; this.clear = function () { }; this.drawLine = function () { this.drawops++; }; }; @@ -131,6 +135,7 @@ function installHeadlessVideo() { return pixels && params ? { pixels, ...params } : null; }, get frameRate() { return frameRate; }, + get keyHandler() { return keyHandler; }, restore() { emu.setVideoClasses(original); } @@ -204,6 +209,8 @@ export class EmuTarget { } setKeyInput(key: number, code: number, flags: number) { + const handler = this.video?.keyHandler; + if (handler) return handler(key, code, flags); const target: any = this.machine || this.platform; if (typeof target.setKeyInput !== 'function') { throw new Error(`platform '${this.id}' does not accept key input`); diff --git a/test/cli/testcli.js b/test/cli/testcli.js index 47824dc2..1064e7d6 100644 --- a/test/cli/testcli.js +++ b/test/cli/testcli.js @@ -131,6 +131,11 @@ describe('8bws CLI', function () { var out = cli('run', '--platform', 'apple2', '-e', 'run 20; hist 5', 'test/roms/apple2/cosmic.c.rom'); assert.ok(/instructions shown/.test(out), out); }); + it('should send keys to a platform without a Machine', function () { + // nes registers its key handler on the video, like it does in the IDE + var out = cli('run', '--platform', 'nes', '-e', 'run 5; keydown enter; run 5; keyup enter', 'test/roms/nes/shoot2.c.rom'); + assert.ok(/keyup enter/.test(out), out); + }); it('should dump memory', function () { var out = cli('run', '--platform', 'apple2', '-f', '20', '--memdump', '400,40f', 'test/roms/apple2/cosmic.c.rom'); assert.ok(/^0400:( [0-9A-F]{2}){8}/m.test(out), out);