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> |