]>
git.proxmox.com Git - mirror_novnc.git/blob - core/input/devices.js
2e41122eb7caa6357bef36d731212a0455a0e734
2 * noVNC: HTML5 VNC client
3 * Copyright (C) 2012 Joel Martin
4 * Copyright (C) 2013 Samuel Mannehed for Cendio AB
5 * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
8 /*jslint browser: true, white: false */
9 /*global window, Util */
12 * import Util from "../util";
13 * import KeyboardUtil from "./util";
16 /* [module] export */ var Keyboard
;
22 // Keyboard event handler
25 Keyboard = function (defaults
) {
26 this._keyDownList
= []; // List of depressed keys
27 // (even if they are happy)
29 Util
.set_defaults(this, defaults
, {
34 // create the keyboard handler
35 this._handler
= new KeyboardUtil
.KeyEventDecoder(KeyboardUtil
.ModifierSync(),
36 KeyboardUtil
.VerifyCharModifier( /* jshint newcap: false */
37 KeyboardUtil
.TrackKeyState(
38 KeyboardUtil
.EscapeModifiers(this._handleRfbEvent
.bind(this))
41 ); /* jshint newcap: true */
43 // keep these here so we can refer to them later
44 this._eventHandlers
= {
45 'keyup': this._handleKeyUp
.bind(this),
46 'keydown': this._handleKeyDown
.bind(this),
47 'keypress': this._handleKeyPress
.bind(this),
48 'blur': this._allKeysUp
.bind(this)
52 Keyboard
.prototype = {
55 _handleRfbEvent: function (e
) {
56 if (this._onKeyPress
) {
57 Util
.Debug("onKeyPress " + (e
.type
== 'keydown' ? "down" : "up") +
58 ", keysym: " + e
.keysym
.keysym
+ "(" + e
.keysym
.keyname
+ ")");
63 setQEMUVNCKeyboardHandler: function () {
64 this._handler
= new KeyboardUtil
.QEMUKeyEventDecoder(KeyboardUtil
.ModifierSync(),
65 KeyboardUtil
.TrackQEMUKeyState(
66 this._handleRfbEvent
.bind(this)
71 _handleKeyDown: function (e
) {
72 if (!this._focused
) { return; }
74 if (this._handler
.keydown(e
)) {
75 // Suppress bubbling/default actions
78 // Allow the event to bubble and become a keyPress event which
79 // will have the character code translated
83 _handleKeyPress: function (e
) {
84 if (!this._focused
) { return; }
86 if (this._handler
.keypress(e
)) {
87 // Suppress bubbling/default actions
92 _handleKeyUp: function (e
) {
93 if (!this._focused
) { return; }
95 if (this._handler
.keyup(e
)) {
96 // Suppress bubbling/default actions
101 _allKeysUp: function () {
102 Util
.Debug(">> Keyboard.allKeysUp");
103 this._handler
.releaseAll();
104 Util
.Debug("<< Keyboard.allKeysUp");
110 //Util.Debug(">> Keyboard.grab");
111 var c
= this._target
;
113 c
.addEventListener('keydown', this._eventHandlers
.keydown
);
114 c
.addEventListener('keyup', this._eventHandlers
.keyup
);
115 c
.addEventListener('keypress', this._eventHandlers
.keypress
);
117 // Release (key up) if window loses focus
118 window
.addEventListener('blur', this._eventHandlers
.blur
);
120 //Util.Debug("<< Keyboard.grab");
123 ungrab: function () {
124 //Util.Debug(">> Keyboard.ungrab");
125 var c
= this._target
;
127 c
.removeEventListener('keydown', this._eventHandlers
.keydown
);
128 c
.removeEventListener('keyup', this._eventHandlers
.keyup
);
129 c
.removeEventListener('keypress', this._eventHandlers
.keypress
);
130 window
.removeEventListener('blur', this._eventHandlers
.blur
);
132 // Release (key up) all keys that are in a down state
135 //Util.Debug(">> Keyboard.ungrab");
139 this._handler
.syncModifiers(e
);
143 Util
.make_properties(Keyboard
, [
144 ['target', 'wo', 'dom'], // DOM element that captures keyboard input
145 ['focused', 'rw', 'bool'], // Capture and send key events
147 ['onKeyPress', 'rw', 'func'] // Handler for key press/release
151 /* [module] export */ var Mouse
;
154 Mouse = function (defaults
) {
155 this._mouseCaptured
= false;
157 this._doubleClickTimer
= null;
158 this._lastTouchPos
= null;
160 // Configuration attributes
161 Util
.set_defaults(this, defaults
, {
167 this._eventHandlers
= {
168 'mousedown': this._handleMouseDown
.bind(this),
169 'mouseup': this._handleMouseUp
.bind(this),
170 'mousemove': this._handleMouseMove
.bind(this),
171 'mousewheel': this._handleMouseWheel
.bind(this),
172 'mousedisable': this._handleMouseDisable
.bind(this)
178 _captureMouse: function () {
179 // capturing the mouse ensures we get the mouseup event
180 Util
.setCapture(this._target
);
182 // some browsers give us mouseup events regardless,
183 // so if we never captured the mouse, we can disregard the event
184 this._mouseCaptured
= true;
187 _releaseMouse: function () {
188 Util
.releaseCapture();
189 this._mouseCaptured
= false;
192 _resetDoubleClickTimer: function () {
193 this._doubleClickTimer
= null;
196 _handleMouseButton: function (e
, down
) {
197 if (!this._focused
) { return; }
203 var pos
= this._getMousePosition(e
);
206 if (e
.touches
|| e
.changedTouches
) {
209 // When two touches occur within 500 ms of each other and are
210 // close enough together a double click is triggered.
212 if (this._doubleClickTimer
=== null) {
213 this._lastTouchPos
= pos
;
215 clearTimeout(this._doubleClickTimer
);
217 // When the distance between the two touches is small enough
218 // force the position of the latter touch to the position of
221 var xs
= this._lastTouchPos
.x
- pos
.x
;
222 var ys
= this._lastTouchPos
.y
- pos
.y
;
223 var d
= Math
.sqrt((xs
* xs
) + (ys
* ys
));
225 // The goal is to trigger on a certain physical width, the
226 // devicePixelRatio brings us a bit closer but is not optimal.
227 var threshold
= 20 * (window
.devicePixelRatio
|| 1);
229 pos
= this._lastTouchPos
;
232 this._doubleClickTimer
= setTimeout(this._resetDoubleClickTimer
.bind(this), 500);
234 bmask
= this._touchButton
;
236 } else if (e
.which
) {
237 /* everything except IE */
238 bmask
= 1 << e
.button
;
241 bmask
= (e
.button
& 0x1) + // Left
242 (e
.button
& 0x2) * 2 + // Right
243 (e
.button
& 0x4) / 2; // Middle
246 if (this._onMouseButton
) {
247 Util
.Debug("onMouseButton " + (down
? "down" : "up") +
248 ", x: " + pos
.x
+ ", y: " + pos
.y
+ ", bmask: " + bmask
);
249 this._onMouseButton(pos
.x
, pos
.y
, down
, bmask
);
254 _handleMouseDown: function (e
) {
255 this._captureMouse();
256 this._handleMouseButton(e
, 1);
259 _handleMouseUp: function (e
) {
260 if (!this._mouseCaptured
) { return; }
262 this._handleMouseButton(e
, 0);
263 this._releaseMouse();
266 _handleMouseWheel: function (e
) {
267 if (!this._focused
) { return; }
273 var pos
= this._getMousePosition(e
);
275 if (this._onMouseButton
) {
277 this._onMouseButton(pos
.x
, pos
.y
, 1, 1 << 5);
278 this._onMouseButton(pos
.x
, pos
.y
, 0, 1 << 5);
279 } else if (e
.deltaX
> 0) {
280 this._onMouseButton(pos
.x
, pos
.y
, 1, 1 << 6);
281 this._onMouseButton(pos
.x
, pos
.y
, 0, 1 << 6);
285 this._onMouseButton(pos
.x
, pos
.y
, 1, 1 << 3);
286 this._onMouseButton(pos
.x
, pos
.y
, 0, 1 << 3);
287 } else if (e
.deltaY
> 0) {
288 this._onMouseButton(pos
.x
, pos
.y
, 1, 1 << 4);
289 this._onMouseButton(pos
.x
, pos
.y
, 0, 1 << 4);
296 _handleMouseMove: function (e
) {
297 if (! this._focused
) { return; }
303 var pos
= this._getMousePosition(e
);
304 if (this._onMouseMove
) {
305 this._onMouseMove(pos
.x
, pos
.y
);
310 _handleMouseDisable: function (e
) {
311 if (!this._focused
) { return; }
314 * Stop propagation if inside canvas area
315 * Note: This is only needed for the 'click' event as it fails
316 * to fire properly for the target element so we have
317 * to listen on the document element instead.
319 if (e
.target
== this._target
) {
320 //Util.Debug("mouse event disabled");
325 // Return coordinates relative to target
326 _getMousePosition: function(e
) {
327 e
= Util
.getPointerEvent(e
);
328 var bounds
= this._target
.getBoundingClientRect();
330 // Clip to target bounds
331 if (e
.clientX
< bounds
.left
) {
333 } else if (e
.clientX
>= bounds
.right
) {
334 x
= bounds
.width
- 1;
336 x
= e
.clientX
- bounds
.left
;
338 if (e
.clientY
< bounds
.top
) {
340 } else if (e
.clientY
>= bounds
.bottom
) {
341 y
= bounds
.height
- 1;
343 y
= e
.clientY
- bounds
.top
;
351 var c
= this._target
;
353 if (Util
.isTouchDevice
) {
354 c
.addEventListener('touchstart', this._eventHandlers
.mousedown
);
355 window
.addEventListener('touchend', this._eventHandlers
.mouseup
);
356 c
.addEventListener('touchend', this._eventHandlers
.mouseup
);
357 c
.addEventListener('touchmove', this._eventHandlers
.mousemove
);
359 c
.addEventListener('mousedown', this._eventHandlers
.mousedown
);
360 window
.addEventListener('mouseup', this._eventHandlers
.mouseup
);
361 c
.addEventListener('mouseup', this._eventHandlers
.mouseup
);
362 c
.addEventListener('mousemove', this._eventHandlers
.mousemove
);
363 c
.addEventListener('wheel', this._eventHandlers
.mousewheel
);
365 /* Prevent middle-click pasting (see above for why we bind to document) */
366 document
.addEventListener('click', this._eventHandlers
.mousedisable
);
368 /* preventDefault() on mousedown doesn't stop this event for some
369 reason so we have to explicitly block it */
370 c
.addEventListener('contextmenu', this._eventHandlers
.mousedisable
);
373 ungrab: function () {
374 var c
= this._target
;
376 if (Util
.isTouchDevice
) {
377 c
.removeEventListener('touchstart', this._eventHandlers
.mousedown
);
378 window
.removeEventListener('touchend', this._eventHandlers
.mouseup
);
379 c
.removeEventListener('touchend', this._eventHandlers
.mouseup
);
380 c
.removeEventListener('touchmove', this._eventHandlers
.mousemove
);
382 c
.removeEventListener('mousedown', this._eventHandlers
.mousedown
);
383 window
.removeEventListener('mouseup', this._eventHandlers
.mouseup
);
384 c
.removeEventListener('mouseup', this._eventHandlers
.mouseup
);
385 c
.removeEventListener('mousemove', this._eventHandlers
.mousemove
);
386 c
.removeEventListener('wheel', this._eventHandlers
.mousewheel
);
388 document
.removeEventListener('click', this._eventHandlers
.mousedisable
);
390 c
.removeEventListener('contextmenu', this._eventHandlers
.mousedisable
);
394 Util
.make_properties(Mouse
, [
395 ['target', 'ro', 'dom'], // DOM element that captures mouse input
396 ['notify', 'ro', 'func'], // Function to call to notify whenever a mouse event is received
397 ['focused', 'rw', 'bool'], // Capture and send mouse clicks/movement
399 ['onMouseButton', 'rw', 'func'], // Handler for mouse button click/release
400 ['onMouseMove', 'rw', 'func'], // Handler for mouse movement
401 ['touchButton', 'rw', 'int'] // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)