Skip to content

Commit

Permalink
vncviewer: support for back/forward mouse buttons
Browse files Browse the repository at this point in the history
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 <[email protected]>.
  • Loading branch information
CendioHalim committed Oct 4, 2024
1 parent 86e1eb5 commit bfece3f
Show file tree
Hide file tree
Showing 18 changed files with 89 additions and 35 deletions.
1 change: 1 addition & 0 deletions common/rfb/CConnection.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions common/rfb/CMsgHandler.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ void CMsgHandler::endOfContinuousUpdates()
server.supportsContinuousUpdates = true;
}

void CMsgHandler::supportsExtendedMouseButtons()
{
server.supportsExtendedMouseButtons = true;
}

void CMsgHandler::supportsQEMUKeyEvent()
{
server.supportsQEMUKeyEvent = true;
Expand Down
1 change: 1 addition & 0 deletions common/rfb/CMsgHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions common/rfb/CMsgReader.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
34 changes: 30 additions & 4 deletions common/rfb/CMsgWriter.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -173,18 +173,44 @@ 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;

/* 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;
lowerBits = buttonMask & 0x7f;
lowerBits |= 0x80; /* Set marker bit to 1 */

higherBits &= 0x03; /* Clear reserved bits */

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();
}

Expand Down
2 changes: 1 addition & 1 deletion common/rfb/CMsgWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion common/rfb/ServerParams.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
1 change: 1 addition & 0 deletions common/rfb/ServerParams.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ namespace rfb {
bool supportsSetDesktopSize;
bool supportsFence;
bool supportsContinuousUpdates;
bool supportsExtendedMouseButtons;

private:

Expand Down
6 changes: 3 additions & 3 deletions tests/unit/emulatemb.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions vncviewer/DesktopWindow.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ DesktopWindow::DesktopWindow(int w, int h, const char *name,
#ifdef __APPLE__
// On OS X we can do the maximize thing properly before the
// window is showned. Other platforms handled further down...
if (maximize) {
if (false) {
int dummy;
Fl::screen_work_area(dummy, dummy, w, h, geom_x, geom_y);
}
Expand Down Expand Up @@ -208,7 +208,7 @@ DesktopWindow::DesktopWindow(int w, int h, const char *name,
// maximized property on Windows and X11 before showing the window.
// See STR #2083 and STR #2178
#ifndef __APPLE__
if (maximize) {
if (false) {
maximizeWindow();
}
#endif
Expand Down
8 changes: 4 additions & 4 deletions vncviewer/EmulateMB.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand All @@ -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;
Expand Down
12 changes: 6 additions & 6 deletions vncviewer/EmulateMB.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
30 changes: 23 additions & 7 deletions vncviewer/Viewport.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ enum { ID_DISCONNECT, ID_FULLSCREEN, ID_MINIMIZE, ID_RESIZE,
static const WORD SCAN_FAKE = 0xaa;
#endif


Viewport::Viewport(int w, int h, const rfb::PixelFormat& /*serverPF*/, CConn* cc_)
: Fl_Widget(0, 0, w, h), cc(cc_), frameBuffer(nullptr),
lastPointerPos(0, 0), lastButtonMask(0),
Expand Down Expand Up @@ -606,6 +607,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)
Expand All @@ -622,7 +637,6 @@ int Viewport::handle(int event)
handlePointerEvent(Point(Fl::event_x() - x(), Fl::event_y() - y()),
buttonMask | wheelMask);
}

handlePointerEvent(Point(Fl::event_x() - x(), Fl::event_y() - y()), buttonMask);
return 1;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -1234,20 +1250,20 @@ void Viewport::initContextMenu()

fltk_menu_add(contextMenu, p_("ContextMenu|", "&Full screen"),
0, nullptr, (void*)ID_FULLSCREEN,
FL_MENU_TOGGLE | (window()->fullscreen_active()?FL_MENU_VALUE:0));
2 | (window()->fullscreen_active()?4:0));
fltk_menu_add(contextMenu, p_("ContextMenu|", "Minimi&ze"),
0, nullptr, (void*)ID_MINIMIZE, 0);
fltk_menu_add(contextMenu, p_("ContextMenu|", "Resize &window to session"),
0, nullptr, (void*)ID_RESIZE,
(window()->fullscreen_active()?FL_MENU_INACTIVE:0) |
(window()->fullscreen_active()?1:0) |
FL_MENU_DIVIDER);

fltk_menu_add(contextMenu, p_("ContextMenu|", "&Ctrl"),
0, nullptr, (void*)ID_CTRL,
FL_MENU_TOGGLE | (menuCtrlKey?FL_MENU_VALUE:0));
2 | (menuCtrlKey?4:0));
fltk_menu_add(contextMenu, p_("ContextMenu|", "&Alt"),
0, nullptr, (void*)ID_ALT,
FL_MENU_TOGGLE | (menuAltKey?FL_MENU_VALUE:0));
2 | (menuAltKey?4:0));

if (menuKeySym) {
char sendMenuKey[64];
Expand Down
6 changes: 3 additions & 3 deletions vncviewer/Viewport.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class Viewport : public Fl_Widget, public EmulateMB {
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();
Expand All @@ -81,7 +81,7 @@ class Viewport : public Fl_Widget, public EmulateMB {

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();
Expand Down Expand Up @@ -111,7 +111,7 @@ class Viewport : public Fl_Widget, public EmulateMB {
PlatformPixelBuffer* frameBuffer;

rfb::Point lastPointerPos;
uint8_t lastButtonMask;
uint16_t lastButtonMask;

typedef std::map<int, uint32_t> DownMap;
DownMap downKeySym;
Expand Down
2 changes: 1 addition & 1 deletion win/rfb_win32/SDisplay.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion win/rfb_win32/SDisplay.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion win/rfb_win32/SInput.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion win/rfb_win32/SInput.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ 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;
Expand Down

0 comments on commit bfece3f

Please sign in to comment.