Skip to content

Commit cb8629a

Browse files
committed
Add support for notifying clients about pointer movements
This change adds support for the VMware Mouse Position pseudo-encoding[1], which is used to notify VNC clients when X11 clients call `XWarpPointer()`[2]. This function is called by SDL (and other similar libraries) when they detect that the server does not support native relative motion, like some RFB clients. With this, RFB clients can choose to adjust the local cursor position under certain circumstances to match what the server has set. For instance, if pointer lock has been enabled on the client's machine and the cursor is not being drawn locally, the local position of the cursor is irrelevant, so the RFB client can use what the server sends as the canonical absolute position of the cursor. This ultimately enables the possibility of games (especially FPS games) to behave how users expect (if the clients implement the corresponding change). Part of: TigerVNC#619 1: https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#vmware-cursor-position-pseudo-encoding 2: https://tronche.com/gui/x/xlib/input/XWarpPointer.html 3: https://hg.libsdl.org/SDL/file/28e3b60e2131/src/events/SDL_mouse.c#l804
1 parent 0c8b68f commit cb8629a

17 files changed

+149
-10
lines changed

common/rfb/ClientParams.cxx

+13-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ ClientParams::ClientParams()
3030
compressLevel(2), qualityLevel(-1), fineQualityLevel(-1),
3131
subsampling(subsampleUndefined),
3232
width_(0), height_(0), name_(0),
33-
ledState_(ledUnknown)
33+
cursorPos_(0, 0), ledState_(ledUnknown)
3434
{
3535
setName("");
3636

@@ -85,6 +85,11 @@ void ClientParams::setCursor(const Cursor& other)
8585
cursor_ = new Cursor(other);
8686
}
8787

88+
void ClientParams::setCursorPos(const Point& pos)
89+
{
90+
cursorPos_ = pos;
91+
}
92+
8893
bool ClientParams::supportsEncoding(rdr::S32 encoding) const
8994
{
9095
return encodings_.count(encoding) != 0;
@@ -182,6 +187,13 @@ bool ClientParams::supportsLocalCursor() const
182187
return false;
183188
}
184189

