diff --git a/common/rfb/CConnection.cxx b/common/rfb/CConnection.cxx index 9a9593e289..1c9490095c 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 deab50ae52..d5d07ca0b0 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 5b14806ad1..43d8df246a 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 f69e21deee..2c823d5624 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 6f075a2461..c0cc36416b 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 3894ef2dd5..202cef4e21 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 cf0c8572fe..f7ad7890e7 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 a6c65a221d..23f3fe24a3 100644 --- a/tests/perf/decperf.cxx +++ b/tests/perf/decperf.cxx @@ -66,6 +66,7 @@ class CConn : public rfb::CConnection { 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 9f30cab7ab..69121ffa4d 100644 --- a/tests/perf/encperf.cxx +++ b/tests/perf/encperf.cxx @@ -95,6 +95,7 @@ class CConn : public rfb::CConnection { 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 4894ddf78e..e1f5f70a27 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 ad3fb797bb..e662ec8726 100644 --- a/vncviewer/CConn.h +++ b/vncviewer/CConn.h @@ -63,6 +63,7 @@ class CConn : public rfb::CConnection 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 6dc85f4a9e..5401b191fa 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 ef3dbb08ec..67be6c6a88 100644 --- a/vncviewer/DesktopWindow.h +++ b/vncviewer/DesktopWindow.h @@ -66,6 +66,9 @@ class DesktopWindow : public Fl_Window { 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);