From 609541f5ec997296be5d74ab2e519fd7990f9c32 Mon Sep 17 00:00:00 2001 From: Max Furtuna Date: Wed, 31 Jul 2019 14:29:06 +0300 Subject: [PATCH] Implementation of qemu relative pointer support --- core/encodings.js | 1 + core/rfb.js | 69 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/core/encodings.js b/core/encodings.js index 9fd38d58..c253654a 100644 --- a/core/encodings.js +++ b/core/encodings.js @@ -19,6 +19,7 @@ export const encodings = { pseudoEncodingDesktopSize: -223, pseudoEncodingLastRect: -224, pseudoEncodingCursor: -239, + pseudoEncodingQEMUPointerMotionChange: -257, pseudoEncodingQEMUExtendedKeyEvent: -258, pseudoEncodingExtendedDesktopSize: -308, pseudoEncodingXvp: -309, diff --git a/core/rfb.js b/core/rfb.js index a608be4f..2ba42086 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -84,6 +84,9 @@ export default class RFB extends EventTargetMixin { this._screen_flags = 0; this._qemuExtKeyEventSupported = false; + this._qemuEnabledRelativeCursor = false; + this._lastMouseX = 0; + this._lastMouseY = 0; // Internal objects this._sock = null; // Websock object @@ -727,6 +730,14 @@ export default class RFB extends EventTargetMixin { this._mouse_buttonMask &= ~bmask; } + let mouseX = this._display.absX(x); + let mouseY = this._display.absY(y); + + if (this._qemuEnabledRelativeCursor) { + mouseX += 0x7FFF - this._lastMouseX; + mouseY += 0x7FFF - this._lastMouseY; + } + if (this.dragViewport) { if (down && !this._viewportDragging) { this._viewportDragging = true; @@ -748,8 +759,8 @@ export default class RFB extends EventTargetMixin { // Send the button down event here, as the button up // event is sent at the end of this function. RFB.messages.pointerEvent(this._sock, - this._display.absX(x), - this._display.absY(y), + mouseX, + mouseY, bmask); } } @@ -757,7 +768,7 @@ export default class RFB extends EventTargetMixin { if (this._viewOnly) { return; } // View only, skip mouse events if (this._rfb_connection_state !== 'connected') { return; } - RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask); + RFB.messages.pointerEvent(this._sock, mouseX, mouseY, this._mouse_buttonMask); } _handleMouseMove(x, y) { @@ -780,7 +791,51 @@ export default class RFB extends EventTargetMixin { if (this._viewOnly) { return; } // View only, skip mouse events if (this._rfb_connection_state !== 'connected') { return; } - RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask); + + + let mouseX = this._display.absX(x); + let mouseY = this._display.absY(y); + + if (this._qemuEnabledRelativeCursor) { + // Clients are advised that when generating events in relative pointer mode, they should grab and hide the local pointer. + // When the local pointer hits any edge of the client window, it should be warped back by 100 pixels. + // This ensures that continued movement of the user's input device will continue to generate relative deltas and + // thus avoid the "invisible wall" problem. + + let deltaX = mouseX - this._lastMouseX; + let deltaY = mouseY - this._lastMouseY; + + this._lastMouseX = mouseX; + this._lastMouseY = mouseY; + + // in javascript we cannot move local pointer so instead of that we use synchronization areas + // near each edge of canvas. + // Ideal solution would be trigger whole screen movement but this doesn't work until qemu 3.0. + // Instead of that novnc continuously trigger big deltas (< 127) when mouse hits edge. + // This will solve problem partially but user still suffer from invisible wall problem + // Related qemu commit: https://github.com/qemu/qemu/commit/7abe7eb29494b4e4a11ec99ae5623083409a2f1e + + if (mouseX < 100) { + deltaX -= 100; + } + + if (mouseX > this._fb_width - 100) { + deltaX += 100; + } + + if (mouseY < 100) { + deltaY -= 100; + } + + if (mouseY > this._fb_height - 100) { + deltaY += 100; + } + + mouseX = deltaX + 0x7fff; + mouseY = deltaY + 0x7fff; + } + + RFB.messages.pointerEvent(this._sock, mouseX, mouseY, this._mouse_buttonMask); } // Message Handlers @@ -1233,6 +1288,7 @@ export default class RFB extends EventTargetMixin { encs.push(encodings.pseudoEncodingXvp); encs.push(encodings.pseudoEncodingFence); encs.push(encodings.pseudoEncodingContinuousUpdates); + encs.push(encodings.pseudoEncodingQEMUPointerMotionChange); if (this._fb_depth == 24) { encs.push(encodings.pseudoEncodingCursor); @@ -1503,6 +1559,11 @@ export default class RFB extends EventTargetMixin { this._resize(this._FBU.width, this._FBU.height); return true; + case encodings.pseudoEncodingQEMUPointerMotionChange: + this._qemuEnabledRelativeCursor = (this._FBU.x == 0); + return true; + + case encodings.pseudoEncodingExtendedDesktopSize: return this._handleExtendedDesktopSize();