Skip to content

Commit

Permalink
libvncserver: Always show ptr. movement to collaborators
Browse files Browse the repository at this point in the history
When multiple viewers are sharing a single TurboVNC session, ordinarily
it would be necessary for a viewer to disable remote cursor shape
updates in order to see the pointer movements initiated by other
viewers.  This commit automates that by using server-side cursor
rendering (drawing the cursor into the framebuffer, not using remote
cursor shape updates) to send cursor updates to all viewers that aren't
moving the pointer ("collaborators.")  The viewer that is moving the
pointer (the "pointer owner") receives remote cursor shape updates while
it is moving the pointer, if it has elected to receive them.

This mimics the behavior of RealVNC.
  • Loading branch information
Volodymyr Samokhatko committed Apr 21, 2023
1 parent 7cb91db commit ff139b4
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 12 deletions.
14 changes: 12 additions & 2 deletions include/rfb/rfb.h
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ typedef struct _rfbScreenInfo
rfbBool neverShared;
rfbBool dontDisconnect;
struct _rfbClientRec* clientHead;
struct _rfbClientRec* pointerClient; /**< "Mutex" for pointer events */
struct _rfbClientRec* pointerClient; /**< "Mutex" for pointer drag events */


/* cursor */
Expand Down Expand Up @@ -378,6 +378,14 @@ typedef struct _rfbScreenInfo
/** where the cursor is shown */
int underCursorBufferX, underCursorBufferY;
RWLOCK(showCursorRWLock);

/* The client that last moved the pointer
Other clients will automatically receive cursor updates via the traditional
mechanism of drawing the cursor into the framebuffer (AKA "server-side
cursor rendering") so they can track the pointer's movement regardless of
whether cursor shape updates (AKA "client-side cursor rendering") are
enabled. */
struct _rfbClientRec* pointerOwner;
} rfbScreenInfo, *rfbScreenInfoPtr;


