diff --git a/core/encodings.js b/core/encodings.js index bf25ac917..7afcb17fc 100644 --- a/core/encodings.js +++ b/core/encodings.js @@ -30,6 +30,7 @@ export const encodings = { pseudoEncodingXvp: -309, pseudoEncodingFence: -312, pseudoEncodingContinuousUpdates: -313, + pseudoEncodingExtendedMouseButtons: -316, pseudoEncodingCompressLevel9: -247, pseudoEncodingCompressLevel0: -256, pseudoEncodingVMwareCursor: 0x574d5664, diff --git a/core/rfb.js b/core/rfb.js index 9559e487d..9cf498627 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -152,6 +152,8 @@ export default class RFB extends EventTargetMixin { this._qemuExtKeyEventSupported = false; + this._extendedPointerEventSupported = false; + this._clipboardText = null; this._clipboardServerCapabilitiesActions = {}; this._clipboardServerCapabilitiesFormats = {}; @@ -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; + } else if (button > 4) { + // Unsupported mouse button + return; + } 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); @@ -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) { @@ -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); @@ -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(); @@ -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); @@ -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); diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 19894ba11..e9717b6d2 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -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)); }); });