From 34808483b454e0f1ed940deae8c8f1daf1a553bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com> Date: Fri, 11 Nov 2022 17:24:45 +0000 Subject: [PATCH] feat: support for responsive physical keyboard --- CHANGELOG.md | 2 +- examples/web/index.ts | 104 ++++-------------- examples/web/react/app.tsx | 30 ++++- .../components/keyboard-gb/keyboard-gb.tsx | 57 +++++++++- 4 files changed, 105 insertions(+), 88 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f372ae0..b525ad21 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -* +* Support for responsive physical keyboard ### Changed diff --git a/examples/web/index.ts b/examples/web/index.ts index 4fd1abe1..ff6e6bc8 100644 --- a/examples/web/index.ts +++ b/examples/web/index.ts @@ -21,8 +21,6 @@ const LOGIC_HZ = 4194304; const VISUAL_HZ = 59.7275; const IDLE_HZ = 10; -const FREQUENCY_DELTA = 400000; - const SAMPLE_RATE = 2; const BACKGROUNDS = [ @@ -35,17 +33,6 @@ const BACKGROUNDS = [ "3a5a40" ]; -const KEYS: Record<string, number> = { - ArrowUp: PadKey.Up, - ArrowDown: PadKey.Down, - ArrowLeft: PadKey.Left, - ArrowRight: PadKey.Right, - Enter: PadKey.Start, - " ": PadKey.Select, - a: PadKey.A, - s: PadKey.B -}; - const KEYS_NAME: Record<string, number> = { ArrowUp: PadKey.Up, ArrowDown: PadKey.Down, @@ -57,13 +44,6 @@ const KEYS_NAME: Record<string, number> = { B: PadKey.B }; -const ARROW_KEYS: Record<string, boolean> = { - ArrowUp: true, - ArrowDown: true, - ArrowLeft: true, - ArrowRight: true -}; - const ROM_PATH = require("../../res/roms/20y.gb"); /** @@ -104,10 +84,6 @@ class GameboyEmulator extends EmulatorBase implements Emulator { // so that the global symbols become available await wasm(); - // initializes the complete set of sub-systems - // and registers the event handlers - await this.register(); - // boots the emulator subsystem with the initial // ROM retrieved from a remote data source await this.boot({ loadRom: true, romPath: romUrl ?? undefined }); @@ -338,42 +314,6 @@ class GameboyEmulator extends EmulatorBase implements Emulator { this.trigger("booted"); } - async register() { - await Promise.all([this.registerKeys()]); - } - - registerKeys() { - document.addEventListener("keydown", (event) => { - const keyCode = KEYS[event.key]; - const isArrow = ARROW_KEYS[event.key] ?? false; - if (isArrow) event.preventDefault(); - if (keyCode !== undefined) { - this.gameBoy?.key_press(keyCode); - return; - } - - switch (event.key) { - case "+": - this.frequency += FREQUENCY_DELTA; - break; - - case "-": - this.frequency -= FREQUENCY_DELTA; - break; - } - }); - - document.addEventListener("keyup", (event) => { - const keyCode = KEYS[event.key]; - const isArrow = ARROW_KEYS[event.key] ?? false; - if (isArrow) event.preventDefault(); - if (keyCode !== undefined) { - this.gameBoy?.key_lift(keyCode); - return; - } - }); - } - setRom(name: string, data: Uint8Array, cartridge: Cartridge) { this.romName = name; this.romData = data; @@ -454,6 +394,28 @@ class GameboyEmulator extends EmulatorBase implements Emulator { return this.fps; } + get registers(): Record<string, string | number> { + const registers = this.gameBoy?.registers(); + if (!registers) return {}; + return { + pc: registers.pc, + sp: registers.sp, + a: registers.a, + b: registers.b, + c: registers.c, + d: registers.d, + e: registers.e, + h: registers.h, + l: registers.l, + scy: registers.scy, + scx: registers.scx, + wy: registers.wy, + wx: registers.wx, + ly: registers.ly, + lyc: registers.lyc + }; + } + getTile(index: number): Uint8Array { return this.gameBoy?.get_tile_buffer(index) ?? new Uint8Array(); } @@ -536,28 +498,6 @@ class GameboyEmulator extends EmulatorBase implements Emulator { data: romData }; } - - get registers(): Record<string, string | number> { - const registers = this.gameBoy?.registers(); - if (!registers) return {}; - return { - pc: registers.pc, - sp: registers.sp, - a: registers.a, - b: registers.b, - c: registers.c, - d: registers.d, - e: registers.e, - h: registers.h, - l: registers.l, - scy: registers.scy, - scx: registers.scx, - wy: registers.wy, - wx: registers.wx, - ly: registers.ly, - lyc: registers.lyc - }; - } } declare global { diff --git a/examples/web/react/app.tsx b/examples/web/react/app.tsx index f8982e18..3f7cd2f1 100644 --- a/examples/web/react/app.tsx +++ b/examples/web/react/app.tsx @@ -293,6 +293,14 @@ export const App: FC<AppProps> = ({ }, [backgroundIndex]); useEffect(() => { switch (keyaction) { + case "Plus": + emulator.frequency += 400000; + setKeyaction(undefined); + break; + case "Minus": + emulator.frequency -= 400000; + setKeyaction(undefined); + break; case "Escape": setFullscreenState(false); setKeyaction(undefined); @@ -305,10 +313,24 @@ export const App: FC<AppProps> = ({ }, [keyaction]); useEffect(() => { const onKeyDown = (event: KeyboardEvent) => { - if (event.key === "Escape") { - setKeyaction("Escape"); - event.stopPropagation(); - event.preventDefault(); + switch (event.key) { + case "+": + setKeyaction("Plus"); + event.stopPropagation(); + event.preventDefault(); + break; + + case "-": + setKeyaction("Minus"); + event.stopPropagation(); + event.preventDefault(); + break; + + case "Escape": + setKeyaction("Escape"); + event.stopPropagation(); + event.preventDefault(); + break; } if (event.key === "f" && event.ctrlKey === true) { setKeyaction("Fullscreen"); diff --git a/examples/web/react/components/keyboard-gb/keyboard-gb.tsx b/examples/web/react/components/keyboard-gb/keyboard-gb.tsx index 0291cf8a..c59c31c0 100644 --- a/examples/web/react/components/keyboard-gb/keyboard-gb.tsx +++ b/examples/web/react/components/keyboard-gb/keyboard-gb.tsx @@ -1,7 +1,25 @@ -import React, { FC, useState } from "react"; +import React, { FC, useEffect, useRef, useState } from "react"; import "./keyboard-gb.css"; +const KEYS: Record<string, string> = { + ArrowUp: "ArrowUp", + ArrowDown: "ArrowDown", + ArrowLeft: "ArrowLeft", + ArrowRight: "ArrowRight", + Enter: "Start", + " ": "Select", + a: "A", + s: "B" +}; + +const ARROW_KEYS: Record<string, boolean> = { + ArrowUp: true, + ArrowDown: true, + ArrowLeft: true, + ArrowRight: true +}; + declare const require: any; type KeyboardGBProps = { @@ -23,6 +41,8 @@ export const KeyboardGB: FC<KeyboardGBProps> = ({ }) => { const containerClasses = () => ["keyboard-container", fullscreen ? "fullscreen" : ""].join(" "); + const recordRef = + useRef<Record<string, React.Dispatch<React.SetStateAction<boolean>>>>(); const classes = () => [ "keyboard", @@ -30,6 +50,38 @@ export const KeyboardGB: FC<KeyboardGBProps> = ({ fullscreen ? "fullscreen" : "", ...style ].join(" "); + useEffect(() => { + const _onKeyDown = (event: KeyboardEvent) => { + const keyCode = KEYS[event.key]; + const isArrow = ARROW_KEYS[event.key] ?? false; + if (isArrow) event.preventDefault(); + if (keyCode !== undefined) { + const records = recordRef.current ?? {}; + const setter = records[keyCode]; + setter(true); + onKeyDown && onKeyDown(keyCode); + return; + } + }; + const _onKeyUp = (event: KeyboardEvent) => { + const keyCode = KEYS[event.key]; + const isArrow = ARROW_KEYS[event.key] ?? false; + if (isArrow) event.preventDefault(); + if (keyCode !== undefined) { + const records = recordRef.current ?? {}; + const setter = records[keyCode]; + setter(false); + onKeyUp && onKeyUp(keyCode); + return; + } + }; + document.addEventListener("keydown", _onKeyDown); + document.addEventListener("keyup", _onKeyUp); + return () => { + document.removeEventListener("keydown", _onKeyDown); + document.removeEventListener("keyup", _onKeyUp); + }; + }, []); const renderKey = ( key: string, keyName?: string, @@ -38,6 +90,9 @@ export const KeyboardGB: FC<KeyboardGBProps> = ({ ) => { const [pressed, setPressed] = useState(selected); const classes = ["key", pressed ? "pressed" : "", ...styles].join(" "); + const records = recordRef.current ?? {}; + records[keyName ?? key ?? "undefined"] = setPressed; + recordRef.current = records; return ( <span className={classes} -- GitLab