Move mouse event handling to RFB class

Move the last remaining bits to the RFB class to keep things simple, as
the Mouse class no longer provides any real value.
This commit is contained in:
Pierre Ossman 2020-06-10 16:13:03 +02:00 committed by Samuel Mannehed
parent 88589a44f7
commit 50cde2faab
5 changed files with 324 additions and 352 deletions

View File

@ -1,127 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/
import * as Log from '../util/logging.js';
import { setCapture, stopEvent, getPointerEvent } from '../util/events.js';
export default class Mouse {
constructor(target) {
this._target = target || document;
this._pos = null;
this._eventHandlers = {
'mousedown': this._handleMouseDown.bind(this),
'mouseup': this._handleMouseUp.bind(this),
'mousemove': this._handleMouseMove.bind(this),
'mousedisable': this._handleMouseDisable.bind(this)
};
// ===== EVENT HANDLERS =====
this.onmousebutton = () => {}; // Handler for mouse button press/release
this.onmousemove = () => {}; // Handler for mouse movement
}
// ===== PRIVATE METHODS =====
_resetDoubleClickTimer() {
this._doubleClickTimer = null;
}
_handleMouseButton(e, down) {
this._updateMousePosition(e);
let pos = this._pos;
let bmask = 1 << e.button;
Log.Debug("onmousebutton " + (down ? "down" : "up") +
", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
this.onmousebutton(pos.x, pos.y, down, bmask);
stopEvent(e);
}
_handleMouseDown(e) {
setCapture(this._target);
this._handleMouseButton(e, 1);
}
_handleMouseUp(e) {
this._handleMouseButton(e, 0);
}
_handleMouseMove(e) {
this._updateMousePosition(e);
this.onmousemove(this._pos.x, this._pos.y);
stopEvent(e);
}
_handleMouseDisable(e) {
/*
* Stop propagation if inside canvas area
* Note: This is only needed for the 'click' event as it fails
* to fire properly for the target element so we have
* to listen on the document element instead.
*/
if (e.target == this._target) {
stopEvent(e);
}
}
// Update coordinates relative to target
_updateMousePosition(e) {
e = getPointerEvent(e);
const bounds = this._target.getBoundingClientRect();
let x;
let y;
// Clip to target bounds
if (e.clientX < bounds.left) {
x = 0;
} else if (e.clientX >= bounds.right) {
x = bounds.width - 1;
} else {
x = e.clientX - bounds.left;
}
if (e.clientY < bounds.top) {
y = 0;
} else if (e.clientY >= bounds.bottom) {
y = bounds.height - 1;
} else {
y = e.clientY - bounds.top;
}
this._pos = {x: x, y: y};
}
// ===== PUBLIC METHODS =====
grab() {
const t = this._target;
t.addEventListener('mousedown', this._eventHandlers.mousedown);
t.addEventListener('mouseup', this._eventHandlers.mouseup);
t.addEventListener('mousemove', this._eventHandlers.mousemove);
// Prevent middle-click pasting (see above for why we bind to document)
document.addEventListener('click', this._eventHandlers.mousedisable);
// preventDefault() on mousedown doesn't stop this event for some
// reason so we have to explicitly block it
t.addEventListener('contextmenu', this._eventHandlers.mousedisable);
}
ungrab() {
const t = this._target;
t.removeEventListener('mousedown', this._eventHandlers.mousedown);
t.removeEventListener('mouseup', this._eventHandlers.mouseup);
t.removeEventListener('mousemove', this._eventHandlers.mousemove);
document.removeEventListener('click', this._eventHandlers.mousedisable);
t.removeEventListener('contextmenu', this._eventHandlers.mousedisable);
}
}

View File

@ -12,12 +12,12 @@ import * as Log from './util/logging.js';
import { encodeUTF8, decodeUTF8 } from './util/strings.js';
import { dragThreshold } from './util/browser.js';
import { clientToElement } from './util/element.js';
import { setCapture } from './util/events.js';
import EventTargetMixin from './util/eventtarget.js';
import Display from "./display.js";
import Inflator from "./inflator.js";
import Deflator from "./deflator.js";
import Keyboard from "./input/keyboard.js";
import Mouse from "./input/mouse.js";
import GestureHandler from "./input/gesturehandler.js";
import Cursor from "./util/cursor.js";
import Websock from "./websock.js";
@ -129,7 +129,6 @@ export default class RFB extends EventTargetMixin {
this._display = null; // Display object
this._flushing = false; // Display flushing state
this._keyboard = null; // Keyboard input handler object
this._mouse = null; // Mouse input handler object
this._gestures = null; // Gesture input handler object
// Timers
@ -169,6 +168,7 @@ export default class RFB extends EventTargetMixin {
this._eventHandlers = {
focusCanvas: this._focusCanvas.bind(this),
windowResize: this._windowResize.bind(this),
handleMouse: this._handleMouse.bind(this),
handleWheel: this._handleWheel.bind(this),
handleGesture: this._handleGesture.bind(this),
};
@ -229,10 +229,6 @@ export default class RFB extends EventTargetMixin {
this._keyboard = new Keyboard(this._canvas);
this._keyboard.onkeyevent = this._handleKeyEvent.bind(this);
this._mouse = new Mouse(this._canvas);
this._mouse.onmousebutton = this._handleMouseButton.bind(this);
this._mouse.onmousemove = this._handleMouseMove.bind(this);
this._gestures = new GestureHandler();
this._sock = new Websock();
@ -321,10 +317,8 @@ export default class RFB extends EventTargetMixin {
this._rfbConnectionState === "connected") {
if (viewOnly) {
this._keyboard.ungrab();
this._mouse.ungrab();
} else {
this._keyboard.grab();
this._mouse.grab();
}
}
}
@ -539,6 +533,16 @@ export default class RFB extends EventTargetMixin {
this._canvas.addEventListener("mousedown", this._eventHandlers.focusCanvas);
this._canvas.addEventListener("touchstart", this._eventHandlers.focusCanvas);
// Mouse events
this._canvas.addEventListener('mousedown', this._eventHandlers.handleMouse);
this._canvas.addEventListener('mouseup', this._eventHandlers.handleMouse);
this._canvas.addEventListener('mousemove', this._eventHandlers.handleMouse);
// Prevent middle-click pasting (see handler for why we bind to document)
this._canvas.addEventListener('click', this._eventHandlers.handleMouse);
// preventDefault() on mousedown doesn't stop this event for some
// reason so we have to explicitly block it
this._canvas.addEventListener('contextmenu', this._eventHandlers.handleMouse);
// Wheel events
this._canvas.addEventListener("wheel", this._eventHandlers.handleWheel);
@ -557,11 +561,15 @@ export default class RFB extends EventTargetMixin {
this._canvas.removeEventListener("gesturemove", this._eventHandlers.handleGesture);
this._canvas.removeEventListener("gestureend", this._eventHandlers.handleGesture);
this._canvas.removeEventListener("wheel", this._eventHandlers.handleWheel);
this._canvas.removeEventListener('mousedown', this._eventHandlers.handleMouse);
this._canvas.removeEventListener('mouseup', this._eventHandlers.handleMouse);
this._canvas.removeEventListener('mousemove', this._eventHandlers.handleMouse);
this._canvas.removeEventListener('click', this._eventHandlers.handleMouse);
this._canvas.removeEventListener('contextmenu', this._eventHandlers.handleMouse);
this._canvas.removeEventListener("mousedown", this._eventHandlers.focusCanvas);
this._canvas.removeEventListener("touchstart", this._eventHandlers.focusCanvas);
window.removeEventListener('resize', this._eventHandlers.windowResize);
this._keyboard.ungrab();
this._mouse.ungrab();
this._gestures.detach();
this._sock.close();
try {
@ -859,6 +867,51 @@ export default class RFB extends EventTargetMixin {
this.sendKey(keysym, code, down);
}
_handleMouse(ev) {
/*
* We don't check connection status or viewOnly here as the
* mouse events might be used to control the viewport
*/
if (ev.type === 'click') {
/*
* Note: This is only needed for the 'click' event as it fails
* to fire properly for the target element so we have
* to listen on the document element instead.
*/
if (ev.target !== this._canvas) {
return;
}
}
// FIXME: if we're in view-only and not dragging,
// should we stop events?
ev.stopPropagation();
ev.preventDefault();
if ((ev.type === 'click') || (ev.type === 'contextmenu')) {
return;
}
let pos = clientToElement(ev.clientX, ev.clientY,
this._canvas);
switch (ev.type) {
case 'mousedown':
setCapture(this._canvas);
this._handleMouseButton(pos.x, pos.y,
true, 1 << ev.button);
break;
case 'mouseup':
this._handleMouseButton(pos.x, pos.y,
false, 1 << ev.button);
break;
case 'mousemove':
this._handleMouseMove(pos.x, pos.y);
break;
}
}
_handleMouseButton(x, y, down, bmask) {
if (this.dragViewport) {
if (down && !this._viewportDragging) {
@ -1678,7 +1731,6 @@ export default class RFB extends EventTargetMixin {
this._resize(width, height);
if (!this._viewOnly) { this._keyboard.grab(); }
if (!this._viewOnly) { this._mouse.grab(); }
this._fbDepth = 24;

View File

@ -11,9 +11,6 @@ official external API.
## 1.1 Module List
* __Mouse__ (core/input/mouse.js): Mouse input event handler with
limited touch support.
* __Keyboard__ (core/input/keyboard.js): Keyboard input event handler with
non-US keyboard support. Translates keyDown and keyUp events to X11
keysym values.
@ -35,52 +32,29 @@ callback event name, and the callback function.
## 2. Modules
## 2.1 Mouse Module
## 2.1 Keyboard Module
### 2.1.1 Configuration Attributes
| name | type | mode | default | description
| ----------- | ---- | ---- | -------- | ------------
| touchButton | int | RW | 1 | Button mask (1, 2, 4) for which click to send on touch devices. 0 means ignore clicks.
### 2.1.2 Methods
| name | parameters | description
| ------ | ---------- | ------------
| grab | () | Begin capturing mouse events
| ungrab | () | Stop capturing mouse events
### 2.1.2 Callbacks
| name | parameters | description
| ------------- | ------------------- | ------------
| onmousebutton | (x, y, down, bmask) | Handler for mouse button click/release
| onmousemove | (x, y) | Handler for mouse movement
## 2.2 Keyboard Module
### 2.2.1 Configuration Attributes
None
### 2.2.2 Methods
### 2.1.2 Methods
| name | parameters | description
| ------ | ---------- | ------------
| grab | () | Begin capturing keyboard events
| ungrab | () | Stop capturing keyboard events
### 2.2.3 Callbacks
### 2.1.3 Callbacks
| name | parameters | description
| ---------- | -------------------- | ------------
| onkeypress | (keysym, code, down) | Handler for key press/release
## 2.3 Display Module
## 2.2 Display Module
### 2.3.1 Configuration Attributes
### 2.2.1 Configuration Attributes
| name | type | mode | default | description
| ------------ | ----- | ---- | ------- | ------------
@ -89,7 +63,7 @@ None
| width | int | RO | | Display area width
| height | int | RO | | Display area height
### 2.3.2 Methods
### 2.2.2 Methods
| name | parameters | description
| ------------------ | ------------------------------------------------------- | ------------
@ -113,7 +87,7 @@ None
| drawImage | (img, x, y) | Draw image and track damage
| autoscale | (containerWidth, containerHeight) | Scale the display
### 2.3.3 Callbacks
### 2.2.3 Callbacks
| name | parameters | description
| ------- | ---------- | ------------

View File

@ -1,73 +0,0 @@
const expect = chai.expect;
import Mouse from '../core/input/mouse.js';
describe('Mouse Event Handling', function () {
"use strict";
let target;
beforeEach(function () {
// For these tests we can assume that the canvas is 100x100
// located at coordinates 10x10
target = document.createElement('canvas');
target.style.position = "absolute";
target.style.top = "10px";
target.style.left = "10px";
target.style.width = "100px";
target.style.height = "100px";
document.body.appendChild(target);
});
afterEach(function () {
document.body.removeChild(target);
target = null;
});
// The real constructors might not work everywhere we
// want to run these tests
const mouseevent = (typeArg, MouseEventInit) => {
const e = { type: typeArg };
for (let key in MouseEventInit) {
e[key] = MouseEventInit[key];
}
e.stopPropagation = sinon.spy();
e.preventDefault = sinon.spy();
return e;
};
describe('Decode Mouse Events', function () {
it('should decode mousedown events', function (done) {
const mouse = new Mouse(target);
mouse.onmousebutton = (x, y, down, bmask) => {
expect(bmask).to.be.equal(0x01);
expect(down).to.be.equal(1);
done();
};
mouse._handleMouseDown(mouseevent('mousedown', { button: 0 }));
});
it('should decode mouseup events', function (done) {
let calls = 0;
const mouse = new Mouse(target);
mouse.onmousebutton = (x, y, down, bmask) => {
expect(bmask).to.be.equal(0x01);
if (calls++ === 1) {
expect(down).to.not.be.equal(1);
done();
}
};
mouse._handleMouseDown(mouseevent('mousedown', { button: 0 }));
mouse._handleMouseUp(mouseevent('mouseup', { button: 0 }));
});
it('should decode mousemove events', function (done) {
const mouse = new Mouse(target);
mouse.onmousemove = (x, y) => {
// Note that target relative coordinates are sent
expect(x).to.be.equal(40);
expect(y).to.be.equal(10);
done();
};
mouse._handleMouseMove(mouseevent('mousemove',
{ clientX: 50, clientY: 20 }));
});
});
});

View File

@ -1579,12 +1579,10 @@ describe('Remote Frame Buffer Protocol Client', function () {
expect(client._display.resize).to.have.been.calledWith(27, 32);
});
it('should grab the mouse and keyboard', function () {
it('should grab the keyboard', function () {
sinon.spy(client._keyboard, 'grab');
sinon.spy(client._mouse, 'grab');
sendServerInit({}, client);
expect(client._keyboard.grab).to.have.been.calledOnce;
expect(client._mouse.grab).to.have.been.calledOnce;
});
describe('Initial Update Request', function () {
@ -2739,6 +2737,11 @@ describe('Remote Frame Buffer Protocol Client', function () {
client = makeRFB();
client._display.resize(100, 100);
// We need to disable this as focusing the canvas will
// cause the browser to scoll to it, messing up our
// client coordinate calculations
client.focusOnClick = false;
pointerEvent = sinon.spy(RFB.messages, 'pointerEvent');
keyEvent = sinon.spy(RFB.messages, 'keyEvent');
qemuKeyEvent = sinon.spy(RFB.messages, 'QEMUExtendedKeyEvent');
@ -2769,136 +2772,279 @@ describe('Remote Frame Buffer Protocol Client', function () {
}
describe('Mouse Events', function () {
function sendMouseMoveEvent(x, y) {
let pos = elementToClient(x, y);
let ev;
try {
ev = new MouseEvent('mousemove',
{ 'screenX': pos.x + window.screenX,
'screenY': pos.y + window.screenY,
'clientX': pos.x,
'clientY': pos.y });
} catch (e) {
ev = document.createEvent('MouseEvent');
ev.initMouseEvent('mousemove',
true, true, window, 0,
pos.x + window.screenX,
pos.y + window.screenY,
pos.x, pos.y,
false, false, false, false,
0, null);
}
client._canvas.dispatchEvent(ev);
}
function sendMouseButtonEvent(x, y, down, button) {
let pos = elementToClient(x, y);
let ev;
try {
ev = new MouseEvent(down ? 'mousedown' : 'mouseup',
{ 'screenX': pos.x + window.screenX,
'screenY': pos.y + window.screenY,
'clientX': pos.x,
'clientY': pos.y,
'button': button,
'buttons': 1 << button });
} catch (e) {
ev = document.createEvent('MouseEvent');
ev.initMouseEvent(down ? 'mousedown' : 'mouseup',
true, true, window, 0,
pos.x + window.screenX,
pos.y + window.screenY,
pos.x, pos.y,
false, false, false, false,
button, null);
}
client._canvas.dispatchEvent(ev);
}
it('should not send button messages in view-only mode', function () {
client._viewOnly = true;
client._handleMouseButton(0, 0, 1, 0x001);
expect(RFB.messages.pointerEvent).to.not.have.been.called;
sendMouseButtonEvent(10, 10, true, 0);
clock.tick(50);
expect(pointerEvent).to.not.have.been.called;
});
it('should not send movement messages in view-only mode', function () {
client._viewOnly = true;
client._handleMouseMove(0, 0);
expect(RFB.messages.pointerEvent).to.not.have.been.called;
sendMouseMoveEvent(10, 10);
clock.tick(50);
expect(pointerEvent).to.not.have.been.called;
});
it('should send a pointer event on mouse button presses', function () {
client._handleMouseButton(10, 12, 1, 0x001);
expect(RFB.messages.pointerEvent).to.have.been.calledOnce;
it('should handle left mouse button', function () {
sendMouseButtonEvent(10, 10, true, 0);
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
10, 10, 0x1);
pointerEvent.resetHistory();
sendMouseButtonEvent(10, 10, false, 0);
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
10, 10, 0x0);
});
it('should send a mask of 1 on mousedown', function () {
client._handleMouseButton(11, 13, 1, 0x001);
expect(RFB.messages.pointerEvent).to.have.been.calledWith(
client._sock, 11, 13, 0x001);
it('should handle middle mouse button', function () {
sendMouseButtonEvent(10, 10, true, 1);
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
10, 10, 0x2);
pointerEvent.resetHistory();
sendMouseButtonEvent(10, 10, false, 1);
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
10, 10, 0x0);
});
it('should send a mask of 0 on mouseup', function () {
client._mouseButtonMask = 0x001;
client._handleMouseButton(105, 120, 0, 0x001);
expect(RFB.messages.pointerEvent).to.have.been.calledWith(
client._sock, 105, 120, 0x000);
it('should handle right mouse button', function () {
sendMouseButtonEvent(10, 10, true, 2);
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
10, 10, 0x4);
pointerEvent.resetHistory();
sendMouseButtonEvent(10, 10, false, 2);
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
10, 10, 0x0);
});
it('should send a mask of 0 on mousemove', function () {
client._handleMouseMove(100, 200);
expect(RFB.messages.pointerEvent).to.have.been.calledWith(
client._sock, 100, 200, 0x000);
it('should handle multiple mouse buttons', function () {
sendMouseButtonEvent(10, 10, true, 0);
sendMouseButtonEvent(10, 10, true, 2);
expect(pointerEvent).to.have.been.calledTwice;
expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
10, 10, 0x1);
expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
10, 10, 0x5);
pointerEvent.resetHistory();
sendMouseButtonEvent(10, 10, false, 0);
sendMouseButtonEvent(10, 10, false, 2);
expect(pointerEvent).to.have.been.calledTwice;
expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
10, 10, 0x4);
expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
10, 10, 0x0);
});
it('should set the button mask so that future mouse movements use it', function () {
client._handleMouseButton(10, 12, 1, 0x010);
client._handleMouseMove(13, 9);
expect(RFB.messages.pointerEvent).to.have.been.calledTwice;
expect(RFB.messages.pointerEvent).to.have.been.calledWith(
client._sock, 13, 9, 0x010);
it('should handle mouse movement', function () {
sendMouseMoveEvent(50, 70);
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
50, 70, 0x0);
});
it('should handle click and drag', function () {
sendMouseButtonEvent(10, 10, true, 0);
sendMouseMoveEvent(50, 70);
expect(pointerEvent).to.have.been.calledTwice;
expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
10, 10, 0x1);
expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
50, 70, 0x1);
pointerEvent.resetHistory();
sendMouseButtonEvent(50, 70, false, 0);
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
50, 70, 0x0);
});
describe('Event Aggregation', function () {
it('should send a single pointer event on mouse movement', function () {
client._handleMouseMove(100, 200);
this.clock.tick(100);
expect(RFB.messages.pointerEvent).to.have.been.calledOnce;
sendMouseMoveEvent(50, 70);
clock.tick(100);
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
50, 70, 0x0);
});
it('should delay one move if two events are too close', function () {
client._handleMouseMove(18, 30);
client._handleMouseMove(20, 50);
expect(RFB.messages.pointerEvent).to.have.been.calledOnce;
this.clock.tick(100);
expect(RFB.messages.pointerEvent).to.have.been.calledTwice;
sendMouseMoveEvent(18, 30);
sendMouseMoveEvent(20, 50);
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
18, 30, 0x0);
pointerEvent.resetHistory();
clock.tick(100);
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
20, 50, 0x0);
});
it('should only send first and last move of many close events', function () {
client._handleMouseMove(18, 40);
client._handleMouseMove(20, 50);
client._handleMouseMove(21, 55);
sendMouseMoveEvent(18, 30);
sendMouseMoveEvent(20, 50);
sendMouseMoveEvent(21, 55);
expect(RFB.messages.pointerEvent).to.have.been.calledOnce;
this.clock.tick(60);
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
18, 30, 0x0);
pointerEvent.resetHistory();
expect(RFB.messages.pointerEvent).to.have.been.calledTwice;
expect(RFB.messages.pointerEvent.firstCall).to.have.been.calledWith(
client._sock, 18, 40, 0x000);
expect(RFB.messages.pointerEvent.secondCall).to.have.been.calledWith(
client._sock, 21, 55, 0x000);
clock.tick(100);
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
21, 55, 0x0);
});
// We selected the 17ms since that is ~60 FPS
it('should send move events every 17 ms', function () {
client._handleMouseMove(1, 10); // instant send
this.clock.tick(10);
client._handleMouseMove(2, 20); // delayed
this.clock.tick(10); // timeout send
client._handleMouseMove(3, 30); // delayed
this.clock.tick(10);
client._handleMouseMove(4, 40); // delayed
this.clock.tick(10); // timeout send
client._handleMouseMove(5, 50); // delayed
sendMouseMoveEvent(1, 10); // instant send
clock.tick(10);
expect(RFB.messages.pointerEvent).to.have.callCount(3);
expect(RFB.messages.pointerEvent.firstCall).to.have.been.calledWith(
client._sock, 1, 10, 0x000);
expect(RFB.messages.pointerEvent.secondCall).to.have.been.calledWith(
client._sock, 2, 20, 0x000);
expect(RFB.messages.pointerEvent.thirdCall).to.have.been.calledWith(
client._sock, 4, 40, 0x000);
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
1, 10, 0x0);
pointerEvent.resetHistory();
sendMouseMoveEvent(2, 20); // delayed
clock.tick(10); // timeout send
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
2, 20, 0x0);
pointerEvent.resetHistory();
sendMouseMoveEvent(3, 30); // delayed
clock.tick(10);
sendMouseMoveEvent(4, 40); // delayed
clock.tick(10); // timeout send
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
4, 40, 0x0);
pointerEvent.resetHistory();
sendMouseMoveEvent(5, 50); // delayed
expect(pointerEvent).to.not.have.been.called;
});
it('should send waiting move events before a button press', function () {
client._handleMouseMove(13, 9);
client._handleMouseMove(20, 70);
client._handleMouseButton(10, 12, 1, 0x100);
expect(RFB.messages.pointerEvent).to.have.been.calledThrice;
expect(RFB.messages.pointerEvent.firstCall).to.have.been.calledWith(
client._sock, 13, 9, 0x000);
expect(RFB.messages.pointerEvent.secondCall).to.have.been.calledWith(
client._sock, 10, 12, 0x000);
expect(RFB.messages.pointerEvent.thirdCall).to.have.been.calledWith(
client._sock, 10, 12, 0x100);
});
sendMouseMoveEvent(13, 9);
it('should not delay events when button mask changes', function () {
client._handleMouseMove(13, 9); // instant
client._handleMouseMove(11, 10); // delayed
client._handleMouseButton(10, 12, 1, 0x010); // flush delayed
expect(RFB.messages.pointerEvent).to.have.been.calledThrice;
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
13, 9, 0x0);
pointerEvent.resetHistory();
sendMouseMoveEvent(20, 70);
expect(pointerEvent).to.not.have.been.called;
sendMouseButtonEvent(20, 70, true, 0);
expect(pointerEvent).to.have.been.calledTwice;
expect(pointerEvent.firstCall).to.have.been.calledWith(client._sock,
20, 70, 0x0);
expect(pointerEvent.secondCall).to.have.been.calledWith(client._sock,
20, 70, 0x1);
});
it('should send move events with enough time apart normally', function () {
client._handleMouseMove(58, 60);
expect(RFB.messages.pointerEvent).to.have.been.calledOnce;
sendMouseMoveEvent(58, 60);
this.clock.tick(20);
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
58, 60, 0x0);
pointerEvent.resetHistory();
client._handleMouseMove(25, 60);
expect(RFB.messages.pointerEvent).to.have.been.calledTwice;
clock.tick(20);
sendMouseMoveEvent(25, 60);
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
25, 60, 0x0);
pointerEvent.resetHistory();
});
it('should not send waiting move events if disconnected', function () {
client._handleMouseMove(88, 99);
client._handleMouseMove(66, 77);
sendMouseMoveEvent(88, 99);
expect(pointerEvent).to.have.been.calledOnceWith(client._sock,
88, 99, 0x0);
pointerEvent.resetHistory();
sendMouseMoveEvent(66, 77);
client.disconnect();
this.clock.tick(20);
expect(RFB.messages.pointerEvent).to.have.been.calledOnce;
clock.tick(20);
expect(pointerEvent).to.not.have.been.called;
});
});
it.skip('should block click events', function () {
/* FIXME */
});
it.skip('should block contextmenu events', function () {
/* FIXME */
});
});