diff --git a/src/ts/protocol/CollabVMClient.ts b/src/ts/protocol/CollabVMClient.ts index 84a2d4a..0a820dd 100644 --- a/src/ts/protocol/CollabVMClient.ts +++ b/src/ts/protocol/CollabVMClient.ts @@ -1,4 +1,4 @@ -import {createNanoEvents } from "nanoevents"; +import {createNanoEvents, Emitter, DefaultEvents } from "nanoevents"; import * as Guacutils from './Guacutils.js'; import VM from "./VM.js"; import { User } from "./User.js"; @@ -24,9 +24,9 @@ export default class CollabVMClient { private voteStatus : VoteStatus | null = null; private node : string | null = null; // events that are used internally and not exposed - private emitter; + private emitter : Emitter; // public events - private publicEmitter; + private publicEmitter : Emitter; constructor(url : string) { // Save the URL @@ -45,28 +45,32 @@ export default class CollabVMClient { if (this.users.find(u => u.username === this.username)?.turn === -1) this.turn(true); }); + // Bind keyboard and mouse this.canvas.addEventListener('mousedown', (e : MouseEvent) => { if (this.users.find(u => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return; - this.mouse.processEvent(e, true); + this.mouse.initFromMouseEvent(e); this.sendmouse(this.mouse.x, this.mouse.y, this.mouse.makeMask()); }, { capture: true }); + this.canvas.addEventListener('mouseup', (e : MouseEvent) => { if (this.users.find(u => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return; - this.mouse.processEvent(e, false); + this.mouse.initFromMouseEvent(e); this.sendmouse(this.mouse.x, this.mouse.y, this.mouse.makeMask()); }, { capture: true }); + this.canvas.addEventListener('mousemove', (e : MouseEvent) => { if (this.users.find(u => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return; - this.mouse.processEvent(e, null); + this.mouse.initFromMouseEvent(e); this.sendmouse(this.mouse.x, this.mouse.y, this.mouse.makeMask()); }, { capture: true }); + this.canvas.addEventListener('keydown', (e : KeyboardEvent) => { e.preventDefault(); if (this.users.find(u => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return; @@ -76,6 +80,7 @@ export default class CollabVMClient { }, { capture: true }); + this.canvas.addEventListener('keyup', (e : KeyboardEvent) => { e.preventDefault(); if (this.users.find(u => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return; @@ -85,6 +90,23 @@ export default class CollabVMClient { }, { capture: true }); + + this.canvas.addEventListener('wheel', (ev: WheelEvent) => { + ev.preventDefault(); + if (this.users.find(u => u.username === this.username)?.turn === -1 && this.rank !== Rank.Admin) return; + this.mouse.initFromWheelEvent(ev); + + this.sendmouse(this.mouse.x, this.mouse.y, this.mouse.makeMask()); + + // this is a very, very ugly hack but it seems to work so /shrug + if(this.mouse.scrollUp) + this.mouse.scrollUp = false; + else if(this.mouse.scrollDown) + this.mouse.scrollDown = false; + + this.sendmouse(this.mouse.x, this.mouse.y, this.mouse.makeMask()); + }, { capture: true }); + this.canvas.addEventListener('contextmenu', e => e.preventDefault()); // Create the WebSocket this.socket = new WebSocket(url, "guacamole"); diff --git a/src/ts/protocol/mouse.ts b/src/ts/protocol/mouse.ts index f26c310..f464e85 100644 --- a/src/ts/protocol/mouse.ts +++ b/src/ts/protocol/mouse.ts @@ -1,9 +1,13 @@ +function maskContains(mask: number, bit: number): boolean { + return (mask & bit) == bit; +} + export default class Mouse { left : boolean = false; middle : boolean = false; right : boolean = false; - scrolldown : boolean = false; - scrollup : boolean = false; + scrollDown : boolean = false; + scrollUp : boolean = false; x : number = 0; y : number = 0; constructor() {} @@ -13,24 +17,31 @@ export default class Mouse { if (this.left) mask |= 1; if (this.middle) mask |= 2; if (this.right) mask |= 4; - if (this.scrollup) mask |= 8; - if (this.scrolldown) mask |= 16; + if (this.scrollUp) mask |= 8; + if (this.scrollDown) mask |= 16; return mask; } - processEvent(e : MouseEvent, down : boolean | null = null) { - if (down !== null) switch (e.button) { - case 0: - this.left = down; - break; - case 1: - this.middle = down; - break; - case 2: - this.right = down; - break; - } + initFromMouseEvent(e: MouseEvent) { + this.left = maskContains(e.buttons, 1); + this.right = maskContains(e.buttons, 2); + this.middle = maskContains(e.buttons, 4); + this.x = e.offsetX; this.y = e.offsetY; } + + // don't think there's a good way of shoehorning this in processEvent so .. + // (I guess could union e to be MouseEvent|WheelEvent and put this in there, but it'd be a + // completely unnesscary runtime check for a one-event situation, so having it be seperate + // and even call the MouseEvent implementation is more than good enough) + initFromWheelEvent(ev: WheelEvent) { + this.initFromMouseEvent(ev as MouseEvent); + + // Now do the actual wheel handling + if (ev.deltaY < 0) + this.scrollUp = true; + else if (ev.deltaY > 0) + this.scrollDown = true; + } } \ No newline at end of file