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/SMsgWriter.cxx b/common/rfb/SMsgWriter.cxx index a29ed9d862..2f40c0a67b 100644 --- a/common/rfb/SMsgWriter.cxx +++ b/common/rfb/SMsgWriter.cxx @@ -42,7 +42,8 @@ SMsgWriter::SMsgWriter(ClientParams* client_, rdr::OutStream* os_) : client(client_), os(os_), nRectsInUpdate(0), nRectsInHeader(0), needSetDesktopName(false), needCursor(false), - needLEDState(false), needQEMUKeyEvent(false) + needCursorPos(false), needLEDState(false), + needQEMUKeyEvent(false) { } @@ -269,6 +270,14 @@ void SMsgWriter::writeCursor() needCursor = true; } +void SMsgWriter::writeCursorPos() +{ + if (!client->supportsEncoding(pseudoEncodingVMwareCursorPosition)) + throw Exception("Client does not support cursor position"); + + needCursorPos = true; +} + void SMsgWriter::writeLEDState() { if (!client->supportsEncoding(pseudoEncodingLEDState) && @@ -294,6 +303,8 @@ bool SMsgWriter::needFakeUpdate() return true; if (needCursor) return true; + if (needCursorPos) + return true; if (needLEDState) return true; if (needQEMUKeyEvent) @@ -340,6 +351,8 @@ void SMsgWriter::writeFramebufferUpdateStart(int nRects) nRects++; if (needCursor) nRects++; + if (needCursorPos) + nRects++; if (needLEDState) nRects++; if (needQEMUKeyEvent) @@ -455,6 +468,18 @@ void SMsgWriter::writePseudoRects() needCursor = false; } + if (needCursorPos) { + const Point& cursorPos = client->cursorPos(); + + if (client->supportsEncoding(pseudoEncodingVMwareCursorPosition)) { + writeSetVMwareCursorPositionRect(cursorPos.x, cursorPos.y); + } else { + throw Exception("Client does not support cursor position"); + } + + needCursorPos = false; + } + if (needSetDesktopName) { writeSetDesktopNameRect(client->name()); needSetDesktopName = false; @@ -650,6 +675,20 @@ void SMsgWriter::writeSetVMwareCursorRect(int width, int height, os->writeBytes(data, width*height*4); } +void SMsgWriter::writeSetVMwareCursorPositionRect(int hotspotX, int hotspotY) +{ + if (!client->supportsEncoding(pseudoEncodingVMwareCursorPosition)) + throw Exception("Client does not support cursor position"); + if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) + throw Exception("SMsgWriter::writeSetVMwareCursorRect: nRects out of sync"); + + os->writeS16(hotspotX); + os->writeS16(hotspotY); + os->writeU16(0); + os->writeU16(0); + os->writeU32(pseudoEncodingVMwareCursorPosition); +} + void SMsgWriter::writeLEDStateRect(rdr::U8 state) { if (!client->supportsEncoding(pseudoEncodingLEDState) && diff --git a/common/rfb/SMsgWriter.h b/common/rfb/SMsgWriter.h index 2cea44d1e2..49381bad04 100644 --- a/common/rfb/SMsgWriter.h +++ b/common/rfb/SMsgWriter.h @@ -83,6 +83,9 @@ namespace rfb { // immediately. void writeCursor(); + // Notifies the client that the cursor pointer was moved by the server. + void writeCursorPos(); + // Same for LED state message void writeLEDState(); @@ -141,6 +144,7 @@ namespace rfb { void writeSetVMwareCursorRect(int width, int height, int hotspotX, int hotspotY, const rdr::U8* data); + void writeSetVMwareCursorPositionRect(int hotspotX, int hotspotY); void writeLEDStateRect(rdr::U8 state); void writeQEMUKeyEventRect(); @@ -152,6 +156,7 @@ namespace rfb { bool needSetDesktopName; bool needCursor; + bool needCursorPos; bool needLEDState; bool needQEMUKeyEvent; diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index 668bae0e50..a966e66cb1 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -370,6 +370,15 @@ void VNCSConnectionST::renderedCursorChange() } } +// cursorPositionChange() is called whenever the cursor has changed position by +// the server. If the client supports being informed about these changes then +// it will arrange for the new cursor position to be sent to the client. + +void VNCSConnectionST::cursorPositionChange() +{ + setCursorPos(); +} + // needRenderedCursor() returns true if this client needs the server-side // rendered cursor. This may be because it does not support local cursor or // because the current cursor position has not been set by this client. @@ -1123,6 +1132,21 @@ void VNCSConnectionST::setCursor() writer()->writeCursor(); } +// setCursorPos() is called whenever the cursor has changed position by the +// server. If the client supports being informed about these changes then it +// will arrange for the new cursor position to be sent to the client. + +void VNCSConnectionST::setCursorPos() +{ + if (state() != RFBSTATE_NORMAL) + return; + + if (client.supportsCursorPosition()) { + client.setCursorPos(server->getCursorPos()); + writer()->writeCursorPos(); + } +} + void VNCSConnectionST::setDesktopName(const char *name) { client.setName(name); diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h index 6d95008d18..72b0c52908 100644 --- a/common/rfb/VNCSConnectionST.h +++ b/common/rfb/VNCSConnectionST.h @@ -93,6 +93,11 @@ namespace rfb { // cursor. void renderedCursorChange(); + // cursorPositionChange() is called whenever the cursor has changed position by + // the server. If the client supports being informed about these changes then + // it will arrange for the new cursor position to be sent to the client. + void cursorPositionChange(); + // needRenderedCursor() returns true if this client needs the server-side // rendered cursor. This may be because it does not support local cursor // or because the current cursor position has not been set by this client. @@ -155,6 +160,7 @@ namespace rfb { void screenLayoutChange(rdr::U16 reason); void setCursor(); + void setCursorPos(); void setDesktopName(const char *name); void setLEDState(unsigned int state); diff --git a/common/rfb/VNCServer.h b/common/rfb/VNCServer.h index 5d04da53eb..4535b56242 100644 --- a/common/rfb/VNCServer.h +++ b/common/rfb/VNCServer.h @@ -97,8 +97,10 @@ namespace rfb { virtual void setCursor(int width, int height, const Point& hotspot, const rdr::U8* cursorData) = 0; - // setCursorPos() tells the server the current position of the cursor. - virtual void setCursorPos(const Point& p) = 0; + // setCursorPos() tells the server the current position of the cursor, and + // whether the server initiated that change (e.g. through another X11 + // client calling XWarpPointer()). + virtual void setCursorPos(const Point& p, bool warped) = 0; // setName() tells the server what desktop title to supply to clients virtual void setName(const char* name) = 0; diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index b32cac594d..39cdde1fa7 100644 --- a/common/rfb/VNCServerST.cxx +++ b/common/rfb/VNCServerST.cxx @@ -429,14 +429,17 @@ void VNCServerST::setCursor(int width, int height, const Point& newHotspot, } } -void VNCServerST::setCursorPos(const Point& pos) +void VNCServerST::setCursorPos(const Point& pos, bool warped) { if (!cursorPos.equals(pos)) { cursorPos = pos; renderedCursorInvalid = true; std::list::iterator ci; - for (ci = clients.begin(); ci != clients.end(); ci++) + for (ci = clients.begin(); ci != clients.end(); ci++) { (*ci)->renderedCursorChange(); + if (warped) + (*ci)->cursorPositionChange(); + } } } diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h index fd20cc37b1..159e3a4bee 100644 --- a/common/rfb/VNCServerST.h +++ b/common/rfb/VNCServerST.h @@ -99,7 +99,7 @@ namespace rfb { virtual void add_copied(const Region &dest, const Point &delta); virtual void setCursor(int width, int height, const Point& hotspot, const rdr::U8* data); - virtual void setCursorPos(const Point& p); + virtual void setCursorPos(const Point& p, bool warped); virtual void setName(const char* name_); virtual void setLEDState(unsigned state); 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/unix/x0vncserver/XDesktop.cxx b/unix/x0vncserver/XDesktop.cxx index eb36467e2d..e8e74fa707 100644 --- a/unix/x0vncserver/XDesktop.cxx +++ b/unix/x0vncserver/XDesktop.cxx @@ -217,7 +217,7 @@ void XDesktop::poll() { &x, &y, &wx, &wy, &mask); x -= geometry->offsetLeft(); y -= geometry->offsetTop(); - server->setCursorPos(rfb::Point(x, y)); + server->setCursorPos(rfb::Point(x, y), false); } } diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index 6f707299db..7ebad353ff 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.cc +++ b/unix/xserver/hw/vnc/XserverDesktop.cc @@ -261,6 +261,15 @@ void XserverDesktop::setCursor(int width, int height, int hotX, int hotY, delete [] cursorData; } +void XserverDesktop::setCursorPos(int x, int y, bool warped) +{ + try { + server->setCursorPos(Point(x, y), warped); + } catch (rdr::Exception& e) { + vlog.error("XserverDesktop::setCursorPos: %s",e.str()); + } +} + void XserverDesktop::add_changed(const rfb::Region ®ion) { try { @@ -377,7 +386,7 @@ void XserverDesktop::blockHandler(int* timeout) if (oldCursorPos.x != cursorX || oldCursorPos.y != cursorY) { oldCursorPos.x = cursorX; oldCursorPos.y = cursorY; - server->setCursorPos(oldCursorPos); + server->setCursorPos(oldCursorPos, false); } // Trigger timers and check when the next will expire diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h index cc50f9e90c..383e0bbff4 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.h +++ b/unix/xserver/hw/vnc/XserverDesktop.h @@ -67,6 +67,7 @@ class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer, void setDesktopName(const char* name); void setCursor(int width, int height, int hotX, int hotY, const unsigned char *rgbaData); + void setCursorPos(int x, int y, bool warped); void add_changed(const rfb::Region ®ion); void add_copied(const rfb::Region &dest, const rfb::Point &delta); void handleSocketEvent(int fd, bool read, bool write); diff --git a/unix/xserver/hw/vnc/vncExtInit.cc b/unix/xserver/hw/vnc/vncExtInit.cc index a45c5bdef1..6c4612d1c7 100644 --- a/unix/xserver/hw/vnc/vncExtInit.cc +++ b/unix/xserver/hw/vnc/vncExtInit.cc @@ -400,6 +400,11 @@ void vncSetCursor(int width, int height, int hotX, int hotY, desktop[scr]->setCursor(width, height, hotX, hotY, rgbaData); } +void vncSetCursorPos(int scrIdx, int x, int y) +{ + desktop[scrIdx]->setCursorPos(x, y, true); +} + void vncPreScreenResize(int scrIdx) { // We need to prevent the RFB core from accessing the framebuffer diff --git a/unix/xserver/hw/vnc/vncExtInit.h b/unix/xserver/hw/vnc/vncExtInit.h index 23c0c6699f..36e52032e3 100644 --- a/unix/xserver/hw/vnc/vncExtInit.h +++ b/unix/xserver/hw/vnc/vncExtInit.h @@ -81,6 +81,7 @@ void vncAddCopied(int scrIdx, int nRects, void vncSetCursor(int width, int height, int hotX, int hotY, const unsigned char *rgbaData); +void vncSetCursorPos(int scrIdx, int x, int y); void vncPreScreenResize(int scrIdx); void vncPostScreenResize(int scrIdx, int success, int width, int height); diff --git a/unix/xserver/hw/vnc/vncHooks.c b/unix/xserver/hw/vnc/vncHooks.c index a8ab917b86..d206e34209 100644 --- a/unix/xserver/hw/vnc/vncHooks.c +++ b/unix/xserver/hw/vnc/vncHooks.c @@ -62,6 +62,9 @@ typedef struct _vncHooksScreenRec { CopyWindowProcPtr CopyWindow; ClearToBackgroundProcPtr ClearToBackground; DisplayCursorProcPtr DisplayCursor; +#if XORG >= 119 + CursorWarpedToProcPtr CursorWarpedTo; +#endif ScreenBlockHandlerProcPtr BlockHandler; #ifdef RENDER CompositeProcPtr Composite; @@ -113,6 +116,12 @@ static void vncHooksClearToBackground(WindowPtr pWin, int x, int y, int w, int h, Bool generateExposures); static Bool vncHooksDisplayCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr cursor); +#if XORG >= 119 +static void vncHooksCursorWarpedTo(DeviceIntPtr pDev, + ScreenPtr pScreen_, ClientPtr pClient, + WindowPtr pWindow, SpritePtr pSprite, + int x, int y); +#endif #if XORG <= 118 static void vncHooksBlockHandler(ScreenPtr pScreen, void * pTimeout, void * pReadmask); @@ -271,6 +280,9 @@ int vncHooksInit(int scrIdx) wrap(vncHooksScreen, pScreen, CopyWindow, vncHooksCopyWindow); wrap(vncHooksScreen, pScreen, ClearToBackground, vncHooksClearToBackground); wrap(vncHooksScreen, pScreen, DisplayCursor, vncHooksDisplayCursor); +#if XORG >= 119 + wrap(vncHooksScreen, pScreen, CursorWarpedTo, vncHooksCursorWarpedTo); +#endif wrap(vncHooksScreen, pScreen, BlockHandler, vncHooksBlockHandler); #ifdef RENDER ps = GetPictureScreenIfSet(pScreen); @@ -631,6 +643,20 @@ static Bool vncHooksDisplayCursor(DeviceIntPtr pDev, return ret; } +// CursorWarpedTo - notify that the cursor was warped + +#if XORG >= 119 +static void vncHooksCursorWarpedTo(DeviceIntPtr pDev, + ScreenPtr pScreen_, ClientPtr pClient, + WindowPtr pWindow, SpritePtr pSprite, + int x, int y) +{ + SCREEN_PROLOGUE(pScreen_, CursorWarpedTo); + vncSetCursorPos(pScreen->myNum, x, y); + SCREEN_EPILOGUE(CursorWarpedTo); +} +#endif + // BlockHandler - ignore any changes during the block handler - it's likely // these are just drawing the cursor. diff --git a/win/rfb_win32/SDisplay.cxx b/win/rfb_win32/SDisplay.cxx index 06eccd9a21..a9ee361579 100644 --- a/win/rfb_win32/SDisplay.cxx +++ b/win/rfb_win32/SDisplay.cxx @@ -417,7 +417,7 @@ SDisplay::processEvent(HANDLE event) { // Update the cursor position // NB: First translate from Screen coordinates to Desktop Point desktopPos = info.position.translate(screenRect.tl.negate()); - server->setCursorPos(desktopPos); + server->setCursorPos(desktopPos, false); old_cursor = info; }