]>
git.proxmox.com Git - mirror_novnc.git/blob - core/input/devices.js
91a202c586c8dad0c80cba785335cc2741d70dcc
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 true; }
74 if (this._handler
.keydown(e
)) {
75 // Suppress bubbling/default actions
79 // Allow the event to bubble and become a keyPress event which
80 // will have the character code translated
85 _handleKeyPress: function (e
) {
86 if (!this._focused
) { return true; }
88 if (this._handler
.keypress(e
)) {
89 // Suppress bubbling/default actions
93 // Allow the event to bubble and become a keyPress event which
94 // will have the character code translated
99 _handleKeyUp: function (e
) {
100 if (!this._focused
) { return true; }
102 if (this._handler
.keyup(e
)) {
103 // Suppress bubbling/default actions
107 // Allow the event to bubble and become a keyPress event which
108 // will have the character code translated
113 _allKeysUp: function () {
114 Util
.Debug(">> Keyboard.allKeysUp");
115 this._handler
.releaseAll();
116 Util
.Debug("<< Keyboard.allKeysUp");
122 //Util.Debug(">> Keyboard.grab");
123 var c
= this._target
;
125 c
.addEventListener('keydown', this._eventHandlers
.keydown
);
126 c
.addEventListener('keyup', this._eventHandlers
.keyup
);
127 c
.addEventListener('keypress', this._eventHandlers
.keypress
);
129 // Release (key up) if window loses focus
130 window
.addEventListener('blur', this._eventHandlers
.blur
);
132 //Util.Debug("<< Keyboard.grab");
135 ungrab: function () {
136 //Util.Debug(">> Keyboard.ungrab");
137 var c
= this._target
;
139 c
.removeEventListener('keydown', this._eventHandlers
.keydown
);
140 c
.removeEventListener('keyup', this._eventHandlers
.keyup
);
141 c
.removeEventListener('keypress', this._eventHandlers
.keypress
);
142 window
.removeEventListener('blur', this._eventHandlers
.blur
);
144 // Release (key up) all keys that are in a down state
147 //Util.Debug(">> Keyboard.ungrab");
151 this._handler
.syncModifiers(e
);
155 Util
.make_properties(Keyboard
, [
156 ['target', 'wo', 'dom'], // DOM element that captures keyboard input
157 ['focused', 'rw', 'bool'], // Capture and send key events
159 ['onKeyPress', 'rw', 'func'] // Handler for key press/release
163 /* [module] export */ var Mouse
;
166 Mouse = function (defaults
) {
167 this._mouseCaptured
= false;
169 this._doubleClickTimer
= null;
170 this._lastTouchPos
= null;
172 // Configuration attributes
173 Util
.set_defaults(this, defaults
, {
180 this._eventHandlers
= {
181 'mousedown': this._handleMouseDown
.bind(this),
182 'mouseup': this._handleMouseUp
.bind(this),
183 'mousemove': this._handleMouseMove
.bind(this),
184 'mousewheel': this._handleMouseWheel
.bind(this),
185 'mousedisable': this._handleMouseDisable
.bind(this)
191 _captureMouse: function () {
192 // capturing the mouse ensures we get the mouseup event
193 Util
.setCapture(this._target
);
195 // some browsers give us mouseup events regardless,
196 // so if we never captured the mouse, we can disregard the event
197 this._mouseCaptured
= true;
200 _releaseMouse: function () {
201 Util
.releaseCapture();
202 this._mouseCaptured
= false;
205 _resetDoubleClickTimer: function () {
206 this._doubleClickTimer
= null;
209 _handleMouseButton: function (e
, down
) {
210 if (!this._focused
) { return true; }
216 var evt
= (e
? e
: window
.event
);
217 var pos
= Util
.getEventPosition(e
, this._target
, this._scale
);
220 if (e
.touches
|| e
.changedTouches
) {
223 // When two touches occur within 500 ms of each other and are
224 // close enough together a double click is triggered.
226 if (this._doubleClickTimer
=== null) {
227 this._lastTouchPos
= pos
;
229 clearTimeout(this._doubleClickTimer
);
231 // When the distance between the two touches is small enough
232 // force the position of the latter touch to the position of
235 var xs
= this._lastTouchPos
.x
- pos
.x
;
236 var ys
= this._lastTouchPos
.y
- pos
.y
;
237 var d
= Math
.sqrt((xs
* xs
) + (ys
* ys
));
239 // The goal is to trigger on a certain physical width, the
240 // devicePixelRatio brings us a bit closer but is not optimal.
241 var threshold
= 20 * (window
.devicePixelRatio
|| 1);
243 pos
= this._lastTouchPos
;
246 this._doubleClickTimer
= setTimeout(this._resetDoubleClickTimer
.bind(this), 500);
248 bmask
= this._touchButton
;
250 } else if (evt
.which
) {
251 /* everything except IE */
252 bmask
= 1 << evt
.button
;
255 bmask
= (evt
.button
& 0x1) + // Left
256 (evt
.button
& 0x2) * 2 + // Right
257 (evt
.button
& 0x4) / 2; // Middle
260 if (this._onMouseButton
) {
261 Util
.Debug("onMouseButton " + (down
? "down" : "up") +
262 ", x: " + pos
.x
+ ", y: " + pos
.y
+ ", bmask: " + bmask
);
263 this._onMouseButton(pos
.x
, pos
.y
, down
, bmask
);
269 _handleMouseDown: function (e
) {
270 this._captureMouse();
271 this._handleMouseButton(e
, 1);
274 _handleMouseUp: function (e
) {
275 if (!this._mouseCaptured
) { return; }
277 this._handleMouseButton(e
, 0);
278 this._releaseMouse();
281 _handleMouseWheel: function (e
) {
282 if (!this._focused
) { return true; }
288 var evt
= (e
? e
: window
.event
);
289 var pos
= Util
.getEventPosition(e
, this._target
, this._scale
);
291 if (this._onMouseButton
) {
292 if (evt
.deltaX
< 0) {
293 this._onMouseButton(pos
.x
, pos
.y
, 1, 1 << 5);
294 this._onMouseButton(pos
.x
, pos
.y
, 0, 1 << 5);
295 } else if (evt
.deltaX
> 0) {
296 this._onMouseButton(pos
.x
, pos
.y
, 1, 1 << 6);
297 this._onMouseButton(pos
.x
, pos
.y
, 0, 1 << 6);
300 if (evt
.deltaY
< 0) {
301 this._onMouseButton(pos
.x
, pos
.y
, 1, 1 << 3);
302 this._onMouseButton(pos
.x
, pos
.y
, 0, 1 << 3);
303 } else if (evt
.deltaY
> 0) {
304 this._onMouseButton(pos
.x
, pos
.y
, 1, 1 << 4);
305 this._onMouseButton(pos
.x
, pos
.y
, 0, 1 << 4);
313 _handleMouseMove: function (e
) {
314 if (! this._focused
) { return true; }
320 var evt
= (e
? e
: window
.event
);
321 var pos
= Util
.getEventPosition(e
, this._target
, this._scale
);
322 if (this._onMouseMove
) {
323 this._onMouseMove(pos
.x
, pos
.y
);
329 _handleMouseDisable: function (e
) {
330 if (!this._focused
) { return true; }
332 var evt
= (e
? e
: window
.event
);
334 * Stop propagation if inside canvas area
335 * Note: This is only needed for the 'click' event as it fails
336 * to fire properly for the target element so we have
337 * to listen on the document element instead.
339 if (evt
.target
== this._target
) {
340 //Util.Debug("mouse event disabled");
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
398 ['scale', 'rw', 'float'], // Viewport scale factor 0.0 - 1.0
400 ['onMouseButton', 'rw', 'func'], // Handler for mouse button click/release
401 ['onMouseMove', 'rw', 'func'], // Handler for mouse movement
402 ['touchButton', 'rw', 'int'] // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)