Skip to content

Commit 4722b98

Browse files
committed
spice fix
1 parent 8f5d396 commit 4722b98

File tree

2 files changed

+123
-48
lines changed

2 files changed

+123
-48
lines changed

nova_spiceproxy/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
FROM quay.io/airshipit/nova:2024.1-ubuntu_jammy
22

3+
RUN cp /usr/share/spice-html5/spice_auto.html /usr/share/spice-html5/spice_auto_bak.html
34
COPY nova_spiceproxy/spice_auto.html /usr/share/spice-html5/spice_auto.html

nova_spiceproxy/spice_auto.html

Lines changed: 122 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,57 @@
3939
<script src="spicedataview.js"></script>
4040
<script src="spicetype.js"></script>
4141
<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>
4293
<script src="wire.js"></script>
4394
<script src="spiceconn.js"></script>
4495
<script src="display.js"></script>
@@ -62,7 +113,7 @@
62113
body {
63114
margin: 0;
64115
padding: 0;
65-
background-color: #000000;
116+
background-color: #333333;
66117
overflow: hidden;
67118
}
68119

@@ -132,18 +183,32 @@
132183
width: 100vw;
133184
height: calc(100vh - 35px);
134185
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;
136202
}
137203

138204
.spice-screen {
139205
display: block;
206+
margin: 0;
140207
padding: 0;
141208
border: none;
142209
background-color: #000000;
143210
box-shadow: none;
144211
border-radius: 0;
145-
width: 100%;
146-
height: 100%;
147212
}
148213

149214
#login {
@@ -332,62 +397,70 @@
332397
document.getElementById('sendCtrlAltDelButton').disabled = false;
333398
clearInterval(checkConnection);
334399

335-
// Set up focus monitoring once canvas is ready
336400
setupFocusMonitoring();
401+
setupFullPageInput();
337402
}
338403
}
339404
if (sc && sc.state === 'closing') {
340405
clearInterval(checkConnection);
341406
}
342407
}, 100);
343408

344-
// Also check for canvas creation
409+
// Focus = iframe/document focus (for "click to type" when embedded)
345410
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());
386413
}
414+
window.addEventListener('focus', refreshFocus);
415+
window.addEventListener('blur', refreshFocus);
416+
setInterval(refreshFocus, 500);
417+
refreshFocus();
387418
}
388419

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+
}
391464
}
392465
catch (e)
393466
{
@@ -464,6 +537,7 @@
464537

465538
<div id="spice-area">
466539
<div id="spice-screen" class="spice-screen"></div>
540+
<div id="spice-input-overlay" tabindex="0" title="Click to focus for keyboard input"></div>
467541
</div>
468542

469543
<div id="message-div" class="spice-message"></div>

0 commit comments

Comments
 (0)