Show dot when there is no visible cursor

Disabled by default.
This commit is contained in:
Alexander E. Patrakov 2018-08-12 03:17:16 +08:00
parent d1314d4b3a
commit 4c38179d15
5 changed files with 84 additions and 12 deletions

View File

@ -161,6 +161,7 @@ const UI = {
UI.initSetting('resize', 'off'); UI.initSetting('resize', 'off');
UI.initSetting('shared', true); UI.initSetting('shared', true);
UI.initSetting('view_only', false); UI.initSetting('view_only', false);
UI.initSetting('show_dot', false);
UI.initSetting('path', 'websockify'); UI.initSetting('path', 'websockify');
UI.initSetting('repeaterID', ''); UI.initSetting('repeaterID', '');
UI.initSetting('reconnect', false); UI.initSetting('reconnect', false);
@ -347,6 +348,8 @@ const UI = {
UI.addSettingChangeHandler('shared'); UI.addSettingChangeHandler('shared');
UI.addSettingChangeHandler('view_only'); UI.addSettingChangeHandler('view_only');
UI.addSettingChangeHandler('view_only', UI.updateViewOnly); UI.addSettingChangeHandler('view_only', UI.updateViewOnly);
UI.addSettingChangeHandler('show_dot');
UI.addSettingChangeHandler('show_dot', UI.updateShowDotCursor);
UI.addSettingChangeHandler('host'); UI.addSettingChangeHandler('host');
UI.addSettingChangeHandler('port'); UI.addSettingChangeHandler('port');
UI.addSettingChangeHandler('path'); UI.addSettingChangeHandler('path');
@ -1015,6 +1018,7 @@ const UI = {
UI.rfb = new RFB(document.getElementById('noVNC_container'), url, UI.rfb = new RFB(document.getElementById('noVNC_container'), url,
{ shared: UI.getSetting('shared'), { shared: UI.getSetting('shared'),
showDotCursor: UI.getSetting('show_dot'),
repeaterID: UI.getSetting('repeaterID'), repeaterID: UI.getSetting('repeaterID'),
credentials: { password: password } }); credentials: { password: password } });
UI.rfb.addEventListener("connect", UI.connectFinished); UI.rfb.addEventListener("connect", UI.connectFinished);
@ -1583,6 +1587,11 @@ const UI = {
UI.setMouseButton(1); //has it's own logic for hiding/showing UI.setMouseButton(1); //has it's own logic for hiding/showing
}, },
updateShowDotCursor() {
if (!UI.rfb) return;
UI.rfb.showDotCursor = UI.getSetting('show_dot');
},
updateLogging() { updateLogging() {
WebUtil.init_logging(UI.getSetting('logging')); WebUtil.init_logging(UI.getSetting('logging'));
}, },

View File

@ -48,6 +48,7 @@ export default class RFB extends EventTargetMixin {
this._rfb_credentials = options.credentials || {}; this._rfb_credentials = options.credentials || {};
this._shared = 'shared' in options ? !!options.shared : true; this._shared = 'shared' in options ? !!options.shared : true;
this._repeaterID = options.repeaterID || ''; this._repeaterID = options.repeaterID || '';
this._showDotCursor = options.showDotCursor || false;
// Internal state // Internal state
this._rfb_connection_state = ''; this._rfb_connection_state = '';
@ -168,13 +169,17 @@ export default class RFB extends EventTargetMixin {
// Cursor // Cursor
this._cursor = new Cursor(); this._cursor = new Cursor();
this._cursorImage = {
rgbaPixels: [], // XXX: TightVNC 2.8.11 sends no cursor at all until Windows changes
hotx: 0, // it. Result: no cursor at all until a window border or an edit field
hoty: 0, // is hit blindly. But there are also VNC servers that draw the cursor
w: 0, // in the framebuffer and don't send the empty local cursor. There is
h: 0, // no way to satisfy both sides.
}; //
// The spec is unclear on this "initial cursor" issue. Many other
// viewers (TigerVNC, RealVNC, Remmina) display an arrow as the
// initial cursor instead.
this._cursorImage = RFB.cursors.none;
// populate encHandlers with bound versions // populate encHandlers with bound versions
this._encHandlers[encodings.encodingRaw] = RFB.encodingHandlers.RAW.bind(this); this._encHandlers[encodings.encodingRaw] = RFB.encodingHandlers.RAW.bind(this);
@ -324,6 +329,12 @@ export default class RFB extends EventTargetMixin {
} }
} }
get showDotCursor() { return this._showDotCursor; }
set showDotCursor(show) {
this._showDotCursor = show;
this._refreshCursor();
}
// ===== PUBLIC METHODS ===== // ===== PUBLIC METHODS =====
disconnect() { disconnect() {
@ -426,6 +437,7 @@ export default class RFB extends EventTargetMixin {
this._target.appendChild(this._screen); this._target.appendChild(this._screen);
this._cursor.attach(this._canvas); this._cursor.attach(this._canvas);
this._refreshCursor();
// Monitor size changes of the screen // Monitor size changes of the screen
// FIXME: Use ResizeObserver, or hidden overflow // FIXME: Use ResizeObserver, or hidden overflow
@ -1617,12 +1629,33 @@ export default class RFB extends EventTargetMixin {
this._refreshCursor(); this._refreshCursor();
} }
_shouldShowDotCursor() {
// Called when this._cursorImage is updated
if (!this._showDotCursor) {
// User does not want to see the dot, so...
return false;
}
// The dot should not be shown if the cursor is already visible,
// i.e. contains at least one not-fully-transparent pixel.
// So iterate through all alpha bytes in rgba and stop at the
// first non-zero.
for (let i = 3; i < this._cursorImage.rgbaPixels.length; i += 4) {
if (this._cursorImage.rgbaPixels[i]) {
return false;
}
}
// At this point, we know that the cursor is fully transparent, and
// the user wants to see the dot instead of this.
return true;
}
_refreshCursor() { _refreshCursor() {
this._cursor.change(this._cursorImage.rgbaPixels, const image = this._shouldShowDotCursor() ? RFB.cursors.dot : this._cursorImage;
this._cursorImage.hotx, this._cursor.change(image.rgbaPixels,
this._cursorImage.hoty, image.hotx, image.hoty,
this._cursorImage.w, image.w, image.h
this._cursorImage.h
); );
} }
@ -2598,3 +2631,21 @@ RFB.encodingHandlers = {
} }
} }
} }
RFB.cursors = {
none: {
rgbaPixels: new Uint8Array(),
w: 0, h: 0,
hotx: 0, hoty: 0,
},
dot: {
rgbaPixels: new Uint8Array([
255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255,
0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 255,
255, 255, 255, 255, 0, 0, 0, 255, 255, 255, 255, 255,
]),
w: 3, h: 3,
hotx: 1, hoty: 1,
}
};

