From 51f10f7c83b0e4a6d7427849ad4cbb1cba6ea7c2 Mon Sep 17 00:00:00 2001 From: lhchavez Date: Mon, 8 Feb 2021 07:09:10 -0800 Subject: [PATCH] Support the VMware Cursor Position extension on vncviewer This change makes it possible for re-synchronizing the remote cursor on the vncviewer when in fullscreen mode. This is done by locally moving the cursor position to what the server thinks it should be. Now SDL games should work! --- common/rfb/CConnection.cxx | 7 +++++-- common/rfb/CConnection.h | 1 + common/rfb/CMsgHandler.h | 1 + common/rfb/CMsgReader.cxx | 4 ++++ common/rfb/ClientParams.cxx | 14 +++++++++++++- common/rfb/ClientParams.h | 5 +++++ common/rfb/encodings.h | 1 + tests/perf/decperf.cxx | 5 +++++ tests/perf/encperf.cxx | 5 +++++ vncviewer/CConn.cxx | 6 ++++++ vncviewer/CConn.h | 1 + vncviewer/DesktopWindow.cxx | 32 ++++++++++++++++++++++++++++++++ vncviewer/DesktopWindow.h | 3 +++ 13 files changed, 82 insertions(+), 3 deletions(-) diff --git a/common/rfb/CConnection.cxx b/common/rfb/CConnection.cxx index 9a9593e2..1c949009 100644 --- a/common/rfb/CConnection.cxx +++ b/common/rfb/CConnection.cxx @@ -43,8 +43,8 @@ static LogWriter vlog("CConnection"); CConnection::CConnection() : csecurity(0), - supportsLocalCursor(false), supportsDesktopResize(false), - supportsLEDState(false), + supportsLocalCursor(false), supportsCursorPosition(false), + supportsDesktopResize(false), supportsLEDState(false), is(0), os(0), reader_(0), writer_(0), shared(false), state_(RFBSTATE_UNINITIALISED), @@ -805,6 +805,9 @@ void CConnection::updateEncodings() encodings.push_back(pseudoEncodingCursor); encodings.push_back(pseudoEncodingXCursor); } + if (supportsCursorPosition) { + encodings.push_back(pseudoEncodingVMwareCursorPosition); + } if (supportsDesktopResize) { encodings.push_back(pseudoEncodingDesktopSize); encodings.push_back(pseudoEncodingExtendedDesktopSize); diff --git a/common/rfb/CConnection.h b/common/rfb/CConnection.h index deab50ae..d5d07ca0 100644 --- a/common/rfb/CConnection.h +++ b/common/rfb/CConnection.h @@ -239,6 +239,7 @@ namespace rfb { // Optional capabilities that a subclass is expected to set to true // if supported bool supportsLocalCursor; + bool supportsCursorPosition; bool supportsDesktopResize; bool supportsLEDState; diff --git a/common/rfb/CMsgHandler.h b/common/rfb/CMsgHandler.h index 5b14806a..43d8df24 100644 --- a/common/rfb/CMsgHandler.h +++ b/common/rfb/CMsgHandler.h @@ -52,6 +52,7 @@ namespace rfb { const ScreenSet& layout); virtual void setCursor(int width, int height, const Point& hotspot, const rdr::U8* data) = 0; + virtual void setCursorPos(const Point& pos) = 0; virtual void setPixelFormat(const PixelFormat& pf); virtual void setName(const char* name); virtual void fence(rdr::U32 flags, unsigned len, const char data[]); diff --git a/common/rfb/CMsgReader.cxx b/common/rfb/CMsgReader.cxx index f69e21de..2c823d56 100644 --- a/common/rfb/CMsgReader.cxx +++ b/common/rfb/CMsgReader.cxx @@ -165,6 +165,10 @@ bool CMsgReader::readMsg() case pseudoEncodingVMwareCursor: ret = readSetVMwareCursor(dataRect.width(), dataRect.height(), dataRect.tl); break; + case pseudoEncodingVMwareCursorPosition: + handler->setCursorPos(dataRect.tl); + ret = true; + break; case pseudoEncodingDesktopName: ret = readSetDesktopName(dataRect.tl.x, dataRect.tl.y, dataRect.width(), dataRect.height()); diff --git a/common/rfb/ClientParams.cxx b/common/rfb/ClientParams.cxx index 6f075a24..c0cc3641 100644 --- a/common/rfb/ClientParams.cxx +++ b/common/rfb/ClientParams.cxx @@ -30,7 +30,7 @@ ClientParams::ClientParams() compressLevel(2), qualityLevel(-1), fineQualityLevel(-1), subsampling(subsampleUndefined), width_(0), height_(0), name_(0), - ledState_(ledUnknown) + cursorPos_(0, 0), ledState_(ledUnknown) { setName(""); @@ -85,6 +85,11 @@ void ClientParams::setCursor(const Cursor& other) cursor_ = new Cursor(other); } +void ClientParams::setCursorPos(const Point& pos) +{ + cursorPos_ = pos; +} + bool ClientParams::supportsEncoding(rdr::S32 encoding) const { return encodings_.count(encoding) != 0; @@ -182,6 +187,13 @@ bool ClientParams::supportsLocalCursor() const return false; } +bool ClientParams::supportsCursorPosition() const +{ + if (supportsEncoding(pseudoEncodingVMwareCursorPosition)) + return true; + return false; +} + bool ClientParams::supportsDesktopSize() const { if (supportsEncoding(pseudoEncodingExtendedDesktopSize)) diff --git a/common/rfb/ClientParams.h b/common/rfb/ClientParams.h index 3894ef2d..202cef4e 100644 --- a/common/rfb/ClientParams.h +++ b/common/rfb/ClientParams.h @@ -77,6 +77,9 @@ namespace rfb { const Cursor& cursor() const { return *cursor_; } void setCursor(const Cursor& cursor); + const Point& cursorPos() const { return cursorPos_; } + void setCursorPos(const Point& pos); + bool supportsEncoding(rdr::S32 encoding) const; void setEncodings(int nEncodings, const rdr::S32* encodings); @@ -91,6 +94,7 @@ namespace rfb { // Wrappers to check for functionality rather than specific // encodings bool supportsLocalCursor() const; + bool supportsCursorPosition() const; bool supportsDesktopSize() const; bool supportsLEDState() const; bool supportsFence() const; @@ -110,6 +114,7 @@ namespace rfb { PixelFormat pf_; char* name_; Cursor* cursor_; + Point cursorPos_; std::set encodings_; unsigned int ledState_; rdr::U32 clipFlags; diff --git a/common/rfb/encodings.h b/common/rfb/encodings.h index cf0c8572..f7ad7890 100644 --- a/common/rfb/encodings.h +++ b/common/rfb/encodings.h @@ -61,6 +61,7 @@ namespace rfb { // VMware-specific const int pseudoEncodingVMwareCursor = 0x574d5664; + const int pseudoEncodingVMwareCursorPosition = 0x574d5666; const int pseudoEncodingVMwareLEDState = 0x574d5668; // UltraVNC-specific diff --git a/tests/perf/decperf.cxx b/tests/perf/decperf.cxx index a6c65a22..23f3fe24 100644 --- a/tests/perf/decperf.cxx +++ b/tests/perf/decperf.cxx @@ -66,6 +66,7 @@ public: virtual void initDone(); virtual void setPixelFormat(const rfb::PixelFormat& pf); virtual void setCursor(int, int, const rfb::Point&, const rdr::U8*); + virtual void setCursorPos(const rfb::Point&); virtual void framebufferUpdateStart(); virtual void framebufferUpdateEnd(); virtual void setColourMapEntries(int, int, rdr::U16*); @@ -144,6 +145,10 @@ void CConn::setCursor(int, int, const rfb::Point&, const rdr::U8*) { } +void CConn::setCursorPos(const rfb::Point&) +{ +} + void CConn::framebufferUpdateStart() { CConnection::framebufferUpdateStart(); diff --git a/tests/perf/encperf.cxx b/tests/perf/encperf.cxx index 9f30cab7..69121ffa 100644 --- a/tests/perf/encperf.cxx +++ b/tests/perf/encperf.cxx @@ -95,6 +95,7 @@ public: virtual void initDone() {}; virtual void resizeFramebuffer(); virtual void setCursor(int, int, const rfb::Point&, const rdr::U8*); + virtual void setCursorPos(const rfb::Point&); virtual void framebufferUpdateStart(); virtual void framebufferUpdateEnd(); virtual bool dataRect(const rfb::Rect&, int); @@ -216,6 +217,10 @@ void CConn::setCursor(int, int, const rfb::Point&, const rdr::U8*) { } +void CConn::setCursorPos(const rfb::Point&) +{ +} + void CConn::framebufferUpdateStart() { CConnection::framebufferUpdateStart(); diff --git a/vncviewer/CConn.cxx b/vncviewer/CConn.cxx index 4894ddf7..e1f5f70a 100644 --- a/vncviewer/CConn.cxx +++ b/vncviewer/CConn.cxx @@ -84,6 +84,7 @@ CConn::CConn(const char* vncServerName, network::Socket* socket=NULL) sock = socket; supportsLocalCursor = true; + supportsCursorPosition = true; supportsDesktopResize = true; supportsLEDState = false; @@ -430,6 +431,11 @@ void CConn::setCursor(int width, int height, const Point& hotspot, desktop->setCursor(width, height, hotspot, data); } +void CConn::setCursorPos(const Point& pos) +{ + desktop->setCursorPos(pos); +} + void CConn::fence(rdr::U32 flags, unsigned len, const char data[]) { CMsgHandler::fence(flags, len, data); diff --git a/vncviewer/CConn.h b/vncviewer/CConn.h index ad3fb797..e662ec87 100644 --- a/vncviewer/CConn.h +++ b/vncviewer/CConn.h @@ -63,6 +63,7 @@ public: void setCursor(int width, int height, const rfb::Point& hotspot, const rdr::U8* data); + void setCursorPos(const rfb::Point& pos); void fence(rdr::U32 flags, unsigned len, const char data[]); diff --git a/vncviewer/DesktopWindow.cxx b/vncviewer/DesktopWindow.cxx index 6dc85f4a..5401b191 100644 --- a/vncviewer/DesktopWindow.cxx +++ b/vncviewer/DesktopWindow.cxx @@ -51,6 +51,7 @@ #ifdef __APPLE__ #include "cocoa.h" +#include #endif #define EDGE_SCROLL_SIZE 32 @@ -186,6 +187,14 @@ DesktopWindow::DesktopWindow(int w, int h, const char *name, // Show hint about menu key Fl::add_timeout(0.5, menuOverlay, this); + + // By default we get a slight delay when we warp the pointer, something + // we don't want or we'll get jerky movement +#ifdef __APPLE__ + CGEventSourceRef event = CGEventSourceCreate(kCGEventSourceStateCombinedSessionState); + CGEventSourceSetLocalEventsSuppressionInterval(event, 0); + CFRelease(event); +#endif } @@ -322,6 +331,29 @@ void DesktopWindow::setCursor(int width, int height, } +void DesktopWindow::setCursorPos(const rfb::Point& pos) +{ + if (!mouseGrabbed) { + // Do nothing if we do not have the mouse captured. + return; + } +#if defined(WIN32) + SetCursorPos(pos.x + x_root() + viewport->x(), + pos.y + y_root() + viewport->y()); +#elif defined(__APPLE__) + CGPoint new_pos; + new_pos.x = pos.x + x_root() + viewport->x(); + new_pos.y = pos.y + y_root() + viewport->y(); + CGWarpMouseCursorPosition(new_pos); +#else // Assume this is Xlib + Window rootwindow = DefaultRootWindow(fl_display); + XWarpPointer(fl_display, rootwindow, rootwindow, 0, 0, 0, 0, + pos.x + x_root() + viewport->x(), + pos.y + y_root() + viewport->y()); +#endif +} + + void DesktopWindow::show() { Fl_Window::show(); diff --git a/vncviewer/DesktopWindow.h b/vncviewer/DesktopWindow.h index ef3dbb08..67be6c6a 100644 --- a/vncviewer/DesktopWindow.h +++ b/vncviewer/DesktopWindow.h @@ -66,6 +66,9 @@ public: void setCursor(int width, int height, const rfb::Point& hotspot, const rdr::U8* data); + // Server-provided cursor position + void setCursorPos(const rfb::Point& pos); + // Change client LED state void setLEDState(unsigned int state); -- 2.39.5