Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,13 @@ if(WITH_THREADS AND (CMAKE_USE_PTHREADS_INIT OR CMAKE_USE_WIN32_THREADS_INIT) AN
)
endif(WITH_THREADS AND (CMAKE_USE_PTHREADS_INIT OR CMAKE_USE_WIN32_THREADS_INIT) AND WITH_LIBVNCSERVER AND WITH_LIBVNCCLIENT)

if(UNIX AND WITH_LIBVNCCLIENT)
set(SIMPLETESTS
${SIMPLETESTS}
extended_mouse_buttons_test
)
endif(UNIX AND WITH_LIBVNCCLIENT)

foreach(t ${SIMPLETESTS})
add_executable(test_${t} ${TESTS_DIR}/${t}.c)
set_target_properties(test_${t} PROPERTIES OUTPUT_NAME ${t})
Expand Down Expand Up @@ -765,6 +772,7 @@ if(UNIX)
endif(WITH_LIBVNCSERVER)
if(WITH_LIBVNCCLIENT)
add_test(NAME includetest_client COMMAND ${TESTS_DIR}/includetest.sh ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_MAKE_PROGRAM} "rfb/rfbclient.h")
add_test(NAME extended_mouse_buttons COMMAND test_extended_mouse_buttons_test)
endif(WITH_LIBVNCCLIENT)
endif(UNIX)
if(WITH_JPEG AND FOUND_LIBJPEG_TURBO)
Expand Down
2 changes: 2 additions & 0 deletions include/rfb/rfb.h
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,8 @@ typedef struct _rfbClientRec {
rfbBool enableSupportedEncodings; /**< client supports SupportedEncodings encoding */
rfbBool enableServerIdentity; /**< client supports ServerIdentity encoding */
rfbBool enableKeyboardLedState; /**< client supports KeyboardState encoding */
rfbBool enableExtendedMouseButtons; /**< client supports ExtendedMouseButtons encoding */
rfbBool extendedMouseButtonsPending; /**< ExtendedMouseButtons acknowledgement should be sent */
rfbBool enableLastRectEncoding; /**< client supports LastRect encoding */
rfbBool enableCursorShapeUpdates; /**< client supports cursor shape updates */
rfbBool enableCursorPosUpdates; /**< client supports cursor position updates */
Expand Down
3 changes: 3 additions & 0 deletions include/rfb/rfbclient.h
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,9 @@ typedef struct _rfbClient {
/** the QoS IP DSCP for this client */
int QoS_DSCP;

/** server acknowledged ExtendedMouseButtons pointer events */
rfbBool extendedMouseButtonsEnabled;

/** hook to handle xvp server messages */
HandleXvpMsgProc HandleXvpMsg;

Expand Down
13 changes: 13 additions & 0 deletions include/rfb/rfbproto.h
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,7 @@ typedef struct {
#define rfbEncodingLastRect 0xFFFFFF20
#define rfbEncodingNewFBSize 0xFFFFFF21
#define rfbEncodingExtDesktopSize 0xFFFFFECC
#define rfbEncodingExtendedMouseButtons 0xFFFFFEC4 /* -316 */

#define rfbEncodingQualityLevel0 0xFFFFFFE0
#define rfbEncodingQualityLevel1 0xFFFFFFE1
Expand Down Expand Up @@ -1431,11 +1432,23 @@ typedef struct {
#define rfbButton3Mask 4
#define rfbButton4Mask 8
#define rfbButton5Mask 16
#define rfbButton6Mask 32
#define rfbButton7Mask 64
#define rfbButton8Mask 128
#define rfbButton9Mask 256
#define rfbButton10Mask 512
#define rfbButton11Mask 1024
#define rfbButton12Mask 2048
#define rfbButton13Mask 4096
#define rfbButton14Mask 8192
#define rfbButton15Mask 16384
#define rfbPointerEventExtendedButtonMask 128
/* RealVNC 335 method */
#define rfbWheelUpMask rfbButton4Mask
#define rfbWheelDownMask rfbButton5Mask

#define sz_rfbPointerEventMsg 6
#define sz_rfbExtendedPointerEventMsg 7



Expand Down
24 changes: 22 additions & 2 deletions src/libvncclient/rfbclient.c
Original file line number Diff line number Diff line change
Expand Up @@ -1445,6 +1445,8 @@ SetFormatAndEncodings(rfbClient* client)

if (se->nEncodings < MAX_ENCODINGS)
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingQemuExtendedKeyEvent);
if (se->nEncodings < MAX_ENCODINGS)
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingExtendedMouseButtons);

#ifdef LIBVNCSERVER_HAVE_LIBZ
/* extendedclipboard. tell server we support it if client has the callback set */
Expand Down Expand Up @@ -1661,17 +1663,30 @@ rfbBool
SendPointerEvent(rfbClient* client,int x, int y, int buttonMask)
{
rfbPointerEventMsg pe;
uint8_t extendedButtonMask;
rfbBool sendExtended = client->extendedMouseButtonsEnabled &&
((buttonMask & ~0x7f) != 0);

if (!SupportsClient2Server(client, rfbPointerEvent)) return TRUE;
if ((buttonMask & ~0xff) != 0 && !client->extendedMouseButtonsEnabled) return FALSE;

pe.type = rfbPointerEvent;
pe.buttonMask = buttonMask;
pe.buttonMask = (uint8_t)(buttonMask & (sendExtended ? 0x7f : 0xff));
if (sendExtended)
pe.buttonMask |= rfbPointerEventExtendedButtonMask;
if (x < 0) x = 0;
if (y < 0) y = 0;

pe.x = rfbClientSwap16IfLE(x);
pe.y = rfbClientSwap16IfLE(y);
return WriteToRFBServer(client, (char *)&pe, sz_rfbPointerEventMsg);
if (!WriteToRFBServer(client, (char *)&pe, sz_rfbPointerEventMsg))
return FALSE;

if (!sendExtended)
return TRUE;

extendedButtonMask = (uint8_t)((buttonMask >> 7) & 0xff);
return WriteToRFBServer(client, (char *)&extendedButtonMask, 1);
}


Expand Down Expand Up @@ -2145,6 +2160,11 @@ HandleRFBServerMessage(rfbClient* client)
continue;
}

if (rect.encoding == rfbEncodingExtendedMouseButtons) {
client->extendedMouseButtonsEnabled = TRUE;
continue;
}

if (rect.encoding == rfbEncodingNewFBSize) {
if(!ResizeClientBuffer(client, rect.r.w, rect.r.h))
return FALSE;
Expand Down
84 changes: 82 additions & 2 deletions src/libvncserver/rfbserver.c
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,8 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen,
cl->useRichCursorEncoding = FALSE;
cl->enableLastRectEncoding = FALSE;
cl->enableKeyboardLedState = FALSE;
cl->enableExtendedMouseButtons = FALSE;
cl->extendedMouseButtonsPending = FALSE;
cl->enableSupportedMessages = FALSE;
cl->enableSupportedEncodings = FALSE;
cl->enableServerIdentity = FALSE;
Expand Down Expand Up @@ -1029,6 +1031,39 @@ rfbSendKeyboardLedState(rfbClientPtr cl)

#define rfbSetBit(buffer, position) (buffer[(position & 255) / 8] |= (1 << (position % 8)))

/*
* Send rfbEncodingExtendedMouseButtons acknowledgement.
*/

static rfbBool
rfbSendExtendedMouseButtons(rfbClientPtr cl)
{
rfbFramebufferUpdateRectHeader rect;

if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) {
if (!rfbSendUpdateBuf(cl))
return FALSE;
}

rect.r.x = 0;
rect.r.y = 0;
rect.r.w = 0;
rect.r.h = 0;
rect.encoding = Swap32IfLE(rfbEncodingExtendedMouseButtons);

memcpy(&cl->updateBuf[cl->ublen], (char *)&rect,
sz_rfbFramebufferUpdateRectHeader);
cl->ublen += sz_rfbFramebufferUpdateRectHeader;

rfbStatRecordEncodingSent(cl, rfbEncodingExtendedMouseButtons,
sz_rfbFramebufferUpdateRectHeader, sz_rfbFramebufferUpdateRectHeader);
if (!rfbSendUpdateBuf(cl))
return FALSE;

return TRUE;
}


/*
* Send rfbEncodingSupportedMessages.
*/
Expand Down Expand Up @@ -1131,6 +1166,7 @@ rfbSendSupportedEncodings(rfbClientPtr cl)
rfbEncodingNewFBSize,
rfbEncodingExtDesktopSize,
rfbEncodingKeyboardLedState,
rfbEncodingExtendedMouseButtons,
rfbEncodingSupportedMessages,
rfbEncodingSupportedEncodings,
rfbEncodingServerIdentity,
Expand Down Expand Up @@ -2398,6 +2434,8 @@ rfbProcessClientNormalMessage(rfbClientPtr cl)
cl->enableCursorShapeUpdates = FALSE;
cl->enableLastRectEncoding = FALSE;
cl->enableKeyboardLedState = FALSE;
cl->enableExtendedMouseButtons = FALSE;
cl->extendedMouseButtonsPending = FALSE;
cl->enableSupportedMessages = FALSE;
cl->enableSupportedEncodings = FALSE;
cl->enableServerIdentity = FALSE;
Expand Down Expand Up @@ -2506,7 +2544,15 @@ rfbProcessClientNormalMessage(rfbClientPtr cl)
"%s\n", cl->host);
cl->enableKeyboardLedState = TRUE;
}
break;
break;
case rfbEncodingExtendedMouseButtons:
if (!cl->enableExtendedMouseButtons) {
rfbLog("Enabling ExtendedMouseButtons protocol extension for client "
"%s\n", cl->host);
cl->enableExtendedMouseButtons = TRUE;
cl->extendedMouseButtonsPending = TRUE;
}
break;
case rfbEncodingSupportedMessages:
if (!cl->enableSupportedMessages) {
rfbLog("Enabling SupportedMessages protocol extension for client "
Expand Down Expand Up @@ -2759,7 +2805,25 @@ rfbProcessClientNormalMessage(rfbClientPtr cl)
return;
}

rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbPointerEventMsg, sz_rfbPointerEventMsg);
if (cl->enableExtendedMouseButtons &&
(msg.pe.buttonMask & rfbPointerEventExtendedButtonMask)) {
uint8_t extendedButtonMask;
if ((n = rfbReadExact(cl, (char *)&extendedButtonMask, 1)) <= 0) {
if (n != 0)
rfbLogPerror("rfbProcessClientNormalMessage: read");
rfbCloseClient(cl);
return;
}
msg.pe.buttonMask = (msg.pe.buttonMask & ~rfbPointerEventExtendedButtonMask) |
(extendedButtonMask << 7);
rfbStatRecordMessageRcvd(cl, msg.type,
sz_rfbExtendedPointerEventMsg,
sz_rfbExtendedPointerEventMsg);
} else {
rfbStatRecordMessageRcvd(cl, msg.type,
sz_rfbPointerEventMsg,
sz_rfbPointerEventMsg);
}