View File

@ -53,6 +53,11 @@ protocol stream.
should be sent whenever the container changes dimensions. Disabled should be sent whenever the container changes dimensions. Disabled
by default. by default.
`showDotCursor`
- Is a `boolean` indicating whether a dot cursor should be shown
instead of a zero-sized or fully-transparent cursor if the server
sets such invisible cursor. Disabled by default.
`capabilities` *Read only* `capabilities` *Read only*
- Is an `Object` indicating which optional extensions are available - Is an `Object` indicating which optional extensions are available
on the server. Some methods may only be called if the corresponding on the server. Some methods may only be called if the corresponding

View File

@ -61,6 +61,9 @@ query string. Currently the following options are available:
* `resize` - How to resize the remote session if it is not the same size as * `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`. the browser window. Can be one of `off`, `scale` and `remote`.
* `show_dot` - If a dot cursor should be shown when the remote server provides
no local cursor, or provides a fully-transparent (invisible) cursor.
* `logging` - The console log level. Can be one of `error`, `warn`, `info` or * `logging` - The console log level. Can be one of `error`, `warn`, `info` or
`debug`. `debug`.

View File

@ -250,6 +250,10 @@
<input id="noVNC_setting_reconnect_delay" type="number" /> <input id="noVNC_setting_reconnect_delay" type="number" />
</li> </li>
<li><hr></li> <li><hr></li>
<li>
<label><input id="noVNC_setting_show_dot" type="checkbox" /> Show Dot when No Cursor</label>
</li>
<li><hr></li>
<!-- Logging selection dropdown --> <!-- Logging selection dropdown -->
<li> <li>
<label>Logging: <label>Logging: