Initial commit
This commit is contained in:
229
remote/public/js/input.js
Normal file
229
remote/public/js/input.js
Normal file
@@ -0,0 +1,229 @@
|
||||
class InputHandler {
|
||||
constructor(canvas, options = {}) {
|
||||
this.canvas = canvas;
|
||||
this.wsUrl = options.wsUrl;
|
||||
this.screenWidth = options.screenWidth || 1280;
|
||||
this.screenHeight = options.screenHeight || 720;
|
||||
|
||||
this.ws = null;
|
||||
this.wsReady = false;
|
||||
|
||||
this.lastMoveTime = 0;
|
||||
this.MOVE_THROTTLE_MS = 33;
|
||||
this.pendingMove = null;
|
||||
this.moveThrottleTimer = null;
|
||||
|
||||
this.pressedKeys = new Set();
|
||||
|
||||
this.codeToKey = {
|
||||
'Enter': 'enter', 'Backspace': 'backspace', 'Tab': 'tab',
|
||||
'Escape': 'escape', 'Delete': 'delete', 'Home': 'home',
|
||||
'End': 'end', 'PageUp': 'pageup', 'PageDown': 'pagedown',
|
||||
'ArrowUp': 'up', 'ArrowDown': 'down', 'ArrowLeft': 'left', 'ArrowRight': 'right',
|
||||
'F1': 'f1', 'F2': 'f2', 'F3': 'f3', 'F4': 'f4',
|
||||
'F5': 'f5', 'F6': 'f6', 'F7': 'f7', 'F8': 'f8',
|
||||
'F9': 'f9', 'F10': 'f10', 'F11': 'f11', 'F12': 'f12',
|
||||
'ControlLeft': 'ctrl', 'ControlRight': 'ctrl',
|
||||
'AltLeft': 'alt', 'AltRight': 'alt',
|
||||
'ShiftLeft': 'shift', 'ShiftRight': 'shift',
|
||||
'MetaLeft': 'win', 'MetaRight': 'win',
|
||||
'Space': 'space',
|
||||
'Digit0': '0', 'Digit1': '1', 'Digit2': '2', 'Digit3': '3', 'Digit4': '4',
|
||||
'Digit5': '5', 'Digit6': '6', 'Digit7': '7', 'Digit8': '8', 'Digit9': '9',
|
||||
'KeyA': 'a', 'KeyB': 'b', 'KeyC': 'c', 'KeyD': 'd', 'KeyE': 'e',
|
||||
'KeyF': 'f', 'KeyG': 'g', 'KeyH': 'h', 'KeyI': 'i', 'KeyJ': 'j',
|
||||
'KeyK': 'k', 'KeyL': 'l', 'KeyM': 'm', 'KeyN': 'n', 'KeyO': 'o',
|
||||
'KeyP': 'p', 'KeyQ': 'q', 'KeyR': 'r', 'KeyS': 's', 'KeyT': 't',
|
||||
'KeyU': 'u', 'KeyV': 'v', 'KeyW': 'w', 'KeyX': 'x', 'KeyY': 'y', 'KeyZ': 'z',
|
||||
'Comma': ',', 'Period': '.', 'Slash': '/', 'Semicolon': ';',
|
||||
'Quote': "'", 'BracketLeft': '[', 'BracketRight': ']',
|
||||
'Backslash': '\\', 'Minus': '-', 'Equal': '=', 'Backquote': '`'
|
||||
};
|
||||
|
||||
this._boundHandlers = {
|
||||
mousemove: this._handleMouseMove.bind(this),
|
||||
mousedown: this._handleMouseDown.bind(this),
|
||||
mouseup: this._handleMouseUp.bind(this),
|
||||
contextmenu: this._handleContextMenu.bind(this),
|
||||
wheel: this._handleWheel.bind(this),
|
||||
keydown: this._handleKeyDown.bind(this),
|
||||
keyup: this._handleKeyUp.bind(this),
|
||||
blur: this._handleBlur.bind(this)
|
||||
};
|
||||
}
|
||||
|
||||
init() {
|
||||
this._initWebSocket();
|
||||
this._bindEvents();
|
||||
return this;
|
||||
}
|
||||
|
||||
_initWebSocket() {
|
||||
this.ws = createReconnectingWebSocket(this.wsUrl, {
|
||||
onOpen: () => {
|
||||
this.wsReady = true;
|
||||
console.log('Input WebSocket connected');
|
||||
},
|
||||
onClose: () => {
|
||||
this.wsReady = false;
|
||||
console.log('Input WebSocket disconnected');
|
||||
},
|
||||
onMessage: (e) => {
|
||||
try {
|
||||
const msg = JSON.parse(e.data);
|
||||
if (msg.type === 'screenInfo') {
|
||||
this.screenWidth = msg.width;
|
||||
this.screenHeight = msg.height;
|
||||
console.log('Screen resolution:', this.screenWidth, 'x', this.screenHeight);
|
||||
}
|
||||
} catch (err) {}
|
||||
},
|
||||
onError: (err) => {
|
||||
console.error('Input WebSocket error:', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_bindEvents() {
|
||||
this.canvas.addEventListener('mousemove', this._boundHandlers.mousemove);
|
||||
this.canvas.addEventListener('mousedown', this._boundHandlers.mousedown);
|
||||
this.canvas.addEventListener('mouseup', this._boundHandlers.mouseup);
|
||||
this.canvas.addEventListener('contextmenu', this._boundHandlers.contextmenu);
|
||||
this.canvas.addEventListener('wheel', this._boundHandlers.wheel);
|
||||
document.addEventListener('keydown', this._boundHandlers.keydown);
|
||||
document.addEventListener('keyup', this._boundHandlers.keyup);
|
||||
window.addEventListener('blur', this._boundHandlers.blur);
|
||||
}
|
||||
|
||||
_unbindEvents() {
|
||||
this.canvas.removeEventListener('mousemove', this._boundHandlers.mousemove);
|
||||
this.canvas.removeEventListener('mousedown', this._boundHandlers.mousedown);
|
||||
this.canvas.removeEventListener('mouseup', this._boundHandlers.mouseup);
|
||||
this.canvas.removeEventListener('contextmenu', this._boundHandlers.contextmenu);
|
||||
this.canvas.removeEventListener('wheel', this._boundHandlers.wheel);
|
||||
document.removeEventListener('keydown', this._boundHandlers.keydown);
|
||||
document.removeEventListener('keyup', this._boundHandlers.keyup);
|
||||
window.removeEventListener('blur', this._boundHandlers.blur);
|
||||
}
|
||||
|
||||
sendInputEvent(event) {
|
||||
if (this.wsReady && this.ws && this.ws.isReady()) {
|
||||
this.ws.getWebSocket().send(JSON.stringify(event));
|
||||
}
|
||||
}
|
||||
|
||||
_sendMouseMove(x, y) {
|
||||
this.pendingMove = { x, y };
|
||||
|
||||
const now = Date.now();
|
||||
if (now - this.lastMoveTime >= this.MOVE_THROTTLE_MS) {
|
||||
this._flushMouseMove();
|
||||
} else if (!this.moveThrottleTimer) {
|
||||
this.moveThrottleTimer = setTimeout(() => {
|
||||
this._flushMouseMove();
|
||||
this.moveThrottleTimer = null;
|
||||
}, this.MOVE_THROTTLE_MS - (now - this.lastMoveTime));
|
||||
}
|
||||
}
|
||||
|
||||
_flushMouseMove() {
|
||||
if (this.pendingMove) {
|
||||
this.sendInputEvent({
|
||||
type: 'mouseMove',
|
||||
x: this.pendingMove.x,
|
||||
y: this.pendingMove.y
|
||||
});
|
||||
this.lastMoveTime = Date.now();
|
||||
this.pendingMove = null;
|
||||
}
|
||||
}
|
||||
|
||||
_handleMouseMove(e) {
|
||||
const { x, y } = getScreenCoordinates(e.clientX, e.clientY, this.canvas, this.screenWidth, this.screenHeight);
|
||||
this._sendMouseMove(x, y);
|
||||
}
|
||||
|
||||
_handleMouseDown(e) {
|
||||
const button = e.button === 2 ? 'right' : 'left';
|
||||
this.sendInputEvent({
|
||||
type: 'mouseDown',
|
||||
button: button
|
||||
});
|
||||
}
|
||||
|
||||
_handleMouseUp(e) {
|
||||
const button = e.button === 2 ? 'right' : 'left';
|
||||
this.sendInputEvent({
|
||||
type: 'mouseUp',
|
||||
button: button
|
||||
});
|
||||
}
|
||||
|
||||
_handleContextMenu(e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
_handleWheel(e) {
|
||||
e.preventDefault();
|
||||
const delta = -Math.sign(e.deltaY) * 120;
|
||||
this.sendInputEvent({
|
||||
type: 'mouseWheel',
|
||||
delta: delta
|
||||
});
|
||||
}
|
||||
|
||||
_getKeyFromEvent(e) {
|
||||
if (this.codeToKey[e.code]) {
|
||||
return this.codeToKey[e.code];
|
||||
}
|
||||
return e.key;
|
||||
}
|
||||
|
||||
_handleKeyDown(e) {
|
||||
const key = this._getKeyFromEvent(e);
|
||||
const keyId = key.toLowerCase();
|
||||
|
||||
if (!this.pressedKeys.has(keyId)) {
|
||||
this.pressedKeys.add(keyId);
|
||||
this.sendInputEvent({
|
||||
type: 'keyDown',
|
||||
key: key
|
||||
});
|
||||
}
|
||||
|
||||
if (e.key === 'Tab' || e.key === ' ' || e.key.startsWith('Arrow')) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
_handleKeyUp(e) {
|
||||
const key = this._getKeyFromEvent(e);
|
||||
const keyId = key.toLowerCase();
|
||||
|
||||
this.pressedKeys.delete(keyId);
|
||||
this.sendInputEvent({
|
||||
type: 'keyUp',
|
||||
key: key
|
||||
});
|
||||
}
|
||||
|
||||
_handleBlur() {
|
||||
this.pressedKeys.forEach(keyId => {
|
||||
this.sendInputEvent({
|
||||
type: 'keyUp',
|
||||
key: keyId
|
||||
});
|
||||
});
|
||||
this.pressedKeys.clear();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this._unbindEvents();
|
||||
if (this.moveThrottleTimer) {
|
||||
clearTimeout(this.moveThrottleTimer);
|
||||
}
|
||||
if (this.ws) {
|
||||
this.ws.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user