Files
XCDesktop/remote/src/server/InputHandler.js
2026-03-08 01:34:54 +08:00

171 lines
4.4 KiB
JavaScript

const logger = require('../utils/logger');
const MessageTypes = require('./messageTypes');
const { clipboardService } = require('../services/clipboard');
class InputHandler {
constructor(inputService) {
this.inputService = inputService;
this.clipboardService = clipboardService;
this.eventQueue = [];
this.isProcessingQueue = false;
this.lastMouseMove = null;
this.mouseMovePending = false;
}
handleMessage(message, ws) {
const { type, ...data } = message;
switch (type) {
case MessageTypes.MOUSE_MOVE:
this.queueMouseMove(data.x, data.y);
break;
case MessageTypes.MOUSE_DOWN:
this.queueEvent('mouseDown', data.button || 'left');
break;
case MessageTypes.MOUSE_UP:
this.queueEvent('mouseUp', data.button || 'left');
break;
case MessageTypes.MOUSE_WHEEL:
this.queueEvent('mouseWheel', data.delta);
break;
case MessageTypes.KEY_DOWN:
this.queueEvent('keyDown', data.key);
break;
case MessageTypes.KEY_UP:
this.queueEvent('keyUp', data.key);
break;
case MessageTypes.CLIPBOARD_GET:
this.handleClipboardGet(ws);
break;
case MessageTypes.CLIPBOARD_SET:
this.handleClipboardSet(ws, data);
break;
default:
logger.debug('Unknown message type', { type });
}
}
async handleClipboardGet(ws) {
try {
const content = await this.clipboardService.read();
if (this.clipboardService.isSmallContent(content.size)) {
ws.send(JSON.stringify({
type: MessageTypes.CLIPBOARD_DATA,
contentType: content.type,
data: content.data,
size: content.size
}));
} else {
ws.send(JSON.stringify({
type: MessageTypes.CLIPBOARD_TOO_LARGE,
size: content.size
}));
}
} catch (error) {
logger.error('Failed to get clipboard', { error: error.message });
ws.send(JSON.stringify({
type: MessageTypes.CLIPBOARD_RESULT,
success: false
}));
}
}
async handleClipboardSet(ws, data) {
try {
const success = await this.clipboardService.set({
type: data.contentType,
data: data.data
});
ws.send(JSON.stringify({
type: MessageTypes.CLIPBOARD_RESULT,
success
}));
} catch (error) {
logger.error('Failed to set clipboard', { error: error.message });
ws.send(JSON.stringify({
type: MessageTypes.CLIPBOARD_RESULT,
success: false
}));
}
}
queueMouseMove(x, y) {
this.lastMouseMove = { x, y };
if (!this.mouseMovePending) {
this.mouseMovePending = true;
setImmediate(() => {
if (this.lastMouseMove) {
this.eventQueue.push({
type: 'mouseMove',
x: this.lastMouseMove.x,
y: this.lastMouseMove.y
});
this.mouseMovePending = false;
this.processQueue();
}
});
}
}
queueEvent(type, data) {
this.eventQueue.push({ type, data });
this.processQueue();
}
async processQueue() {
if (this.isProcessingQueue || this.eventQueue.length === 0) {
return;
}
this.isProcessingQueue = true;
while (this.eventQueue.length > 0) {
const event = this.eventQueue.shift();
await this.executeEvent(event);
}
this.isProcessingQueue = false;
}
async executeEvent(event) {
try {
switch (event.type) {
case 'mouseMove':
await this.inputService.mouseMove(event.x, event.y);
break;
case 'mouseDown':
await this.inputService.mouseDown(event.data);
break;
case 'mouseUp':
await this.inputService.mouseUp(event.data);
break;
case 'mouseWheel':
await this.inputService.mouseWheel(event.data);
break;
case 'keyDown':
await this.inputService.keyDown(event.data);
break;
case 'keyUp':
await this.inputService.keyUp(event.data);
break;
}
} catch (error) {
logger.error('Failed to execute event', {
type: event.type,
error: error.message
});
}
}
stop() {
logger.info('Stopping InputHandler');
this.eventQueue = [];
this.isProcessingQueue = false;
this.lastMouseMove = null;
this.mouseMovePending = false;
}
}
module.exports = InputHandler;