port entire project to using parcel + strict TypeScript
Mostly out of cleanliness, and actually bundling the libraries properly. Yes, this includes the backend, because.. why not? It seems to work, at least. The VNC client for instance also is now fully strict TypeScript.
This commit is contained in:
parent
7b9c71d534
commit
4212050ae5
29 changed files with 686 additions and 383 deletions
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
|||
Copyright 2023 Lily Tsuru/modeco80 <lily.modeco80@protonmail.ch>
|
||||
Copyright 2023-2024 Lily Tsuru/modeco80 <lily.modeco80@protonmail.ch>
|
||||
Please see qemu/src/rfb/LICENSE for additional terms.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
|
|
@ -3,13 +3,26 @@
|
|||
"version": "1.0.0",
|
||||
"private": "true",
|
||||
"description": "socket 2.0 backend",
|
||||
|
||||
"type": "module",
|
||||
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
"build": "parcel build src/index.ts --target node"
|
||||
},
|
||||
|
||||
"author": "modeco80",
|
||||
"license": "MIT",
|
||||
|
||||
"targets": {
|
||||
"node":{
|
||||
"context": "node",
|
||||
"outputFormat": "esmodule"
|
||||
}
|
||||
},
|
||||
|
||||
"dependencies": {
|
||||
"parcel": "^2.12.0",
|
||||
|
||||
"@fastify/websocket": "^10.0.1",
|
||||
"@julusian/jpeg-turbo": "^2.1.0",
|
||||
"@socketcomputer/qemu": "*",
|
||||
|
|
|
@ -64,7 +64,7 @@ export function Slot_PCDef(
|
|||
pushOption('-device usb-tablet');
|
||||
|
||||
return {
|
||||
id: "socketvm1",
|
||||
id: "socketvm2-test",
|
||||
command: qCommand
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { QemuVmDefinition, QemuDisplay, QemuVM, VMState, setSnapshot, GenMacAddress } from '@socketcomputer/qemu';
|
||||
import { QemuVmDefinition, QemuDisplay, QemuVM, VMState, setSnapshot } from '@socketcomputer/qemu';
|
||||
import { Slot_PCDef } from './SlotQemuDefs.js';
|
||||
import { ExtendableTimer } from './ExtendableTimer.js';
|
||||
import { EventEmitter } from 'node:events';
|
||||
|
@ -20,7 +20,7 @@ const kCanvasJpegQuality = 0.25;
|
|||
class VMUser {
|
||||
public connection: WebSocket;
|
||||
public address: string;
|
||||
public username: string;
|
||||
public username: string = "";
|
||||
private vm: SocketVM;
|
||||
|
||||
|
||||
|
@ -49,10 +49,10 @@ class VMUser {
|
|||
async SendBuffer(buffer: ArrayBuffer): Promise<void> {
|
||||
return new Promise((res, rej) => {
|
||||
if (this.connection.readyState !== WebSocket.CLOSED) {
|
||||
this.connection.send(buffer, (err) => {
|
||||
res();
|
||||
});
|
||||
this.connection.send(buffer);
|
||||
res();
|
||||
}
|
||||
rej(new Error('connection haves closed'));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -73,14 +73,17 @@ type userAndTime = {
|
|||
class TurnQueue extends EventEmitter {
|
||||
private queue: Queue<VMUser> = new Queue<VMUser>();
|
||||
private turnTime = kTurnTimeSeconds;
|
||||
private interval: NodeJS.Timeout = null;
|
||||
private interval: NodeJS.Timeout|null = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
public CurrentUser(): VMUser {
|
||||
return this.queue.peek();
|
||||
public CurrentUser(): VMUser|null {
|
||||
if(this.queue.peek() == undefined)
|
||||
return null;
|
||||
// We already check if it'll be undefined
|
||||
return this.queue.peek()!;
|
||||
}
|
||||
|
||||
public TryEnqueue(user: VMUser) {
|
||||
|
@ -132,7 +135,7 @@ class TurnQueue extends EventEmitter {
|
|||
}
|
||||
|
||||
private nextTurn() {
|
||||
clearInterval(this.interval);
|
||||
clearInterval(this.interval!);
|
||||
if (this.queue.size === 0) {
|
||||
} else {
|
||||
this.turnTime = kTurnTimeSeconds;
|
||||
|
@ -145,7 +148,7 @@ class TurnQueue extends EventEmitter {
|
|||
|
||||
class SocketVM extends EventEmitter {
|
||||
private vm: QemuVM;
|
||||
private display: QemuDisplay;
|
||||
private display: QemuDisplay|null = null;
|
||||
|
||||
private timer: ExtendableTimer = new ExtendableTimer(15);
|
||||
private users: Array<VMUser> = [];
|
||||
|
@ -325,7 +328,7 @@ class SocketVM extends EventEmitter {
|
|||
let self = this;
|
||||
|
||||
// Hook up the display
|
||||
this.display.on('resize', async (width, height) => {
|
||||
this.display?.on('resize', async (width: number, height: number) => {
|
||||
if(self.display == null)
|
||||
return;
|
||||
|
||||
|
@ -349,7 +352,7 @@ class SocketVM extends EventEmitter {
|
|||
});
|
||||
});
|
||||
|
||||
this.display.on('rect', async (x, y, rect: ImageData) => {
|
||||
this.display?.on('rect', async (x: number, y: number, rect: ImageData) => {
|
||||
let canvas = new Canvas(rect.width, rect.height);
|
||||
canvas.getContext('2d').putImageData(rect, 0, 0);
|
||||
|
||||
|
@ -372,7 +375,7 @@ class SocketVM extends EventEmitter {
|
|||
|
||||
await user.SendMessage((encoder: Shared.MessageEncoder) => {
|
||||
encoder.Init(8);
|
||||
encoder.SetDisplaySizeMessage(this.display.Size().width, this.display.Size().height);
|
||||
encoder.SetDisplaySizeMessage(this.display!.Size().width, this.display!.Size().height);
|
||||
return encoder.Finish();
|
||||
});
|
||||
|
||||
|
@ -402,7 +405,7 @@ export type SocketComputerConfig = {
|
|||
};
|
||||
|
||||
export class SocketComputerServer {
|
||||
private vm: SocketVM = null;
|
||||
private vm: SocketVM|null = null;
|
||||
private fastify: FastifyInstance = fastify({
|
||||
exposeHeadRoutes: false
|
||||
});
|
||||
|
@ -442,12 +445,13 @@ export class SocketComputerServer {
|
|||
CTRoutes(app: FastifyInstance) {
|
||||
let self = this;
|
||||
|
||||
// @ts-ignore (fastify types are broken...)
|
||||
app.get('/', { websocket: true }, (connection: fastifyWebsocket.WebSocket, req: FastifyRequest) => {
|
||||
let address = req.ip;
|
||||
if(req.headers["cf-connecting-ip"] !== undefined) {
|
||||
address = req.headers["cf-connecting-ip"] as string;
|
||||
}
|
||||
new VMUser(connection, self.vm, address);
|
||||
new VMUser(connection, self.vm!, address);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
15
package.json
15
package.json
|
@ -8,15 +8,20 @@
|
|||
"webapp"
|
||||
],
|
||||
"scripts": {
|
||||
"build:frontend": "npm -w shared run build && npm -w webapp run build",
|
||||
"build:service": "npm -w shared run build && npm -w qemu run build && npm -w backend run build"
|
||||
"build": "yarn build:service && yarn build:frontend",
|
||||
"build:service": "npm -w shared run build && npm -w qemu run build && npm -w backend run build",
|
||||
"build:frontend": "npm -w shared run build && npm -w webapp run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"canvas": "^2.11.2",
|
||||
"parcel": "^2.12.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"canvas": "^2.11.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@parcel/packager-ts": "2.12.0",
|
||||
"@parcel/transformer-typescript-types": "2.12.0",
|
||||
"@types/node": "^20.12.2",
|
||||
"prettier": "^3.2.5",
|
||||
"stream-http": "^3.1.0",
|
||||
"typescript": "^5.4.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,17 +3,30 @@
|
|||
"version": "1.0.0",
|
||||
"private": "true",
|
||||
"description": "QEMU runtime for socketcomputer backend",
|
||||
"main": "dist/src/index.js",
|
||||
"exports": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc"
|
||||
"build": "parcel build src/index.ts --target node --target types"
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"targets": {
|
||||
"types": {},
|
||||
"node": {
|
||||
"context": "node",
|
||||
"isLibrary": true,
|
||||
"outputFormat": "esmodule"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
"canvas": "^2.11.2",
|
||||
"execa": "^8.0.1",
|
||||
"parcel": "^2.12.0",
|
||||
"split": "^1.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.12.2",
|
||||
"@types/split": "^1.0.5"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,11 +138,11 @@ export class QemuDisplay extends EventEmitter {
|
|||
};
|
||||
}
|
||||
|
||||
MouseEvent(x, y, buttons) {
|
||||
MouseEvent(x: number, y: number, buttons: number) {
|
||||
this.displayVnc.sendPointerEvent(x, y, buttons);
|
||||
}
|
||||
|
||||
KeyboardEvent(keysym, pressed) {
|
||||
KeyboardEvent(keysym: number, pressed: boolean) {
|
||||
this.displayVnc.sendKeyEvent(keysym, pressed);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ const kMaxFailCount = 5;
|
|||
|
||||
let gVMShouldSnapshot = false;
|
||||
|
||||
async function Sleep(ms) {
|
||||
async function Sleep(ms: number) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
|
@ -145,7 +145,7 @@ export class QemuVM extends EventEmitter {
|
|||
if (this.state !== stateShouldBe) throw new Error(message);
|
||||
}
|
||||
|
||||
private SetState(state) {
|
||||
private SetState(state: VMState) {
|
||||
this.state = state;
|
||||
this.emit('statechange', this.state);
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ export class QemuVM extends EventEmitter {
|
|||
return `${kVmTmpPathBase}/socket2-${this.definition.id}-vnc`;
|
||||
}
|
||||
|
||||
private async StartQemu(split) {
|
||||
private async StartQemu(split: Array<string>) {
|
||||
let self = this;
|
||||
|
||||
this.SetState(VMState.Starting);
|
||||
|
@ -227,11 +227,11 @@ export class QemuVM extends EventEmitter {
|
|||
switch (ev.event) {
|
||||
// Handle the STOP event sent when using -no-shutdown
|
||||
case 'STOP':
|
||||
await self.qmpInstance.Execute('system_reset');
|
||||
await self.qmpInstance?.Execute('system_reset');
|
||||
break;
|
||||
case 'RESET':
|
||||
self.VMLog('got a reset event!');
|
||||
await self.qmpInstance.Execute('cont');
|
||||
await self.qmpInstance?.Execute('cont');
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
@ -250,7 +250,7 @@ export class QemuVM extends EventEmitter {
|
|||
|
||||
try {
|
||||
await Sleep(500);
|
||||
this.qmpInstance.ConnectUNIX(this.GetQmpPath());
|
||||
this.qmpInstance?.ConnectUNIX(this.GetQmpPath());
|
||||
} catch (err) {
|
||||
// just try again
|
||||
await Sleep(500);
|
||||
|
@ -261,7 +261,7 @@ export class QemuVM extends EventEmitter {
|
|||
|
||||
private async DisconnectDisplay() {
|
||||
try {
|
||||
this.display.Disconnect();
|
||||
this.display?.Disconnect();
|
||||
this.display = null; // disassociate with that display object.
|
||||
|
||||
await unlink(this.GetVncPath());
|
||||
|
|
|
@ -123,7 +123,7 @@ export default class QmpClient extends Socket {
|
|||
});
|
||||
}
|
||||
|
||||
Connect(host, port) {
|
||||
Connect(host: string, port: number) {
|
||||
super.connect(port, host);
|
||||
this.ConnectImpl();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
Copyright 2021 Filipe Calaça Barbosa
|
||||
Copyright 2022 dither
|
||||
Copyright 2023 Lily Tsuru/modeco80 <lily.modeco80@protonmail.ch>
|
||||
Copyright 2023-2024 Lily Tsuru/modeco80 <lily.modeco80@protonmail.ch>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
|
||||
The source here was originally taken from a fork of [vnc-rfb-client](https://github.com/ayunami2000/vnc-rfb-client) made for the LucidVM project, available [here](https://github.com/lucidvm/rfb).
|
||||
|
||||
It has been grossly modified for the usecases for the `@socketcomputer/qemu` package:
|
||||
It has been grossly modified for the usecases for the socket2 backend, but is more than likely usable to other clients.
|
||||
|
||||
- converted to TypeScript
|
||||
- converted to (strict) TypeScript; my initial port beforehand was not strict and it Sucked
|
||||
- Also, actually use interfaces, and reduce the amount of code duplicated significantly in the rect decoders.
|
||||
- all modules rewritten to use ESM
|
||||
- some noisy debug prints removed
|
||||
- (some, very tiny) code cleanup
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
|
||||
import { IRectDecoder } from './decoders/decoder.js';
|
||||
import { HextileDecoder } from './decoders/hextile.js';
|
||||
import { RawDecoder } from './decoders/raw.js';
|
||||
import { ZrleDecoder } from './decoders/zrle.js';
|
||||
|
@ -13,57 +15,79 @@ import * as crypto from 'node:crypto';
|
|||
|
||||
import { SocketBuffer } from './socketbuffer.js';
|
||||
|
||||
import { VncRectangle, Color3, PixelFormat, Cursor } from './types.js';
|
||||
|
||||
export class VncClient extends EventEmitter {
|
||||
// These are in no particular order.
|
||||
|
||||
public debug: Boolean;
|
||||
public debug: boolean = false;
|
||||
|
||||
private _connected: Boolean;
|
||||
private _authenticated: Boolean;
|
||||
private _version: string;
|
||||
private _password: string;
|
||||
private _connected: boolean = false;
|
||||
private _authenticated: boolean = false;
|
||||
private _version: string = "";
|
||||
private _password: string = "";
|
||||
|
||||
private _audioChannels: number;
|
||||
private _audioFrequency: number;
|
||||
private _audioChannels: number = 2;
|
||||
private _audioFrequency: number = 22050;
|
||||
|
||||
private _rects: number;
|
||||
private _rects: number = 0;
|
||||
|
||||
private _decoders: any; // no real good way to type this yet. will do it later
|
||||
private _decoders: Array<IRectDecoder> = [];
|
||||
|
||||
private _fps: number;
|
||||
private _timerInterval: number;
|
||||
private _timerPointer;
|
||||
private _timerPointer : NodeJS.Timeout|null = null;
|
||||
|
||||
public fb: Buffer;
|
||||
public fb: Buffer = Buffer.from([]);
|
||||
|
||||
private _handshaked: Boolean;
|
||||
private _waitingServerInit: Boolean;
|
||||
private _expectingChallenge: Boolean;
|
||||
private _challengeResponseSent: Boolean;
|
||||
private _handshaked: boolean = false;
|
||||
private _waitingServerInit: boolean = false;
|
||||
private _expectingChallenge: boolean = false;
|
||||
private _challengeResponseSent: boolean = false;
|
||||
|
||||
private _set8BitColor: Boolean;
|
||||
private _set8BitColor: boolean = false;
|
||||
private _frameBufferReady = false;
|
||||
private _firstFrameReceived = false;
|
||||
private _processingFrame = false;
|
||||
|
||||
private _relativePointer: Boolean;
|
||||
private _relativePointer: boolean = false;
|
||||
|
||||
public bigEndianFlag: Boolean;
|
||||
public bigEndianFlag: boolean = false;
|
||||
|
||||
public clientWidth: number;
|
||||
public clientHeight: number;
|
||||
public clientName: string;
|
||||
public clientWidth: number = 0;
|
||||
public clientHeight: number = 0;
|
||||
public clientName: string = "";
|
||||
|
||||
public pixelFormat: any;
|
||||
public pixelFormat: PixelFormat = {
|
||||
bitsPerPixel: 0,
|
||||
depth: 0,
|
||||
bigEndianFlag: 0,
|
||||
trueColorFlag: 0,
|
||||
redMax: 0,
|
||||
greenMax: 0,
|
||||
blueMax: 0,
|
||||
redShift: 0,
|
||||
greenShift: 0,
|
||||
blueShift: 0
|
||||
};
|
||||
|
||||
private _colorMap: any[];
|
||||
private _audioData: Buffer;
|
||||
private _colorMap: Color3[] = [];
|
||||
private _audioData: Buffer = Buffer.from([]);
|
||||
|
||||
private _cursor: any;
|
||||
private _cursor: Cursor = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
cursorPixels: null,
|
||||
bitmask: null,
|
||||
posX: 0,
|
||||
posY: 0
|
||||
};
|
||||
|
||||
public encodings: number[];
|
||||
|
||||
private _connection: net.Socket;
|
||||
private _connection: net.Socket|null = null;
|
||||
private _socketBuffer: SocketBuffer;
|
||||
|
||||
static get consts() {
|
||||
|
@ -101,7 +125,7 @@ export class VncClient extends EventEmitter {
|
|||
* @returns {number}
|
||||
*/
|
||||
get localPort() {
|
||||
return this._connection ? this._connection.localPort : 0;
|
||||
return this._connection ? this._connection?.localPort : 0;
|
||||
}
|
||||
|
||||
constructor(options: any = { debug: false, fps: 0, encodings: [] }) {
|
||||
|
@ -125,7 +149,7 @@ export class VncClient extends EventEmitter {
|
|||
this._audioFrequency = options.audioFrequency || 22050;
|
||||
|
||||
this._rects = 0;
|
||||
this._decoders = {};
|
||||
|
||||
this._decoders[consts.encodings.raw] = new RawDecoder();
|
||||
// TODO: Implement tight encoding
|
||||
// this._decoders[encodings.tight] = new tightDecoder();
|
||||
|
@ -155,7 +179,7 @@ export class VncClient extends EventEmitter {
|
|||
* Adjuste the configured FPS
|
||||
* @param fps {number} - Number of update requests send by second
|
||||
*/
|
||||
changeFps(fps) {
|
||||
changeFps(fps: number) {
|
||||
if (!Number.isNaN(fps)) {
|
||||
this._fps = Number(fps);
|
||||
this._timerInterval = this._fps > 0 ? 1000 / this._fps : 0;
|
||||
|
@ -178,7 +202,7 @@ export class VncClient extends EventEmitter {
|
|||
* @param options
|
||||
*/
|
||||
connect(
|
||||
options /* = {
|
||||
options: any /* = {
|
||||
host: '',
|
||||
password: '',
|
||||
path: '',
|
||||
|
@ -199,31 +223,31 @@ export class VncClient extends EventEmitter {
|
|||
this._connection = net.connect(options.port || 5900, options.host);
|
||||
|
||||
// disable nagle's algorithm for TCP
|
||||
this._connection.setNoDelay();
|
||||
this._connection?.setNoDelay();
|
||||
} else {
|
||||
// unix socket. bodged in but oh well
|
||||
this._connection = net.connect(options.path);
|
||||
}
|
||||
|
||||
this._connection.on('connect', () => {
|
||||
this._connection?.on('connect', () => {
|
||||
this._connected = true;
|
||||
this.emit('connected');
|
||||
});
|
||||
|
||||
this._connection.on('close', () => {
|
||||
this._connection?.on('close', () => {
|
||||
this.resetState();
|
||||
this.emit('closed');
|
||||
});
|
||||
|
||||
this._connection.on('timeout', () => {
|
||||
this._connection?.on('timeout', () => {
|
||||
this.emit('connectTimeout');
|
||||
});
|
||||
|
||||
this._connection.on('error', (err) => {
|
||||
this._connection?.on('error', (err) => {
|
||||
this.emit('connectError', err);
|
||||
});
|
||||
|
||||
this._connection.on('data', async (data) => {
|
||||
this._connection?.on('data', async (data) => {
|
||||
this._socketBuffer.pushData(data);
|
||||
|
||||
if (!this._handshaked) {
|
||||
|
@ -243,7 +267,7 @@ export class VncClient extends EventEmitter {
|
|||
*/
|
||||
disconnect() {
|
||||
if (this._connection) {
|
||||
this._connection.end();
|
||||
this._connection?.end();
|
||||
this.resetState();
|
||||
this.emit('disconnected');
|
||||
}
|
||||
|
@ -269,7 +293,7 @@ export class VncClient extends EventEmitter {
|
|||
message.writeUInt16BE(width, 6); // Width
|
||||
message.writeUInt16BE(height, 8); // Height
|
||||
|
||||
this._connection.write(message);
|
||||
this._connection?.write(message);
|
||||
|
||||
this._frameBufferReady = true;
|
||||
}
|
||||
|
@ -283,15 +307,15 @@ export class VncClient extends EventEmitter {
|
|||
// Handshake, negotiating protocol version
|
||||
if (this._socketBuffer.toString() === consts.versionString.V3_003) {
|
||||
this._log('Sending 3.3', true);
|
||||
this._connection.write(consts.versionString.V3_003);
|
||||
this._connection?.write(consts.versionString.V3_003);
|
||||
this._version = '3.3';
|
||||
} else if (this._socketBuffer.toString() === consts.versionString.V3_007) {
|
||||
this._log('Sending 3.7', true);
|
||||
this._connection.write(consts.versionString.V3_007);
|
||||
this._connection?.write(consts.versionString.V3_007);
|
||||
this._version = '3.7';
|
||||
} else if (this._socketBuffer.toString() === consts.versionString.V3_008) {
|
||||
this._log('Sending 3.8', true);
|
||||
this._connection.write(consts.versionString.V3_008);
|
||||
this._connection?.write(consts.versionString.V3_008);
|
||||
this._version = '3.8';
|
||||
} else {
|
||||
// Negotiating auth mechanism
|
||||
|
@ -299,10 +323,10 @@ export class VncClient extends EventEmitter {
|
|||
if (this._socketBuffer.includes(0x02) && this._password) {
|
||||
this._log('Password provided and server support VNC auth. Choosing VNC auth.', true);
|
||||
this._expectingChallenge = true;
|
||||
this._connection.write(Buffer.from([0x02]));
|
||||
this._connection?.write(Buffer.from([0x02]));
|
||||
} else if (this._socketBuffer.includes(1)) {
|
||||
this._log('Password not provided or server does not support VNC auth. Trying none.', true);
|
||||
this._connection.write(Buffer.from([0x01]));
|
||||
this._connection?.write(Buffer.from([0x01]));
|
||||
if (this._version === '3.7') {
|
||||
this._waitingServerInit = true;
|
||||
} else {
|
||||
|
@ -352,7 +376,7 @@ export class VncClient extends EventEmitter {
|
|||
response.fill(des1.update(this._socketBuffer.buffer.slice(0, 8)), 0, 8);
|
||||
response.fill(des2.update(this._socketBuffer.buffer.slice(8, 16)), 8, 16);
|
||||
|
||||
this._connection.write(response);
|
||||
this._connection?.write(response);
|
||||
this._challengeResponseSent = true;
|
||||
}
|
||||
|
||||
|
@ -363,7 +387,7 @@ export class VncClient extends EventEmitter {
|
|||
* Reverse bits order of a byte
|
||||
* @param buf - Buffer to be flipped
|
||||
*/
|
||||
reverseBits(buf) {
|
||||
reverseBits(buf: Buffer) {
|
||||
for (let x = 0; x < buf.length; x++) {
|
||||
let newByte = 0;
|
||||
newByte += buf[x] & 128 ? 1 : 0;
|
||||
|
@ -390,6 +414,7 @@ export class VncClient extends EventEmitter {
|
|||
|
||||
this.clientWidth = this._socketBuffer.readUInt16BE();
|
||||
this.clientHeight = this._socketBuffer.readUInt16BE();
|
||||
|
||||
this.pixelFormat.bitsPerPixel = this._socketBuffer.readUInt8();
|
||||
this.pixelFormat.depth = this._socketBuffer.readUInt8();
|
||||
this.pixelFormat.bigEndianFlag = this._socketBuffer.readUInt8();
|
||||
|
@ -457,7 +482,7 @@ export class VncClient extends EventEmitter {
|
|||
message.writeUInt8(0, 19); // PixelFormat - Padding
|
||||
|
||||
// Envia um setPixelFormat trocando para mapa de cores
|
||||
this._connection.write(message);
|
||||
this._connection?.write(message);
|
||||
|
||||
this.pixelFormat.bitsPerPixel = 8;
|
||||
this.pixelFormat.depth = 8;
|
||||
|
@ -487,7 +512,7 @@ export class VncClient extends EventEmitter {
|
|||
message.writeInt32BE(consts.encodings.raw, offset + 4);
|
||||
}
|
||||
|
||||
this._connection.write(message);
|
||||
this._connection?.write(message);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -498,7 +523,7 @@ export class VncClient extends EventEmitter {
|
|||
//this._log(`Sending clientInit`);
|
||||
this._waitingServerInit = true;
|
||||
// Shared bit set
|
||||
this._connection.write('1');
|
||||
this._connection?.write('1');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -558,6 +583,10 @@ export class VncClient extends EventEmitter {
|
|||
data: Buffer.alloc(4)
|
||||
};
|
||||
const { width, height, bitmask, cursorPixels } = this._cursor;
|
||||
|
||||
if(bitmask == null || cursorPixels == null)
|
||||
throw new Error('No cursor data to get!');
|
||||
|
||||
const data = Buffer.alloc(height * width * 4);
|
||||
for (var y = 0; y < height; y++) {
|
||||
for (var x = 0; x < width; x++) {
|
||||
|
@ -568,6 +597,7 @@ export class VncClient extends EventEmitter {
|
|||
case 8:
|
||||
console.log(8);
|
||||
const index = cursorPixels.readUInt8(offset);
|
||||
// @ts-ignore (This line is extremely suspect anyways. I bet this is horribly broken!!)
|
||||
const color = this._colorMap[index] | 0xff;
|
||||
data.writeIntBE(color, offset, 4);
|
||||
break;
|
||||
|
@ -606,12 +636,14 @@ export class VncClient extends EventEmitter {
|
|||
|
||||
while (this._rects) {
|
||||
await this._socketBuffer.waitBytes(12);
|
||||
const rect: any = {};
|
||||
rect.x = this._socketBuffer.readUInt16BE();
|
||||
rect.y = this._socketBuffer.readUInt16BE();
|
||||
rect.width = this._socketBuffer.readUInt16BE();
|
||||
rect.height = this._socketBuffer.readUInt16BE();
|
||||
rect.encoding = this._socketBuffer.readInt32BE();
|
||||
const rect: VncRectangle = {
|
||||
x: this._socketBuffer.readUInt16BE(),
|
||||
y: this._socketBuffer.readUInt16BE(),
|
||||
width: this._socketBuffer.readUInt16BE(),
|
||||
height: this._socketBuffer.readUInt16BE(),
|
||||
encoding: this._socketBuffer.readInt32BE(),
|
||||
data: null // for now
|
||||
};
|
||||
|
||||
if (rect.encoding === consts.encodings.pseudoQemuAudio) {
|
||||
this.sendAudio(true);
|
||||
|
@ -700,7 +732,7 @@ export class VncClient extends EventEmitter {
|
|||
this._colorMap[firstColor] = {
|
||||
r: Math.floor((this._socketBuffer.readUInt16BE() / 65535) * 255),
|
||||
g: Math.floor((this._socketBuffer.readUInt16BE() / 65535) * 255),
|
||||
b: Math.floor((this._socketBuffer.readUInt16BE() / 65535) * 255)
|
||||
b: Math.floor((this._socketBuffer.readUInt16BE() / 65535) * 255),
|
||||
};
|
||||
firstColor++;
|
||||
}
|
||||
|
@ -733,7 +765,7 @@ export class VncClient extends EventEmitter {
|
|||
*/
|
||||
resetState() {
|
||||
if (this._connection) {
|
||||
this._connection.end();
|
||||
this._connection?.end();
|
||||
}
|
||||
|
||||
if (this._timerPointer) {
|
||||
|
@ -782,7 +814,7 @@ export class VncClient extends EventEmitter {
|
|||
this._rects = 0;
|
||||
|
||||
this._colorMap = [];
|
||||
this.fb = null;
|
||||
this.fb = Buffer.from([]);
|
||||
|
||||
this._socketBuffer?.flush(false);
|
||||
|
||||
|
@ -792,7 +824,9 @@ export class VncClient extends EventEmitter {
|
|||
x: 0,
|
||||
y: 0,
|
||||
cursorPixels: null,
|
||||
bitmask: null
|
||||
bitmask: null,
|
||||
posX: 0,
|
||||
posY: 0
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -803,7 +837,7 @@ export class VncClient extends EventEmitter {
|
|||
* @param key - Key code (keysym) defined by X Window System, check https://wiki.linuxquestions.org/wiki/List_of_keysyms
|
||||
* @param down - True if the key is pressed, false if it is not
|
||||
*/
|
||||
sendKeyEvent(key, down = false) {
|
||||
sendKeyEvent(key: number, down: boolean = false) {
|
||||
const message = Buffer.alloc(8);
|
||||
message.writeUInt8(4); // Message type
|
||||
message.writeUInt8(down ? 1 : 0, 1); // Down flag
|
||||
|
@ -812,7 +846,7 @@ export class VncClient extends EventEmitter {
|
|||
|
||||
message.writeUInt32BE(key, 4); // Key code
|
||||
|
||||
this._connection.write(message);
|
||||
this._connection?.write(message);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -821,7 +855,7 @@ export class VncClient extends EventEmitter {
|
|||
* @param yPosition - Y Position
|
||||
* @param mask - Raw RFB button mask
|
||||
*/
|
||||
sendPointerEvent(xPosition, yPosition, buttonMask) {
|
||||
sendPointerEvent(xPosition: number, yPosition: number, buttonMask: number) {
|
||||
const message = Buffer.alloc(6);
|
||||
message.writeUInt8(consts.clientMsgTypes.pointerEvent); // Message type
|
||||
message.writeUInt8(buttonMask, 1); // Button Mask
|
||||
|
@ -832,14 +866,14 @@ export class VncClient extends EventEmitter {
|
|||
this._cursor.posX = xPosition;
|
||||
this._cursor.posY = yPosition;
|
||||
|
||||
this._connection.write(message);
|
||||
this._connection?.write(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send client cut message to server
|
||||
* @param text - latin1 encoded
|
||||
*/
|
||||
clientCutText(text) {
|
||||
clientCutText(text: string) {
|
||||
const textBuffer = Buffer.from(text, 'latin1');
|
||||
const message = Buffer.alloc(8 + textBuffer.length);
|
||||
message.writeUInt8(6); // Message type
|
||||
|
@ -849,18 +883,18 @@ export class VncClient extends EventEmitter {
|
|||
message.writeUInt32BE(textBuffer.length, 4); // Padding
|
||||
textBuffer.copy(message, 8);
|
||||
|
||||
this._connection.write(message);
|
||||
this._connection?.write(message);
|
||||
}
|
||||
|
||||
sendAudio(enable) {
|
||||
sendAudio(enable: boolean) {
|
||||
const message = Buffer.alloc(4);
|
||||
message.writeUInt8(consts.clientMsgTypes.qemuAudio); // Message type
|
||||
message.writeUInt8(1, 1); // Submessage Type
|
||||
message.writeUInt16BE(enable ? 0 : 1, 2); // Operation
|
||||
this._connection.write(message);
|
||||
this._connection?.write(message);
|
||||
}
|
||||
|
||||
sendAudioConfig(channels, frequency) {
|
||||
sendAudioConfig(channels: number, frequency: number) {
|
||||
const message = Buffer.alloc(10);
|
||||
message.writeUInt8(consts.clientMsgTypes.qemuAudio); // Message type
|
||||
message.writeUInt8(1, 1); // Submessage Type
|
||||
|
@ -868,7 +902,7 @@ export class VncClient extends EventEmitter {
|
|||
message.writeUInt8(0 /*U8*/, 4); // Sample Format
|
||||
message.writeUInt8(channels, 5); // Number of Channels
|
||||
message.writeUInt32BE(frequency, 6); // Frequency
|
||||
this._connection.write(message);
|
||||
this._connection?.write(message);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -877,7 +911,7 @@ export class VncClient extends EventEmitter {
|
|||
* @param debug
|
||||
* @private
|
||||
*/
|
||||
_log(text, debug = false) {
|
||||
_log(text: string, debug = false) {
|
||||
if (!debug || (debug && this.debug)) {
|
||||
console.log(text);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
export class CopyRectDecoder {
|
||||
getPixelBytePos(x, y, width, height) {
|
||||
return (y * width + x) * 4;
|
||||
}
|
||||
import { SocketBuffer } from "../socketbuffer";
|
||||
import { IRectDecoder } from "./decoder";
|
||||
import { getPixelBytePos } from "./util";
|
||||
|
||||
async decode(rect, fb, bitsPerPixel, colorMap, screenW, screenH, socket, depth): Promise<void> {
|
||||
import { VncRectangle, Color3 } from "../types";
|
||||
|
||||
export class CopyRectDecoder implements IRectDecoder {
|
||||
async decode(rect: VncRectangle, fb: Buffer, bitsPerPixel: number, colorMap: Array<Color3>, screenW: number, screenH: number, socket: SocketBuffer, depth: number): Promise<void> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
await socket.waitBytes(4);
|
||||
rect.data = socket.readNBytesOffset(4);
|
||||
|
@ -13,8 +15,8 @@ export class CopyRectDecoder {
|
|||
|
||||
for (let h = 0; h < rect.height; h++) {
|
||||
for (let w = 0; w < rect.width; w++) {
|
||||
const fbOrigBytePosOffset = this.getPixelBytePos(x + w, y + h, screenW, screenH);
|
||||
const fbBytePosOffset = this.getPixelBytePos(rect.x + w, rect.y + h, screenW, screenH);
|
||||
const fbOrigBytePosOffset = getPixelBytePos(x + w, y + h, screenW, screenH);
|
||||
const fbBytePosOffset = getPixelBytePos(rect.x + w, rect.y + h, screenW, screenH);
|
||||
|
||||
fb.writeUInt8(fb.readUInt8(fbOrigBytePosOffset), fbBytePosOffset);
|
||||
fb.writeUInt8(fb.readUInt8(fbOrigBytePosOffset + 1), fbBytePosOffset + 1);
|
||||
|
|
7
qemu/src/rfb/decoders/decoder.ts
Normal file
7
qemu/src/rfb/decoders/decoder.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { SocketBuffer } from "../socketbuffer";
|
||||
import { Color3, VncRectangle } from "../types";
|
||||
|
||||
|
||||
export interface IRectDecoder {
|
||||
decode(rect: VncRectangle, fb: Buffer, bitsPerPixel: number, colorMap: Array<Color3>, screenW: number, screenH: number, socket: SocketBuffer, depth: number): Promise<void>;
|
||||
}
|
|
@ -1,27 +1,25 @@
|
|||
export class HextileDecoder {
|
||||
getPixelBytePos(x, y, width, height) {
|
||||
return (y * width + x) * 4;
|
||||
}
|
||||
import { SocketBuffer } from "../socketbuffer";
|
||||
import { IRectDecoder } from "./decoder";
|
||||
import { applyColor, getPixelBytePos } from "./util";
|
||||
|
||||
async decode(rect, fb, bitsPerPixel, colorMap, screenW, screenH, socket, depth): Promise<void> {
|
||||
import { VncRectangle, Color3 } from "../types";
|
||||
|
||||
export class HextileDecoder implements IRectDecoder {
|
||||
|
||||
async decode(rect: VncRectangle, fb: Buffer, bitsPerPixel: number, colorMap: Array<Color3>, screenW: number, screenH: number, socket: SocketBuffer, depth: number): Promise<void> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const initialOffset = socket.offset;
|
||||
let dataSize = 0;
|
||||
|
||||
let tiles;
|
||||
let totalTiles;
|
||||
let tilesX;
|
||||
let tilesY;
|
||||
|
||||
let lastSubEncoding;
|
||||
let lastSubEncoding = 0;
|
||||
|
||||
const backgroundColor = { r: 0, g: 0, b: 0, a: 255 };
|
||||
const foregroundColor = { r: 0, g: 0, b: 0, a: 255 };
|
||||
|
||||
tilesX = Math.ceil(rect.width / 16);
|
||||
tilesY = Math.ceil(rect.height / 16);
|
||||
tiles = tilesX * tilesY;
|
||||
totalTiles = tiles;
|
||||
let tilesX = Math.ceil(rect.width / 16);
|
||||
let tilesY = Math.ceil(rect.height / 16);
|
||||
let tiles = tilesX * tilesY;
|
||||
let totalTiles = tiles;
|
||||
|
||||
while (tiles) {
|
||||
await socket.waitBytes(1);
|
||||
|
@ -42,7 +40,7 @@ export class HextileDecoder {
|
|||
// We need to ignore zeroed tile after a raw tile
|
||||
} else {
|
||||
// If zeroed tile and last tile was not raw, use the last backgroundColor
|
||||
this.applyColor(tw, th, tx, ty, screenW, screenH, backgroundColor, fb);
|
||||
applyColor(tw, th, tx, ty, screenW, screenH, backgroundColor, fb);
|
||||
}
|
||||
} else if (subEncoding & 0x01) {
|
||||
// If Raw, ignore all other bits
|
||||
|
@ -50,7 +48,7 @@ export class HextileDecoder {
|
|||
dataSize += th * tw * (bitsPerPixel / 8);
|
||||
for (let h = 0; h < th; h++) {
|
||||
for (let w = 0; w < tw; w++) {
|
||||
const fbBytePosOffset = this.getPixelBytePos(tx + w, ty + h, screenW, screenH);
|
||||
const fbBytePosOffset = getPixelBytePos(tx + w, ty + h, screenW, screenH);
|
||||
if (bitsPerPixel === 8) {
|
||||
const index = socket.readUInt8();
|
||||
const color = colorMap[index];
|
||||
|
@ -148,7 +146,7 @@ export class HextileDecoder {
|
|||
}
|
||||
|
||||
// Initialize tile with the background color
|
||||
this.applyColor(tw, th, tx, ty, screenW, screenH, backgroundColor, fb);
|
||||
applyColor(tw, th, tx, ty, screenW, screenH, backgroundColor, fb);
|
||||
|
||||
// AnySubrects bit
|
||||
if (subEncoding & 0x08) {
|
||||
|
@ -206,13 +204,13 @@ export class HextileDecoder {
|
|||
const sw = (wh >> 4) + 1;
|
||||
const sh = (wh & 0x0f) + 1;
|
||||
|
||||
this.applyColor(sw, sh, tx + sx, ty + sy, screenW, screenH, color, fb);
|
||||
applyColor(sw, sh, tx + sx, ty + sy, screenW, screenH, color, fb);
|
||||
}
|
||||
} else {
|
||||
this.applyColor(tw, th, tx, ty, screenW, screenH, backgroundColor, fb);
|
||||
applyColor(tw, th, tx, ty, screenW, screenH, backgroundColor, fb);
|
||||
}
|
||||
} else {
|
||||
this.applyColor(tw, th, tx, ty, screenW, screenH, backgroundColor, fb);
|
||||
applyColor(tw, th, tx, ty, screenW, screenH, backgroundColor, fb);
|
||||
}
|
||||
|
||||
lastSubEncoding = subEncoding;
|
||||
|
@ -226,16 +224,5 @@ export class HextileDecoder {
|
|||
});
|
||||
}
|
||||
|
||||
// Apply color to a rect on buffer
|
||||
applyColor(tw, th, tx, ty, screenW, screenH, color, fb) {
|
||||
for (let h = 0; h < th; h++) {
|
||||
for (let w = 0; w < tw; w++) {
|
||||
const fbBytePosOffset = this.getPixelBytePos(tx + w, ty + h, screenW, screenH);
|
||||
fb.writeUInt8(color.r || 255, fbBytePosOffset + 2);
|
||||
fb.writeUInt8(color.g || 255, fbBytePosOffset + 1);
|
||||
fb.writeUInt8(color.b || 255, fbBytePosOffset);
|
||||
fb.writeUInt8(255, fbBytePosOffset + 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
export class RawDecoder {
|
||||
getPixelBytePos(x, y, width, height) {
|
||||
return (y * width + x) * 4;
|
||||
}
|
||||
import { SocketBuffer } from "../socketbuffer";
|
||||
import { IRectDecoder } from "./decoder";
|
||||
import { getPixelBytePos } from "./util";
|
||||
|
||||
async decode(rect, fb, bitsPerPixel, colorMap, screenW, screenH, socket, depth): Promise<void> {
|
||||
import { VncRectangle, Color3 } from "../types";
|
||||
|
||||
export class RawDecoder implements IRectDecoder {
|
||||
|
||||
async decode(rect: VncRectangle, fb: Buffer, bitsPerPixel: number, colorMap: Array<Color3>, screenW: number, screenH: number, socket: SocketBuffer, depth: number): Promise<void> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
await socket.waitBytes(rect.width * rect.height * (bitsPerPixel / 8));
|
||||
rect.data = socket.readNBytesOffset(rect.width * rect.height * (bitsPerPixel / 8));
|
||||
|
||||
for (let h = 0; h < rect.height; h++) {
|
||||
for (let w = 0; w < rect.width; w++) {
|
||||
const fbBytePosOffset = this.getPixelBytePos(rect.x + w, rect.y + h, screenW, screenH);
|
||||
const fbBytePosOffset = getPixelBytePos(rect.x + w, rect.y + h, screenW, screenH);
|
||||
if (bitsPerPixel === 8) {
|
||||
const bytePosOffset = h * rect.width + w;
|
||||
const index = rect.data.readUInt8(bytePosOffset);
|
||||
|
|
19
qemu/src/rfb/decoders/util.ts
Normal file
19
qemu/src/rfb/decoders/util.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
|
||||
import { Color3 } from "../types";
|
||||
|
||||
export function getPixelBytePos(x: number, y: number, width: number, height: number, stride: number = 4): number {
|
||||
return (y * width + x) * stride;
|
||||
}
|
||||
|
||||
// Apply color to a rect on buffer
|
||||
export function applyColor(tw: number, th: number, tx: number, ty: number, screenW: number, screenH: number, color: Color3, fb: Buffer) {
|
||||
for (let h = 0; h < th; h++) {
|
||||
for (let w = 0; w < tw; w++) {
|
||||
const fbBytePosOffset = getPixelBytePos(tx + w, ty + h, screenW, screenH);
|
||||
fb.writeUInt8(color.r || 255, fbBytePosOffset + 2);
|
||||
fb.writeUInt8(color.g || 255, fbBytePosOffset + 1);
|
||||
fb.writeUInt8(color.b || 255, fbBytePosOffset);
|
||||
fb.writeUInt8(255, fbBytePosOffset + 3);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,11 @@
|
|||
import * as zlib from 'node:zlib';
|
||||
import { SocketBuffer } from '../socketbuffer.js';
|
||||
import { SocketBuffer } from "../socketbuffer";
|
||||
import { IRectDecoder } from "./decoder";
|
||||
import { applyColor, getPixelBytePos } from "./util";
|
||||
|
||||
export class ZrleDecoder {
|
||||
import { VncRectangle, Color3, Color4 } from "../types";
|
||||
|
||||
export class ZrleDecoder implements IRectDecoder {
|
||||
private zlib: zlib.Inflate;
|
||||
private unBuffer: SocketBuffer;
|
||||
|
||||
|
@ -14,11 +18,7 @@ export class ZrleDecoder {
|
|||
});
|
||||
}
|
||||
|
||||
getPixelBytePos(x, y, width, height) {
|
||||
return (y * width + x) * 4;
|
||||
}
|
||||
|
||||
async decode(rect, fb, bitsPerPixel, colorMap, screenW, screenH, socket, depth): Promise<void> {
|
||||
async decode(rect: VncRectangle, fb: Buffer, bitsPerPixel: number, colorMap: Array<Color3>, screenW: number, screenH: number, socket: SocketBuffer, depth: number): Promise<void> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
await socket.waitBytes(4);
|
||||
|
||||
|
@ -35,15 +35,10 @@ export class ZrleDecoder {
|
|||
this.zlib.write(compressedData, async () => {
|
||||
this.zlib.flush();
|
||||
|
||||
let tiles;
|
||||
let totalTiles;
|
||||
let tilesX;
|
||||
let tilesY;
|
||||
|
||||
tilesX = Math.ceil(rect.width / 64);
|
||||
tilesY = Math.ceil(rect.height / 64);
|
||||
tiles = tilesX * tilesY;
|
||||
totalTiles = tiles;
|
||||
let tilesX = Math.ceil(rect.width / 64);
|
||||
let tilesY = Math.ceil(rect.height / 64);
|
||||
let tiles = tilesX * tilesY;
|
||||
let totalTiles = tiles;
|
||||
|
||||
while (tiles) {
|
||||
await this.unBuffer.waitBytes(1, 'tile begin.');
|
||||
|
@ -67,7 +62,7 @@ export class ZrleDecoder {
|
|||
// Raw
|
||||
for (let h = 0; h < th; h++) {
|
||||
for (let w = 0; w < tw; w++) {
|
||||
const fbBytePosOffset = this.getPixelBytePos(tx + w, ty + h, screenW, screenH);
|
||||
const fbBytePosOffset = getPixelBytePos(tx + w, ty + h, screenW, screenH);
|
||||
if (bitsPerPixel === 8) {
|
||||
await this.unBuffer.waitBytes(1, 'raw 8bits');
|
||||
const index = this.unBuffer.readUInt8();
|
||||
|
@ -105,11 +100,11 @@ export class ZrleDecoder {
|
|||
}
|
||||
} else if (subEncoding === 1) {
|
||||
// Single Color
|
||||
let color = { r: 0, g: 0, b: 0, a: 255 };
|
||||
let color: Color4 = { r: 0, g: 0, b: 0, a: 255 };
|
||||
if (bitsPerPixel === 8) {
|
||||
await this.unBuffer.waitBytes(1, 'single color 8bits');
|
||||
const index = this.unBuffer.readUInt8();
|
||||
color = colorMap[index];
|
||||
color = (colorMap[index] as Color4);
|
||||
} else if (bitsPerPixel === 24 || (bitsPerPixel === 32 && depth === 24)) {
|
||||
await this.unBuffer.waitBytes(3, 'single color 24bits');
|
||||
color.r = this.unBuffer.readUInt8();
|
||||
|
@ -122,7 +117,7 @@ export class ZrleDecoder {
|
|||
color.b = this.unBuffer.readUInt8();
|
||||
color.a = this.unBuffer.readUInt8();
|
||||
}
|
||||
this.applyColor(tw, th, tx, ty, screenW, screenH, color, fb);
|
||||
applyColor(tw, th, tx, ty, screenW, screenH, color, fb);
|
||||
} else if (subEncoding >= 2 && subEncoding <= 16) {
|
||||
// Palette
|
||||
const palette = [];
|
||||
|
@ -152,7 +147,7 @@ export class ZrleDecoder {
|
|||
// const i = (tw * th) / (8 / bitsPerIndex);
|
||||
// const pixels = [];
|
||||
|
||||
let byte;
|
||||
let byte = 0;
|
||||
let bitPos = 0;
|
||||
|
||||
for (let h = 0; h < th; h++) {
|
||||
|
@ -162,7 +157,7 @@ export class ZrleDecoder {
|
|||
byte = this.unBuffer.readUInt8();
|
||||
bitPos = 0;
|
||||
}
|
||||
let color;
|
||||
let color : Color4 = { r: 0, g: 0, b: 0, a: 255 };
|
||||
switch (bitsPerIndex) {
|
||||
case 1:
|
||||
if (bitPos === 0) {
|
||||
|
@ -216,7 +211,7 @@ export class ZrleDecoder {
|
|||
}
|
||||
break;
|
||||
}
|
||||
const fbBytePosOffset = this.getPixelBytePos(tx + w, ty + h, screenW, screenH);
|
||||
const fbBytePosOffset = getPixelBytePos(tx + w, ty + h, screenW, screenH);
|
||||
fb.writeUInt8(color.b ?? 0, fbBytePosOffset);
|
||||
fb.writeUInt8(color.g ?? 0, fbBytePosOffset + 1);
|
||||
fb.writeUInt8(color.r ?? 0, fbBytePosOffset + 2);
|
||||
|
@ -259,7 +254,7 @@ export class ZrleDecoder {
|
|||
totalRun += runLength;
|
||||
runs++;
|
||||
}
|
||||
const fbBytePosOffset = this.getPixelBytePos(tx + w, ty + h, screenW, screenH);
|
||||
const fbBytePosOffset = getPixelBytePos(tx + w, ty + h, screenW, screenH);
|
||||
fb.writeUInt8(color.b ?? 0, fbBytePosOffset);
|
||||
fb.writeUInt8(color.g ?? 0, fbBytePosOffset + 1);
|
||||
fb.writeUInt8(color.r ?? 0, fbBytePosOffset + 2);
|
||||
|
@ -321,7 +316,7 @@ export class ZrleDecoder {
|
|||
totalRun += runLength;
|
||||
runs++;
|
||||
}
|
||||
const fbBytePosOffset = this.getPixelBytePos(tx + w, ty + h, screenW, screenH);
|
||||
const fbBytePosOffset = getPixelBytePos(tx + w, ty + h, screenW, screenH);
|
||||
fb.writeUInt8(color.b ?? 0, fbBytePosOffset);
|
||||
fb.writeUInt8(color.g ?? 0, fbBytePosOffset + 1);
|
||||
fb.writeUInt8(color.r ?? 0, fbBytePosOffset + 2);
|
||||
|
@ -342,16 +337,4 @@ export class ZrleDecoder {
|
|||
});
|
||||
}
|
||||
|
||||
// Apply color to a rect on buffer
|
||||
applyColor(tw, th, tx, ty, screenW, screenH, color, fb) {
|
||||
for (let h = 0; h < th; h++) {
|
||||
for (let w = 0; w < tw; w++) {
|
||||
const fbBytePosOffset = this.getPixelBytePos(tx + w, ty + h, screenW, screenH);
|
||||
fb.writeUInt8(color.b || 255, fbBytePosOffset);
|
||||
fb.writeUInt8(color.g || 255, fbBytePosOffset + 1);
|
||||
fb.writeUInt8(color.r || 255, fbBytePosOffset + 2);
|
||||
fb.writeUInt8(255, fbBytePosOffset + 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
/// this is a pretty poor name.
|
||||
export class SocketBuffer {
|
||||
public buffer?: Buffer;
|
||||
private offset: number;
|
||||
public buffer: Buffer;
|
||||
public offset: number; // :(
|
||||
|
||||
constructor() {
|
||||
this.buffer = Buffer.from([]);
|
||||
this.offset = 0;
|
||||
this.flush();
|
||||
}
|
||||
|
||||
|
@ -21,11 +23,11 @@ export class SocketBuffer {
|
|||
return this.buffer.toString();
|
||||
}
|
||||
|
||||
includes(check) {
|
||||
includes(check: Buffer|number|string) {
|
||||
return this.buffer.includes(check);
|
||||
}
|
||||
|
||||
pushData(data) {
|
||||
pushData(data: Buffer) {
|
||||
this.buffer = Buffer.concat([this.buffer, data]);
|
||||
}
|
||||
|
||||
|
@ -77,17 +79,17 @@ export class SocketBuffer {
|
|||
return data;
|
||||
}
|
||||
|
||||
readNBytes(bytes, offset = this.offset) {
|
||||
readNBytes(bytes: number, offset = this.offset) {
|
||||
return this.buffer.slice(offset, offset + bytes);
|
||||
}
|
||||
|
||||
readNBytesOffset(bytes) {
|
||||
readNBytesOffset(bytes: number) {
|
||||
const data = this.buffer.slice(this.offset, this.offset + bytes);
|
||||
this.offset += bytes;
|
||||
return data;
|
||||
}
|
||||
|
||||
setOffset(n) {
|
||||
setOffset(n: number) {
|
||||
this.offset = n;
|
||||
}
|
||||
|
||||
|
@ -96,7 +98,7 @@ export class SocketBuffer {
|
|||
}
|
||||
|
||||
// name is nullable because there are Many(yay....) times it just isn't passed
|
||||
waitBytes(bytes, name: any | null = null): Promise<void> {
|
||||
async waitBytes(bytes: number, name: any | null = null): Promise<void> {
|
||||
if (this.bytesLeft() >= bytes) {
|
||||
return;
|
||||
}
|
||||
|
@ -114,17 +116,17 @@ export class SocketBuffer {
|
|||
});
|
||||
}
|
||||
|
||||
fill(data) {
|
||||
fill(data: Buffer) {
|
||||
this.buffer.fill(data, this.offset, this.offset + data.length);
|
||||
this.offset += data.length;
|
||||
}
|
||||
|
||||
fillMultiple(data, repeats) {
|
||||
fillMultiple(data: Buffer, repeats: number) {
|
||||
this.buffer.fill(data, this.offset, this.offset + data.length * repeats);
|
||||
this.offset += data.length * repeats;
|
||||
}
|
||||
|
||||
sleep(n): Promise<void> {
|
||||
sleep(n: number): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(resolve, n);
|
||||
});
|
||||
|