]> source.dussan.org Git - tigervnc.git/commitdiff
vncviewer: support for back/forward mouse buttons 1826/head
authorAdam Halim <adaha@cendio.se>
Fri, 4 Oct 2024 08:09:36 +0000 (10:09 +0200)
committerAdam Halim <adaha@cendio.se>
Tue, 22 Oct 2024 12:52:36 +0000 (14:52 +0200)
This commit implements the pseudo-encoding ExtendedMouseButtons which
makes it possible to use the back/forward mouse buttons.

This commit contains work originally done by
PixelSmith <manny33@frontbuffer.com>.

13 files changed:
common/rfb/CConnection.cxx
common/rfb/CMsgHandler.cxx
common/rfb/CMsgHandler.h
common/rfb/CMsgReader.cxx
common/rfb/CMsgWriter.cxx
common/rfb/CMsgWriter.h
common/rfb/ServerParams.cxx
common/rfb/ServerParams.h
tests/unit/emulatemb.cxx
vncviewer/EmulateMB.cxx
vncviewer/EmulateMB.h
vncviewer/Viewport.cxx
vncviewer/Viewport.h

index b4017dba8e658c832e4ff778f4fc51a1e31c4f87..a6763c055ae198acdc5cd4520de7c34475a767be 100644 (file)
@@ -835,6 +835,7 @@ void CConnection::updateEncodings()
   encodings.push_back(pseudoEncodingContinuousUpdates);
   encodings.push_back(pseudoEncodingFence);
   encodings.push_back(pseudoEncodingQEMUKeyEvent);
+  encodings.push_back(pseudoEncodingExtendedMouseButtons);
 
   if (Decoder::supported(preferredEncoding)) {
     encodings.push_back(preferredEncoding);
index 4489dbd4e4ed81458ae3b2d5ed1a7e3badeebfb2..0f3f6cd56be628184b530f193561c1a568d1067b 100644 (file)
@@ -75,6 +75,11 @@ void CMsgHandler::endOfContinuousUpdates()
   server.supportsContinuousUpdates = true;
 }
 
