Skip to content

Commit

Permalink
Add support for forward and back mouse buttons
Browse files Browse the repository at this point in the history
This commit implements the extendedMouseButtons pseudo-encoding, which
makes it possible to use the forward and back mouse buttons.
  • Loading branch information
CendioHalim committed Nov 28, 2024
1 parent 52ddb20 commit 897d269
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 5 deletions.
1 change: 1 addition & 0 deletions core/encodings.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const encodings = {
pseudoEncodingXvp: -309,
pseudoEncodingFence: -312,
pseudoEncodingContinuousUpdates: -313,
pseudoEncodingExtendedMouseButtons: -316,
pseudoEncodingCompressLevel9: -247,
pseudoEncodingCompressLevel0: -256,
pseudoEncodingVMwareCursor: 0x574d5664,
Expand Down
64 changes: 60 additions & 4 deletions core/rfb.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ export default class RFB extends EventTargetMixin {

this._qemuExtKeyEventSupported = false;

this._extendedPointerEventSupported = false;

this._clipboardText = null;
this._clipboardServerCapabilitiesActions = {};
this._clipboardServerCapabilitiesFormats = {};
Expand Down Expand Up @@ -1060,15 +1062,27 @@ export default class RFB extends EventTargetMixin {
let pos = clientToElement(ev.clientX, ev.clientY,
this._canvas);

/* Map mouse back and forward mouse buttons (3 and 4) to 7 and 8
*
* NOTE: This only works on chromium-based browsers. There is
* no support for firefox/safari.
*/
let button = ev.button;
if (button == 3 || button == 4) {
button += 4

Check failure on line 1072 in core/rfb.js

View workflow job for this annotation

GitHub Actions / eslint

Missing semicolon
} else if (button > 4) {
// Unsupported mouse button
return;

Check failure on line 1075 in core/rfb.js

View workflow job for this annotation

GitHub Actions / eslint

Expected indentation of 12 spaces but found 11
}
switch (ev.type) {
case 'mousedown':
setCapture(this._canvas);
this._handleMouseButton(pos.x, pos.y,
true, 1 << ev.button);
true, 1 << button);
break;
case 'mouseup':
this._handleMouseButton(pos.x, pos.y,
false, 1 << ev.button);
false, 1 << button);
break;
case 'mousemove':
this._handleMouseMove(pos.x, pos.y);
Expand Down Expand Up @@ -1163,8 +1177,20 @@ export default class RFB extends EventTargetMixin {
if (this._rfbConnectionState !== 'connected') { return; }
if (this._viewOnly) { return; } // View only, skip mouse events

RFB.messages.pointerEvent(this._sock, this._display.absX(x),
this._display.absY(y), mask);
// Highest bit in mask is never sent to the server
if (mask & 0x8000) {
throw new Error("Illegal mouse button mask (mask: " + mask + ")");
}

let extendedMouseButtons = mask & 0x7f80;

if (this._extendedPointerEventSupported && extendedMouseButtons) {
RFB.messages.extendedPointerEvent(this._sock, this._display.absX(x),
this._display.absY(y), mask);
} else {
RFB.messages.pointerEvent(this._sock, this._display.absX(x),
this._display.absY(y), mask);
}
}

_handleWheel(ev) {
Expand Down Expand Up @@ -2146,6 +2172,7 @@ export default class RFB extends EventTargetMixin {
encs.push(encodings.pseudoEncodingContinuousUpdates);
encs.push(encodings.pseudoEncodingDesktopName);
encs.push(encodings.pseudoEncodingExtendedClipboard);
encs.push(encodings.pseudoEncodingExtendedMouseButtons);

if (this._fbDepth == 24) {
encs.push(encodings.pseudoEncodingVMwareCursor);
Expand Down Expand Up @@ -2575,6 +2602,10 @@ export default class RFB extends EventTargetMixin {
case encodings.pseudoEncodingExtendedDesktopSize:
return this._handleExtendedDesktopSize();

case encodings.pseudoEncodingExtendedMouseButtons:
this._extendedPointerEventSupported = true;
return true;

case encodings.pseudoEncodingQEMULedEvent:
return this._handleLedEvent();

Expand Down Expand Up @@ -2983,6 +3014,10 @@ RFB.messages = {
pointerEvent(sock, x, y, mask) {
sock.sQpush8(5); // msg-type

// Marker bit must be set to 0, otherwise the server might
// confuse the marker bit with the highest bit in a normal
// PointerEvent message.
mask = mask & 0x7f;
sock.sQpush8(mask);

sock.sQpush16(x);
Expand All @@ -2991,6 +3026,27 @@ RFB.messages = {
sock.flush();
},

extendedPointerEvent(sock, x, y, mask) {
sock.sQpush8(5); // msg-type

let higherBits = (mask >> 7) & 0xff;

// Bits 2-7 are reserved
if (higherBits & 0xfc) {
throw new Error("Invalid mouse button mask: " + mask);
}

let lowerBits = mask & 0x7f;
lowerBits |= 0x80; // Set marker bit to 1

sock.sQpush8(lowerBits);
sock.sQpush16(x);
sock.sQpush16(y);
sock.sQpush8(higherBits);

sock.flush();
},

// Used to build Notify and Request data.
_buildExtendedClipboardFlags(actions, formats) {
let data = new Uint8Array(4);
Expand Down
9 changes: 8 additions & 1 deletion tests/test.rfb.js
Original file line number Diff line number Diff line change
Expand Up @@ -4931,7 +4931,14 @@ describe('RFB messages', function () {
it('should send correct data for pointer events', function () {
RFB.messages.pointerEvent(sock, 12345, 54321, 0xab);
let expected =
[ 5, 0xab, 0x30, 0x39, 0xd4, 0x31];
[ 5, 0x2b, 0x30, 0x39, 0xd4, 0x31];
expect(sock).to.have.sent(new Uint8Array(expected));
});

it('should send correct data for extended pointer events', function () {
RFB.messages.extendedPointerEvent(sock, 12345, 54321, 0xab);
let expected =
[ 5, 0xab, 0x30, 0x39, 0xd4, 0x31, 0x01];
expect(sock).to.have.sent(new Uint8Array(expected));
});
});
Expand Down

0 comments on commit 897d269

Please sign in to comment.