|
39 | 39 | <script src="spicedataview.js"></script> |
40 | 40 | <script src="spicetype.js"></script> |
41 | 41 | <script src="spicemsg.js"></script> |
| 42 | + <script> |
| 43 | + /* Patch mouse position to use getBoundingClientRect() so coordinates are correct |
| 44 | + when the canvas is centered or has layout offset (offsetLeft/offsetTop are wrong). */ |
| 45 | + (function() { |
| 46 | + var canvasCoords = function(sc, e) { |
| 47 | + var canvas = sc.display && sc.display.surfaces && sc.display.primary_surface !== undefined |
| 48 | + ? sc.display.surfaces[sc.display.primary_surface].canvas |
| 49 | + : null; |
| 50 | + if (!canvas) return { x: 0, y: 0 }; |
| 51 | + var r = canvas.getBoundingClientRect(); |
| 52 | + return { |
| 53 | + x: Math.round(e.clientX - r.left), |
| 54 | + y: Math.round(e.clientY - r.top) |
| 55 | + }; |
| 56 | + }; |
| 57 | + var origMousePosition = window.SpiceMsgcMousePosition; |
| 58 | + var origMouseMotion = window.SpiceMsgcMouseMotion; |
| 59 | + if (origMousePosition) { |
| 60 | + window.SpiceMsgcMousePosition = function(sc, e) { |
| 61 | + this.display_id = 0; |
| 62 | + this.buttons_state = sc.buttons_state; |
| 63 | + if (e) { |
| 64 | + var c = canvasCoords(sc, e); |
| 65 | + this.x = c.x; |
| 66 | + this.y = c.y; |
| 67 | + sc.mousex = c.x; |
| 68 | + sc.mousey = c.y; |
| 69 | + } else { |
| 70 | + this.x = this.y = this.buttons_state = 0; |
| 71 | + } |
| 72 | + }; |
| 73 | + window.SpiceMsgcMousePosition.prototype = origMousePosition.prototype; |
| 74 | + } |
| 75 | + if (origMouseMotion) { |
| 76 | + window.SpiceMsgcMouseMotion = function(sc, e) { |
| 77 | + this.display_id = 0; |
| 78 | + this.buttons_state = sc.buttons_state; |
| 79 | + if (e) { |
| 80 | + var c = canvasCoords(sc, e); |
| 81 | + this.x = c.x - (sc.mousex !== undefined ? sc.mousex : 0); |
| 82 | + this.y = c.y - (sc.mousey !== undefined ? sc.mousey : 0); |
| 83 | + sc.mousex = c.x; |
| 84 | + sc.mousey = c.y; |
| 85 | + } else { |
| 86 | + this.x = this.y = this.buttons_state = 0; |
| 87 | + } |
| 88 | + }; |
| 89 | + window.SpiceMsgcMouseMotion.prototype = origMouseMotion.prototype; |
| 90 | + } |
| 91 | + })(); |
| 92 | + </script> |
42 | 93 | <script src="wire.js"></script> |
43 | 94 | <script src="spiceconn.js"></script> |
44 | 95 | <script src="display.js"></script> |
|
62 | 113 | body { |
63 | 114 | margin: 0; |
64 | 115 | padding: 0; |
65 | | - background-color: #000000; |
| 116 | + background-color: #333333; |
66 | 117 | overflow: hidden; |
67 | 118 | } |
68 | 119 |
|
|
132 | 183 | width: 100vw; |
133 | 184 | height: calc(100vh - 35px); |
134 | 185 | overflow: hidden; |
135 | | - background-color: #000000; |
| 186 | + background-color: #333333; |
| 187 | + display: flex; |
| 188 | + align-items: center; |
| 189 | + justify-content: center; |
| 190 | + position: relative; |
| 191 | + } |
| 192 | + |
| 193 | + /* Overlay captures mouse for whole page; events forwarded to canvas */ |
| 194 | + #spice-input-overlay { |
| 195 | + position: absolute; |
| 196 | + top: 0; |
| 197 | + left: 0; |
| 198 | + right: 0; |
| 199 | + bottom: 0; |
| 200 | + pointer-events: auto; |
| 201 | + cursor: default; |
136 | 202 | } |
137 | 203 |
|
138 | 204 | .spice-screen { |
139 | 205 | display: block; |
| 206 | + margin: 0; |
140 | 207 | padding: 0; |
141 | 208 | border: none; |
142 | 209 | background-color: #000000; |
143 | 210 | box-shadow: none; |
144 | 211 | border-radius: 0; |
145 | | - width: 100%; |
146 | | - height: 100%; |
147 | 212 | } |
148 | 213 |
|
149 | 214 | #login { |
|
332 | 397 | document.getElementById('sendCtrlAltDelButton').disabled = false; |
333 | 398 | clearInterval(checkConnection); |
334 | 399 |
|
335 | | - // Set up focus monitoring once canvas is ready |
336 | 400 | setupFocusMonitoring(); |
| 401 | + setupFullPageInput(); |
337 | 402 | } |
338 | 403 | } |
339 | 404 | if (sc && sc.state === 'closing') { |
340 | 405 | clearInterval(checkConnection); |
341 | 406 | } |
342 | 407 | }, 100); |
343 | 408 |
|
344 | | - // Also check for canvas creation |
| 409 | + // Focus = iframe/document focus (for "click to type" when embedded) |
345 | 410 | function setupFocusMonitoring() { |
346 | | - var canvas = document.querySelector('#spice-screen canvas'); |
347 | | - if (canvas) { |
348 | | - canvas.setAttribute('tabindex', '0'); // Make it focusable |
349 | | - |
350 | | - canvas.addEventListener('focus', function() { |
351 | | - updateFocusIndicator(true); |
352 | | - }); |
353 | | - |
354 | | - canvas.addEventListener('blur', function() { |
355 | | - updateFocusIndicator(false); |
356 | | - }); |
357 | | - |
358 | | - canvas.addEventListener('mouseover', function() { |
359 | | - updateFocusIndicator(true); |
360 | | - }); |
361 | | - |
362 | | - canvas.addEventListener('mouseout', function() { |
363 | | - // Only update if not actually focused |
364 | | - if (document.activeElement !== canvas) { |
365 | | - updateFocusIndicator(false); |
366 | | - } |
367 | | - }); |
368 | | - |
369 | | - canvas.addEventListener('click', function() { |
370 | | - canvas.focus(); |
371 | | - updateFocusIndicator(true); |
372 | | - }); |
373 | | - |
374 | | - // Check focus state periodically |
375 | | - setInterval(function() { |
376 | | - if (document.activeElement === canvas) { |
377 | | - updateFocusIndicator(true); |
378 | | - } |
379 | | - }, 500); |
380 | | - |
381 | | - // Initial focus check |
382 | | - updateFocusIndicator(document.activeElement === canvas); |
383 | | - } else { |
384 | | - // Retry if canvas not found yet |
385 | | - setTimeout(setupFocusMonitoring, 200); |
| 411 | + function refreshFocus() { |
| 412 | + updateFocusIndicator(document.hasFocus()); |
386 | 413 | } |
| 414 | + window.addEventListener('focus', refreshFocus); |
| 415 | + window.addEventListener('blur', refreshFocus); |
| 416 | + setInterval(refreshFocus, 500); |
| 417 | + refreshFocus(); |
387 | 418 | } |
388 | 419 |
|
389 | | - // Start trying to set up focus monitoring |
390 | | - setTimeout(setupFocusMonitoring, 500); |
| 420 | + // Capture mouse on whole page and forward to canvas with correct coordinates |
| 421 | + function setupFullPageInput() { |
| 422 | + var canvas = document.querySelector('#spice-screen canvas'); |
| 423 | + var overlay = document.getElementById('spice-input-overlay'); |
| 424 | + if (!canvas || !overlay) { |
| 425 | + setTimeout(setupFullPageInput, 200); |
| 426 | + return; |
| 427 | + } |
| 428 | + canvas.setAttribute('tabindex', '0'); |
| 429 | + |
| 430 | + function forwardMouse(type, e) { |
| 431 | + var opts = { |
| 432 | + bubbles: true, cancelable: true, |
| 433 | + clientX: e.clientX, clientY: e.clientY, |
| 434 | + screenX: e.screenX, screenY: e.screenY, |
| 435 | + button: e.button, buttons: e.buttons, |
| 436 | + relatedTarget: e.relatedTarget, |
| 437 | + view: e.view |
| 438 | + }; |
| 439 | + if (type === 'wheel') { |
| 440 | + canvas.dispatchEvent(new WheelEvent('mousewheel', { |
| 441 | + bubbles: true, cancelable: true, |
| 442 | + clientX: e.clientX, clientY: e.clientY, |
| 443 | + deltaX: e.deltaX, deltaY: e.deltaY, deltaZ: e.deltaZ, |
| 444 | + deltaMode: e.deltaMode |
| 445 | + })); |
| 446 | + } else { |
| 447 | + canvas.dispatchEvent(new MouseEvent(type, opts)); |
| 448 | + } |
| 449 | + } |
| 450 | + |
| 451 | + ['mousedown','mouseup','mousemove','contextmenu','click','dblclick'].forEach(function(type) { |
| 452 | + overlay.addEventListener(type, function(e) { |
| 453 | + if (type === 'mousedown') canvas.focus(); |
| 454 | + forwardMouse(type, e); |
| 455 | + if (type === 'contextmenu') e.preventDefault(); |
| 456 | + }); |
| 457 | + }); |
| 458 | + |
| 459 | + overlay.addEventListener('mousewheel', function(e) { |
| 460 | + e.preventDefault(); |
| 461 | + forwardMouse('wheel', e); |
| 462 | + }, { passive: false }); |
| 463 | + } |
391 | 464 | } |
392 | 465 | catch (e) |
393 | 466 | { |
|
464 | 537 |
|
465 | 538 | <div id="spice-area"> |
466 | 539 | <div id="spice-screen" class="spice-screen"></div> |
| 540 | + <div id="spice-input-overlay" tabindex="0" title="Click to focus for keyboard input"></div> |
467 | 541 | </div> |
468 | 542 |
|
469 | 543 | <div id="message-div" class="spice-message"></div> |
|
0 commit comments