Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 81 additions & 11 deletions src/server/InputHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,88 @@ export interface InputMessage {
}

export class InputHandler {
private lastMoveTime = 0;
private lastScrollTime = 0;
private pendingMove: InputMessage | null = null;
private pendingScroll: InputMessage | null = null;
private moveTimer: ReturnType<typeof setTimeout> | null = null;
private scrollTimer: ReturnType<typeof setTimeout> | null = null;

constructor() {
mouse.config.mouseSpeed = 1000;
}

async handleMessage(msg: InputMessage) {
// Validation: Text length sanitation
if (msg.text && msg.text.length > 500) {
msg.text = msg.text.substring(0, 500);
}

// Validation: Sane bounds for coordinates
const MAX_COORD = 2000;
if (typeof msg.dx === 'number' && Number.isFinite(msg.dx)) {
msg.dx = Math.max(-MAX_COORD, Math.min(MAX_COORD, msg.dx));
}
if (typeof msg.dy === 'number' && Number.isFinite(msg.dy)) {
msg.dy = Math.max(-MAX_COORD, Math.min(MAX_COORD, msg.dy));
}

// Throttling: Limit high-frequency events to ~60fps (16ms)
if (msg.type === 'move') {
const now = Date.now();
if (now - this.lastMoveTime < 16) {
this.pendingMove = msg;
if (!this.moveTimer) {
this.moveTimer = setTimeout(() => {
this.moveTimer = null;
if (this.pendingMove) {
const pending = this.pendingMove;
this.pendingMove = null;
this.handleMessage(pending);
}
}, 16);
}
return;
}
this.lastMoveTime = now;
} else if (msg.type === 'scroll') {
const now = Date.now();
if (now - this.lastScrollTime < 16) {
this.pendingScroll = msg;
if (!this.scrollTimer) {
this.scrollTimer = setTimeout(() => {
this.scrollTimer = null;
if (this.pendingScroll) {
const pending = this.pendingScroll;
this.pendingScroll = null;
this.handleMessage(pending);
}
}, 16);
}
return;
}
this.lastScrollTime = now;
}

switch (msg.type) {
case 'move':
if (msg.dx !== undefined && msg.dy !== undefined) {
const currentPos = await mouse.getPosition();

await mouse.setPosition(new Point(
currentPos.x + msg.dx,
currentPos.y + msg.dy
));
await mouse.setPosition(
new Point(currentPos.x + msg.dx, currentPos.y + msg.dy)
);
}
break;

case 'click':
if (msg.button) {
const btn = msg.button === 'left' ? Button.LEFT : msg.button === 'right' ? Button.RIGHT : Button.MIDDLE;
const btn =
msg.button === 'left'
? Button.LEFT
: msg.button === 'right'
? Button.RIGHT
: Button.MIDDLE;

if (msg.press) {
await mouse.pressButton(btn);
} else {
Expand Down Expand Up @@ -70,15 +132,18 @@ export class InputHandler {

case 'zoom':
if (msg.delta !== undefined && msg.delta !== 0) {
const sensitivityFactor = 0.5;
const sensitivityFactor = 0.5;
const MAX_ZOOM_STEP = 5;

const scaledDelta =
Math.sign(msg.delta) *
Math.min(Math.abs(msg.delta) * sensitivityFactor, MAX_ZOOM_STEP);
Math.min(
Math.abs(msg.delta) * sensitivityFactor,
MAX_ZOOM_STEP
);

const amount = -scaledDelta;

await keyboard.pressKey(Key.LeftControl);
try {
await mouse.scrollDown(amount);
Expand All @@ -92,6 +157,7 @@ export class InputHandler {
if (msg.key) {
console.log(`Processing key: ${msg.key}`);
const nutKey = KEY_MAP[msg.key.toLowerCase()];

if (nutKey !== undefined) {
await keyboard.type(nutKey);
} else if (msg.key.length === 1) {
Expand All @@ -105,9 +171,11 @@ export class InputHandler {
case 'combo':
if (msg.keys && msg.keys.length > 0) {
const nutKeys: (Key | string)[] = [];

for (const k of msg.keys) {
const lowerKey = k.toLowerCase();
const nutKey = KEY_MAP[lowerKey];

if (nutKey !== undefined) {
nutKeys.push(nutKey);
} else if (lowerKey.length === 1) {
Expand All @@ -127,15 +195,17 @@ export class InputHandler {

try {
for (const k of nutKeys) {
if (typeof k === "string") {
if (typeof k === 'string') {
await keyboard.type(k);
} else {
await keyboard.pressKey(k);
pressedKeys.push(k);
}
}

await new Promise(resolve => setTimeout(resolve, 10));
await new Promise(resolve =>
setTimeout(resolve, 10)
);
} finally {
for (const k of pressedKeys.reverse()) {
await keyboard.releaseKey(k);
Expand Down