Skip to content
Snippets Groups Projects
Verified Commit 34808483 authored by João Magalhães's avatar João Magalhães :rocket:
Browse files

feat: support for responsive physical keyboard

parent bae83223
No related branches found
No related tags found
No related merge requests found
Pipeline #1508 passed
...@@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ...@@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### Added
* * Support for responsive physical keyboard
### Changed ### Changed
......
...@@ -21,8 +21,6 @@ const LOGIC_HZ = 4194304; ...@@ -21,8 +21,6 @@ const LOGIC_HZ = 4194304;
const VISUAL_HZ = 59.7275; const VISUAL_HZ = 59.7275;
const IDLE_HZ = 10; const IDLE_HZ = 10;
const FREQUENCY_DELTA = 400000;
const SAMPLE_RATE = 2; const SAMPLE_RATE = 2;
const BACKGROUNDS = [ const BACKGROUNDS = [
...@@ -35,17 +33,6 @@ const BACKGROUNDS = [ ...@@ -35,17 +33,6 @@ const BACKGROUNDS = [
"3a5a40" "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> = { const KEYS_NAME: Record<string, number> = {
ArrowUp: PadKey.Up, ArrowUp: PadKey.Up,
ArrowDown: PadKey.Down, ArrowDown: PadKey.Down,
...@@ -57,13 +44,6 @@ const KEYS_NAME: Record<string, number> = { ...@@ -57,13 +44,6 @@ const KEYS_NAME: Record<string, number> = {
B: PadKey.B 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"); const ROM_PATH = require("../../res/roms/20y.gb");
/** /**
...@@ -104,10 +84,6 @@ class GameboyEmulator extends EmulatorBase implements Emulator { ...@@ -104,10 +84,6 @@ class GameboyEmulator extends EmulatorBase implements Emulator {
// so that the global symbols become available // so that the global symbols become available
await wasm(); await wasm();
// initializes the complete set of sub-systems
// and registers the event handlers
await this.register();
// boots the emulator subsystem with the initial // boots the emulator subsystem with the initial
// ROM retrieved from a remote data source // ROM retrieved from a remote data source
await this.boot({ loadRom: true, romPath: romUrl ?? undefined }); await this.boot({ loadRom: true, romPath: romUrl ?? undefined });
...@@ -338,42 +314,6 @@ class GameboyEmulator extends EmulatorBase implements Emulator { ...@@ -338,42 +314,6 @@ class GameboyEmulator extends EmulatorBase implements Emulator {
this.trigger("booted"); 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) { setRom(name: string, data: Uint8Array, cartridge: Cartridge) {
this.romName = name; this.romName = name;
this.romData = data; this.romData = data;
...@@ -454,6 +394,28 @@ class GameboyEmulator extends EmulatorBase implements Emulator { ...@@ -454,6 +394,28 @@ class GameboyEmulator extends EmulatorBase implements Emulator {
return this.fps; 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 { getTile(index: number): Uint8Array {
return this.gameBoy?.get_tile_buffer(index) ?? new Uint8Array(); return this.gameBoy?.get_tile_buffer(index) ?? new Uint8Array();
} }
...@@ -536,28 +498,6 @@ class GameboyEmulator extends EmulatorBase implements Emulator { ...@@ -536,28 +498,6 @@ class GameboyEmulator extends EmulatorBase implements Emulator {
data: romData 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 { declare global {
......
...@@ -293,6 +293,14 @@ export const App: FC<AppProps> = ({ ...@@ -293,6 +293,14 @@ export const App: FC<AppProps> = ({
}, [backgroundIndex]); }, [backgroundIndex]);
useEffect(() => { useEffect(() => {
switch (keyaction) { switch (keyaction) {
case "Plus":
emulator.frequency += 400000;
setKeyaction(undefined);
break;
case "Minus":
emulator.frequency -= 400000;
setKeyaction(undefined);
break;
case "Escape": case "Escape":
setFullscreenState(false); setFullscreenState(false);
setKeyaction(undefined); setKeyaction(undefined);
...@@ -305,10 +313,24 @@ export const App: FC<AppProps> = ({ ...@@ -305,10 +313,24 @@ export const App: FC<AppProps> = ({
}, [keyaction]); }, [keyaction]);
useEffect(() => { useEffect(() => {
const onKeyDown = (event: KeyboardEvent) => { const onKeyDown = (event: KeyboardEvent) => {
if (event.key === "Escape") { switch (event.key) {
setKeyaction("Escape"); case "+":
event.stopPropagation(); setKeyaction("Plus");
event.preventDefault(); 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) { if (event.key === "f" && event.ctrlKey === true) {
setKeyaction("Fullscreen"); setKeyaction("Fullscreen");
......
import React, { FC, useState } from "react"; import React, { FC, useEffect, useRef, useState } from "react";
import "./keyboard-gb.css"; 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; declare const require: any;
type KeyboardGBProps = { type KeyboardGBProps = {
...@@ -23,6 +41,8 @@ export const KeyboardGB: FC<KeyboardGBProps> = ({ ...@@ -23,6 +41,8 @@ export const KeyboardGB: FC<KeyboardGBProps> = ({
}) => { }) => {
const containerClasses = () => const containerClasses = () =>
["keyboard-container", fullscreen ? "fullscreen" : ""].join(" "); ["keyboard-container", fullscreen ? "fullscreen" : ""].join(" ");
const recordRef =
useRef<Record<string, React.Dispatch<React.SetStateAction<boolean>>>>();
const classes = () => const classes = () =>
[ [
"keyboard", "keyboard",
...@@ -30,6 +50,38 @@ export const KeyboardGB: FC<KeyboardGBProps> = ({ ...@@ -30,6 +50,38 @@ export const KeyboardGB: FC<KeyboardGBProps> = ({
fullscreen ? "fullscreen" : "", fullscreen ? "fullscreen" : "",
...style ...style
].join(" "); ].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 = ( const renderKey = (
key: string, key: string,
keyName?: string, keyName?: string,
...@@ -38,6 +90,9 @@ export const KeyboardGB: FC<KeyboardGBProps> = ({ ...@@ -38,6 +90,9 @@ export const KeyboardGB: FC<KeyboardGBProps> = ({
) => { ) => {
const [pressed, setPressed] = useState(selected); const [pressed, setPressed] = useState(selected);
const classes = ["key", pressed ? "pressed" : "", ...styles].join(" "); const classes = ["key", pressed ? "pressed" : "", ...styles].join(" ");
const records = recordRef.current ?? {};
records[keyName ?? key ?? "undefined"] = setPressed;
recordRef.current = records;
return ( return (
<span <span
className={classes} className={classes}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment