From 6eb28b890d0c4debbb3520d1f09e329364557aaf Mon Sep 17 00:00:00 2001 From: Daniel Hammerschmidt Date: Mon, 15 Sep 2025 21:43:25 +0200 Subject: [PATCH 01/16] Make local cursor accessible/configurable --- core/util/cursor.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/util/cursor.js b/core/util/cursor.js index 6d689e7d..193372da 100644 --- a/core/util/cursor.js +++ b/core/util/cursor.js @@ -106,7 +106,7 @@ export default class Cursor { } clear() { - this._target.style.cursor = 'none'; + this.setLocalCursor('none'); this._canvas.width = 0; this._canvas.height = 0; this._position.x = this._position.x + this._hotSpot.x; @@ -115,6 +115,10 @@ export default class Cursor { this._hotSpot.y = 0; } + setLocalCursor(cursor) { + this._target.style.cursor = cursor; + } + // Mouse events might be emulated, this allows // moving the cursor in such cases move(clientX, clientY) { From 355dd27a452df124e06853aa275af6ea12404d6c Mon Sep 17 00:00:00 2001 From: Daniel Hammerschmidt Date: Mon, 15 Sep 2025 21:47:43 +0200 Subject: [PATCH 02/16] Make RFB::dragViewport a private Variable with getter and setter --- core/rfb.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/core/rfb.js b/core/rfb.js index 80011e4a..739d5809 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -290,12 +290,12 @@ export default class RFB extends EventTargetMixin { // ===== PROPERTIES ===== - this.dragViewport = false; this.focusOnClick = true; this._viewOnly = false; this._clipViewport = false; this._clippingViewport = false; + this._dragViewport = false; this._scaleViewport = false; this._resizeSession = false; @@ -342,6 +342,11 @@ export default class RFB extends EventTargetMixin { this._updateClip(); } + get dragViewport() { return this._dragViewport; } + set dragViewport(dragViewport) { + this._dragViewport = dragViewport; + } + get scaleViewport() { return this._scaleViewport; } set scaleViewport(scale) { this._scaleViewport = scale; @@ -1115,7 +1120,7 @@ export default class RFB extends EventTargetMixin { switch (ev.type) { case 'mousedown': case 'mouseup': - if (this.dragViewport) { + if (this._dragViewport) { if (down && !this._viewportDragging) { this._viewportDragging = true; this._viewportDragPos = {'x': pos.x, 'y': pos.y}; @@ -1334,7 +1339,7 @@ export default class RFB extends EventTargetMixin { this._handleTapEvent(ev, 0x2); break; case 'drag': - if (this.dragViewport) { + if (this._dragViewport) { this._viewportHasMoved = false; this._viewportDragging = true; this._viewportDragPos = {'x': pos.x, 'y': pos.y}; @@ -1344,7 +1349,7 @@ export default class RFB extends EventTargetMixin { } break; case 'longpress': - if (this.dragViewport) { + if (this._dragViewport) { // If dragViewport is true, we need to wait to see // if we have dragged outside the threshold before // sending any events to the server. @@ -1376,7 +1381,7 @@ export default class RFB extends EventTargetMixin { break; case 'drag': case 'longpress': - if (this.dragViewport) { + if (this._dragViewport) { this._viewportDragging = true; const deltaX = this._viewportDragPos.x - pos.x; const deltaY = this._viewportDragPos.y - pos.y; @@ -1451,7 +1456,7 @@ export default class RFB extends EventTargetMixin { case 'twodrag': break; case 'drag': - if (this.dragViewport) { + if (this._dragViewport) { this._viewportDragging = false; } else { this._fakeMouseMove(ev, pos.x, pos.y); @@ -1465,7 +1470,7 @@ export default class RFB extends EventTargetMixin { break; } - if (this.dragViewport && !this._viewportHasMoved) { + if (this._dragViewport && !this._viewportHasMoved) { this._fakeMouseMove(ev, pos.x, pos.y); // If dragViewport is true, we need to wait to see // if we have dragged outside the threshold before From 0f48f31e08a70045e4fe5deebcac7d7b04fd816d Mon Sep 17 00:00:00 2001 From: Daniel Hammerschmidt Date: Mon, 15 Sep 2025 21:49:58 +0200 Subject: [PATCH 03/16] Show local (grab/grabbing) cursor while dragging the viewport --- core/rfb.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/core/rfb.js b/core/rfb.js index 739d5809..d2075a0d 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -345,6 +345,7 @@ export default class RFB extends EventTargetMixin { get dragViewport() { return this._dragViewport; } set dragViewport(dragViewport) { this._dragViewport = dragViewport; + this._refreshCursor(); } get scaleViewport() { return this._scaleViewport; } @@ -1121,6 +1122,7 @@ export default class RFB extends EventTargetMixin { case 'mousedown': case 'mouseup': if (this._dragViewport) { + this._cursor.setLocalCursor(down ? 'grabbing' : 'grab'); if (down && !this._viewportDragging) { this._viewportDragging = true; this._viewportDragPos = {'x': pos.x, 'y': pos.y}; @@ -3037,7 +3039,7 @@ export default class RFB extends EventTargetMixin { _shouldShowDotCursor() { // Called when this._cursorImage is updated - if (!this._showDotCursor) { + if (!this._showDotCursor || this._dragViewport) { // User does not want to see the dot, so... return false; } @@ -3067,6 +3069,11 @@ export default class RFB extends EventTargetMixin { image.hotx, image.hoty, image.w, image.h ); + if (this._dragViewport) { + this._cursor.setLocalCursor(this._viewportDragging ? 'grabbing' : 'grab'); + } else { + this._cursor.setLocalCursor('none'); + } } static genDES(password, challenge) { From 57829159839e7a2358f5c22a53414565f5704087 Mon Sep 17 00:00:00 2001 From: Daniel Hammerschmidt Date: Mon, 15 Sep 2025 21:52:12 +0200 Subject: [PATCH 04/16] Do not compare to 'mousedown' on every 'mousemove' --- core/rfb.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/rfb.js b/core/rfb.js index d2075a0d..ea40fd55 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -1117,9 +1117,11 @@ export default class RFB extends EventTargetMixin { let bmask = RFB._convertButtonMask(ev.buttons); - let down = ev.type == 'mousedown'; + let down = false; switch (ev.type) { case 'mousedown': + down = true; + // eslint-disable-next-line no-fallthrough case 'mouseup': if (this._dragViewport) { this._cursor.setLocalCursor(down ? 'grabbing' : 'grab'); From 97c0ad4beceee6ae34edec23b773701a6c77d1c1 Mon Sep 17 00:00:00 2001 From: Daniel Hammerschmidt Date: Mon, 15 Sep 2025 21:53:55 +0200 Subject: [PATCH 05/16] Add view_drag parameter to enable dragging when connected --- app/ui.js | 2 ++ docs/EMBEDDING.md | 3 +++ vnc.html | 4 ++++ 3 files changed, 9 insertions(+) diff --git a/app/ui.js b/app/ui.js index 2542e059..3e551715 100644 --- a/app/ui.js +++ b/app/ui.js @@ -178,6 +178,7 @@ const UI = { UI.initSetting('password'); UI.initSetting('autoconnect', false); UI.initSetting('view_clip', false); + UI.initSetting('view_drag', false); UI.initSetting('resize', 'off'); UI.initSetting('quality', 6); UI.initSetting('compression', 2); @@ -1096,6 +1097,7 @@ const UI = { UI.rfb.addEventListener("bell", UI.bell); UI.rfb.addEventListener("desktopname", UI.updateDesktopName); UI.rfb.clipViewport = UI.getSetting('view_clip'); + UI.rfb.dragViewport = UI.getSetting('view_drag'); UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale'; UI.rfb.resizeSession = UI.getSetting('resize') === 'remote'; UI.rfb.qualityLevel = parseInt(UI.getSetting('quality')); diff --git a/docs/EMBEDDING.md b/docs/EMBEDDING.md index 9e927d0d..32ac049e 100644 --- a/docs/EMBEDDING.md +++ b/docs/EMBEDDING.md @@ -82,6 +82,9 @@ Currently, the following options are available: * `view_clip` - If the remote session should be clipped or use scrollbars if it cannot fit in the browser. +* `view_drag` - If the remote session is clipped enable dragging the viewport + when connected. + * `resize` - How to resize the remote session if it is not the same size as the browser window. Can be one of `off`, `scale` and `remote`. diff --git a/vnc.html b/vnc.html index 82cacd58..2d7e9b7c 100644 --- a/vnc.html +++ b/vnc.html @@ -227,6 +227,10 @@ Clip to window +
  • + + +
  • +
  • From 254fff94acc708f3829a52f3cf20b3df43a91be9 Mon Sep 17 00:00:00 2001 From: Daniel Hammerschmidt Date: Wed, 17 Sep 2025 03:24:54 +0200 Subject: [PATCH 07/16] Revert "Show local (grab/grabbing) cursor while dragging the viewport" This reverts commit 0f48f31e08a70045e4fe5deebcac7d7b04fd816d. --- core/rfb.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/core/rfb.js b/core/rfb.js index ea40fd55..c61facf3 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -345,7 +345,6 @@ export default class RFB extends EventTargetMixin { get dragViewport() { return this._dragViewport; } set dragViewport(dragViewport) { this._dragViewport = dragViewport; - this._refreshCursor(); } get scaleViewport() { return this._scaleViewport; } @@ -1124,7 +1123,6 @@ export default class RFB extends EventTargetMixin { // eslint-disable-next-line no-fallthrough case 'mouseup': if (this._dragViewport) { - this._cursor.setLocalCursor(down ? 'grabbing' : 'grab'); if (down && !this._viewportDragging) { this._viewportDragging = true; this._viewportDragPos = {'x': pos.x, 'y': pos.y}; @@ -3041,7 +3039,7 @@ export default class RFB extends EventTargetMixin { _shouldShowDotCursor() { // Called when this._cursorImage is updated - if (!this._showDotCursor || this._dragViewport) { + if (!this._showDotCursor) { // User does not want to see the dot, so... return false; } @@ -3071,11 +3069,6 @@ export default class RFB extends EventTargetMixin { image.hotx, image.hoty, image.w, image.h ); - if (this._dragViewport) { - this._cursor.setLocalCursor(this._viewportDragging ? 'grabbing' : 'grab'); - } else { - this._cursor.setLocalCursor('none'); - } } static genDES(password, challenge) { From 59e40b443552a882c9ead0518e89fd5baaf502c6 Mon Sep 17 00:00:00 2001 From: Daniel Hammerschmidt Date: Wed, 17 Sep 2025 03:25:38 +0200 Subject: [PATCH 08/16] Revert "Make local cursor accessible/configurable" This reverts commit 6eb28b890d0c4debbb3520d1f09e329364557aaf. --- core/util/cursor.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core/util/cursor.js b/core/util/cursor.js index 193372da..6d689e7d 100644 --- a/core/util/cursor.js +++ b/core/util/cursor.js @@ -106,7 +106,7 @@ export default class Cursor { } clear() { - this.setLocalCursor('none'); + this._target.style.cursor = 'none'; this._canvas.width = 0; this._canvas.height = 0; this._position.x = this._position.x + this._hotSpot.x; @@ -115,10 +115,6 @@ export default class Cursor { this._hotSpot.y = 0; } - setLocalCursor(cursor) { - this._target.style.cursor = cursor; - } - // Mouse events might be emulated, this allows // moving the cursor in such cases move(clientX, clientY) { From b1745d5cbc06e986b94bc9e98e6822ec05cde336 Mon Sep 17 00:00:00 2001 From: Daniel Hammerschmidt Date: Wed, 17 Sep 2025 03:44:56 +0200 Subject: [PATCH 09/16] Drop a note about isTouchDevice and useFallback --- core/util/cursor.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/util/cursor.js b/core/util/cursor.js index 6d689e7d..67c54575 100644 --- a/core/util/cursor.js +++ b/core/util/cursor.js @@ -6,6 +6,10 @@ import { supportsCursorURIs, isTouchDevice } from './browser.js'; +// Sometimes (at least with Chrome and Firefox on Windows) +// isTouchDevice is true even if there is no touch device, in +// this case useFallback ist also true and cursor URIs are never +// used. const useFallback = !supportsCursorURIs || isTouchDevice; export default class Cursor { From 5a9a81c07574503e460a002ab41f34c36459846b Mon Sep 17 00:00:00 2001 From: Daniel Hammerschmidt Date: Wed, 17 Sep 2025 03:48:32 +0200 Subject: [PATCH 10/16] Add __FORCE_NO_TOUCH_DEVICE__ to not use fallback --- core/util/cursor.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/util/cursor.js b/core/util/cursor.js index 67c54575..3f14b95c 100644 --- a/core/util/cursor.js +++ b/core/util/cursor.js @@ -10,7 +10,8 @@ import { supportsCursorURIs, isTouchDevice } from './browser.js'; // isTouchDevice is true even if there is no touch device, in // this case useFallback ist also true and cursor URIs are never // used. -const useFallback = !supportsCursorURIs || isTouchDevice; +const __FORCE_NO_TOUCH_DEVICE__ = false; +const useFallback = !supportsCursorURIs || (!__FORCE_NO_TOUCH_DEVICE__ && isTouchDevice); export default class Cursor { constructor() { From 4dd16ad9d88b91c8670dc8d7018c5858a651e20b Mon Sep 17 00:00:00 2001 From: Daniel Hammerschmidt Date: Wed, 17 Sep 2025 03:52:37 +0200 Subject: [PATCH 11/16] Always initalize canvas.style in case of showing local cursors --- core/util/cursor.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/core/util/cursor.js b/core/util/cursor.js index 3f14b95c..03dbe1f1 100644 --- a/core/util/cursor.js +++ b/core/util/cursor.js @@ -19,17 +19,16 @@ export default class Cursor { this._canvas = document.createElement('canvas'); - if (useFallback) { - this._canvas.style.position = 'fixed'; - this._canvas.style.zIndex = '65535'; - this._canvas.style.pointerEvents = 'none'; - // Safari on iOS can select the cursor image - // https://bugs.webkit.org/show_bug.cgi?id=249223 - this._canvas.style.userSelect = 'none'; - this._canvas.style.WebkitUserSelect = 'none'; - // Can't use "display" because of Firefox bug #1445997 - this._canvas.style.visibility = 'hidden'; - } + // always initalize canvas.style in case of showing local cursors + this._canvas.style.position = 'fixed'; + this._canvas.style.zIndex = '65535'; + this._canvas.style.pointerEvents = 'none'; + // Safari on iOS can select the cursor image + // https://bugs.webkit.org/show_bug.cgi?id=249223 + this._canvas.style.userSelect = 'none'; + this._canvas.style.WebkitUserSelect = 'none'; + // Can't use "display" because of Firefox bug #1445997 + this._canvas.style.visibility = 'hidden'; this._position = { x: 0, y: 0 }; this._hotSpot = { x: 0, y: 0 }; From b5b7ebe7345d8ac085f1fa6e195ca1b605c85ed6 Mon Sep 17 00:00:00 2001 From: Daniel Hammerschmidt Date: Wed, 17 Sep 2025 03:54:13 +0200 Subject: [PATCH 12/16] Extend Cursor-API to show local cursors --- core/util/cursor.js | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/core/util/cursor.js b/core/util/cursor.js index 03dbe1f1..9670b7a7 100644 --- a/core/util/cursor.js +++ b/core/util/cursor.js @@ -41,14 +41,15 @@ export default class Cursor { }; } - attach(target) { + attach(target, { showLocalCursor } = {}) { if (this._target) { this.detach(); } this._target = target; + this._showLocalCursor = !!showLocalCursor; - if (useFallback) { + if (useFallback || this._showLocalCursor) { document.body.appendChild(this._canvas); const options = { capture: true, passive: true }; @@ -58,7 +59,7 @@ export default class Cursor { this._target.addEventListener('mouseup', this._eventHandlers.mouseup, options); } - this.clear(); + this.clear({ localCursor: showLocalCursor }); } detach() { @@ -66,7 +67,7 @@ export default class Cursor { return; } - if (useFallback) { + if (useFallback || this._showLocalCursor) { const options = { capture: true, passive: true }; this._target.removeEventListener('mouseover', this._eventHandlers.mouseover, options); this._target.removeEventListener('mouseleave', this._eventHandlers.mouseleave, options); @@ -81,9 +82,9 @@ export default class Cursor { this._target = null; } - change(rgba, hotx, hoty, w, h) { + change(rgba, hotx, hoty, w, h, { localCursor } = {}) { if ((w === 0) || (h === 0)) { - this.clear(); + this.clear({ localCursor }); return; } @@ -101,7 +102,8 @@ export default class Cursor { ctx.clearRect(0, 0, w, h); ctx.putImageData(img, 0, 0); - if (useFallback) { + if (useFallback || this._showLocalCursor) { + this._target.style.cursor = localCursor ?? 'none'; this._updatePosition(); } else { let url = this._canvas.toDataURL(); @@ -109,8 +111,12 @@ export default class Cursor { } } - clear() { - this._target.style.cursor = 'none'; + clear({ localCursor } = {}) { + // whilst dragging the viewport and changes to the remote cursor + // are made the target might be detached + if (this._target) { + this._target.style.cursor = localCursor ?? 'none'; + } this._canvas.width = 0; this._canvas.height = 0; this._position.x = this._position.x + this._hotSpot.x; @@ -122,7 +128,7 @@ export default class Cursor { // Mouse events might be emulated, this allows // moving the cursor in such cases move(clientX, clientY) { - if (!useFallback) { + if (!useFallback && !this._showLocalCursor) { return; } // clientX/clientY are relative the _visual viewport_, From 3b79d45f6a23a8b635464466bf3f2e594fdb5e5a Mon Sep 17 00:00:00 2001 From: Daniel Hammerschmidt Date: Wed, 17 Sep 2025 04:10:18 +0200 Subject: [PATCH 13/16] Add members for local cursors --- core/rfb.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/core/rfb.js b/core/rfb.js index c61facf3..7c05d68d 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -233,6 +233,14 @@ export default class RFB extends EventTargetMixin { // Cursor this._cursor = new Cursor(); + this._showLocalCursor = false; + this._localCursors = { + dragging: null, + drag: null, + viewOnly: null, + default: null, + empty: null, + }; // XXX: TightVNC 2.8.11 sends no cursor at all until Windows changes // it. Result: no cursor at all until a window border or an edit field @@ -375,6 +383,24 @@ export default class RFB extends EventTargetMixin { this._refreshCursor(); } + get showLocalCursor() { return this._showLocalCursor; } + set showLocalCursor(cursors) { + cursors ??= false; + this._showLocalCursor = !!cursors; + const { + default: defaultCursor, + viewOnly: viewOnlyCursor, + drag: dragCursor, + dragging: draggingCursor, + empty: emptyCursor, + } = cursors; + defaultCursor && (this._localCursors.default = defaultCursor); + viewOnlyCursor && (this._localCursors.viewOnly = viewOnlyCursor); + dragCursor && (this._localCursors.drag = dragCursor); + draggingCursor && (this._localCursors.dragging = draggingCursor); + emptyCursor && (this._localCursors.empty = emptyCursor); + } + get background() { return this._screen.style.background; } set background(cssValue) { this._screen.style.background = cssValue; } From 68a2db4da53a7997fbc64389d311746fe93509ec Mon Sep 17 00:00:00 2001 From: Daniel Hammerschmidt Date: Wed, 17 Sep 2025 04:17:01 +0200 Subject: [PATCH 14/16] Refresh, attach, detach cursor on API-calls and mouse events --- core/rfb.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/core/rfb.js b/core/rfb.js index 7c05d68d..a5b64d98 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -323,8 +323,10 @@ export default class RFB extends EventTargetMixin { this._rfbConnectionState === "connected") { if (viewOnly) { this._keyboard.ungrab(); + this._refreshCursor(); } else { this._keyboard.grab(); + this._refreshCursor(); } } } @@ -353,6 +355,7 @@ export default class RFB extends EventTargetMixin { get dragViewport() { return this._dragViewport; } set dragViewport(dragViewport) { this._dragViewport = dragViewport; + this._refreshCursor(); } get scaleViewport() { return this._scaleViewport; } @@ -399,6 +402,11 @@ export default class RFB extends EventTargetMixin { dragCursor && (this._localCursors.drag = dragCursor); draggingCursor && (this._localCursors.dragging = draggingCursor); emptyCursor && (this._localCursors.empty = emptyCursor); + this._cursor.detach(); + this._cursor.attach(this._canvas, { + showLocalCursor: this._showLocalCursor, + }); + this._refreshCursor(); } get background() { return this._screen.style.background; } @@ -605,7 +613,9 @@ export default class RFB extends EventTargetMixin { this._gestures.attach(this._canvas); - this._cursor.attach(this._canvas); + this._cursor.attach(this._canvas, { + showLocalCursor: this._showLocalCursor + }); this._refreshCursor(); // Monitor size changes of the screen element @@ -1154,6 +1164,11 @@ export default class RFB extends EventTargetMixin { this._viewportDragPos = {'x': pos.x, 'y': pos.y}; this._viewportHasMoved = false; + if (this._showLocalCursor) { + this._refreshCursor(); + this._cursor.detach(); + } + this._flushMouseMoveTimer(pos.x, pos.y); // Skip sending mouse events, instead save the current @@ -1163,6 +1178,13 @@ export default class RFB extends EventTargetMixin { } else { this._viewportDragging = false; + if (this._showLocalCursor) { + this._cursor.attach(this._canvas, { + showLocalCursor: this._showLocalCursor, + }); + this._refreshCursor(); + } + // If we actually performed a drag then we are done // here and should not send any mouse events if (this._viewportHasMoved) { From 4fc3fc91e14f81930a5e13b829d5c42c249b286d Mon Sep 17 00:00:00 2001 From: Daniel Hammerschmidt Date: Wed, 17 Sep 2025 04:20:33 +0200 Subject: [PATCH 15/16] Implement local cursors --- core/rfb.js | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/core/rfb.js b/core/rfb.js index a5b64d98..99848ce8 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -3087,10 +3087,20 @@ export default class RFB extends EventTargetMixin { _shouldShowDotCursor() { // Called when this._cursorImage is updated - if (!this._showDotCursor) { - // User does not want to see the dot, so... + if (!this._showDotCursor && !(this._showLocalCursor && this._localCursors.empty)) { + // User does not want to see the dot or has no local cursor, so... return false; } + if (this._showLocalCursor) { + // Do not show the dot in states with a local cursor + if (this._viewportDragging) { + if (this._localCursors.dragging) { return false; } + } else if (this._dragViewport) { + if (this._localCursors.drag) { return false; } + } else if (this._viewOnly) { + if (this._localCursors.viewOnly) { return false; } + } + } // The dot should not be shown if the cursor is already visible, // i.e. contains at least one not-fully-transparent pixel. @@ -3112,6 +3122,10 @@ export default class RFB extends EventTargetMixin { this._rfbConnectionState !== "connected") { return; } + if (this._showLocalCursor) { + this._refreshCursorWithLocalCursors(); + return; + } const image = this._shouldShowDotCursor() ? RFB.cursors.dot : this._cursorImage; this._cursor.change(image.rgbaPixels, image.hotx, image.hoty, @@ -3119,6 +3133,30 @@ export default class RFB extends EventTargetMixin { ); } + _refreshCursorWithLocalCursors() { + let image = this._cursorImage; + let localCursor; // = 'none'; + if (this._viewportDragging) { + localCursor = this._localCursors.dragging; + } else if (this._dragViewport) { + localCursor = this._localCursors.drag; + } else if (this._viewOnly) { + localCursor = this._localCursors.viewOnly; + // clear locally rendered cursor when switching to view-only whilst connected + image = RFB.cursors.none; + } else if (this._shouldShowDotCursor()) { + localCursor = this._localCursors.empty; + image = this._showDotCursor ? RFB.cursors.dot : this._cursorImage; + } else { + localCursor = this._localCursors.default; + } + this._cursor.change(image.rgbaPixels, + image.hotx, image.hoty, + image.w, image.h, + { localCursor } + ); + } + static genDES(password, challenge) { const passwordChars = password.split('').map(c => c.charCodeAt(0)); const key = legacyCrypto.importKey( From 702302fcc54b3ad41d129f5c479b400d0d3f05cd Mon Sep 17 00:00:00 2001 From: Daniel Hammerschmidt Date: Wed, 17 Sep 2025 04:24:00 +0200 Subject: [PATCH 16/16] Add default local cursor set to UI --- app/ui.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/ui.js b/app/ui.js index 3e551715..d0941d81 100644 --- a/app/ui.js +++ b/app/ui.js @@ -1103,6 +1103,11 @@ const UI = { UI.rfb.qualityLevel = parseInt(UI.getSetting('quality')); UI.rfb.compressionLevel = parseInt(UI.getSetting('compression')); UI.rfb.showDotCursor = UI.getSetting('show_dot'); + UI.rfb.showLocalCursor = { + drag: 'grab', + dragging: 'grabbing', + empty: 'default', + }; UI.updateViewOnly(); // requires UI.rfb },