Adding functional controls.
This commit is contained in:
parent
282a4b671d
commit
93cc25adf5
@ -1,12 +1,14 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import {HOME_BUTTON_IMAGE} from '/constants';
|
import {HOME_BUTTON_IMAGE} from '/constants';
|
||||||
|
import {sendKeyDown} from '/packet';
|
||||||
|
|
||||||
export interface OverlayControlsProps {
|
export interface OverlayControlsProps {
|
||||||
firstMenuElement: React.RefObject<HTMLAnchorElement>,
|
firstMenuElement: React.RefObject<HTMLAnchorElement>,
|
||||||
setHiddenMenu: (c: boolean) => void,
|
setHiddenMenu: (c: boolean) => void,
|
||||||
|
webSocket: WebSocket | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function OverlayControls({firstMenuElement, setHiddenMenu}: OverlayControlsProps) {
|
export default function OverlayControls({firstMenuElement, setHiddenMenu, webSocket}: OverlayControlsProps) {
|
||||||
function showOverlayMenu() {
|
function showOverlayMenu() {
|
||||||
setHiddenMenu(false);
|
setHiddenMenu(false);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@ -16,20 +18,103 @@ export default function OverlayControls({firstMenuElement, setHiddenMenu}: Overl
|
|||||||
firstMenuElement.current.focus();
|
firstMenuElement.current.focus();
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
|
const [onGoingTouches, setOnGoingTouches] = React.useState<{[id: string]: number}>({});
|
||||||
|
function mouseDown(e: React.MouseEvent<HTMLAnchorElement> | React.TouchEvent<HTMLAnchorElement>, key: number) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (webSocket == null) {
|
||||||
|
console.log('There is not websocket');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendKeyDown(webSocket, true, key);
|
||||||
|
}
|
||||||
|
function touchStart(e: React.TouchEvent<HTMLAnchorElement>, key: number) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (webSocket == null) {
|
||||||
|
console.log('There is not websocket');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < e.changedTouches.length; i++) {
|
||||||
|
const touch = e.changedTouches[i];
|
||||||
|
const idx = touch.identifier;
|
||||||
|
onGoingTouches[idx] = key;
|
||||||
|
sendKeyDown(webSocket, true, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function touchEnd(e: React.TouchEvent<HTMLAnchorElement|HTMLDivElement>) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (webSocket == null) {
|
||||||
|
console.log('There is not websocket');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < e.changedTouches.length; i++) {
|
||||||
|
const touch = e.changedTouches[i];
|
||||||
|
const idx = touch.identifier;
|
||||||
|
const key = onGoingTouches[idx];
|
||||||
|
delete onGoingTouches[idx];
|
||||||
|
sendKeyDown(webSocket, false, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function mouseUp(e: React.MouseEvent<HTMLAnchorElement> | React.TouchEvent<HTMLAnchorElement>, key: number) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (webSocket == null) {
|
||||||
|
console.log('There is not websocket');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendKeyDown(webSocket, false, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.onselectstart = () => false;
|
||||||
return (
|
return (
|
||||||
<div className="overlay">
|
<div className="overlay" onTouchEnd={touchEnd}>
|
||||||
<div className="vertical-padding">
|
<div className="vertical-padding">
|
||||||
</div>
|
</div>
|
||||||
<div className="controls">
|
<div className="controls">
|
||||||
<a href="#" className="gear control" onClick={showOverlayMenu}>
|
<a tabIndex={-1} className="gear control" onClick={showOverlayMenu} onTouchStart={showOverlayMenu}>
|
||||||
<img src={HOME_BUTTON_IMAGE} alt="Go to menu. (House icon)"/>
|
<img src={HOME_BUTTON_IMAGE} alt="Go to menu. (House icon)"/>
|
||||||
</a>
|
</a>
|
||||||
<a href="#" className="control-a control control-button">A</a>
|
<a tabIndex={-1}
|
||||||
<a href="#" className="control-b control control-button">B</a>
|
onMouseDown={(e: React.MouseEvent<HTMLAnchorElement>) => mouseDown(e, 0)}
|
||||||
<a href="#" className="control-up control control-pad-button">^</a>
|
onMouseUp={(e: React.MouseEvent<HTMLAnchorElement>) => mouseUp(e, 0)}
|
||||||
<a href="#" className="control-down control control-pad-button">v</a>
|
onTouchStart={(e: React.TouchEvent<HTMLAnchorElement>) => touchStart(e, 0)}
|
||||||
<a href="#" className="control-left control control-pad-button"><</a>
|
onTouchEnd={(e: React.TouchEvent<HTMLAnchorElement>) => touchEnd(e)}
|
||||||
<a href="#" className="control-right control control-pad-button">></a>
|
onClick={(e: React.MouseEvent<HTMLAnchorElement>) => e.preventDefault()}
|
||||||
|
className="control-a control control-button">A</a>
|
||||||
|
<a tabIndex={-1}
|
||||||
|
onMouseDown={(e: React.MouseEvent<HTMLAnchorElement>) => mouseDown(e, 1)}
|
||||||
|
onMouseUp={(e: React.MouseEvent<HTMLAnchorElement>) => mouseUp(e, 1)}
|
||||||
|
onTouchStart={(e: React.TouchEvent<HTMLAnchorElement>) => touchStart(e, 1)}
|
||||||
|
onTouchEnd={(e: React.TouchEvent<HTMLAnchorElement>) => touchEnd(e)}
|
||||||
|
onClick={(e: React.MouseEvent<HTMLAnchorElement>) => e.preventDefault()}
|
||||||
|
className="control-b control control-button">B</a>
|
||||||
|
<a tabIndex={-1}
|
||||||
|
onMouseDown={(e: React.MouseEvent<HTMLAnchorElement>) => mouseDown(e, 6)}
|
||||||
|
onMouseUp={(e: React.MouseEvent<HTMLAnchorElement>) => mouseUp(e, 6)}
|
||||||
|
onTouchStart={(e: React.TouchEvent<HTMLAnchorElement>) => touchStart(e, 6)}
|
||||||
|
onTouchEnd={(e: React.TouchEvent<HTMLAnchorElement>) => touchEnd(e)}
|
||||||
|
onClick={(e: React.MouseEvent<HTMLAnchorElement>) => e.preventDefault()}
|
||||||
|
className="control-up control control-pad-button">^</a>
|
||||||
|
<a tabIndex={-1}
|
||||||
|
onMouseDown={(e: React.MouseEvent<HTMLAnchorElement>) => mouseDown(e, 7)}
|
||||||
|
onMouseUp={(e: React.MouseEvent<HTMLAnchorElement>) => mouseUp(e, 7)}
|
||||||
|
onTouchStart={(e: React.TouchEvent<HTMLAnchorElement>) => touchStart(e, 7)}
|
||||||
|
onTouchEnd={(e: React.TouchEvent<HTMLAnchorElement>) => touchEnd(e)}
|
||||||
|
onClick={(e: React.MouseEvent<HTMLAnchorElement>) => e.preventDefault()}
|
||||||
|
className="control-down control control-pad-button">v</a>
|
||||||
|
<a tabIndex={-1}
|
||||||
|
onMouseDown={(e: React.MouseEvent<HTMLAnchorElement>) => mouseDown(e, 8)}
|
||||||
|
onMouseUp={(e: React.MouseEvent<HTMLAnchorElement>) => mouseUp(e, 8)}
|
||||||
|
onTouchStart={(e: React.TouchEvent<HTMLAnchorElement>) => touchStart(e, 8)}
|
||||||
|
onTouchEnd={(e: React.TouchEvent<HTMLAnchorElement>) => touchEnd(e)}
|
||||||
|
onClick={(e: React.MouseEvent<HTMLAnchorElement>) => e.preventDefault()}
|
||||||
|
className="control-left control control-pad-button"><</a>
|
||||||
|
<a tabIndex={-1}
|
||||||
|
onMouseDown={(e: React.MouseEvent<HTMLAnchorElement>) => mouseDown(e, 9)}
|
||||||
|
onMouseUp={(e: React.MouseEvent<HTMLAnchorElement>) => mouseUp(e, 9)}
|
||||||
|
onTouchStart={(e: React.TouchEvent<HTMLAnchorElement>) => touchStart(e, 9)}
|
||||||
|
onTouchEnd={(e: React.TouchEvent<HTMLAnchorElement>) => touchEnd(e)}
|
||||||
|
onClick={(e: React.MouseEvent<HTMLAnchorElement>) => e.preventDefault()}
|
||||||
|
className="control-right control control-pad-button">></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -7,28 +7,28 @@ import OverlayControls from '/components/overlay-controls';
|
|||||||
import OverlayMenu from '/components/overlay-menu';
|
import OverlayMenu from '/components/overlay-menu';
|
||||||
import OverlaySelectFiles from '/components/overlay-select-files';
|
import OverlaySelectFiles from '/components/overlay-select-files';
|
||||||
import CloseButton from '/components/close-button';
|
import CloseButton from '/components/close-button';
|
||||||
|
import {sendHello, handleSendFrame} from '/packet';
|
||||||
|
|
||||||
import Endian from '/endian';
|
import Endian from '/endian';
|
||||||
|
|
||||||
import {MIN_WIDTH, MIN_HEIGHT, PACKET_ID_HELLO, PACKET_ID_SEND_FRAME} from '/constants';
|
import {MIN_WIDTH, MIN_HEIGHT, PACKET_ID_HELLO, PACKET_ID_SEND_FRAME} from '/constants';
|
||||||
|
|
||||||
type setBooleanCallback = (c: boolean) => boolean;
|
|
||||||
|
|
||||||
export interface handleClickStartEmulationButtonObjectArgs {
|
export interface handleClickStartEmulationButtonObjectArgs {
|
||||||
e: React.MouseEvent<HTMLInputElement>;
|
e: React.MouseEvent<HTMLInputElement>;
|
||||||
inputRom: HTMLInputElement | null;
|
inputRom: HTMLInputElement | null;
|
||||||
inputSaveState: HTMLInputElement | null;
|
inputSaveState: HTMLInputElement | null;
|
||||||
canvas: HTMLCanvasElement | null;
|
canvas: HTMLCanvasElement | null;
|
||||||
printingFrame: boolean;
|
printingFrame: boolean;
|
||||||
setPrintingFrame: (c: setBooleanCallback) => void;
|
setPrintingFrame: (c: boolean) => void;
|
||||||
setEmulationStarted: (c: boolean) => void,
|
setEmulationStarted: (c: boolean) => void,
|
||||||
setHiddenMenu: (c: boolean) => void;
|
setHiddenMenu: (c: boolean) => void;
|
||||||
setHiddenFormSelectFiles: (c: boolean) => void;
|
setHiddenFormSelectFiles: (c: boolean) => void;
|
||||||
|
setWebSocket: (c: WebSocket) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
function handleClickStartEmulationButton({e, inputRom, inputSaveState, canvas, printingFrame,
|
function handleClickStartEmulationButton({e, inputRom, inputSaveState, canvas, printingFrame,
|
||||||
setPrintingFrame, setEmulationStarted, setHiddenMenu,
|
setPrintingFrame, setEmulationStarted, setHiddenMenu,
|
||||||
setHiddenFormSelectFiles}: handleClickStartEmulationButtonObjectArgs) {
|
setHiddenFormSelectFiles, setWebSocket}: handleClickStartEmulationButtonObjectArgs) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (canvas == null) {
|
if (canvas == null) {
|
||||||
alert('Canvas does not exists?');
|
alert('Canvas does not exists?');
|
||||||
@ -57,60 +57,29 @@ function handleClickStartEmulationButton({e, inputRom, inputSaveState, canvas, p
|
|||||||
savestate_file.arrayBuffer().then((savestate_buffer) => {
|
savestate_file.arrayBuffer().then((savestate_buffer) => {
|
||||||
const rom_array = new Uint8Array(rom_buffer);
|
const rom_array = new Uint8Array(rom_buffer);
|
||||||
const savestate_array = new Uint8Array(savestate_buffer);
|
const savestate_array = new Uint8Array(savestate_buffer);
|
||||||
const websocket = new WebSocket(`ws://${window.location.host}/ws`);
|
const webSocket = new WebSocket(`ws://${window.location.host}/ws`);
|
||||||
websocket.binaryType = 'arraybuffer';
|
setWebSocket(webSocket);
|
||||||
websocket.onclose = (message) => {
|
webSocket.binaryType = 'arraybuffer';
|
||||||
|
webSocket.onclose = (message) => {
|
||||||
setEmulationStarted(false);
|
setEmulationStarted(false);
|
||||||
console.log('Closing websocket.');
|
console.log('Closing websocket.');
|
||||||
}
|
}
|
||||||
websocket.onopen = () => {
|
webSocket.onopen = () => {
|
||||||
console.log('Opened websocket.');
|
console.log('Opened websocket.');
|
||||||
setEmulationStarted(true);
|
setEmulationStarted(true);
|
||||||
sendHello(websocket, rom_array, savestate_array);
|
sendHello(webSocket, rom_array, savestate_array);
|
||||||
setHiddenMenu(true);
|
setHiddenMenu(true);
|
||||||
setHiddenFormSelectFiles(true);
|
setHiddenFormSelectFiles(true);
|
||||||
};
|
};
|
||||||
setPrintingFrame(c => false);
|
setPrintingFrame(false);
|
||||||
websocket.addEventListener('message', (event) => {
|
webSocket.addEventListener('message', (event) => {
|
||||||
onWebSocketPacket(event, canvas, ctx, printingFrame, setPrintingFrame)
|
onWebSocketPacket(event, canvas, ctx, printingFrame, setPrintingFrame)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function concatU8Array(array1: Uint8Array, array2: Uint8Array) {
|
function onWebSocketPacket(event: MessageEvent, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, printingFrame: boolean, setPrintingFrame: (c: boolean) => void) {
|
||||||
const final_array = new Uint8Array(array1.length + array2.length);
|
|
||||||
final_array.set(array1);
|
|
||||||
final_array.set(array2, array1.length);
|
|
||||||
return final_array;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendPacket(websocket: WebSocket, id: bigint, raw_data: Uint8Array) {
|
|
||||||
const packet_u8 = concatU8Array(
|
|
||||||
concatU8Array(Endian.u64ToByteArrayBigEndian(id), Endian.u64ToByteArrayBigEndian(BigInt(raw_data.length))),
|
|
||||||
raw_data
|
|
||||||
);
|
|
||||||
const packet_buffer = packet_u8.buffer;
|
|
||||||
console.log('Sending packet');
|
|
||||||
websocket.send(packet_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendHello(websocket: WebSocket, rom_array: Uint8Array, savestate_array: Uint8Array) {
|
|
||||||
console.log('Sending hello.');
|
|
||||||
const length_rom = BigInt(rom_array.length);
|
|
||||||
const length_savestate = BigInt(savestate_array.length);
|
|
||||||
const raw_data =
|
|
||||||
concatU8Array(
|
|
||||||
concatU8Array(
|
|
||||||
concatU8Array(Endian.u64ToByteArrayBigEndian(length_rom), rom_array),
|
|
||||||
Endian.u64ToByteArrayBigEndian(length_savestate)
|
|
||||||
),
|
|
||||||
savestate_array
|
|
||||||
);
|
|
||||||
sendPacket(websocket, PACKET_ID_HELLO, raw_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onWebSocketPacket(event: MessageEvent, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, printingFrame: boolean, setPrintingFrame: (c: setBooleanCallback) => void) {
|
|
||||||
const buffer = event.data;
|
const buffer = event.data;
|
||||||
let packet_u8: Uint8Array | null = new Uint8Array(buffer);
|
let packet_u8: Uint8Array | null = new Uint8Array(buffer);
|
||||||
const id = Endian.byteArrayToU64BigEndian(packet_u8.slice(0, 8));
|
const id = Endian.byteArrayToU64BigEndian(packet_u8.slice(0, 8));
|
||||||
@ -127,34 +96,6 @@ function onWebSocketPacket(event: MessageEvent, canvas: HTMLCanvasElement, ctx:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSendFrame(raw_data: Uint8Array, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, printingFrame: boolean, setPrintingFrame: (c: setBooleanCallback) => void) {
|
|
||||||
if (printingFrame) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setPrintingFrame(c => true);
|
|
||||||
let data: Uint8Array | null = raw_data;
|
|
||||||
const stride = Endian.byteArrayToU32BigEndian(data.slice(0, 4));
|
|
||||||
data = data.slice(4, data.length);
|
|
||||||
const output_buffer_size = Endian.byteArrayToU64BigEndian(data.slice(0, 8));
|
|
||||||
data = data.slice(8, data.length);
|
|
||||||
const img_data = ctx.createImageData(MIN_WIDTH, MIN_HEIGHT);
|
|
||||||
const img_data_u8 = new Uint8Array(img_data.data.buffer);
|
|
||||||
for (let i = 0; i<data.length; i++) {
|
|
||||||
if (i % 4 == 3) {
|
|
||||||
img_data_u8[i] = 255;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
img_data_u8[i] = data[i];
|
|
||||||
}
|
|
||||||
data = null;
|
|
||||||
createImageBitmap(img_data).then((bitmap) => drawBitmap(bitmap, canvas, ctx, printingFrame));
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawBitmap(bitmap: ImageBitmap, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, printingFrame: boolean) {
|
|
||||||
ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
|
|
||||||
printingFrame = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
const screenDimensions = useScreenDimensions();
|
const screenDimensions = useScreenDimensions();
|
||||||
const emulatorDimensions = calculateSizeEmulator(screenDimensions);
|
const emulatorDimensions = calculateSizeEmulator(screenDimensions);
|
||||||
@ -182,6 +123,7 @@ export default function Page() {
|
|||||||
const refInputSaveState = React.useRef<HTMLInputElement | null>(null);
|
const refInputSaveState = React.useRef<HTMLInputElement | null>(null);
|
||||||
const [emulationStarted, setEmulationStarted] = React.useState<boolean>(false);
|
const [emulationStarted, setEmulationStarted] = React.useState<boolean>(false);
|
||||||
const [hiddenMenu, setHiddenMenu] = React.useState<boolean>(true);
|
const [hiddenMenu, setHiddenMenu] = React.useState<boolean>(true);
|
||||||
|
const [webSocket, setWebSocket] = React.useState<WebSocket | null>(null);
|
||||||
const onStartEmulation = (e: React.MouseEvent<HTMLInputElement>) => {
|
const onStartEmulation = (e: React.MouseEvent<HTMLInputElement>) => {
|
||||||
handleClickStartEmulationButton({
|
handleClickStartEmulationButton({
|
||||||
e: e,
|
e: e,
|
||||||
@ -193,6 +135,8 @@ export default function Page() {
|
|||||||
printingFrame: printingFrame,
|
printingFrame: printingFrame,
|
||||||
setHiddenMenu: setHiddenMenu,
|
setHiddenMenu: setHiddenMenu,
|
||||||
setHiddenFormSelectFiles: setHiddenFormSelectFiles,
|
setHiddenFormSelectFiles: setHiddenFormSelectFiles,
|
||||||
|
setWebSocket: setWebSocket,
|
||||||
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
const firstMenuElement = React.useRef<HTMLAnchorElement>(null);
|
const firstMenuElement = React.useRef<HTMLAnchorElement>(null);
|
||||||
@ -201,7 +145,7 @@ export default function Page() {
|
|||||||
return (
|
return (
|
||||||
<div ref={screenRef}>
|
<div ref={screenRef}>
|
||||||
<OverlayControls firstMenuElement={firstMenuElement}
|
<OverlayControls firstMenuElement={firstMenuElement}
|
||||||
setHiddenMenu={setHiddenMenu}/>
|
setHiddenMenu={setHiddenMenu} webSocket={webSocket}/>
|
||||||
<OverlayMenu hiddenMenu={hiddenMenu}
|
<OverlayMenu hiddenMenu={hiddenMenu}
|
||||||
setHiddenMenu={setHiddenMenu} emulationStarted={emulationStarted}
|
setHiddenMenu={setHiddenMenu} emulationStarted={emulationStarted}
|
||||||
setHiddenFormSelectFiles={setHiddenFormSelectFiles} screenRef={screenRef}
|
setHiddenFormSelectFiles={setHiddenFormSelectFiles} screenRef={screenRef}
|
||||||
|
@ -2,6 +2,7 @@ export const MIN_WIDTH = 240;
|
|||||||
export const MIN_HEIGHT = 160;
|
export const MIN_HEIGHT = 160;
|
||||||
export const PACKET_ID_HELLO = 0n;
|
export const PACKET_ID_HELLO = 0n;
|
||||||
export const PACKET_ID_SEND_FRAME = 1n;
|
export const PACKET_ID_SEND_FRAME = 1n;
|
||||||
|
export const PACKET_ID_KEY_DOWN = 2n;
|
||||||
export const CLOSE_BUTTON_IMAGE: string = "/img/close.png";
|
export const CLOSE_BUTTON_IMAGE: string = "/img/close.png";
|
||||||
export const HOME_BUTTON_IMAGE: string = "/img/home.png";
|
export const HOME_BUTTON_IMAGE: string = "/img/home.png";
|
||||||
|
|
||||||
|
@ -37,4 +37,15 @@ export default class Endian {
|
|||||||
}
|
}
|
||||||
return buffer8;
|
return buffer8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32ToByteArrayBigEndian(inputNumber: number) {
|
||||||
|
const buffer = new ArrayBuffer(4);
|
||||||
|
const buffer8 = new Uint8Array(buffer);
|
||||||
|
const buffer32 = new Uint32Array(buffer);
|
||||||
|
buffer32[0] = inputNumber;
|
||||||
|
if (Endian.isLittleEndian()) {
|
||||||
|
buffer8.reverse();
|
||||||
|
}
|
||||||
|
return buffer8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
72
js-src/packet.ts
Normal file
72
js-src/packet.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import Endian from '/endian';
|
||||||
|
import {MIN_WIDTH, MIN_HEIGHT, PACKET_ID_HELLO, PACKET_ID_SEND_FRAME, PACKET_ID_KEY_DOWN} from '/constants';
|
||||||
|
|
||||||
|
function concatU8Array(array1: Uint8Array, array2: Uint8Array) {
|
||||||
|
const final_array = new Uint8Array(array1.length + array2.length);
|
||||||
|
final_array.set(array1);
|
||||||
|
final_array.set(array2, array1.length);
|
||||||
|
return final_array;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sendHello(websocket: WebSocket, rom_array: Uint8Array, savestate_array: Uint8Array) {
|
||||||
|
console.log('Sending hello.');
|
||||||
|
const length_rom = BigInt(rom_array.length);
|
||||||
|
const length_savestate = BigInt(savestate_array.length);
|
||||||
|
const raw_data =
|
||||||
|
concatU8Array(
|
||||||
|
concatU8Array(
|
||||||
|
concatU8Array(Endian.u64ToByteArrayBigEndian(length_rom), rom_array),
|
||||||
|
Endian.u64ToByteArrayBigEndian(length_savestate)
|
||||||
|
),
|
||||||
|
savestate_array
|
||||||
|
);
|
||||||
|
sendPacket(websocket, PACKET_ID_HELLO, raw_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sendKeyDown(websocket: WebSocket, isDown: boolean, key: number) {
|
||||||
|
console.log('Sending keyDown.');
|
||||||
|
console.log(PACKET_ID_KEY_DOWN);
|
||||||
|
const isDownArray = new Uint8Array(1);
|
||||||
|
isDownArray[0] = isDown ? 1: 0;
|
||||||
|
const rawData = concatU8Array(isDownArray, Endian.u32ToByteArrayBigEndian(key));
|
||||||
|
console.log(rawData.length);
|
||||||
|
sendPacket(websocket, PACKET_ID_KEY_DOWN, rawData);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sendPacket(websocket: WebSocket, id: bigint, raw_data: Uint8Array) {
|
||||||
|
const packet_u8 = concatU8Array(
|
||||||
|
concatU8Array(Endian.u64ToByteArrayBigEndian(id), Endian.u64ToByteArrayBigEndian(BigInt(raw_data.length))),
|
||||||
|
raw_data
|
||||||
|
);
|
||||||
|
const packet_buffer = packet_u8.buffer;
|
||||||
|
console.log('Sending packet');
|
||||||
|
websocket.send(packet_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function handleSendFrame(raw_data: Uint8Array, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, printingFrame: boolean, setPrintingFrame: (c: boolean) => void) {
|
||||||
|
if (printingFrame) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setPrintingFrame(true);
|
||||||
|
let data: Uint8Array | null = raw_data;
|
||||||
|
const stride = Endian.byteArrayToU32BigEndian(data.slice(0, 4));
|
||||||
|
data = data.slice(4, data.length);
|
||||||
|
const output_buffer_size = Endian.byteArrayToU64BigEndian(data.slice(0, 8));
|
||||||
|
data = data.slice(8, data.length);
|
||||||
|
const img_data = ctx.createImageData(MIN_WIDTH, MIN_HEIGHT);
|
||||||
|
const img_data_u8 = new Uint8Array(img_data.data.buffer);
|
||||||
|
for (let i = 0; i<data.length; i++) {
|
||||||
|
if (i % 4 == 3) {
|
||||||
|
img_data_u8[i] = 255;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
img_data_u8[i] = data[i];
|
||||||
|
}
|
||||||
|
data = null;
|
||||||
|
createImageBitmap(img_data).then((bitmap) => drawBitmap(bitmap, canvas, ctx, printingFrame));
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawBitmap(bitmap: ImageBitmap, canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, printingFrame: boolean) {
|
||||||
|
ctx.drawImage(bitmap, 0, 0, canvas.width, canvas.height);
|
||||||
|
printingFrame = false;
|
||||||
|
}
|
@ -1,3 +1,7 @@
|
|||||||
|
*:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: #0E0E10;
|
background: #0E0E10;
|
||||||
color: #F5F5F5;
|
color: #F5F5F5;
|
||||||
|
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user