Abstract the VM, so that we can use other hypervisors in the future

This commit is contained in:
Elijahr2411
2023-02-25 15:34:59 -05:00
parent 6c50219ac7
commit a09d5fc6c1
3 changed files with 38 additions and 10 deletions

View File

@@ -8,8 +8,9 @@ import BatchRects from "./RectBatcher.js";
import { createCanvas, Canvas, CanvasRenderingContext2D, createImageData } from "canvas";
import { Mutex } from "async-mutex";
import log from "./log.js";
import VM from "./VM.js";
export default class QEMUVM extends EventEmitter {
export default class QEMUVM extends VM {
vnc? : rfb.RfbClient;
vncPort : number;
framebuffer : Canvas;
@@ -116,7 +117,7 @@ export default class QEMUVM extends EventEmitter {
this.vnc.on("resize", (s) => this.onVNCSize(s));
}
getSize() {
public getSize() {
if (!this.vnc) return {height:0,width:0};
return {height: this.vnc.height, width: this.vnc.width}
}
@@ -240,4 +241,16 @@ export default class QEMUVM extends EventEmitter {
res();
})
}
public pointerEvent(x: number, y: number, mask: number) {
if (!this.vnc) throw new Error("VNC was not instantiated.");
this.vnc.pointerEvent(x, y, mask);
}
public acceptingInput(): boolean {
return this.vncOpen;
}
public keyEvent(keysym: number, down: boolean): void {
if (!this.vnc) throw new Error("VNC was not instantiated.");
this.vnc.keyEvent(keysym, down ? 1 : 0);
}
}

12
src/VM.ts Normal file
View File

@@ -0,0 +1,12 @@
import { Canvas } from "canvas";
import EventEmitter from "events";
export default abstract class VM extends EventEmitter {
public abstract getSize() : {height:number;width:number;};
public abstract get framebuffer() : Canvas;
public abstract pointerEvent(x : number, y : number, mask : number) : void;
public abstract acceptingInput() : boolean;
public abstract keyEvent(keysym : number, down : boolean) : void;
public abstract Restore() : void;
public abstract Reboot() : Promise<void>;
}

View File

@@ -14,6 +14,7 @@ import QEMUVM from './QEMUVM.js';
import { Canvas, createCanvas, CanvasRenderingContext2D } from 'canvas';
import { IPData } from './IPData.js';
import log from './log.js';
import VM from './VM.js';
export default class WSServer {
private Config : IConfig;
@@ -44,8 +45,8 @@ export default class WSServer {
// Indefinite turn
private indefiniteTurn : User | null;
private ModPerms : number;
private VM : QEMUVM;
constructor(config : IConfig, vm : QEMUVM) {
private VM : VM;
constructor(config : IConfig, vm : VM) {
this.ChatHistory = new CircularBuffer<{user:string,msg:string}>(Array, 5);
this.TurnQueue = new Queue<User>();
this.TurnTime = 0;
@@ -254,22 +255,20 @@ export default class WSServer {
break;
case "mouse":
if (this.TurnQueue.peek() !== client && client.rank !== Rank.Admin) return;
if (!this.VM.vncOpen) return;
if (!this.VM.vnc) throw new Error("VNC Client was undefined");
if (!this.VM.acceptingInput()) return;
var x = parseInt(msgArr[1]);
var y = parseInt(msgArr[2]);
var mask = parseInt(msgArr[3]);
if (x === undefined || y === undefined || mask === undefined) return;
this.VM.vnc.pointerEvent(x, y, mask);
this.VM.pointerEvent(x, y, mask);
break;
case "key":
if (this.TurnQueue.peek() !== client && client.rank !== Rank.Admin) return;
if (!this.VM.vncOpen) return;
if (!this.VM.vnc) throw new Error("VNC Client was undefined");
if (!this.VM.acceptingInput()) return;
var keysym = parseInt(msgArr[1]);
var down = parseInt(msgArr[2]);
if (keysym === undefined || (down !== 0 && down !== 1)) return;
this.VM.vnc.keyEvent(keysym, down);
this.VM.keyEvent(keysym, down === 1 ? true : false);
break;
case "vote":
if (!this.Config.vm.snapshots) return;
@@ -327,6 +326,10 @@ export default class WSServer {
case "5":
// QEMU Monitor
if (client.rank !== Rank.Admin) return;
if (!(this.VM instanceof QEMUVM)) {
client.sendMsg(guacutils.encode("admin", "2", "This is not a QEMU VM and therefore QEMU monitor commands cannot be run."));
return;
}
if (msgArr.length !== 4 || msgArr[2] !== this.Config.collabvm.node) return;
var output = await this.VM.qmpClient.runMonitorCmd(msgArr[3]);
client.sendMsg(guacutils.encode("admin", "2", String(output)));