+void CMsgHandler::supportsExtendedMouseButtons()
+{
+  server.supportsExtendedMouseButtons = true;
+}
+
 void CMsgHandler::supportsQEMUKeyEvent()
 {
   server.supportsQEMUKeyEvent = true;
index 9e5f7de21c23e863551a73d365b77dbc9a22bc0a..b484b6952679a268819a5b3f1b6f14f0019e1221 100644 (file)
@@ -57,6 +57,7 @@ namespace rfb {
     virtual void fence(uint32_t flags, unsigned len, const uint8_t data[]);
     virtual void endOfContinuousUpdates();
     virtual void supportsQEMUKeyEvent();
+    virtual void supportsExtendedMouseButtons();
     virtual void serverInit(int width, int height,
                             const PixelFormat& pf,
                             const char* name) = 0;
index 8bcdbfd04e2fa06ffbe2c8ad4a9a9bc69f6f1ff3..d7cbc2fd75b8c4239eda1acd60663438687f0da6 100644 (file)
@@ -202,6 +202,10 @@ bool CMsgReader::readMsg()
       handler->supportsQEMUKeyEvent();
       ret = true;
       break;
+    case pseudoEncodingExtendedMouseButtons:
+      handler->supportsExtendedMouseButtons();
+      ret = true;
+      break;
     default:
       ret = readRect(dataRect, rectEncoding);
       break;
index 1bd8040f7ab478a1d4eef2155e939df0b27db760..8c379d660b579fca6f3d45d82ce1c60b09b4b703 100644 (file)
@@ -22,6 +22,7 @@
 #endif
 
 #include <stdio.h>
+#include <assert.h>
 
 #include <rdr/OutStream.h>
 #include <rdr/MemOutStream.h>
@@ -173,18 +174,47 @@ void CMsgWriter::writeKeyEvent(uint32_t keysym, uint32_t keycode, bool down)
 }
 
 
-void CMsgWriter::writePointerEvent(const Point& pos, uint8_t buttonMask)
+void CMsgWriter::writePointerEvent(const Point& pos, uint16_t buttonMask)
 {
   Point p(pos);
+  bool extendedMouseButtons;
+
   if (p.x < 0) p.x = 0;
   if (p.y < 0) p.y = 0;
   if (p.x >= server->width()) p.x = server->width() - 1;
   if (p.y >= server->height()) p.y = server->height() - 1;
 
+  /* The highest bit in buttonMask is never sent to the server */
+  assert(!(buttonMask & 0x8000));
+
+  /* Only send extended pointerEvent message when needed */
+  extendedMouseButtons = buttonMask & 0x7f80;
+
   startMsg(msgTypePointerEvent);
-  os->writeU8(buttonMask);
-  os->writeU16(p.x);
-  os->writeU16(p.y);
+  if (server->supportsExtendedMouseButtons && extendedMouseButtons) {
+    int higherBits;
+    int lowerBits;
+
+    higherBits = (buttonMask  >> 7) & 0xff;
+    assert(!(higherBits & 0xfc)); /* Bits 2-7 are reserved */
+
+    lowerBits = buttonMask & 0x7f;
+    lowerBits |= 0x80; /* Set marker bit to 1 */
+
+    os->writeU8(lowerBits);
+    os->writeU16(p.x);
+    os->writeU16(p.y);
+    os->writeU8(higherBits);
+  } else {
+    /* Marker bit must be set to 0, otherwise the server might confuse
+     * the marker bit with the highest bit in a normal PointerEvent
+     * message.
+    */
+    buttonMask &= 0x7f;
+    os->writeU8(buttonMask);
+    os->writeU16(p.x);
+    os->writeU16(p.y);
+  }
   endMsg();
 }
 
index 61df567f1bc668daa15bcf9bdcf77c9a32fc8941..9cb4adec6249bc1eec52f501950dd0c4770bfbf0 100644 (file)
@@ -54,7 +54,7 @@ namespace rfb {
     void writeFence(uint32_t flags, unsigned len, const uint8_t data[]);
 
     void writeKeyEvent(uint32_t keysym, uint32_t keycode, bool down);
-    void writePointerEvent(const Point& pos, uint8_t buttonMask);
+    void writePointerEvent(const Point& pos, uint16_t buttonMask);
 
     void writeClientCutText(const char* str);
 
index 9f6f530764a5950bb11172d7ba4808c98faec86f..7c5960361af7fb66553a701aa500c1cd8a306bc6 100644 (file)
@@ -32,7 +32,7 @@ ServerParams::ServerParams()
   : majorVersion(0), minorVersion(0),
     supportsQEMUKeyEvent(false),
     supportsSetDesktopSize(false), supportsFence(false),
-    supportsContinuousUpdates(false),
+    supportsContinuousUpdates(false), supportsExtendedMouseButtons(false),
     width_(0), height_(0),
     ledState_(ledUnknown)
 {
index 791e3e7f33a93c0fd46a6330d7a9fe9a59524a34..d730b8913943c1f1693101d3737c9befe7d746d2 100644 (file)
@@ -79,6 +79,7 @@ namespace rfb {
     bool supportsSetDesktopSize;
     bool supportsFence;
     bool supportsContinuousUpdates;
+    bool supportsExtendedMouseButtons;
 
   private:
 
index ae022c066ce4a16ede2d376ac6bcfce7ca6e1f7d..6db8ea380a214fb63ec583881fbaf8f7bbb5bee5 100644 (file)
@@ -42,14 +42,14 @@ rfb::BoolParameter emulateMiddleButton("dummy_name", "dummy_desc", true);
 class TestClass : public EmulateMB
 {
 public:
-  void sendPointerEvent(const rfb::Point& pos, uint8_t buttonMask) override;
+  void sendPointerEvent(const rfb::Point& pos, uint16_t buttonMask) override;
 
-  struct PointerEventParams {rfb::Point pos; uint8_t mask; };
+  struct PointerEventParams {rfb::Point pos; uint16_t mask; };
 
   std::vector<PointerEventParams> results;
 };
 
-void TestClass::sendPointerEvent(const rfb::Point& pos, uint8_t buttonMask)
+void TestClass::sendPointerEvent(const rfb::Point& pos, uint16_t buttonMask)
 {
   PointerEventParams params;
   params.pos = pos;
index fef8b3d90ea9f3d5164f74bb0953bd9eb44fff56..ef19ace487f2e3c33c88b9e2d7781b873060a565 100644 (file)
@@ -199,7 +199,7 @@ EmulateMB::EmulateMB()
 {
 }
 
-void EmulateMB::filterPointerEvent(const rfb::Point& pos, uint8_t buttonMask)
+void EmulateMB::filterPointerEvent(const rfb::Point& pos, uint16_t buttonMask)
 {
   int btstate;
   int action1, action2;
@@ -280,7 +280,7 @@ void EmulateMB::filterPointerEvent(const rfb::Point& pos, uint8_t buttonMask)
 void EmulateMB::handleTimeout(rfb::Timer *t)
 {
   int action1, action2;
-  uint8_t buttonMask;
+  uint16_t buttonMask;
 
   if (&timer != t)
     return;
@@ -312,7 +312,7 @@ void EmulateMB::handleTimeout(rfb::Timer *t)
   state = stateTab[state][4][2];
 }
 
-void EmulateMB::sendAction(const rfb::Point& pos, uint8_t buttonMask, int action)
+void EmulateMB::sendAction(const rfb::Point& pos, uint16_t buttonMask, int action)
 {
   assert(action != 0);
 
@@ -325,7 +325,7 @@ void EmulateMB::sendAction(const rfb::Point& pos, uint8_t buttonMask, int action
   sendPointerEvent(pos, buttonMask);
 }
 
-int EmulateMB::createButtonMask(uint8_t buttonMask)
+int EmulateMB::createButtonMask(uint16_t buttonMask)
 {
   // Unset left and right buttons in the mask
   buttonMask &= ~0x5;
index 1afa4881d31848baee90d6f2fc03ac444a67e4db..127c34a4044dc365979893ccbbe282dc7af516d7 100644 (file)
@@ -26,22 +26,22 @@ class EmulateMB : public rfb::Timer::Callback {
 public:
   EmulateMB();
 
-  void filterPointerEvent(const rfb::Point& pos, uint8_t buttonMask);
+  void filterPointerEvent(const rfb::Point& pos, uint16_t buttonMask);
 
 protected:
-  virtual void sendPointerEvent(const rfb::Point& pos, uint8_t buttonMask)=0;
+  virtual void sendPointerEvent(const rfb::Point& pos, uint16_t buttonMask)=0;
 
   void handleTimeout(rfb::Timer *t) override;
 
 private:
-  void sendAction(const rfb::Point& pos, uint8_t buttonMask, int action);
+  void sendAction(const rfb::Point& pos, uint16_t buttonMask, int action);
 
-  int createButtonMask(uint8_t buttonMask);
+  int createButtonMask(uint16_t buttonMask);
 
 private:
   int state;
-  uint8_t emulatedButtonMask;
-  uint8_t lastButtonMask;
+  uint16_t emulatedButtonMask;
+  uint16_t lastButtonMask;
   rfb::Point lastPos, origPos;
   rfb::Timer timer;
 };
index 9d71a859c3356e4209da053b84d2b203f27c572b..6cda65e9e1f21df6c3495f7adb37612fff635786 100644 (file)
@@ -606,6 +606,20 @@ int Viewport::handle(int event)
     if (Fl::event_button3())
       buttonMask |= 1 << 2;
 
+  // The back/forward buttons are not supported by FTLK 1.3 and require
+  // a patch which adds these buttons to the FLTK API. These buttons
+  // will be part of the upcoming 1.4 API:
+  //   * https://github.com/fltk/fltk/pull/1081
+  //
+  // A backport for branch-1.3 is available here:
+  //   * https://github.com/fltk/fltk/pull/1083
+#if defined(FL_BUTTON4) && defined(FL_BUTTON5)
+    if (Fl::event_button4())
+      buttonMask |= 1 << 7;
+    if (Fl::event_button5())
+      buttonMask |= 1 << 8;
+#endif
+
     if (event == FL_MOUSEWHEEL) {
       wheelMask = 0;
       if (Fl::event_dy() < 0)
@@ -660,7 +674,7 @@ int Viewport::handle(int event)
   return Fl_Widget::handle(event);
 }
 
-void Viewport::sendPointerEvent(const rfb::Point& pos, uint8_t buttonMask)
+void Viewport::sendPointerEvent(const rfb::Point& pos, uint16_t buttonMask)
 {
   if (viewOnly)
       return;
@@ -790,7 +804,7 @@ void Viewport::flushPendingClipboard()
 }
 
 
-void Viewport::handlePointerEvent(const rfb::Point& pos, uint8_t buttonMask)
+void Viewport::handlePointerEvent(const rfb::Point& pos, uint16_t buttonMask)
 {
   filterPointerEvent(pos, buttonMask);
 }
@@ -937,6 +951,8 @@ int Viewport::handleSystemEvent(void *event, void *data)
       (msg->message == WM_RBUTTONUP) ||
       (msg->message == WM_MBUTTONDOWN) ||
       (msg->message == WM_MBUTTONUP) ||
+      (msg->message == WM_XBUTTONDOWN) ||
+      (msg->message == WM_XBUTTONUP) ||
       (msg->message == WM_MOUSEWHEEL) ||
       (msg->message == WM_MOUSEHWHEEL)) {
     // We can't get a mouse event in the middle of an AltGr sequence, so
index 5f4c1ca7f169fc78da3274bcb242e61b1be2cfe7..c5222a883d18e9fdd13f4655c74d02870340ec58 100644 (file)
@@ -70,7 +70,7 @@ public:
   int handle(int event) override;
 
 protected:
-  void sendPointerEvent(const rfb::Point& pos, uint8_t buttonMask) override;
+  void sendPointerEvent(const rfb::Point& pos, uint16_t buttonMask) override;
 
 private:
   bool hasFocus();
@@ -81,7 +81,7 @@ private:
 
   void flushPendingClipboard();
 
-  void handlePointerEvent(const rfb::Point& pos, uint8_t buttonMask);
+  void handlePointerEvent(const rfb::Point& pos, uint16_t buttonMask);
   static void handlePointerTimeout(void *data);
 
   void resetKeyboard();
@@ -111,7 +111,7 @@ private:
   PlatformPixelBuffer* frameBuffer;
 
   rfb::Point lastPointerPos;
-  uint8_t lastButtonMask;
+  uint16_t lastButtonMask;
 
   typedef std::map<int, uint32_t> DownMap;
   DownMap downKeySym;