diff --git a/include/rfb/rfb.h b/include/rfb/rfb.h index 6cfac6d4..42c88470 100644 --- a/include/rfb/rfb.h +++ b/include/rfb/rfb.h @@ -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 */ @@ -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; @@ -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) || \ diff --git a/src/libvncserver/cursor.c b/src/libvncserver/cursor.c index 51008bde..fd9e43af 100644 --- a/src/libvncserver/cursor.c +++ b/src/libvncserver/cursor.c @@ -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); @@ -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); diff --git a/src/libvncserver/main.c b/src/libvncserver/main.c index a8e9a581..132ef1fb 100644 --- a/src/libvncserver/main.c +++ b/src/libvncserver/main.c @@ -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); @@ -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; diff --git a/src/libvncserver/rfbserver.c b/src/libvncserver/rfbserver.c index cd3952b6..8849a49f 100644 --- a/src/libvncserver/rfbserver.c +++ b/src/libvncserver/rfbserver.c @@ -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); @@ -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, @@ -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; @@ -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) { @@ -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) { @@ -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); @@ -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);