]> source.dussan.org Git - tigervnc.git/commitdiff
Add server support for forward/back mouse buttons
authorAdam Halim <adaha@cendio.se>
Tue, 24 Sep 2024 13:55:21 +0000 (15:55 +0200)
committerAdam Halim <adaha@cendio.se>
Tue, 22 Oct 2024 12:52:31 +0000 (14:52 +0200)
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 <manny33@frontbuffer.com>.

24 files changed:
common/rfb/ClientParams.cxx
common/rfb/ClientParams.h
common/rfb/SConnection.cxx
common/rfb/SConnection.h
common/rfb/SDesktop.h
common/rfb/SMsgHandler.cxx
common/rfb/SMsgHandler.h
common/rfb/SMsgReader.cxx
common/rfb/SMsgWriter.cxx
common/rfb/SMsgWriter.h
common/rfb/VNCSConnectionST.cxx
common/rfb/VNCSConnectionST.h
common/rfb/VNCServerST.cxx
common/rfb/VNCServerST.h
common/rfb/encodings.h
unix/x0vncserver/XDesktop.cxx
unix/x0vncserver/XDesktop.h
unix/xserver/hw/vnc/XserverDesktop.cc
unix/xserver/hw/vnc/XserverDesktop.h
unix/xserver/hw/vnc/vncInput.c
win/rfb_win32/SDisplay.cxx
win/rfb_win32/SDisplay.h
win/rfb_win32/SInput.cxx
win/rfb_win32/SInput.h

index bc20c3d7261a965eb7a1e4521349aec0732cd0fe..5ea104cf1a43044b1c6ab1b4912316081b9973d8 100644 (file)
@@ -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
index ea86ea785d0bd449556726a4fb700919ac0b2e4d..f715c47f164dcca2e1f79d532b23c7fa631d3fc0 100644 (file)
@@ -101,6 +101,7 @@ namespace rfb {
     bool supportsLEDState() const;
     bool supportsFence() const;
     bool supportsContinuousUpdates() const;
+    bool supportsExtendedMouseButtons() const;
 
     int compressLevel;
     int qualityLevel;
index 905f88a43ab9a842cc7566607d2e1ebdedf7a1f6..9d481017671020a6a89161ea84d070c24c21e77b 100644 (file)
@@ -433,6 +433,11 @@ void SConnection::supportsQEMUKeyEvent()
   writer()->writeQEMUKeyEvent();
 }
 
+void SConnection::supportsExtendedMouseButtons()
+{
+  writer()->writeExtendedMouseButtonsSupport();
+}
+
 void SConnection::versionReceived()
 {
 }
index 0a11f67b6c8efa293e6c892339d3b74b43150e45..a839f663fb417365b72ec1ba0ace44a5406971e6 100644 (file)
@@ -98,6 +98,8 @@ namespace rfb {
 
     void supportsQEMUKeyEvent() override;
 
+    virtual void supportsExtendedMouseButtons() override;
+
 
     // Methods to be overridden in a derived class
 
index 1d3c325fd3f6d1e741528a58bf552a6accdd6da7..c97e788a3bf93799ba26ace7a06e6ecf611692c0 100644 (file)
@@ -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
index 03917926e1bf2a2e07662acf0a08a8bdc0ebc487..1dce634dd5c581cc93c3c5031cfa01352d302343 100644 (file)
@@ -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
index cff8b1bdeb163a83c8d1b001ddf7332492e226ca..c5d13d7827b2341bac852640243f3e25b0425bfd 100644 (file)
@@ -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;
   };
 }
index 9ddea53df6bf83f213feaf1cf89ac102f1277e1a..0aa83e3a97f6139310ff59cbbaf72774aa4a1ffd 100644 (file)
@@ -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;
 }
