diff --git a/CMakeLists.txt b/CMakeLists.txt index d9709c809..b810330cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -701,6 +701,7 @@ if(WITH_LIBVNCSERVER) list(APPEND SIMPLETESTS cargstest copyrecttest + cursor_update_region_test ) endif(WITH_LIBVNCSERVER) @@ -758,6 +759,7 @@ endif(LIBVNCSERVER_WITH_WEBSOCKETS AND WITH_LIBVNCSERVER) if(WITH_LIBVNCSERVER) add_test(NAME cargs COMMAND test_cargstest) + add_test(NAME cursor_update_region COMMAND test_cursor_update_region_test) endif(WITH_LIBVNCSERVER) if(UNIX) if(WITH_LIBVNCSERVER) diff --git a/src/libvncserver/cursor.c b/src/libvncserver/cursor.c index 1d14e6350..72a73efe3 100644 --- a/src/libvncserver/cursor.c +++ b/src/libvncserver/cursor.c @@ -714,7 +714,7 @@ void rfbShowCursor(rfbClientPtr cl) * region gets redrawn. */ -void rfbRedrawAfterHideCursor(rfbClientPtr cl,sraRegionPtr updateRegion) +void rfbRedrawAfterHideCursor(rfbClientPtr cl, sraRegionPtr updateRegion, const sraRegionPtr requestedRegion) { rfbScreenInfoPtr s = cl->screen; rfbCursorPtr c = s->cursor; @@ -731,7 +731,10 @@ void rfbRedrawAfterHideCursor(rfbClientPtr cl,sraRegionPtr updateRegion) sraRegionPtr rect; rect = sraRgnCreateRect(x,y,x2,y2); if(updateRegion) { - sraRgnOr(updateRegion,rect); + if(requestedRegion) + sraRgnAnd(rect, requestedRegion); + if(!sraRgnEmpty(rect)) + sraRgnOr(updateRegion, rect); } else { LOCK(cl->updateMutex); sraRgnOr(cl->modifiedRegion,rect); @@ -771,7 +774,7 @@ void rfbSetCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr c) iterator=rfbGetClientIterator(rfbScreen); while((cl=rfbClientIteratorNext(iterator))) if(!cl->enableCursorShapeUpdates) - rfbRedrawAfterHideCursor(cl,NULL); + rfbRedrawAfterHideCursor(cl, NULL, NULL); rfbReleaseClientIterator(iterator); if(rfbScreen->cursor->cleanup) @@ -784,7 +787,7 @@ void rfbSetCursor(rfbScreenInfoPtr rfbScreen,rfbCursorPtr c) while((cl=rfbClientIteratorNext(iterator))) { cl->cursorWasChanged = TRUE; if(!cl->enableCursorShapeUpdates) - rfbRedrawAfterHideCursor(cl,NULL); + rfbRedrawAfterHideCursor(cl, NULL, NULL); } rfbReleaseClientIterator(iterator); diff --git a/src/libvncserver/private.h b/src/libvncserver/private.h index 6f28a2956..bb698a0e1 100644 --- a/src/libvncserver/private.h +++ b/src/libvncserver/private.h @@ -5,7 +5,7 @@ void rfbShowCursor(rfbClientPtr cl); void rfbHideCursor(rfbClientPtr cl); -void rfbRedrawAfterHideCursor(rfbClientPtr cl,sraRegionPtr updateRegion); +void rfbRedrawAfterHideCursor(rfbClientPtr cl, sraRegionPtr updateRegion, const sraRegionPtr requestedRegion); /* from main.c */ diff --git a/src/libvncserver/rfbserver.c b/src/libvncserver/rfbserver.c index 22af22d7d..3010aee0e 100644 --- a/src/libvncserver/rfbserver.c +++ b/src/libvncserver/rfbserver.c @@ -2453,7 +2453,7 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) cl->host); /* if cursor was drawn, hide the cursor */ if(!cl->enableCursorShapeUpdates) - rfbRedrawAfterHideCursor(cl,NULL); + rfbRedrawAfterHideCursor(cl, NULL, NULL); cl->enableCursorShapeUpdates = TRUE; cl->cursorWasChanged = TRUE; @@ -2464,7 +2464,7 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) cl->host); /* if cursor was drawn, hide the cursor */ if(!cl->enableCursorShapeUpdates) - rfbRedrawAfterHideCursor(cl,NULL); + rfbRedrawAfterHideCursor(cl, NULL, NULL); cl->enableCursorShapeUpdates = TRUE; cl->useRichCursorEncoding = TRUE; @@ -3194,7 +3194,7 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, sraRect rect; int nUpdateRegionRects; rfbFramebufferUpdateMsg *fu = (rfbFramebufferUpdateMsg *)cl->updateBuf; - sraRegionPtr updateRegion,updateCopyRegion,tmpRegion; + sraRegionPtr updateRegion,updateCopyRegion,tmpRegion,requestedRegion; int dx, dy; rfbBool sendCursorShape = FALSE; rfbBool sendCursorPos = FALSE; @@ -3366,6 +3366,8 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, * updateCopyRegion to this. */ + requestedRegion = sraRgnCreateRgn(cl->requestedRegion); + updateCopyRegion = sraRgnCreateRgn(cl->copyRegion); sraRgnAnd(updateCopyRegion,cl->requestedRegion); tmpRegion = sraRgnCreateRgn(cl->requestedRegion); @@ -3403,12 +3405,12 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, if (!cl->enableCursorShapeUpdates) { if(cl->cursorX != cl->screen->cursorX || cl->cursorY != cl->screen->cursorY) { - rfbRedrawAfterHideCursor(cl,updateRegion); + rfbRedrawAfterHideCursor(cl, updateRegion, requestedRegion); LOCK(cl->screen->cursorMutex); cl->cursorX = cl->screen->cursorX; cl->cursorY = cl->screen->cursorY; UNLOCK(cl->screen->cursorMutex); - rfbRedrawAfterHideCursor(cl,updateRegion); + rfbRedrawAfterHideCursor(cl, updateRegion, requestedRegion); } rfbShowCursor(cl); } @@ -3661,6 +3663,7 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, sraRgnReleaseIterator(i); sraRgnDestroy(updateRegion); sraRgnDestroy(updateCopyRegion); + sraRgnDestroy(requestedRegion); if(cl->screen->displayFinishedHook) cl->screen->displayFinishedHook(cl, result); diff --git a/test/cursor_update_region_test.c b/test/cursor_update_region_test.c new file mode 100644 index 000000000..5dad35b32 --- /dev/null +++ b/test/cursor_update_region_test.c @@ -0,0 +1,139 @@ +#include +#include + +#include "private.h" + +#include +#include + +static void +init_cursor_client(rfbScreenInfoPtr screen, rfbClientPtr client, rfbCursorPtr cursor) +{ + memset(screen, 0, sizeof(*screen)); + memset(client, 0, sizeof(*client)); + memset(cursor, 0, sizeof(*cursor)); + + screen->width = 100; + screen->height = 100; + screen->cursor = cursor; + + cursor->width = 10; + cursor->height = 10; + cursor->xhot = 0; + cursor->yhot = 0; + + client->screen = screen; + client->cursorX = 0; + client->cursorY = 0; +} + +static int +expect_single_rect(sraRegionPtr region, int x1, int y1, int x2, int y2) +{ + sraRectangleIterator *iterator; + sraRect rect; + int failed = 0; + + if(sraRgnCountRects(region) != 1) { + fprintf(stderr, "expected one rectangle, got %lu\n", sraRgnCountRects(region)); + return 1; + } + + iterator = sraRgnGetIterator(region); + if(!iterator || !sraRgnIteratorNext(iterator, &rect)) { + fprintf(stderr, "failed to read region rectangle\n"); + if(iterator) + sraRgnReleaseIterator(iterator); + return 1; + } + + if(rect.x1 != x1 || rect.y1 != y1 || rect.x2 != x2 || rect.y2 != y2) { + fprintf(stderr, "expected rectangle %d,%d-%d,%d, got %d,%d-%d,%d\n", + x1, y1, x2, y2, rect.x1, rect.y1, rect.x2, rect.y2); + failed = 1; + } + + sraRgnReleaseIterator(iterator); + return failed; +} + +static int +check_unrestricted_cursor_redraw(void) +{ + rfbScreenInfo screen; + rfbClientRec client; + rfbCursor cursor; + sraRegionPtr update; + int failed; + + init_cursor_client(&screen, &client, &cursor); + + update = sraRgnCreate(); + rfbRedrawAfterHideCursor(&client, update, NULL); + failed = expect_single_rect(update, 0, 0, 10, 10); + sraRgnDestroy(update); + + return failed; +} + +static int +check_empty_requested_region(void) +{ + rfbScreenInfo screen; + rfbClientRec client; + rfbCursor cursor; + sraRegionPtr update; + sraRegionPtr requested; + int failed = 0; + + init_cursor_client(&screen, &client, &cursor); + + update = sraRgnCreate(); + requested = sraRgnCreate(); + rfbRedrawAfterHideCursor(&client, update, requested); + + if(!sraRgnEmpty(update)) { + fprintf(stderr, "empty requested region must not add cursor redraw rectangles\n"); + failed = 1; + } + + sraRgnDestroy(requested); + sraRgnDestroy(update); + + return failed; +} + +static int +check_partially_requested_region(void) +{ + rfbScreenInfo screen; + rfbClientRec client; + rfbCursor cursor; + sraRegionPtr update; + sraRegionPtr requested; + int failed; + + init_cursor_client(&screen, &client, &cursor); + + update = sraRgnCreate(); + requested = sraRgnCreateRect(5, 5, 15, 15); + rfbRedrawAfterHideCursor(&client, update, requested); + failed = expect_single_rect(update, 5, 5, 10, 10); + + sraRgnDestroy(requested); + sraRgnDestroy(update); + + return failed; +} + +int +main(void) +{ + int failed = 0; + + failed |= check_unrestricted_cursor_redraw(); + failed |= check_empty_requested_region(); + failed |= check_partially_requested_region(); + + return failed; +}