Skip to content

Commit

Permalink
Merge branch 'mouse-buttonstate-remove' of https://github.com/CendioH…
Browse files Browse the repository at this point in the history
  • Loading branch information
CendioOssman committed Jan 14, 2025
2 parents 3193f80 + 6383fa6 commit 9cdbd28
Show file tree
Hide file tree
Showing 2 changed files with 504 additions and 282 deletions.
259 changes: 170 additions & 89 deletions core/rfb.js
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,35 @@ export default class RFB extends EventTargetMixin {
this.sendKey(keysym, code, down);
}

static _convertButtonMask(buttons) {
/* The bits in MouseEvent.buttons property correspond
* to the following mouse buttons:
* 0: Left
* 1: Right
* 2: Middle
* 3: Back
* 4: Forward
*
* These bits needs to be converted to what they are defined as
* in the RFB protocol.
*/

const buttonMaskMap = {
0: 1 << 0, // Left
1: 1 << 2, // Right
2: 1 << 1, // Middle
3: 1 << 7, // Back
};

let bmask = 0;
for (let i = 0; i < 4; i++) {
if (buttons & (1 << i)) {
bmask |= buttonMaskMap[i];
}
}
return bmask;
}

_handleMouse(ev) {
/*
* We don't check connection status or viewOnly here as the
Expand Down Expand Up @@ -1062,80 +1091,75 @@ export default class RFB extends EventTargetMixin {
let pos = clientToElement(ev.clientX, ev.clientY,
this._canvas);

let bmask = RFB._convertButtonMask(ev.buttons);

let down = ev.type == 'mousedown';
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);
if (this.dragViewport) {
if (down && !this._viewportDragging) {
this._viewportDragging = true;
this._viewportDragPos = {'x': pos.x, 'y': pos.y};
this._viewportHasMoved = false;

this._flushMouseMoveTimer(pos.x, pos.y);

// Skip sending mouse events, instead save the current
// mouse mask so we can send it later.
this._mouseButtonMask = bmask;
break;
} else {
this._viewportDragging = false;

// If we actually performed a drag then we are done
// here and should not send any mouse events
if (this._viewportHasMoved) {
this._mouseButtonMask = bmask;
break;
}
// Otherwise we treat this as a mouse click event.
// Send the previously saved button mask, followed
// by the current button mask at the end of this
// function.
this._sendMouse(pos.x, pos.y, this._mouseButtonMask);
}
}
if (down) {
setCapture(this._canvas);
}
this._handleMouseButton(pos.x, pos.y, bmask);
break;
case 'mousemove':
this._handleMouseMove(pos.x, pos.y);
break;
}
}
if (this._viewportDragging) {
const deltaX = this._viewportDragPos.x - pos.x;
const deltaY = this._viewportDragPos.y - pos.y;

_handleMouseButton(x, y, down, bmask) {
if (this.dragViewport) {
if (down && !this._viewportDragging) {
this._viewportDragging = true;
this._viewportDragPos = {'x': x, 'y': y};
this._viewportHasMoved = false;
if (this._viewportHasMoved || (Math.abs(deltaX) > dragThreshold ||
Math.abs(deltaY) > dragThreshold)) {
this._viewportHasMoved = true;

// Skip sending mouse events
return;
} else {
this._viewportDragging = false;
this._viewportDragPos = {'x': pos.x, 'y': pos.y};
this._display.viewportChangePos(deltaX, deltaY);
}

// If we actually performed a drag then we are done
// here and should not send any mouse events
if (this._viewportHasMoved) {
return;
// Skip sending mouse events
break;
}

// Otherwise we treat this as a mouse click event.
// Send the button down event here, as the button up
// event is sent at the end of this function.
this._sendMouse(x, y, bmask);
}
this._handleMouseMove(pos.x, pos.y);
break;
}
}

_handleMouseButton(x, y, bmask) {
// Flush waiting move event first
if (this._mouseMoveTimer !== null) {
clearTimeout(this._mouseMoveTimer);
this._mouseMoveTimer = null;
this._sendMouse(x, y, this._mouseButtonMask);
}

if (down) {
this._mouseButtonMask |= bmask;
} else {
this._mouseButtonMask &= ~bmask;
}
this._flushMouseMoveTimer(x, y);

this._mouseButtonMask = bmask;
this._sendMouse(x, y, this._mouseButtonMask);
}

_handleMouseMove(x, y) {
if (this._viewportDragging) {
const deltaX = this._viewportDragPos.x - x;
const deltaY = this._viewportDragPos.y - y;

if (this._viewportHasMoved || (Math.abs(deltaX) > dragThreshold ||
Math.abs(deltaY) > dragThreshold)) {
this._viewportHasMoved = true;

this._viewportDragPos = {'x': x, 'y': y};
this._display.viewportChangePos(deltaX, deltaY);
}

// Skip sending mouse events
return;
}

this._mousePos = { 'x': x, 'y': y };

// Limit many mouse move events to one every MOUSE_MOVE_DELAY ms
Expand Down Expand Up @@ -1179,6 +1203,7 @@ export default class RFB extends EventTargetMixin {
let pos = clientToElement(ev.clientX, ev.clientY,
this._canvas);

let bmask = RFB._convertButtonMask(ev.buttons);
let dX = ev.deltaX;
let dY = ev.deltaY;

Expand All @@ -1198,26 +1223,27 @@ export default class RFB extends EventTargetMixin {
this._accumulatedWheelDeltaX += dX;
this._accumulatedWheelDeltaY += dY;


// Generate a mouse wheel step event when the accumulated delta
// for one of the axes is large enough.
if (Math.abs(this._accumulatedWheelDeltaX) >= WHEEL_STEP) {
if (this._accumulatedWheelDeltaX < 0) {
this._handleMouseButton(pos.x, pos.y, true, 1 << 5);
this._handleMouseButton(pos.x, pos.y, false, 1 << 5);
this._handleMouseButton(pos.x, pos.y, bmask | 1 << 5);
this._handleMouseButton(pos.x, pos.y, bmask);
} else if (this._accumulatedWheelDeltaX > 0) {
this._handleMouseButton(pos.x, pos.y, true, 1 << 6);
this._handleMouseButton(pos.x, pos.y, false, 1 << 6);
this._handleMouseButton(pos.x, pos.y, bmask | 1 << 6);
this._handleMouseButton(pos.x, pos.y, bmask);
}

this._accumulatedWheelDeltaX = 0;
}
if (Math.abs(this._accumulatedWheelDeltaY) >= WHEEL_STEP) {
if (this._accumulatedWheelDeltaY < 0) {
this._handleMouseButton(pos.x, pos.y, true, 1 << 3);
this._handleMouseButton(pos.x, pos.y, false, 1 << 3);
this._handleMouseButton(pos.x, pos.y, bmask | 1 << 3);
this._handleMouseButton(pos.x, pos.y, bmask);
} else if (this._accumulatedWheelDeltaY > 0) {
this._handleMouseButton(pos.x, pos.y, true, 1 << 4);
this._handleMouseButton(pos.x, pos.y, false, 1 << 4);
this._handleMouseButton(pos.x, pos.y, bmask | 1 << 4);
this._handleMouseButton(pos.x, pos.y, bmask);
}

this._accumulatedWheelDeltaY = 0;
Expand Down Expand Up @@ -1256,8 +1282,8 @@ export default class RFB extends EventTargetMixin {
this._gestureLastTapTime = Date.now();

this._fakeMouseMove(this._gestureFirstDoubleTapEv, pos.x, pos.y);
this._handleMouseButton(pos.x, pos.y, true, bmask);
this._handleMouseButton(pos.x, pos.y, false, bmask);
this._handleMouseButton(pos.x, pos.y, bmask);
this._handleMouseButton(pos.x, pos.y, 0x0);
}

_handleGesture(ev) {
Expand All @@ -1278,14 +1304,27 @@ export default class RFB extends EventTargetMixin {
this._handleTapEvent(ev, 0x2);
break;
case 'drag':
this._fakeMouseMove(ev, pos.x, pos.y);
this._handleMouseButton(pos.x, pos.y, true, 0x1);
if (this.dragViewport) {
this._viewportHasMoved = false;
this._viewportDragging = true;
this._viewportDragPos = {'x': pos.x, 'y': pos.y};
} else {
this._fakeMouseMove(ev, pos.x, pos.y);
this._handleMouseButton(pos.x, pos.y, 0x1);
}
break;
case 'longpress':
this._fakeMouseMove(ev, pos.x, pos.y);
this._handleMouseButton(pos.x, pos.y, true, 0x4);
if (this.dragViewport) {
// If dragViewport is true, we need to wait to see
// if we have dragged outside the threshold before
// sending any events to the server.
this._viewportHasMoved = false;
this._viewportDragPos = {'x': pos.x, 'y': pos.y};
} else {
this._fakeMouseMove(ev, pos.x, pos.y);
this._handleMouseButton(pos.x, pos.y, 0x4);
}
break;

case 'twodrag':
this._gestureLastMagnitudeX = ev.detail.magnitudeX;
this._gestureLastMagnitudeY = ev.detail.magnitudeY;
Expand All @@ -1307,31 +1346,45 @@ export default class RFB extends EventTargetMixin {
break;
case 'drag':
case 'longpress':
this._fakeMouseMove(ev, pos.x, pos.y);
if (this.dragViewport) {
this._viewportDragging = true;
const deltaX = this._viewportDragPos.x - pos.x;
const deltaY = this._viewportDragPos.y - pos.y;

if (this._viewportHasMoved || (Math.abs(deltaX) > dragThreshold ||
Math.abs(deltaY) > dragThreshold)) {
this._viewportHasMoved = true;

this._viewportDragPos = {'x': pos.x, 'y': pos.y};
this._display.viewportChangePos(deltaX, deltaY);
}
} else {
this._fakeMouseMove(ev, pos.x, pos.y);
}
break;
case 'twodrag':
// Always scroll in the same position.
// We don't know if the mouse was moved so we need to move it
// every update.
this._fakeMouseMove(ev, pos.x, pos.y);
while ((ev.detail.magnitudeY - this._gestureLastMagnitudeY) > GESTURE_SCRLSENS) {
this._handleMouseButton(pos.x, pos.y, true, 0x8);
this._handleMouseButton(pos.x, pos.y, false, 0x8);
this._handleMouseButton(pos.x, pos.y, 0x8);
this._handleMouseButton(pos.x, pos.y, 0x0);
this._gestureLastMagnitudeY += GESTURE_SCRLSENS;
}
while ((ev.detail.magnitudeY - this._gestureLastMagnitudeY) < -GESTURE_SCRLSENS) {
this._handleMouseButton(pos.x, pos.y, true, 0x10);
this._handleMouseButton(pos.x, pos.y, false, 0x10);
this._handleMouseButton(pos.x, pos.y, 0x10);
this._handleMouseButton(pos.x, pos.y, 0x0);
this._gestureLastMagnitudeY -= GESTURE_SCRLSENS;
}
while ((ev.detail.magnitudeX - this._gestureLastMagnitudeX) > GESTURE_SCRLSENS) {
this._handleMouseButton(pos.x, pos.y, true, 0x20);
this._handleMouseButton(pos.x, pos.y, false, 0x20);
this._handleMouseButton(pos.x, pos.y, 0x20);
this._handleMouseButton(pos.x, pos.y, 0x0);
this._gestureLastMagnitudeX += GESTURE_SCRLSENS;
}
while ((ev.detail.magnitudeX - this._gestureLastMagnitudeX) < -GESTURE_SCRLSENS) {
this._handleMouseButton(pos.x, pos.y, true, 0x40);
this._handleMouseButton(pos.x, pos.y, false, 0x40);
this._handleMouseButton(pos.x, pos.y, 0x40);
this._handleMouseButton(pos.x, pos.y, 0x0);
this._gestureLastMagnitudeX -= GESTURE_SCRLSENS;
}
break;
Expand All @@ -1344,13 +1397,13 @@ export default class RFB extends EventTargetMixin {
if (Math.abs(magnitude - this._gestureLastMagnitudeX) > GESTURE_ZOOMSENS) {
this._handleKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
while ((magnitude - this._gestureLastMagnitudeX) > GESTURE_ZOOMSENS) {
this._handleMouseButton(pos.x, pos.y, true, 0x8);
this._handleMouseButton(pos.x, pos.y, false, 0x8);
this._handleMouseButton(pos.x, pos.y, 0x8);
this._handleMouseButton(pos.x, pos.y, 0x0);
this._gestureLastMagnitudeX += GESTURE_ZOOMSENS;
}
while ((magnitude - this._gestureLastMagnitudeX) < -GESTURE_ZOOMSENS) {
this._handleMouseButton(pos.x, pos.y, true, 0x10);
this._handleMouseButton(pos.x, pos.y, false, 0x10);
this._handleMouseButton(pos.x, pos.y, 0x10);
this._handleMouseButton(pos.x, pos.y, 0x0);
this._gestureLastMagnitudeX -= GESTURE_ZOOMSENS;
}
}
Expand All @@ -1368,18 +1421,46 @@ export default class RFB extends EventTargetMixin {
case 'twodrag':
break;
case 'drag':
this._fakeMouseMove(ev, pos.x, pos.y);
this._handleMouseButton(pos.x, pos.y, false, 0x1);
if (this.dragViewport) {
this._viewportDragging = false;
} else {
this._fakeMouseMove(ev, pos.x, pos.y);
this._handleMouseButton(pos.x, pos.y, 0x0);
}
break;
case 'longpress':
this._fakeMouseMove(ev, pos.x, pos.y);
this._handleMouseButton(pos.x, pos.y, false, 0x4);
if (this._viewportHasMoved) {
// We don't want to send any events if we have moved
// our viewport
break;
}

if (this.dragViewport && !this._viewportHasMoved) {
this._fakeMouseMove(ev, pos.x, pos.y);
// If dragViewport is true, we need to wait to see
// if we have dragged outside the threshold before
// sending any events to the server.
this._handleMouseButton(pos.x, pos.y, 0x4);
this._handleMouseButton(pos.x, pos.y, 0x0);
this._viewportDragging = false;
} else {
this._fakeMouseMove(ev, pos.x, pos.y);
this._handleMouseButton(pos.x, pos.y, 0x0);
}
break;
}
break;
}
}

_flushMouseMoveTimer(x, y) {
if (this._mouseMoveTimer !== null) {
clearTimeout(this._mouseMoveTimer);
this._mouseMoveTimer = null;
this._sendMouse(x, y, this._mouseButtonMask);
}
}

// Message handlers

_negotiateProtocolVersion() {
Expand Down
Loading

0 comments on commit 9cdbd28

Please sign in to comment.