index 0c03b51dc18751530f6c471363752d69d52c92a3..d1218c1157d2b46f88f8e4b4e220429857895f19 100644 (file)
@@ -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);
+}
index c46551e9a819af93cb306d4ce99c8c74637fb728..7bc0ed6a50014bdc2d6b2f6a2f458c84df5888c9 100644 (file)
@@ -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;
index 88deff8cc8e96ef36576ecbd78ea46fb8a01e0ee..7a796dc265c382d7b3b99f4c9d591edd94c88043 100644 (file)
@@ -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));
index d857ef323a85d07565da5ec85b1618ed2d6b157f..17de9d01e8d5b7e50006e7d4aac1a197d949a6d3 100644 (file)
@@ -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,
index 114ff347752548cad9cf97d2ed484055c8f48dec..977fa937988f89e2c45ad3ff2ae56fa45143e5c6 100644 (file)
@@ -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)
index 6cc75a68fb0ea15898f2a1cede8d8c025b151cdc..dc4f9aad677b91406c96c56498a21249c2dced5a 100644 (file)
@@ -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);
index e427572f6fb3315c8c0e33fe168fd046bd15e777..168684607c88489481f8f3d47bc22b2ae70f4ffb 100644 (file)
@@ -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;
index 29af059f16152f510578c9039cb53b0eefcdf510..bf7964523d4ffcb406039ac3d6a7b17c3b7096f0 100644 (file)
@@ -242,9 +242,9 @@ void XDesktop::init(VNCServer* vs)
 void XDesktop::start()
 {
   // Determine actual number of buttons of the X pointer device.
-  unsigned char btnMap[8];
-  int numButtons = XGetPointerMapping(dpy, btnMap, 8);
-  maxButtons = (numButtons > 8) ? 8 : numButtons;
+  unsigned char btnMap[9];
+  int numButtons = XGetPointerMapping(dpy, btnMap, 9);
+  maxButtons = (numButtons > 9) ? 9 : numButtons;
   vlog.info("Enabling %d button%s of X pointer device",
             maxButtons, (maxButtons != 1) ? "s" : "");
 
@@ -342,7 +342,7 @@ void XDesktop::queryConnection(network::Socket* sock,
   queryConnectDialog->map();
 }
 
-void XDesktop::pointerEvent(const Point& pos, uint8_t buttonMask) {
+void XDesktop::pointerEvent(const Point& pos, uint16_t buttonMask) {
 #ifdef HAVE_XTEST
   if (!haveXtest) return;
   XTestFakeMotionEvent(dpy, DefaultScreen(dpy),
index cf374fb9ba7c845a7e8e0f1b7bc4a96d5764b7be..711d6893d52e960de3f112688ecd6d45a0aad919 100644 (file)
@@ -60,7 +60,7 @@ public:
   bool isRunning();
   void queryConnection(network::Socket* sock,
                        const char* userName) override;
-  void pointerEvent(const rfb::Point& pos, uint8_t buttonMask) override;
+  void pointerEvent(const rfb::Point& pos, uint16_t buttonMask) override;
   void keyEvent(uint32_t keysym, uint32_t xtcode, bool down) override;
   unsigned int setScreenLayout(int fb_width, int fb_height,
                                const rfb::ScreenSet& layout) override;
@@ -79,7 +79,7 @@ protected:
   rfb::VNCServer* server;
   QueryConnectDialog* queryConnectDialog;
   network::Socket* queryConnectSock;
-  uint8_t oldButtonMask;
+  uint16_t oldButtonMask;
   bool haveXtest;
   bool haveDamage;
   int maxButtons;
index e3bc57d870b4c82da616e7f194e6a8772fcce76f..328b0886a16195ae5ccecb3d27879fd7660da84f 100644 (file)
@@ -463,7 +463,7 @@ void XserverDesktop::terminate()
   kill(getpid(), SIGTERM);
 }
 
-void XserverDesktop::pointerEvent(const Point& pos, uint8_t buttonMask)
+void XserverDesktop::pointerEvent(const Point& pos, uint16_t buttonMask)
 {
   vncPointerMove(pos.x + vncGetScreenX(screenIndex),
                  pos.y + vncGetScreenY(screenIndex));
index d287b72f2d35e88b012c7012c373f41346f35c60..8c543db75ed5819327906079b5b918345240ebf2 100644 (file)
@@ -95,7 +95,7 @@ public:
   void terminate() override;
   void queryConnection(network::Socket* sock,
                        const char* userName) override;
-  void pointerEvent(const rfb::Point& pos, uint8_t buttonMask) override;
+  void pointerEvent(const rfb::Point& pos, uint16_t buttonMask) override;
   void keyEvent(uint32_t keysym, uint32_t keycode, bool down) override;
   unsigned int setScreenLayout(int fb_width, int fb_height,
                                const rfb::ScreenSet& layout) override;
index 1de41430782836efafdd02415d3519997453581f..a705a85a6743b3d5dd9ff67dcb69ddd455744ee1 100644 (file)
@@ -50,7 +50,7 @@ extern const unsigned int code_map_qnum_to_xorgevdev_len;
 extern const unsigned short code_map_qnum_to_xorgkbd[];
 extern const unsigned int code_map_qnum_to_xorgkbd_len;
 
-#define BUTTONS 7
+#define BUTTONS 9
 
 DeviceIntPtr vncKeyboardDev;
 DeviceIntPtr vncPointerDev;
@@ -207,6 +207,23 @@ static int vncPointerProc(DeviceIntPtr pDevice, int onoff)
                btn_labels[5] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_LEFT);
                btn_labels[6] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_RIGHT);
 
+               /*
+                * The labels BTN_LABEL_PROP_BTN_SIDE and BTN_LABEL_PROP_BTN_EXTRA
+                * represent the side buttons on mice that are typically used to
+                * navigate back/forward respectively in web browsers.
+                *
+                * In X11, these labels are mapped to the BTN_SIDE and BTN_EXTRA
+                * input codes, which are mapped in the Linux HID driver. These
+                * are not to be confused with the BTN_FORWARD and BTN_BACK input
+                * codes, which some applications also use for back/forward
+                * navigation.
+                *
+                * It seems like most mice have their side buttons mapped to
+                * BTN_SIDE and BTN_EXTRA.
+                */
+               btn_labels[7] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_SIDE);
+               btn_labels[8] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_EXTRA);
+
                axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_X);
                axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y);
 
