From 63327145959e26193682e2b691a6027e26c1ce16 Mon Sep 17 00:00:00 2001 From: Adam Halim Date: Tue, 24 Sep 2024 15:55:21 +0200 Subject: Add server support for forward/back mouse buttons This commit adds support for the pseudo-encoding ExtendedMouseButtons in Xvnc and x0vncserver, which makes it possible to use to use the back/forward mouse buttons. This commit contains work originally done by PixelSmith . --- common/rfb/ClientParams.cxx | 7 +++++++ common/rfb/ClientParams.h | 1 + common/rfb/SConnection.cxx | 5 +++++ common/rfb/SConnection.h | 2 ++ common/rfb/SDesktop.h | 2 +- common/rfb/SMsgHandler.cxx | 11 +++++++++-- common/rfb/SMsgHandler.h | 7 ++++++- common/rfb/SMsgReader.cxx | 27 ++++++++++++++++++++++++--- common/rfb/SMsgWriter.cxx | 33 ++++++++++++++++++++++++++++++++- common/rfb/SMsgWriter.h | 5 +++++ common/rfb/VNCSConnectionST.cxx | 2 +- common/rfb/VNCSConnectionST.h | 2 +- common/rfb/VNCServerST.cxx | 2 +- common/rfb/VNCServerST.h | 2 +- common/rfb/encodings.h | 1 + 15 files changed, 97 insertions(+), 12 deletions(-) (limited to 'common') diff --git a/common/rfb/ClientParams.cxx b/common/rfb/ClientParams.cxx index bc20c3d7..5ea104cf 100644 --- a/common/rfb/ClientParams.cxx +++ b/common/rfb/ClientParams.cxx @@ -228,3 +228,10 @@ bool ClientParams::supportsContinuousUpdates() const return true; return false; } + +bool ClientParams::supportsExtendedMouseButtons() const +{ + if (supportsEncoding(pseudoEncodingExtendedMouseButtons)) + return true; + return false; +} \ No newline at end of file diff --git a/common/rfb/ClientParams.h b/common/rfb/ClientParams.h index ea86ea78..f715c47f 100644 --- a/common/rfb/ClientParams.h +++ b/common/rfb/ClientParams.h @@ -101,6 +101,7 @@ namespace rfb { bool supportsLEDState() const; bool supportsFence() const; bool supportsContinuousUpdates() const; + bool supportsExtendedMouseButtons() const; int compressLevel; int qualityLevel; diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx index 905f88a4..9d481017 100644 --- a/common/rfb/SConnection.cxx +++ b/common/rfb/SConnection.cxx @@ -433,6 +433,11 @@ void SConnection::supportsQEMUKeyEvent() writer()->writeQEMUKeyEvent(); } +void SConnection::supportsExtendedMouseButtons() +{ + writer()->writeExtendedMouseButtonsSupport(); +} + void SConnection::versionReceived() { } diff --git a/common/rfb/SConnection.h b/common/rfb/SConnection.h index 0a11f67b..a839f663 100644 --- a/common/rfb/SConnection.h +++ b/common/rfb/SConnection.h @@ -98,6 +98,8 @@ namespace rfb { void supportsQEMUKeyEvent() override; + virtual void supportsExtendedMouseButtons() override; + // Methods to be overridden in a derived class diff --git a/common/rfb/SDesktop.h b/common/rfb/SDesktop.h index 1d3c325f..c97e788a 100644 --- a/common/rfb/SDesktop.h +++ b/common/rfb/SDesktop.h @@ -98,7 +98,7 @@ namespace rfb { // pointerEvent() is called whenever a client sends an event that // the pointer moved, or a button was pressed or released. virtual void pointerEvent(const Point& /*pos*/, - uint8_t /*buttonMask*/) {}; + uint16_t /*buttonMask*/) {}; // handleClipboardRequest() is called whenever a client requests // the server to send over its clipboard data. It will only be diff --git a/common/rfb/SMsgHandler.cxx b/common/rfb/SMsgHandler.cxx index 03917926..1dce634d 100644 --- a/common/rfb/SMsgHandler.cxx +++ b/common/rfb/SMsgHandler.cxx @@ -53,12 +53,13 @@ void SMsgHandler::setPixelFormat(const PixelFormat& pf) void SMsgHandler::setEncodings(int nEncodings, const int32_t* encodings) { bool firstFence, firstContinuousUpdates, firstLEDState, - firstQEMUKeyEvent; + firstQEMUKeyEvent, firstExtMouseButtonsEvent; firstFence = !client.supportsFence(); firstContinuousUpdates = !client.supportsContinuousUpdates(); firstLEDState = !client.supportsLEDState(); firstQEMUKeyEvent = !client.supportsEncoding(pseudoEncodingQEMUKeyEvent); + firstExtMouseButtonsEvent = !client.supportsEncoding(pseudoEncodingExtendedMouseButtons); client.setEncodings(nEncodings, encodings); @@ -72,6 +73,8 @@ void SMsgHandler::setEncodings(int nEncodings, const int32_t* encodings) supportsLEDState(); if (client.supportsEncoding(pseudoEncodingQEMUKeyEvent) && firstQEMUKeyEvent) supportsQEMUKeyEvent(); + if (client.supportsEncoding(pseudoEncodingExtendedMouseButtons) && firstExtMouseButtonsEvent) + supportsExtendedMouseButtons(); } void SMsgHandler::keyEvent(uint32_t /*keysym*/, uint32_t /*keycode*/, @@ -80,7 +83,7 @@ void SMsgHandler::keyEvent(uint32_t /*keysym*/, uint32_t /*keycode*/, } void SMsgHandler::pointerEvent(const Point& /*pos*/, - uint8_t /*buttonMask*/) + uint16_t /*buttonMask*/) { } @@ -167,3 +170,7 @@ void SMsgHandler::supportsLEDState() void SMsgHandler::supportsQEMUKeyEvent() { } + +void SMsgHandler::supportsExtendedMouseButtons() +{ +} \ No newline at end of file diff --git a/common/rfb/SMsgHandler.h b/common/rfb/SMsgHandler.h index cff8b1bd..c5d13d78 100644 --- a/common/rfb/SMsgHandler.h +++ b/common/rfb/SMsgHandler.h @@ -57,7 +57,7 @@ namespace rfb { virtual void keyEvent(uint32_t keysym, uint32_t keycode, bool down); virtual void pointerEvent(const Point& pos, - uint8_t buttonMask); + uint16_t buttonMask); virtual void clientCutText(const char* str); @@ -98,6 +98,11 @@ namespace rfb { // handler will send a pseudo-rect back, signalling server support. virtual void supportsQEMUKeyEvent(); + // supportsExtendedMouseButtons() is called the first time we detect that the + // client supports sending 16 bit mouse button state. This lets us pass more button + // states between server and client. + virtual void supportsExtendedMouseButtons(); + ClientParams client; }; } diff --git a/common/rfb/SMsgReader.cxx b/common/rfb/SMsgReader.cxx index 9ddea53d..0aa83e3a 100644 --- a/common/rfb/SMsgReader.cxx +++ b/common/rfb/SMsgReader.cxx @@ -272,11 +272,32 @@ bool SMsgReader::readKeyEvent() bool SMsgReader::readPointerEvent() { + int mask; + int x; + int y; + if (!is->hasData(1 + 2 + 2)) return false; - int mask = is->readU8(); - int x = is->readU16(); - int y = is->readU16(); + + is->setRestorePoint(); + + mask = is->readU8(); + x = is->readU16(); + y = is->readU16(); + + if (handler->client.supportsExtendedMouseButtons() && mask & 0x80 ) { + int highBits; + int lowBits; + + if (!is->hasDataOrRestore(1)) + return false; + + highBits = is->readU8(); + lowBits = mask & 0x7f; /* Clear marker bit */ + mask = (highBits << 7) | lowBits; + } + + is->clearRestorePoint(); handler->pointerEvent(Point(x, y), mask); return true; } diff --git a/common/rfb/SMsgWriter.cxx b/common/rfb/SMsgWriter.cxx index 0c03b51d..d1218c11 100644 --- a/common/rfb/SMsgWriter.cxx +++ b/common/rfb/SMsgWriter.cxx @@ -49,7 +49,7 @@ SMsgWriter::SMsgWriter(ClientParams* client_, rdr::OutStream* os_) nRectsInUpdate(0), nRectsInHeader(0), needSetDesktopName(false), needCursor(false), needCursorPos(false), needLEDState(false), - needQEMUKeyEvent(false) + needQEMUKeyEvent(false), needExtMouseButtonsEvent(false) { } @@ -303,6 +303,14 @@ void SMsgWriter::writeQEMUKeyEvent() needQEMUKeyEvent = true; } +void SMsgWriter::writeExtendedMouseButtonsSupport() +{ + if (!client->supportsEncoding(pseudoEncodingExtendedMouseButtons)) + throw Exception("Client does not support Extended Mouse Buttons"); + + needExtMouseButtonsEvent = true; +} + bool SMsgWriter::needFakeUpdate() { if (needSetDesktopName) @@ -315,6 +323,8 @@ bool SMsgWriter::needFakeUpdate() return true; if (needQEMUKeyEvent) return true; + if (needExtMouseButtonsEvent) + return true; if (needNoDataUpdate()) return true; @@ -363,6 +373,8 @@ void SMsgWriter::writeFramebufferUpdateStart(int nRects) nRects++; if (needQEMUKeyEvent) nRects++; + if (needExtMouseButtonsEvent) + nRects++; } os->writeU16(nRects); @@ -502,6 +514,11 @@ void SMsgWriter::writePseudoRects() writeQEMUKeyEventRect(); needQEMUKeyEvent = false; } + + if (needExtMouseButtonsEvent) { + writeExtendedMouseButtonsRect(); + needExtMouseButtonsEvent = false; + } } void SMsgWriter::writeNoDataRects() @@ -734,3 +751,17 @@ void SMsgWriter::writeQEMUKeyEventRect() os->writeU16(0); os->writeU32(pseudoEncodingQEMUKeyEvent); } + +void SMsgWriter::writeExtendedMouseButtonsRect() +{ + if (!client->supportsEncoding(pseudoEncodingExtendedMouseButtons)) + throw Exception("Client does not support extended mouse button events"); + if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) + throw Exception("SMsgWriter::writeExtendedMouseButtonsRect: nRects out of sync"); + + os->writeS16(0); + os->writeS16(0); + os->writeU16(0); + os->writeU16(0); + os->writeU32(pseudoEncodingExtendedMouseButtons); +} diff --git a/common/rfb/SMsgWriter.h b/common/rfb/SMsgWriter.h index c46551e9..7bc0ed6a 100644 --- a/common/rfb/SMsgWriter.h +++ b/common/rfb/SMsgWriter.h @@ -93,6 +93,9 @@ namespace rfb { // And QEMU keyboard event handshake void writeQEMUKeyEvent(); + // let the client know we support extended mouse button support + void writeExtendedMouseButtonsSupport(); + // needFakeUpdate() returns true when an immediate update is needed in // order to flush out pseudo-rectangles to the client. bool needFakeUpdate(); @@ -148,6 +151,7 @@ namespace rfb { void writeSetVMwareCursorPositionRect(int hotspotX, int hotspotY); void writeLEDStateRect(uint8_t state); void writeQEMUKeyEventRect(); + void writeExtendedMouseButtonsRect(); ClientParams* client; rdr::OutStream* os; @@ -160,6 +164,7 @@ namespace rfb { bool needCursorPos; bool needLEDState; bool needQEMUKeyEvent; + bool needExtMouseButtonsEvent; typedef struct { uint16_t reason, result; diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index 88deff8c..7a796dc2 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -477,7 +477,7 @@ void VNCSConnectionST::setPixelFormat(const PixelFormat& pf) setCursor(); } -void VNCSConnectionST::pointerEvent(const Point& pos, uint8_t buttonMask) +void VNCSConnectionST::pointerEvent(const Point& pos, uint16_t buttonMask) { if (rfb::Server::idleTimeout) idleTimer.start(secsToMillis(rfb::Server::idleTimeout)); diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h index d857ef32..17de9d01 100644 --- a/common/rfb/VNCSConnectionST.h +++ b/common/rfb/VNCSConnectionST.h @@ -123,7 +123,7 @@ namespace rfb { void queryConnection(const char* userName) override; void clientInit(bool shared) override; void setPixelFormat(const PixelFormat& pf) override; - void pointerEvent(const Point& pos, uint8_t buttonMask) override; + void pointerEvent(const Point& pos, uint16_t buttonMask) override; void keyEvent(uint32_t keysym, uint32_t keycode, bool down) override; void framebufferUpdateRequest(const Rect& r, diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index 114ff347..977fa937 100644 --- a/common/rfb/VNCServerST.cxx +++ b/common/rfb/VNCServerST.cxx @@ -482,7 +482,7 @@ void VNCServerST::keyEvent(uint32_t keysym, uint32_t keycode, bool down) } void VNCServerST::pointerEvent(VNCSConnectionST* client, - const Point& pos, uint8_t buttonMask) + const Point& pos, uint16_t buttonMask) { time_t now = time(nullptr); if (rfb::Server::maxIdleTime) diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h index 6cc75a68..dc4f9aad 100644 --- a/common/rfb/VNCServerST.h +++ b/common/rfb/VNCServerST.h @@ -117,7 +117,7 @@ namespace rfb { // Event handlers void keyEvent(uint32_t keysym, uint32_t keycode, bool down); - void pointerEvent(VNCSConnectionST* client, const Point& pos, uint8_t buttonMask); + void pointerEvent(VNCSConnectionST* client, const Point& pos, uint16_t buttonMask); void handleClipboardRequest(VNCSConnectionST* client); void handleClipboardAnnounce(VNCSConnectionST* client, bool available); diff --git a/common/rfb/encodings.h b/common/rfb/encodings.h index e427572f..16868460 100644 --- a/common/rfb/encodings.h +++ b/common/rfb/encodings.h @@ -36,6 +36,7 @@ namespace rfb { const int pseudoEncodingXCursor = -240; const int pseudoEncodingCursor = -239; + const int pseudoEncodingExtendedMouseButtons = -316; const int pseudoEncodingDesktopSize = -223; const int pseudoEncodingLEDState = -261; const int pseudoEncodingExtendedDesktopSize = -308; -- cgit v1.2.3