Only grab key events on canvas

Give the canvas proper focus handling. This avoids messy logic that
needs to disable and enable event handling when we want to interact
with other UI elements.

It also makes sure we can properly inhibit the browser from triggering
local actions on key presses.
This commit is contained in:
Pierre Ossman 2016-10-05 10:21:17 +02:00
parent 69411b9ea3
commit 2afda54456
6 changed files with 31 additions and 43 deletions

View File

@ -870,6 +870,9 @@ select:active {
/* IE miscalculates width without this :( */ /* IE miscalculates width without this :( */
flex-shrink: 0; flex-shrink: 0;
} }
#noVNC_canvas:focus {
outline: none;
}
/*Default noVNC logo.*/ /*Default noVNC logo.*/
/* From: http://fonts.googleapis.com/css?family=Orbitron:700 */ /* From: http://fonts.googleapis.com/css?family=Orbitron:700 */

View File

@ -292,6 +292,8 @@ var UI = {
document.documentElement document.documentElement
.addEventListener('mousedown', UI.keepVirtualKeyboard, true); .addEventListener('mousedown', UI.keepVirtualKeyboard, true);
document.documentElement
.addEventListener('touchstart', UI.keepVirtualKeyboard, true);
document.getElementById("noVNC_control_bar") document.getElementById("noVNC_control_bar")
.addEventListener('touchstart', UI.activateControlbar); .addEventListener('touchstart', UI.activateControlbar);
@ -356,10 +358,6 @@ var UI = {
addClipboardHandlers: function() { addClipboardHandlers: function() {
document.getElementById("noVNC_clipboard_button") document.getElementById("noVNC_clipboard_button")
.addEventListener('click', UI.toggleClipboardPanel); .addEventListener('click', UI.toggleClipboardPanel);
document.getElementById("noVNC_clipboard_text")
.addEventListener('focus', UI.displayBlur);
document.getElementById("noVNC_clipboard_text")
.addEventListener('blur', UI.displayFocus);
document.getElementById("noVNC_clipboard_text") document.getElementById("noVNC_clipboard_text")
.addEventListener('change', UI.clipboardSend); .addEventListener('change', UI.clipboardSend);
document.getElementById("noVNC_clipboard_clear_button") document.getElementById("noVNC_clipboard_clear_button")
@ -440,6 +438,7 @@ var UI = {
msg = _("Connected (unencrypted) to ") + UI.desktopName; msg = _("Connected (unencrypted) to ") + UI.desktopName;
} }
UI.showStatus(msg); UI.showStatus(msg);
document.getElementById('noVNC_canvas').focus();
break; break;
case 'disconnecting': case 'disconnecting':
UI.connected = false; UI.connected = false;
@ -1492,7 +1491,14 @@ var UI = {
} }
} }
// The default action of touchstart is to generate other
// events, which other elements might depend on. So we can't
// blindly prevent that. Instead restore focus right away.
if (event.type === "touchstart") {
setTimeout(input.focus.bind(input));
} else {
event.preventDefault(); event.preventDefault();
}
}, },
keyboardinputReset: function() { keyboardinputReset: function() {
@ -1662,20 +1668,6 @@ var UI = {
} }
}, },
displayBlur: function() {
if (UI.rfb && !UI.rfb.get_view_only()) {
UI.rfb.get_keyboard().set_focused(false);
UI.rfb.get_mouse().set_focused(false);
}
},
displayFocus: function() {
if (UI.rfb && !UI.rfb.get_view_only()) {
UI.rfb.get_keyboard().set_focused(true);
UI.rfb.get_mouse().set_focused(true);
}
},
updateLocalCursor: function() { updateLocalCursor: function() {
if (!UI.rfb) return; if (!UI.rfb) return;
UI.rfb.set_local_cursor(UI.getSetting('cursor')); UI.rfb.set_local_cursor(UI.getSetting('cursor'));

View File

@ -24,8 +24,7 @@ export default function Keyboard(defaults) {
this._pendingKey = null; // Key waiting for keypress this._pendingKey = null; // Key waiting for keypress
set_defaults(this, defaults, { set_defaults(this, defaults, {
'target': document, 'target': null,
'focused': true
}); });
// keep these here so we can refer to them later // keep these here so we can refer to them later
@ -131,8 +130,6 @@ Keyboard.prototype = {
}, },
_handleKeyDown: function (e) { _handleKeyDown: function (e) {
if (!this._focused) { return; }
var code = this._getKeyCode(e); var code = this._getKeyCode(e);
var keysym = KeyboardUtil.getKeysym(e); var keysym = KeyboardUtil.getKeysym(e);
@ -214,8 +211,6 @@ Keyboard.prototype = {
// Legacy event for browsers without code/key // Legacy event for browsers without code/key
_handleKeyPress: function (e) { _handleKeyPress: function (e) {
if (!this._focused) { return; }
stopEvent(e); stopEvent(e);
// Are we expecting a keypress? // Are we expecting a keypress?
@ -244,8 +239,6 @@ Keyboard.prototype = {
this._sendKeyEvent(keysym, code, true); this._sendKeyEvent(keysym, code, true);
}, },
_handleKeyPressTimeout: function (e) { _handleKeyPressTimeout: function (e) {
if (!this._focused) { return; }
// Did someone manage to sort out the key already? // Did someone manage to sort out the key already?
if (this._pendingKey === null) { if (this._pendingKey === null) {
return; return;
@ -282,8 +275,6 @@ Keyboard.prototype = {
}, },
_handleKeyUp: function (e) { _handleKeyUp: function (e) {
if (!this._focused) { return; }
stopEvent(e); stopEvent(e);
var code = this._getKeyCode(e); var code = this._getKeyCode(e);
@ -348,7 +339,6 @@ Keyboard.prototype = {
make_properties(Keyboard, [ make_properties(Keyboard, [
['target', 'wo', 'dom'], // DOM element that captures keyboard input ['target', 'wo', 'dom'], // DOM element that captures keyboard input
['focused', 'rw', 'bool'], // Capture and send key events
['onKeyEvent', 'rw', 'func'] // Handler for key press/release ['onKeyEvent', 'rw', 'func'] // Handler for key press/release
]); ]);

View File

@ -31,7 +31,6 @@ export default function Mouse(defaults) {
// Configuration attributes // Configuration attributes
set_defaults(this, defaults, { set_defaults(this, defaults, {
'target': document, 'target': document,
'focused': true,
'touchButton': 1 'touchButton': 1
}); });
@ -52,8 +51,6 @@ Mouse.prototype = {
}, },
_handleMouseButton: function (e, down) { _handleMouseButton: function (e, down) {
if (!this._focused) { return; }
this._updateMousePosition(e); this._updateMousePosition(e);
var pos = this._pos; var pos = this._pos;
@ -156,7 +153,7 @@ Mouse.prototype = {
}, },
_handleMouseWheel: function (e) { _handleMouseWheel: function (e) {
if (!this._focused || !this._onMouseButton) { return; } if (!this._onMouseButton) { return; }
this._resetWheelStepTimers(); this._resetWheelStepTimers();
@ -201,8 +198,6 @@ Mouse.prototype = {
}, },
_handleMouseMove: function (e) { _handleMouseMove: function (e) {
if (! this._focused) { return; }
this._updateMousePosition(e); this._updateMousePosition(e);
if (this._onMouseMove) { if (this._onMouseMove) {
this._onMouseMove(this._pos.x, this._pos.y); this._onMouseMove(this._pos.x, this._pos.y);
@ -211,8 +206,6 @@ Mouse.prototype = {
}, },
_handleMouseDisable: function (e) { _handleMouseDisable: function (e) {
if (!this._focused) { return; }
/* /*
* Stop propagation if inside canvas area * Stop propagation if inside canvas area
* Note: This is only needed for the 'click' event as it fails * Note: This is only needed for the 'click' event as it fails
@ -292,7 +285,6 @@ Mouse.prototype = {
make_properties(Mouse, [ make_properties(Mouse, [
['target', 'ro', 'dom'], // DOM element that captures mouse input ['target', 'ro', 'dom'], // DOM element that captures mouse input
['focused', 'rw', 'bool'], // Capture and send mouse clicks/movement
['onMouseButton', 'rw', 'func'], // Handler for mouse button click/release ['onMouseButton', 'rw', 'func'], // Handler for mouse button click/release
['onMouseMove', 'rw', 'func'], // Handler for mouse movement ['onMouseMove', 'rw', 'func'], // Handler for mouse movement

View File

@ -120,7 +120,6 @@ export default function RFB(defaults) {
// set the default value on user-facing properties // set the default value on user-facing properties
set_defaults(this, defaults, { set_defaults(this, defaults, {
'target': 'null', // VNC display rendering Canvas object 'target': 'null', // VNC display rendering Canvas object
'focusContainer': document, // DOM element that captures keyboard input
'encrypt': false, // Use TLS/SSL/wss encryption 'encrypt': false, // Use TLS/SSL/wss encryption
'local_cursor': false, // Request locally rendered cursor 'local_cursor': false, // Request locally rendered cursor
'shared': true, // Request shared mode 'shared': true, // Request shared mode
@ -171,7 +170,7 @@ export default function RFB(defaults) {
throw exc; throw exc;
} }
this._keyboard = new Keyboard({target: this._focusContainer, this._keyboard = new Keyboard({target: this._target,
onKeyEvent: this._handleKeyEvent.bind(this)}); onKeyEvent: this._handleKeyEvent.bind(this)});
this._mouse = new Mouse({target: this._target, this._mouse = new Mouse({target: this._target,
@ -385,11 +384,17 @@ RFB.prototype = {
} }
} }
// Always grab focus on some kind of click event
this._target.addEventListener("mousedown", this._focusCanvas);
this._target.addEventListener("touchstart", this._focusCanvas);
Log.Debug("<< RFB.connect"); Log.Debug("<< RFB.connect");
}, },
_disconnect: function () { _disconnect: function () {
Log.Debug(">> RFB.disconnect"); Log.Debug(">> RFB.disconnect");
this._target.removeEventListener("mousedown", this._focusCanvas);
this._target.removeEventListener("touchstart", this._focusCanvas);
this._cleanup(); this._cleanup();
this._sock.close(); this._sock.close();
this._print_stats(); this._print_stats();
@ -448,6 +453,13 @@ RFB.prototype = {
} }
}, },
// Event handler for canvas so this points to the canvas element
_focusCanvas: function(event) {
// Respect earlier handlers' request to not do side-effects
if (!event.defaultPrevented)
this.focus();
},
/* /*
* Connection states: * Connection states:
* connecting * connecting
@ -1457,7 +1469,6 @@ RFB.prototype = {
make_properties(RFB, [ make_properties(RFB, [
['target', 'wo', 'dom'], // VNC display rendering Canvas object ['target', 'wo', 'dom'], // VNC display rendering Canvas object
['focusContainer', 'wo', 'dom'], // DOM element that captures keyboard input
['encrypt', 'rw', 'bool'], // Use TLS/SSL/wss encryption ['encrypt', 'rw', 'bool'], // Use TLS/SSL/wss encryption
['local_cursor', 'rw', 'bool'], // Request locally rendered cursor ['local_cursor', 'rw', 'bool'], // Request locally rendered cursor
['shared', 'rw', 'bool'], // Request shared mode ['shared', 'rw', 'bool'], // Request shared mode

View File

@ -331,7 +331,7 @@
autocorrect="off" autocomplete="off" spellcheck="false" autocorrect="off" autocomplete="off" spellcheck="false"
mozactionhint="Enter"></textarea> mozactionhint="Enter"></textarea>
<canvas id="noVNC_canvas" width="0" height="0"> <canvas id="noVNC_canvas" width="0" height="0" tabindex="-1">
Canvas not supported. Canvas not supported.
</canvas> </canvas>
</div> </div>