190+
bool ClientParams::supportsCursorPosition() const
191+
{
192+
if (supportsEncoding(pseudoEncodingVMwareCursorPosition))
193+
return true;
194+
return false;
195+
}
196+
185197
bool ClientParams::supportsDesktopSize() const
186198
{
187199
if (supportsEncoding(pseudoEncodingExtendedDesktopSize))

common/rfb/ClientParams.h

+5
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ namespace rfb {
7777
const Cursor& cursor() const { return *cursor_; }
7878
void setCursor(const Cursor& cursor);
7979

80+
const Point& cursorPos() const { return cursorPos_; }
81+
void setCursorPos(const Point& pos);
82+
8083
bool supportsEncoding(rdr::S32 encoding) const;
8184

8285
void setEncodings(int nEncodings, const rdr::S32* encodings);
@@ -91,6 +94,7 @@ namespace rfb {
9194
// Wrappers to check for functionality rather than specific
9295
// encodings
9396
bool supportsLocalCursor() const;
97+
bool supportsCursorPosition() const;
9498
bool supportsDesktopSize() const;
9599
bool supportsLEDState() const;
96100
bool supportsFence() const;
@@ -110,6 +114,7 @@ namespace rfb {
110114
PixelFormat pf_;
111115
char* name_;
112116
Cursor* cursor_;
117+
Point cursorPos_;
113118
std::set<rdr::S32> encodings_;
114119
unsigned int ledState_;
115120
rdr::U32 clipFlags;

common/rfb/SMsgWriter.cxx

+40-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ SMsgWriter::SMsgWriter(ClientParams* client_, rdr::OutStream* os_)
4242
: client(client_), os(os_),
4343
nRectsInUpdate(0), nRectsInHeader(0),
4444
needSetDesktopName(false), needCursor(false),
45-
needLEDState(false), needQEMUKeyEvent(false)
45+
needCursorPos(false), needLEDState(false),
46+
needQEMUKeyEvent(false)
4647
{
4748
}
4849

@@ -269,6 +270,14 @@ void SMsgWriter::writeCursor()
269270
needCursor = true;
270271
}
271272

273+
void SMsgWriter::writeCursorPos()
274+
{
275+
if (!client->supportsEncoding(pseudoEncodingVMwareCursorPosition))
276+
throw Exception("Client does not support cursor position");
277+
278+
needCursorPos = true;
279+
}
280+
272281
void SMsgWriter::writeLEDState()
273282
{
274283
if (!client->supportsEncoding(pseudoEncodingLEDState) &&
@@ -294,6 +303,8 @@ bool SMsgWriter::needFakeUpdate()
294303
return true;
295304
if (needCursor)
296305
return true;
306+
if (needCursorPos)
307+
return true;
297308
if (needLEDState)
298309
return true;
299310
if (needQEMUKeyEvent)
@@ -340,6 +351,8 @@ void SMsgWriter::writeFramebufferUpdateStart(int nRects)
340351
nRects++;
341352
if (needCursor)
342353
nRects++;
354+
if (needCursorPos)
355+
nRects++;
343356
if (needLEDState)
344357
nRects++;
345358
if (needQEMUKeyEvent)
@@ -455,6 +468,18 @@ void SMsgWriter::writePseudoRects()
455468
needCursor = false;
456469
}
457470

471+
if (needCursorPos) {
472+
const Point& cursorPos = client->cursorPos();
473+
474+
if (client->supportsEncoding(pseudoEncodingVMwareCursorPosition)) {
475+
writeSetVMwareCursorPositionRect(cursorPos.x, cursorPos.y);
476+
} else {
477+
throw Exception("Client does not support cursor position");
478+
}
479+
480+
needCursorPos = false;
481+
}
482+
458483
if (needSetDesktopName) {
459484
writeSetDesktopNameRect(client->name());
460485
needSetDesktopName = false;
@@ -650,6 +675,20 @@ void SMsgWriter::writeSetVMwareCursorRect(int width, int height,
650675
os->writeBytes(data, width*height*4);
651676
}
652677

678+
void SMsgWriter::writeSetVMwareCursorPositionRect(int hotspotX, int hotspotY)
679+
{
680+
if (!client->supportsEncoding(pseudoEncodingVMwareCursorPosition))
681+
throw Exception("Client does not support cursor position");
682+
if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
683+
throw Exception("SMsgWriter::writeSetVMwareCursorRect: nRects out of sync");
684+
685+
os->writeS16(hotspotX);
686+
os->writeS16(hotspotY);
687+
os->writeU16(0);
688+
os->writeU16(0);
689+
os->writeU32(pseudoEncodingVMwareCursorPosition);
690+
}
691+
653692
void SMsgWriter::writeLEDStateRect(rdr::U8 state)
654693
{
655694
if (!client->supportsEncoding(pseudoEncodingLEDState) &&

common/rfb/SMsgWriter.h

+5
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ namespace rfb {
8383
// immediately.
8484
void writeCursor();
8585

86+
// Notifies the client that the cursor pointer was moved by the server.
87+
void writeCursorPos();
88+
8689
// Same for LED state message
8790
void writeLEDState();
8891

@@ -141,6 +144,7 @@ namespace rfb {
141144
void writeSetVMwareCursorRect(int width, int height,
142145
int hotspotX, int hotspotY,
143146
const rdr::U8* data);
147+
void writeSetVMwareCursorPositionRect(int hotspotX, int hotspotY);
144148
void writeLEDStateRect(rdr::U8 state);
145149
void writeQEMUKeyEventRect();
146150

@@ -152,6 +156,7 @@ namespace rfb {
152156

153157
bool needSetDesktopName;
154158
bool needCursor;
159+
bool needCursorPos;
155160
bool needLEDState;
156161
bool needQEMUKeyEvent;
157162

common/rfb/VNCSConnectionST.cxx

+24
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,15 @@ void VNCSConnectionST::renderedCursorChange()
370370
}
371371
}
372372

373+
// cursorPositionChange() is called whenever the cursor has changed position by
374+
// the server. If the client supports being informed about these changes then
375+
// it will arrange for the new cursor position to be sent to the client.
376+
377+
void VNCSConnectionST::cursorPositionChange()
378+
{
379+
setCursorPos();
380+
}
381+
373382
// needRenderedCursor() returns true if this client needs the server-side
374383
// rendered cursor. This may be because it does not support local cursor or
375384
// because the current cursor position has not been set by this client.
@@ -1123,6 +1132,21 @@ void VNCSConnectionST::setCursor()
11231132
writer()->writeCursor();
11241133
}
11251134

1135+
// setCursorPos() is called whenever the cursor has changed position by the
1136+
// server. If the client supports being informed about these changes then it
1137+
// will arrange for the new cursor position to be sent to the client.
1138+
1139+
void VNCSConnectionST::setCursorPos()
1140+
{
1141+
if (state() != RFBSTATE_NORMAL)
1142+
return;
1143+
1144+
if (client.supportsCursorPosition()) {
1145+
client.setCursorPos(server->getCursorPos());
1146+
writer()->writeCursorPos();
1147+
}
1148+
}
1149+
11261150
void VNCSConnectionST::setDesktopName(const char *name)
11271151
{
11281152
client.setName(name);

common/rfb/VNCSConnectionST.h

+6
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ namespace rfb {
9393
// cursor.
9494
void renderedCursorChange();
9595

96+
// cursorPositionChange() is called whenever the cursor has changed position by
97+
// the server. If the client supports being informed about these changes then
98+
// it will arrange for the new cursor position to be sent to the client.
99+
void cursorPositionChange();
100+
96101
// needRenderedCursor() returns true if this client needs the server-side
97102
// rendered cursor. This may be because it does not support local cursor
98103
// or because the current cursor position has not been set by this client.
@@ -155,6 +160,7 @@ namespace rfb {
155160

156161
void screenLayoutChange(rdr::U16 reason);
157162
void setCursor();
163+
void setCursorPos();
158164
void setDesktopName(const char *name);
159165
void setLEDState(unsigned int state);
160166

common/rfb/VNCServer.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,10 @@ namespace rfb {
9797
virtual void setCursor(int width, int height, const Point& hotspot,
9898
const rdr::U8* cursorData) = 0;
9999

100-
// setCursorPos() tells the server the current position of the cursor.
101-
virtual void setCursorPos(const Point& p) = 0;
100+
// setCursorPos() tells the server the current position of the cursor, and
101+
// whether the server initiated that change (e.g. through another X11
102+
// client calling XWarpPointer()).
103+
virtual void setCursorPos(const Point& p, bool warped) = 0;
102104

103105
// setName() tells the server what desktop title to supply to clients
104106
virtual void setName(const char* name) = 0;

common/rfb/VNCServerST.cxx

+5-2
Original file line numberDiff line numberDiff line change
@@ -429,14 +429,17 @@ void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
429429
}
430430
}
431431

432-
void VNCServerST::setCursorPos(const Point& pos)
432+
void VNCServerST::setCursorPos(const Point& pos, bool warped)
433433
{
434434
if (!cursorPos.equals(pos)) {
435435
cursorPos = pos;
436436
renderedCursorInvalid = true;
437437
std::list<VNCSConnectionST*>::iterator ci;
438-
for (ci = clients.begin(); ci != clients.end(); ci++)
438+
for (ci = clients.begin(); ci != clients.end(); ci++) {
439439
(*ci)->renderedCursorChange();
440+
if (warped)
441+
(*ci)->cursorPositionChange();
442+
}
440443
}
441444
}
442445

common/rfb/VNCServerST.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ namespace rfb {
9999
virtual void add_copied(const Region &dest, const Point &delta);
100100
virtual void setCursor(int width, int height, const Point& hotspot,
101101
const rdr::U8* data);
102-
virtual void setCursorPos(const Point& p);
102+
virtual void setCursorPos(const Point& p, bool warped);
103103
virtual void setName(const char* name_);
104104
virtual void setLEDState(unsigned state);
105105

common/rfb/encodings.h

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ namespace rfb {
6161

6262
// VMware-specific
6363
const int pseudoEncodingVMwareCursor = 0x574d5664;
64+
const int pseudoEncodingVMwareCursorPosition = 0x574d5666;
6465
const int pseudoEncodingVMwareLEDState = 0x574d5668;
6566

6667
// UltraVNC-specific

unix/x0vncserver/XDesktop.cxx

+1-1
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ void XDesktop::poll() {
217217
&x, &y, &wx, &wy, &mask);
218218
x -= geometry->offsetLeft();
219219
y -= geometry->offsetTop();
220-
server->setCursorPos(rfb::Point(x, y));
220+
server->setCursorPos(rfb::Point(x, y), false);
221221
}
222222
}
223223

unix/xserver/hw/vnc/XserverDesktop.cc

+10-1
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,15 @@ void XserverDesktop::setCursor(int width, int height, int hotX, int hotY,
261261
delete [] cursorData;
262262
}
263263

264+
void XserverDesktop::setCursorPos(int x, int y, bool warped)
265+
{
266+
try {
267+
server->setCursorPos(Point(x, y), warped);
268+
} catch (rdr::Exception& e) {
269+
vlog.error("XserverDesktop::setCursorPos: %s",e.str());
270+
}
271+
}
272+
264273
void XserverDesktop::add_changed(const rfb::Region &region)
265274
{
266275
try {
@@ -377,7 +386,7 @@ void XserverDesktop::blockHandler(int* timeout)
377386
if (oldCursorPos.x != cursorX || oldCursorPos.y != cursorY) {
378387
oldCursorPos.x = cursorX;
379388
oldCursorPos.y = cursorY;
380-
server->setCursorPos(oldCursorPos);
389+
server->setCursorPos(oldCursorPos, false);
381390
}
382391

383392
// Trigger timers and check when the next will expire

unix/xserver/hw/vnc/XserverDesktop.h

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class XserverDesktop : public rfb::SDesktop, public rfb::FullFramePixelBuffer,
6767
void setDesktopName(const char* name);
6868
void setCursor(int width, int height, int hotX, int hotY,
6969
const unsigned char *rgbaData);
70+
void setCursorPos(int x, int y, bool warped);
7071
void add_changed(const rfb::Region &region);
7172
void add_copied(const rfb::Region &dest, const rfb::Point &delta);
7273
void handleSocketEvent(int fd, bool read, bool write);

unix/xserver/hw/vnc/vncExtInit.cc

+5
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,11 @@ void vncSetCursor(int width, int height, int hotX, int hotY,
400400
desktop[scr]->setCursor(width, height, hotX, hotY, rgbaData);
401401
}
402402

403+
void vncSetCursorPos(int scrIdx, int x, int y)
404+
{
405+
desktop[scrIdx]->setCursorPos(x, y, true);
406+
}
407+
403408
void vncPreScreenResize(int scrIdx)
404409
{
405410
// We need to prevent the RFB core from accessing the framebuffer

unix/xserver/hw/vnc/vncExtInit.h

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ void vncAddCopied(int scrIdx, int nRects,
8181

8282
void vncSetCursor(int width, int height, int hotX, int hotY,
8383
const unsigned char *rgbaData);
84+
void vncSetCursorPos(int scrIdx, int x, int y);
8485

8586
void vncPreScreenResize(int scrIdx);
8687
void vncPostScreenResize(int scrIdx, int success, int width, int height);

unix/xserver/hw/vnc/vncHooks.c

+26
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ typedef struct _vncHooksScreenRec {
6262
CopyWindowProcPtr CopyWindow;
6363
ClearToBackgroundProcPtr ClearToBackground;
6464
DisplayCursorProcPtr DisplayCursor;
65+
#if XORG >= 119
66+
CursorWarpedToProcPtr CursorWarpedTo;
67+
#endif
6568
ScreenBlockHandlerProcPtr BlockHandler;
6669
#ifdef RENDER
6770
CompositeProcPtr Composite;
@@ -113,6 +116,12 @@ static void vncHooksClearToBackground(WindowPtr pWin, int x, int y, int w,
113116
int h, Bool generateExposures);
114117
static Bool vncHooksDisplayCursor(DeviceIntPtr pDev,
115118
ScreenPtr pScreen, CursorPtr cursor);
119+
#if XORG >= 119
120+
static void vncHooksCursorWarpedTo(DeviceIntPtr pDev,
121+
ScreenPtr pScreen_, ClientPtr pClient,
122+
WindowPtr pWindow, SpritePtr pSprite,
123+
int x, int y);
124+
#endif
116125
#if XORG <= 118
117126
static void vncHooksBlockHandler(ScreenPtr pScreen, void * pTimeout,
118127
void * pReadmask);
@@ -271,6 +280,9 @@ int vncHooksInit(int scrIdx)
271280
wrap(vncHooksScreen, pScreen, CopyWindow, vncHooksCopyWindow);
272281
wrap(vncHooksScreen, pScreen, ClearToBackground, vncHooksClearToBackground);
273282
wrap(vncHooksScreen, pScreen, DisplayCursor, vncHooksDisplayCursor);
283+
#if XORG >= 119
284+
wrap(vncHooksScreen, pScreen, CursorWarpedTo, vncHooksCursorWarpedTo);
285+
#endif
274286
wrap(vncHooksScreen, pScreen, BlockHandler, vncHooksBlockHandler);
275287
#ifdef RENDER
276288
ps = GetPictureScreenIfSet(pScreen);
@@ -631,6 +643,20 @@ static Bool vncHooksDisplayCursor(DeviceIntPtr pDev,
631643
return ret;
632644
}
633645

646+
// CursorWarpedTo - notify that the cursor was warped
647+
648+
#if XORG >= 119
649+
static void vncHooksCursorWarpedTo(DeviceIntPtr pDev,
650+
ScreenPtr pScreen_, ClientPtr pClient,
651+
WindowPtr pWindow, SpritePtr pSprite,
652+
int x, int y)
653+
{
654+
SCREEN_PROLOGUE(pScreen_, CursorWarpedTo);
655+
vncSetCursorPos(pScreen->myNum, x, y);
656+
SCREEN_EPILOGUE(CursorWarpedTo);
657+
}
658+
#endif
659+
634660
// BlockHandler - ignore any changes during the block handler - it's likely
635661
// these are just drawing the cursor.
636662

0 commit comments

Comments
 (0)