From 6ffd97ceeca59c88c17ea4a2970660fda111654b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com> Date: Sat, 29 Oct 2022 01:19:39 +0100 Subject: [PATCH] feat: added frame event trigger --- examples/web/index.ts | 5 +++-- examples/web/react/app.tsx | 31 ++++++++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/examples/web/index.ts b/examples/web/index.ts index f1e48f98..f7951ea9 100644 --- a/examples/web/index.ts +++ b/examples/web/index.ts @@ -1,4 +1,4 @@ -import { Emulator, PixelFormat, startApp } from "./react/app"; +import { Emulator, Observable, PixelFormat, startApp } from "./react/app"; import { default as _wasm, GameBoy, PadKey, PpuMode } from "./lib/boytacean.js"; import info from "./package.json"; @@ -48,7 +48,7 @@ const ROM_PATH = require("../../res/roms/20y.gb"); * and "joins" all the elements together to bring input/output * of the associated machine. */ -class GameboyEmulator implements Emulator { +class GameboyEmulator extends Observable implements Emulator { /** * The Game Boy engine (probably coming from WASM) that * is going to be used for the emulation. @@ -218,6 +218,7 @@ class GameboyEmulator implements Emulator { this.gameBoy!.frame_buffer_eager(), PixelFormat.RGB ); + this.trigger("frame"); lastFrame = this.gameBoy!.ppu_frame(); } } diff --git a/examples/web/react/app.tsx b/examples/web/react/app.tsx index a70e8960..ab92d0f9 100644 --- a/examples/web/react/app.tsx +++ b/examples/web/react/app.tsx @@ -22,12 +22,36 @@ import { import "./app.css"; +export type Callback<T> = (owner: T) => void; + +/** + * Abstract class that implements the basic functionality + * part of the definition of the observable pattern. + * + * @see {@link https://en.wikipedia.org/wiki/Observer_pattern} + */ +export class Observable { + private events: Record<string, [Callback<this>]> = {}; + + bind(event: string, callback: Callback<this>) { + const callbacks = this.events[event] ?? []; + if (callbacks.includes(callback)) return; + callbacks.push(callback); + this.events[event] = callbacks; + } + + trigger(event: string) { + const callbacks = this.events[event] ?? []; + callbacks.forEach((c) => c(this)); + } +} + /** * Top level interface that declares the main abstract * interface of an emulator structured entity. * Should allow typical hardware operations to be performed. */ -export interface Emulator { +export interface Emulator extends Observable { getName(): string; getVersion(): string; getVersionUrl(): string; @@ -80,9 +104,10 @@ export const App: FC<AppProps> = ({ emulator, backgrounds = ["264653"] }) => { }; const onDrawHandler = (handler: DrawHandler) => { if (intervalRef.current) return; - intervalRef.current = setInterval(() => { + intervalRef.current = 1; + emulator.bind("frame", () => { handler(emulator.getImageBuffer(), PixelFormat.RGB); - }, 1000); + }); }; useEffect(() => { document.body.style.backgroundColor = `#${getBackground()}`; -- GitLab