index 0ec5e231f473c97781551362ffae6b5b5cfb61fb..dee521e5d6db030514dd6ca73e55cb3b672d6aea 100644 (file)
@@ -312,7 +312,7 @@ void SDisplay::handleClipboardData(const char* data) {
 }
 
 
-void SDisplay::pointerEvent(const Point& pos, uint8_t buttonmask) {
+void SDisplay::pointerEvent(const Point& pos, uint16_t buttonmask) {
   if (pb->getRect().contains(pos)) {
     Point screenPos = pos.translate(screenRect.tl);
     // - Check that the SDesktop doesn't need restarting
index d4cf23e46f4a5ab65c473e78f8942c83a93a43a0..aa1a69e553330fa9668eb868d11663e16a8d1ed5 100644 (file)
@@ -80,7 +80,7 @@ namespace rfb {
       void handleClipboardRequest() override;
       void handleClipboardAnnounce(bool available) override;
       void handleClipboardData(const char* data) 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;
 
       // -=- Clipboard events
index 65d4a703c51845bec749098f36f38c3effc376cf..6441129d17d2c94fae09553406832ebfccd9c25e 100644 (file)
@@ -65,7 +65,7 @@ win32::SPointer::SPointer()
 }
 
 void
-win32::SPointer::pointerEvent(const Point& pos, uint8_t buttonmask)
+win32::SPointer::pointerEvent(const Point& pos, uint16_t buttonmask)
 {
   // - We are specifying absolute coordinates
   DWORD flags = MOUSEEVENTF_ABSOLUTE;
index 29e1df41c8aa4e99be5e2e4e34960613b803bb92..018bec55c43c81126cb9c14ae72680a94885d0c6 100644 (file)
@@ -44,10 +44,10 @@ namespace rfb {
       // - Create a pointer event at a the given coordinates, with the
       //   specified button state.  The event must be specified using
       //   Screen coordinates.
-      void pointerEvent(const Point& pos, uint8_t buttonmask);
+      void pointerEvent(const Point& pos, uint16_t buttonmask);
     protected:
       Point last_position;
-      uint8_t last_buttonmask;
+      uint16_t last_buttonmask;
     };
 
     // -=- Keyboard event handling