Expand Down Expand Up @@ -722,7 +730,9 @@ typedef struct _rfbClientRec {

#define FB_UPDATE_PENDING(cl) \
(((cl)->enableCursorShapeUpdates && (cl)->cursorWasChanged) || \
(((cl)->enableCursorShapeUpdates == FALSE && \
(( \
((cl)->enableCursorShapeUpdates == FALSE || \
(cl)->screen->pointerOwner != (cl)) && \
((cl)->cursorX != (cl)->screen->cursorX || \
(cl)->cursorY != (cl)->screen->cursorY))) || \
((cl)->useNewFBSize && (cl)->newFBSizePending) || \
Expand Down
7 changes: 5 additions & 2 deletions src/libvncserver/cursor.c
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ void rfbSetCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr c)
if(rfbScreen->cursor) {
iterator=rfbGetClientIterator(rfbScreen);
while((cl=rfbClientIteratorNext(iterator)))
if(!cl->enableCursorShapeUpdates)
if(!cl->enableCursorShapeUpdates || (cl->screen->pointerOwner && cl->screen->pointerOwner != cl))
rfbRedrawAfterHideCursor(cl,NULL);
rfbReleaseClientIterator(iterator);

Expand All @@ -794,8 +794,11 @@ void rfbSetCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr c)
iterator=rfbGetClientIterator(rfbScreen);
while((cl=rfbClientIteratorNext(iterator))) {
cl->cursorWasChanged = TRUE;
if(!cl->enableCursorShapeUpdates)
if(!cl->enableCursorShapeUpdates || (cl->screen->pointerOwner && cl->screen->pointerOwner != cl))
rfbRedrawAfterHideCursor(cl,NULL);
LOCK(cl->updateMutex);
TSIGNAL(cl->updateCond);
UNLOCK(cl->updateMutex);
}
rfbReleaseClientIterator(iterator);

Expand Down
21 changes: 19 additions & 2 deletions src/libvncserver/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -732,11 +732,26 @@ rfbDefaultPtrAddEvent(int buttonMask, int x, int y, rfbClientPtr cl)
if (cl->enableCursorPosUpdates)
cl->cursorWasMoved = FALSE;

/* Will become the pointer owner, need to hide the server-side cursor. */
if (!cl->screen->pointerOwner) {
rfbRedrawAfterHideCursor(cl, NULL);
LOCK(cl->updateMutex);
TSIGNAL(cl->updateCond);
UNLOCK(cl->updateMutex);
}

/* But inform all remaining clients about this cursor movement. */
iterator = rfbGetClientIterator(s);
while ((other_client = rfbClientIteratorNext(iterator)) != NULL) {
if (other_client != cl && other_client->enableCursorPosUpdates) {
other_client->cursorWasMoved = TRUE;
if (other_client != cl) {
if (other_client->enableCursorPosUpdates) {
other_client->cursorWasMoved = TRUE;
}
if (other_client != cl->screen->pointerOwner) {
LOCK(other_client->updateMutex);
TSIGNAL(other_client->updateCond);
UNLOCK(other_client->updateMutex);
}
}
}
rfbReleaseClientIterator(iterator);
Expand Down Expand Up @@ -1001,6 +1016,8 @@ rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv,
screen->underCursorBufferY = 0;
INIT_RWLOCK(screen->showCursorRWLock);

screen->pointerOwner = NULL;

if(!rfbProcessArguments(screen,argc,argv)) {
free(screen);
return NULL;
Expand Down
26 changes: 20 additions & 6 deletions src/libvncserver/rfbserver.c
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,9 @@ rfbClientConnectionGone(rfbClientPtr cl)
if (cl->screen->pointerClient == cl)
cl->screen->pointerClient = NULL;

if (cl->screen->pointerOwner == cl)
cl->screen->pointerOwner = NULL;

sraRgnDestroy(cl->modifiedRegion);
sraRgnDestroy(cl->requestedRegion);
sraRgnDestroy(cl->copyRegion);
Expand Down Expand Up @@ -2688,6 +2691,14 @@ rfbProcessClientNormalMessage(rfbClientPtr cl)
cl->screen->pointerClient = cl;

if(!cl->viewOnly) {
/* If the pointer was most recently moved by another client, we set
pointerOwner to NULL here so that the client that is currently
moving the pointer (cl), assuming it understands cursor shape
updates, will receive a cursor shape update with the last known
pointer position. */
if (cl->screen->pointerOwner != cl)
cl->screen->pointerOwner = NULL;

if (msg.pe.buttonMask != cl->lastPtrButtons ||
cl->screen->deferPtrUpdateTime == 0) {
cl->screen->ptrAddEvent(msg.pe.buttonMask,
Expand All @@ -2700,6 +2711,8 @@ rfbProcessClientNormalMessage(rfbClientPtr cl)
cl->lastPtrY = ScaleY(cl->scaledScreen, cl->screen, Swap16IfLE(msg.pe.y));
cl->lastPtrButtons = msg.pe.buttonMask;
}

cl->screen->pointerOwner = cl;
}
return;

Expand Down Expand Up @@ -3158,8 +3171,9 @@ rfbSendFramebufferUpdate(rfbClientPtr cl,
}

/*
* If this client understands cursor shape updates, cursor should be
* removed from the framebuffer. Otherwise, make sure it's put up.
* If this client understands cursor shape updates and owns the pointer or is
* about to own the pointer, then the cursor should be removed from the
* framebuffer. Otherwise, make sure it's drawn.
*/

if (cl->enableCursorShapeUpdates) {
Expand Down Expand Up @@ -3264,7 +3278,7 @@ rfbSendFramebufferUpdate(rfbClientPtr cl,
sraRgnOr(updateRegion,cl->copyRegion);
if(!sraRgnAnd(updateRegion,cl->requestedRegion) &&
sraRgnEmpty(updateRegion) &&
(cl->enableCursorShapeUpdates ||
((cl->enableCursorShapeUpdates && (!cl->screen->pointerOwner || cl->screen->pointerOwner == cl)) ||
(cl->cursorX == cl->screen->cursorX && cl->cursorY == cl->screen->cursorY)) &&
!sendCursorShape && !sendCursorPos && !sendKeyboardLedState &&
!sendSupportedMessages && !sendSupportedEncodings && !sendServerIdentity) {
Expand Down Expand Up @@ -3319,7 +3333,7 @@ rfbSendFramebufferUpdate(rfbClientPtr cl,

UNLOCK(cl->updateMutex);

if (!cl->enableCursorShapeUpdates) {
if (!cl->enableCursorShapeUpdates || (cl->screen->pointerOwner && cl->screen->pointerOwner != cl)) {
if(cl->cursorX != cl->screen->cursorX || cl->cursorY != cl->screen->cursorY) {
rfbRedrawAfterHideCursor(cl,updateRegion);
LOCK(cl->screen->cursorMutex);
Expand Down Expand Up @@ -3576,8 +3590,8 @@ rfbSendFramebufferUpdate(rfbClientPtr cl,
result = FALSE;
}

if (!cl->enableCursorShapeUpdates) {
rfbHideCursor(cl);
if (!cl->enableCursorShapeUpdates || (cl->screen->pointerOwner && cl->screen->pointerOwner != cl)) {
rfbHideCursor(cl->screen);
}
RWUNLOCK(cl->screen->showCursorRWLock);

Expand Down

0 comments on commit ff139b4

Please sign in to comment.