if (cl->screen->pointerClient && cl->screen->pointerClient != cl)
return;
Expand Down Expand Up @@ -3199,6 +3263,7 @@ rfbSendFramebufferUpdate(rfbClientPtr cl,
rfbBool sendCursorShape = FALSE;
rfbBool sendCursorPos = FALSE;
rfbBool sendKeyboardLedState = FALSE;
rfbBool sendExtendedMouseButtons = FALSE;
rfbBool sendSupportedMessages = FALSE;
rfbBool sendSupportedEncodings = FALSE;
rfbBool sendServerIdentity = FALSE;
Expand Down Expand Up @@ -3271,6 +3336,15 @@ rfbSendFramebufferUpdate(rfbClientPtr cl,
}
}

/*
* Do we plan to acknowledge ExtendedMouseButtons support?
*/
if (cl->extendedMouseButtonsPending)
{
sendExtendedMouseButtons = TRUE;
cl->extendedMouseButtonsPending = FALSE;
}

/*
* Do we plan to send a rfbEncodingSupportedMessages?
*/
Expand Down Expand Up @@ -3541,6 +3615,7 @@ rfbSendFramebufferUpdate(rfbClientPtr cl,
fu->nRects = Swap16IfLE((uint16_t)(sraRgnCountRects(updateCopyRegion) +
nUpdateRegionRects +
!!sendCursorShape + !!sendCursorPos + !!sendKeyboardLedState +
!!sendExtendedMouseButtons +
!!sendSupportedMessages + !!sendSupportedEncodings + !!sendServerIdentity));
} else {
fu->nRects = 0xFFFF;
Expand All @@ -3564,6 +3639,11 @@ rfbSendFramebufferUpdate(rfbClientPtr cl,
goto updateFailed;
}

if (sendExtendedMouseButtons) {
if (!rfbSendExtendedMouseButtons(cl))
goto updateFailed;
}

if (sendSupportedMessages) {
if (!rfbSendSupportedMessages(cl))
goto updateFailed;
Expand Down
99 changes: 99 additions & 0 deletions test/extended_mouse_buttons_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#include <rfb/rfbclient.h>

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

static void enablePointerEvent(rfbClient *client)
{
memset(&client->supportedMessages, 0, sizeof(client->supportedMessages));
client->supportedMessages.client2server[(rfbPointerEvent & 0xff) / 8] |=
(1 << (rfbPointerEvent % 8));
}

static int readExact(int fd, unsigned char *buf, size_t len)
{
size_t off = 0;
while (off < len) {
ssize_t n = read(fd, buf + off, len - off);
if (n <= 0)
return 0;
off += (size_t)n;
}
return 1;
}

static int expectNoByte(int fd)
{
unsigned char ch;
ssize_t n = recv(fd, &ch, 1, MSG_DONTWAIT);
if (n < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
return 1;
return 0;
}

static int expectBytes(int fd, const unsigned char *expected, size_t len)
{
unsigned char got[16];
if (len > sizeof(got))
return 0;
if (!readExact(fd, got, len))
return 0;
if (memcmp(got, expected, len) != 0) {
size_t i;
fprintf(stderr, "unexpected bytes:\n");
for (i = 0; i < len; ++i)
fprintf(stderr, " %zu: got 0x%02x expected 0x%02x\n", i, got[i], expected[i]);
return 0;
}
return 1;
}

int main(void)
{
int sv[2];
rfbClient client;
unsigned char expectedNormalBack[] = {
rfbPointerEvent, rfbButton8Mask, 0x00, 0x0a, 0x00, 0x14
};
unsigned char expectedExtendedBackForward[] = {
rfbPointerEvent, rfbPointerEventExtendedButtonMask,
0x00, 0x0a, 0x00, 0x14, 0x03
};

if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0)
return 1;

memset(&client, 0, sizeof(client));
client.sock = sv[0];
client.endianTest = 1;
enablePointerEvent(&client);

if (!SendPointerEvent(&client, 10, 20, rfbButton8Mask)) {
fprintf(stderr, "normal back button event failed\n");
return 1;
}
if (!expectBytes(sv[1], expectedNormalBack, sizeof(expectedNormalBack)))
return 1;

if (SendPointerEvent(&client, 10, 20, rfbButton9Mask)) {
fprintf(stderr, "button 9 succeeded without ExtendedMouseButtons acknowledgement\n");
return 1;
}
if (!expectNoByte(sv[1]))
return 1;

client.extendedMouseButtonsEnabled = TRUE;
if (!SendPointerEvent(&client, 10, 20, rfbButton8Mask | rfbButton9Mask)) {
fprintf(stderr, "extended back/forward button event failed\n");
return 1;
}
if (!expectBytes(sv[1], expectedExtendedBackForward, sizeof(expectedExtendedBackForward)))
return 1;

close(sv[0]);
close(sv[1]);
return 0;
}
Loading