fix(rdp): proper display scaling via Guacamole display.scale()
Remove CSS width/height !important override that broke Guacamole's internal rendering pipeline. Replace with display.scale() auto-fitting using ResizeObserver for responsive container sizing. Scale mouse coordinates back to remote display space to keep input accurate. Clean up diagnostic instruction logging. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
f9070c81f3
commit
95271f065a
@ -56,12 +56,8 @@ onBeforeUnmount(() => {
|
|||||||
@apply absolute inset-0 bg-gray-950 overflow-hidden cursor-default;
|
@apply absolute inset-0 bg-gray-950 overflow-hidden cursor-default;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Guacamole appends a display div; make it fill the container */
|
/* Guacamole manages its own display element sizing via display.scale().
|
||||||
.rdp-canvas-container :deep(> div) {
|
Do NOT override width/height — it breaks the internal rendering pipeline. */
|
||||||
width: 100% !important;
|
|
||||||
height: 100% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rdp-canvas-container :deep(canvas) {
|
.rdp-canvas-container :deep(canvas) {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,6 @@ function createJsonWsTunnel(wsUrl: string, connectMsg: object) {
|
|||||||
let onDisconnected: ((reason: string) => void) | null = null
|
let onDisconnected: ((reason: string) => void) | null = null
|
||||||
let onGatewayError: ((message: string) => void) | null = null
|
let onGatewayError: ((message: string) => void) | null = null
|
||||||
|
|
||||||
let instructionCount = 0
|
|
||||||
function dispatchInstructions(raw: string) {
|
function dispatchInstructions(raw: string) {
|
||||||
if (!tunnel.oninstruction) return
|
if (!tunnel.oninstruction) return
|
||||||
let remaining = raw
|
let remaining = raw
|
||||||
@ -38,17 +37,6 @@ function createJsonWsTunnel(wsUrl: string, connectMsg: object) {
|
|||||||
pos = dotIdx + 1 + len + 1
|
pos = dotIdx + 1 + len + 1
|
||||||
}
|
}
|
||||||
if (parts.length > 0) {
|
if (parts.length > 0) {
|
||||||
instructionCount++
|
|
||||||
const opcode = parts[0]
|
|
||||||
// Log first 50 instructions, then every 200th, plus any layer-0 draw ops
|
|
||||||
const isLayer0Draw = (opcode === 'img' || opcode === 'rect' || opcode === 'cfill' || opcode === 'copy' || opcode === 'transfer')
|
|
||||||
&& parts[1] !== undefined && parseInt(parts[1]) === 0
|
|
||||||
if (instructionCount <= 50 || instructionCount % 200 === 0 || isLayer0Draw) {
|
|
||||||
const argSummary = opcode === 'blob'
|
|
||||||
? `[stream=${parts[1]}, ${(parts[2] || '').length} bytes]`
|
|
||||||
: parts.slice(1).join(',')
|
|
||||||
console.log(`[RDP] Instruction #${instructionCount}: ${opcode}(${argSummary})`)
|
|
||||||
}
|
|
||||||
tunnel.oninstruction(parts[0], parts.slice(1))
|
tunnel.oninstruction(parts[0], parts.slice(1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -202,22 +190,48 @@ export function useRdp() {
|
|||||||
console.log(`[RDP] State: ${states[state] || state}`)
|
console.log(`[RDP] State: ${states[state] || state}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log when display is ready
|
|
||||||
client.onready = () => {
|
|
||||||
const display = client.getDisplay()
|
|
||||||
console.log(`[RDP] Ready! Display: ${display.getWidth()}x${display.getHeight()}, element: ${displayEl.offsetWidth}x${displayEl.offsetHeight}, container: ${container.offsetWidth}x${container.offsetHeight}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attach Guacamole display element to container
|
// Attach Guacamole display element to container
|
||||||
const displayEl = client.getDisplay().getElement()
|
const display = client.getDisplay()
|
||||||
|
const displayEl = display.getElement()
|
||||||
container.appendChild(displayEl)
|
container.appendChild(displayEl)
|
||||||
|
|
||||||
// Mouse input
|
// Auto-scale the Guacamole display to fit the container
|
||||||
|
function fitDisplay() {
|
||||||
|
const dw = display.getWidth()
|
||||||
|
const dh = display.getHeight()
|
||||||
|
if (!dw || !dh) return
|
||||||
|
const scale = Math.min(container.clientWidth / dw, container.clientHeight / dh)
|
||||||
|
display.scale(scale)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-fit when guacd sends a sync (display dimensions may have changed)
|
||||||
|
const origOnSync = display.onresize
|
||||||
|
display.onresize = (w: number, h: number) => {
|
||||||
|
origOnSync?.call(display, w, h)
|
||||||
|
fitDisplay()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-fit when the browser container resizes
|
||||||
|
const resizeObserver = new ResizeObserver(() => fitDisplay())
|
||||||
|
resizeObserver.observe(container)
|
||||||
|
|
||||||
|
// Mouse input — bind to the display element
|
||||||
const mouse = new Guacamole.Mouse(displayEl)
|
const mouse = new Guacamole.Mouse(displayEl)
|
||||||
mouse.onEach(
|
mouse.onEach(
|
||||||
['mousedown', 'mousemove', 'mouseup'],
|
['mousedown', 'mousemove', 'mouseup'],
|
||||||
(e: Guacamole.Mouse.Event) => {
|
(e: Guacamole.Mouse.Event) => {
|
||||||
client.sendMouseState(e.state)
|
// Scale mouse coordinates back to remote display space
|
||||||
|
const scale = display.getScale()
|
||||||
|
const scaledState = new Guacamole.Mouse.State(
|
||||||
|
e.state.x / scale,
|
||||||
|
e.state.y / scale,
|
||||||
|
e.state.left,
|
||||||
|
e.state.middle,
|
||||||
|
e.state.right,
|
||||||
|
e.state.up,
|
||||||
|
e.state.down,
|
||||||
|
)
|
||||||
|
client.sendMouseState(scaledState)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -230,6 +244,7 @@ export function useRdp() {
|
|||||||
client.connect()
|
client.connect()
|
||||||
|
|
||||||
function disconnect() {
|
function disconnect() {
|
||||||
|
resizeObserver.disconnect()
|
||||||
keyboard.onkeydown = null
|
keyboard.onkeydown = null
|
||||||
keyboard.onkeyup = null
|
keyboard.onkeyup = null
|
||||||
client.disconnect()
|
client.disconnect()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user