return true;
return false;
}
+
+bool ClientParams::supportsExtendedMouseButtons() const
+{
+ if (supportsEncoding(pseudoEncodingExtendedMouseButtons))
+ return true;
+ return false;
+}
\ No newline at end of file
bool supportsLEDState() const;
bool supportsFence() const;
bool supportsContinuousUpdates() const;
+ bool supportsExtendedMouseButtons() const;
int compressLevel;
int qualityLevel;
writer()->writeQEMUKeyEvent();
}
+void SConnection::supportsExtendedMouseButtons()
+{
+ writer()->writeExtendedMouseButtonsSupport();
+}
+
void SConnection::versionReceived()
{
}
void supportsQEMUKeyEvent() override;
+ virtual void supportsExtendedMouseButtons() override;
+
// Methods to be overridden in a derived class
// 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
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);
supportsLEDState();
if (client.supportsEncoding(pseudoEncodingQEMUKeyEvent) && firstQEMUKeyEvent)
supportsQEMUKeyEvent();
+ if (client.supportsEncoding(pseudoEncodingExtendedMouseButtons) && firstExtMouseButtonsEvent)
+ supportsExtendedMouseButtons();
}
void SMsgHandler::keyEvent(uint32_t /*keysym*/, uint32_t /*keycode*/,
}
void SMsgHandler::pointerEvent(const Point& /*pos*/,
- uint8_t /*buttonMask*/)
+ uint16_t /*buttonMask*/)
{
}
void SMsgHandler::supportsQEMUKeyEvent()
{
}
+
+void SMsgHandler::supportsExtendedMouseButtons()
+{
+}
\ No newline at end of file
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);
// 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;
};
}
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;
}
nRectsInUpdate(0), nRectsInHeader(0),
needSetDesktopName(false), needCursor(false),
needCursorPos(false), needLEDState(false),
- needQEMUKeyEvent(false)
+ needQEMUKeyEvent(false), needExtMouseButtonsEvent(false)
{
}
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)
return true;
if (needQEMUKeyEvent)
return true;
+ if (needExtMouseButtonsEvent)
+ return true;
if (needNoDataUpdate())
return true;
nRects++;
if (needQEMUKeyEvent)
nRects++;
+ if (needExtMouseButtonsEvent)
+ nRects++;
}
os->writeU16(nRects);
writeQEMUKeyEventRect();
needQEMUKeyEvent = false;
}
+
+ if (needExtMouseButtonsEvent) {
+ writeExtendedMouseButtonsRect();
+ needExtMouseButtonsEvent = false;
+ }
}
void SMsgWriter::writeNoDataRects()
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);
+}
// 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();
void writeSetVMwareCursorPositionRect(int hotspotX, int hotspotY);
void writeLEDStateRect(uint8_t state);
void writeQEMUKeyEventRect();
+ void writeExtendedMouseButtonsRect();
ClientParams* client;
rdr::OutStream* os;
bool needCursorPos;
bool needLEDState;
bool needQEMUKeyEvent;
+ bool needExtMouseButtonsEvent;
typedef struct {
uint16_t reason, result;
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));
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,
}
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)
// 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);
const int pseudoEncodingXCursor = -240;
const int pseudoEncodingCursor = -239;
+ const int pseudoEncodingExtendedMouseButtons = -316;
const int pseudoEncodingDesktopSize = -223;
const int pseudoEncodingLEDState = -261;
const int pseudoEncodingExtendedDesktopSize = -308;
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" : "");
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),
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;
rfb::VNCServer* server;
QueryConnectDialog* queryConnectDialog;
network::Socket* queryConnectSock;
- uint8_t oldButtonMask;
+ uint16_t oldButtonMask;
bool haveXtest;
bool haveDamage;
int maxButtons;
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));
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;
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;
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);
}
-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
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
}
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;
// - 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