Spaces:
Runtime error
Runtime error
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Simulator</title> | |
<style> | |
/* Add this style rule to hide the cursor inside the canvas */ | |
#displayCanvas { | |
cursor: none; | |
} | |
</style> | |
</head> | |
<body> | |
<canvas id="displayCanvas" width="512" height="384"></canvas> | |
<script> | |
const canvas = document.getElementById('displayCanvas'); | |
const ctx = canvas.getContext('2d'); | |
let socket; | |
let isConnected = false; | |
let reconnectAttempts = 0; | |
const MAX_RECONNECT_DELAY = 30000; // Maximum delay between reconnection attempts (30 seconds) | |
let isProcessing = false; | |
// Add flag to control trace visibility, default to false (hidden) | |
let showTrace = false; | |
function connect() { | |
socket = new WebSocket(`wss://${window.location.host}/ws`); | |
socket.onopen = function(event) { | |
console.log("WebSocket connection established"); | |
isConnected = true; | |
reconnectAttempts = 0; | |
//startHeartbeat(); | |
}; | |
socket.onclose = function(event) { | |
console.log("WebSocket connection closed. Attempting to reconnect..."); | |
isConnected = false; | |
clearInterval(heartbeatInterval); | |
scheduleReconnection(); | |
}; | |
socket.onerror = function(error) { | |
console.error("WebSocket error:", error); | |
}; | |
socket.onmessage = function (event) { | |
const data = JSON.parse(event.data); | |
if (data.type === "heartbeat_response") { | |
console.log("Heartbeat response received"); | |
} else if (data.image) { | |
let img = new Image(); | |
img.onload = function() { | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
ctx.drawImage(img, 0, 0); | |
//isProcessing = false; // Reset the processing flag when we get a response | |
}; | |
img.src = 'data:image/png;base64,' + data.image; | |
} | |
}; | |
} | |
function scheduleReconnection() { | |
const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), MAX_RECONNECT_DELAY); | |
console.log(`Scheduling reconnection in ${delay}ms`); | |
setTimeout(connect, delay); | |
reconnectAttempts++; | |
} | |
let heartbeatInterval; | |
function startHeartbeat() { | |
heartbeatInterval = setInterval(() => { | |
if (isConnected) { | |
try { | |
socket.send(JSON.stringify({ type: "heartbeat" })); | |
console.error("finished sending heartbeat:", error); | |
} catch (error) { | |
console.error("Error sending heartbeat:", error); | |
} | |
} | |
}, 1000); // Send heartbeat every 15 seconds | |
} | |
// Initial connection | |
connect(); | |
let lastSentPosition = null; | |
let lastSentTime = 0; | |
const SEND_INTERVAL = 10; // Send updates every 50ms | |
// Track currently pressed keys | |
const pressedKeys = new Set(); | |
function sendInputState(x, y, isLeftClick = false, isRightClick = false) { | |
const currentTime = Date.now(); | |
if (isConnected && (isLeftClick || isRightClick || !lastSentPosition || currentTime - lastSentTime >= SEND_INTERVAL)) { | |
try { | |
socket.send(JSON.stringify({ | |
"x": x, | |
"y": y, | |
"is_left_click": isLeftClick, | |
"is_right_click": isRightClick, | |
"keys_down": Array.from(pressedKeys), | |
"keys_up": [], | |
})); | |
lastSentPosition = { x, y }; | |
lastSentTime = currentTime; | |
//if (isLeftClick || isRightClick) { | |
// isProcessing = true; // Block further inputs until response | |
//} | |
} catch (error) { | |
console.error("Error sending input state:", error); | |
} | |
} | |
} | |
// Capture mouse movements and clicks | |
canvas.addEventListener("mousemove", function (event) { | |
if (!isConnected || isProcessing) return; | |
let rect = canvas.getBoundingClientRect(); | |
let x = event.clientX - rect.left; | |
let y = event.clientY - rect.top; | |
// Only draw the trace if showTrace is true | |
if (showTrace && lastSentPosition) { | |
ctx.beginPath(); | |
ctx.moveTo(lastSentPosition.x, lastSentPosition.y); | |
ctx.lineTo(x, y); | |
ctx.stroke(); | |
} | |
sendInputState(x, y); | |
}); | |
canvas.addEventListener("click", function (event) { | |
if (!isConnected || isProcessing) return; | |
let rect = canvas.getBoundingClientRect(); | |
let x = event.clientX - rect.left; | |
let y = event.clientY - rect.top; | |
sendInputState(x, y, true, false); | |
}); | |
// Handle right clicks | |
canvas.addEventListener("contextmenu", function (event) { | |
event.preventDefault(); // Prevent default context menu | |
if (!isConnected || isProcessing) return; | |
let rect = canvas.getBoundingClientRect(); | |
let x = event.clientX - rect.left; | |
let y = event.clientY - rect.top; | |
sendInputState(x, y, false, true); | |
}); | |
// Track keyboard events | |
document.addEventListener("keydown", function (event) { | |
if (!isConnected || isProcessing) return; | |
// Add the key to our set of pressed keys | |
pressedKeys.add(event.key); | |
// Get the current mouse position | |
let rect = canvas.getBoundingClientRect(); | |
let x = lastSentPosition ? lastSentPosition.x : canvas.width / 2; | |
let y = lastSentPosition ? lastSentPosition.y : canvas.height / 2; | |
sendInputState(x, y); | |
}); | |
document.addEventListener("keyup", function (event) { | |
if (!isConnected) return; | |
// Remove the key from our set of pressed keys | |
pressedKeys.delete(event.key); | |
// Get the current mouse position | |
let rect = canvas.getBoundingClientRect(); | |
let x = lastSentPosition ? lastSentPosition.x : canvas.width / 2; | |
let y = lastSentPosition ? lastSentPosition.y : canvas.height / 2; | |
// For key up events, we send the key in the keys_up array | |
try { | |
socket.send(JSON.stringify({ | |
"x": x, | |
"y": y, | |
"is_left_click": false, | |
"is_right_click": false, | |
"keys_down": Array.from(pressedKeys), | |
"keys_up": [event.key], | |
})); | |
} catch (error) { | |
console.error("Error sending key up event:", error); | |
} | |
}); | |
// Graceful disconnection | |
window.addEventListener('beforeunload', function (e) { | |
if (isConnected) { | |
try { | |
//socket.send(JSON.stringify({ type: "disconnect" })); | |
//socket.close(); | |
} catch (error) { | |
console.error("Error during disconnection:", error); | |
} | |
} | |
}); | |
</script> | |
</body> | |
</html> |