diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index 44ca21531..05e40976c 100644 --- a/libvncserver/rfbserver.c +++ b/libvncserver/rfbserver.c @@ -980,6 +980,8 @@ rfbSendSupportedMessages(rfbClientPtr cl) rfbSetBit(msgs.server2client, rfbPalmVNCReSizeFrameBuffer); rfbSetBit(msgs.client2server, rfbSetDesktopSize); + rfbSetBit(msgs.server2client, rfbQemuClientMessage); + if (cl->screen->xvpHook) { rfbSetBit(msgs.client2server, rfbXvp); rfbSetBit(msgs.server2client, rfbXvp); @@ -1036,9 +1038,11 @@ rfbSendSupportedEncodings(rfbClientPtr cl) rfbEncodingSupportedMessages, rfbEncodingSupportedEncodings, rfbEncodingServerIdentity, + rfbEncodingQEMUKeyEvent, }; uint32_t nEncodings = sizeof(supported) / sizeof(supported[0]), i; + /* think rfbSetEncodingsMsg */ if (cl->ublen + sz_rfbFramebufferUpdateRectHeader @@ -1133,6 +1137,38 @@ rfbSendServerIdentity(rfbClientPtr cl) return TRUE; } +/* + * Send rfbEncodingQEMUKeyEvent. + */ + +rfbBool +rfbSendQemuKeyEvent(rfbClientPtr cl) +{ + rfbFramebufferUpdateRectHeader rect; + + if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rect.encoding = Swap32IfLE(rfbEncodingQEMUKeyEvent); + rect.r.x = 0; + rect.r.y = 0; + rect.r.w = 0; + rect.r.h = 0; + + memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + cl->ublen += sz_rfbFramebufferUpdateRectHeader; + + rfbStatRecordEncodingSent(cl, rfbEncodingQEMUKeyEvent, sz_rfbFramebufferUpdateRectHeader, sz_rfbFramebufferUpdateRectHeader); + + if (!rfbSendUpdateBuf(cl)) + return FALSE; + + return TRUE; +} + /* * Send an xvp server message */ @@ -2130,6 +2166,7 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) cl->enableSupportedMessages = FALSE; cl->enableSupportedEncodings = FALSE; cl->enableServerIdentity = FALSE; + cl->enableQemuKeyEvent = FALSE; #if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG) cl->tightQualityLevel = -1; #ifdef LIBVNCSERVER_HAVE_LIBJPEG @@ -2267,6 +2304,13 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) } } break; + case rfbEncodingQEMUKeyEvent: + if (!cl->enableQemuKeyEvent) { + rfbLog("Enabling Qemu Key Event extension for client " + "%s\n", cl->host); + cl->enableQemuKeyEvent = TRUE; + } + break; default: #if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG) if ( enc >= (uint32_t)rfbEncodingCompressLevel0 && @@ -2457,11 +2501,28 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbKeyEventMsg, sz_rfbKeyEventMsg); if(!cl->viewOnly) { - cl->screen->kbdAddEvent(msg.ke.down, (rfbKeySym)Swap32IfLE(msg.ke.key), cl); + cl->screen->kbdAddEvent(msg.ke.down, (rfbKeySym)Swap32IfLE(msg.ke.key), 0, cl); } return; + case rfbQemuClientMessage: + if ((n = rfbReadExact(cl, ((char *)&msg) + 1, + sz_rfbQemuClientMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseClient(cl); + return; + } + rfbStatRecordMessageRcvd(cl, msg.type, sz_rfbQemuClientMsg, sz_rfbQemuClientMsg); + + if(!cl->viewOnly) { + if (msg.qcm.subtype == 0 ) { + cl->screen->kbdAddEvent(msg.qcm.down_flag ? 1 : 0, (rfbKeySym)Swap32IfLE(msg.qcm.keysym), (rfbKeyCode)Swap32IfLE(msg.qcm.keycode), cl); + } + } + return; + case rfbPointerEvent: @@ -2842,6 +2903,7 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, rfbBool sendSupportedMessages = FALSE; rfbBool sendSupportedEncodings = FALSE; rfbBool sendServerIdentity = FALSE; + rfbBool sendQemuKeyEvent = FALSE; rfbBool result = TRUE; @@ -2945,6 +3007,15 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, cl->enableServerIdentity = FALSE; } + if (cl->enableQemuKeyEvent) + { + sendQemuKeyEvent = TRUE; + /* We only send this message ONCE + * (We disable it here) + */ + cl->enableQemuKeyEvent = FALSE; + } + LOCK(cl->updateMutex); /* @@ -2989,7 +3060,7 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, (cl->enableCursorShapeUpdates || (cl->cursorX == cl->screen->cursorX && cl->cursorY == cl->screen->cursorY)) && !sendCursorShape && !sendCursorPos && !sendKeyboardLedState && - !sendSupportedMessages && !sendSupportedEncodings && !sendServerIdentity) { + !sendSupportedMessages && !sendSupportedEncodings && !sendServerIdentity && !sendQemuKeyEvent) { sraRgnDestroy(updateRegion); UNLOCK(cl->updateMutex); if(cl->screen->displayFinishedHook) @@ -3181,7 +3252,7 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, fu->nRects = Swap16IfLE((uint16_t)(sraRgnCountRects(updateCopyRegion) + nUpdateRegionRects + !!sendCursorShape + !!sendCursorPos + !!sendKeyboardLedState + - !!sendSupportedMessages + !!sendSupportedEncodings + !!sendServerIdentity)); + !!sendSupportedMessages + !!sendSupportedEncodings + !!sendServerIdentity + !!sendQemuKeyEvent)); } else { fu->nRects = 0xFFFF; } @@ -3216,6 +3287,10 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, if (!rfbSendServerIdentity(cl)) goto updateFailed; } + if (sendQemuKeyEvent) { + if (!rfbSendQemuKeyEvent(cl)) + goto updateFailed; + } if (!sraRgnEmpty(updateCopyRegion)) { if (!rfbSendCopyRegion(cl,updateCopyRegion,dx,dy)) @@ -3805,9 +3880,19 @@ rfbProcessUDPInput(rfbScreenInfoPtr rfbScreen) rfbDisconnectUDPSock(rfbScreen); return; } - cl->screen->kbdAddEvent(msg.ke.down, (rfbKeySym)Swap32IfLE(msg.ke.key), cl); + cl->screen->kbdAddEvent(msg.ke.down, (rfbKeySym)Swap32IfLE(msg.ke.key), 0, cl); break; + case rfbQemuClientMessage: + if (n != sz_rfbQemuClientMsg) { + rfbErr("rfbProcessUDPInput: key event incorrect length\n"); + rfbDisconnectUDPSock(rfbScreen); + return; + } + if (msg.qcm.subtype == 0 ) + cl->screen->kbdAddEvent(msg.qcm.down_flag, (rfbKeySym)Swap32IfLE(msg.qcm.keysym), (rfbKeyCode)Swap32IfLE(msg.qcm.keycode), cl); + return; + case rfbPointerEvent: if (n != sz_rfbPointerEventMsg) { rfbErr("rfbProcessUDPInput: ptr event incorrect length\n"); diff --git a/libvncserver/stats.c b/libvncserver/stats.c index 8af04e3ff..4297d1597 100644 --- a/libvncserver/stats.c +++ b/libvncserver/stats.c @@ -79,6 +79,7 @@ char *messageNameClient2Server(uint32_t type, char *buf, int len) { case rfbPalmVNCSetScaleFactor: snprintf(buf, len, "PalmVNCSetScale"); break; case rfbXvp: snprintf(buf, len, "XvpClientMessage"); break; case rfbSetDesktopSize: snprintf(buf, len, "SetDesktopSize"); break; + case rfbQemuClientMessage: snprintf(buf, len, "QemuClientMessage"); break; default: snprintf(buf, len, "cli2svr-0x%08X", type); @@ -128,6 +129,7 @@ char *encodingName(uint32_t type, char *buf, int len) { case rfbEncodingSupportedMessages: snprintf(buf, len, "SupportedMessage"); break; case rfbEncodingSupportedEncodings: snprintf(buf, len, "SupportedEncoding"); break; case rfbEncodingServerIdentity: snprintf(buf, len, "ServerIdentify"); break; + case rfbEncodingQEMUKeyEvent: snprintf(buf, len, "QEMUKeyEvent"); break; /* The following lookups do not report in stats */ case rfbEncodingCompressLevel0: snprintf(buf, len, "CompressLevel0"); break; diff --git a/rfb/rfb.h b/rfb/rfb.h index f2e0b679d..1b93b5c54 100644 --- a/rfb/rfb.h +++ b/rfb/rfb.h @@ -100,7 +100,7 @@ enum rfbSocketState { RFB_SOCKET_SHUTDOWN }; -typedef void (*rfbKbdAddEventProcPtr) (rfbBool down, rfbKeySym keySym, struct _rfbClientRec* cl); +typedef void (*rfbKbdAddEventProcPtr) (rfbBool down, rfbKeySym keySym, rfbKeyCode keyCode, struct _rfbClientRec* cl); typedef void (*rfbKbdReleaseAllKeysProcPtr) (struct _rfbClientRec* cl); typedef void (*rfbPtrAddEventProcPtr) (int buttonMask, int x, int y, struct _rfbClientRec* cl); typedef void (*rfbSetXCutTextProcPtr) (char* str,int len, struct _rfbClientRec* cl); @@ -608,6 +608,7 @@ typedef struct _rfbClientRec { rfbBool useRichCursorEncoding; /**< rfbEncodingRichCursor is preferred */ rfbBool cursorWasChanged; /**< cursor shape update should be sent */ rfbBool cursorWasMoved; /**< cursor position update should be sent */ + rfbBool enableQemuKeyEvent; /**< client supports QemuKeyEvent */ int cursorX,cursorY; /**< the coordinates of the cursor, if enableCursorShapeUpdates = FALSE */ diff --git a/rfb/rfbproto.h b/rfb/rfbproto.h index 3cf018b1b..955e0359d 100644 --- a/rfb/rfbproto.h +++ b/rfb/rfbproto.h @@ -113,6 +113,7 @@ typedef int8_t rfbBool; #endif typedef uint32_t rfbKeySym; +typedef uint32_t rfbKeyCode; typedef uint32_t rfbPixel; #ifdef LIBVNCSERVER_NEED_INADDR_T @@ -427,6 +428,7 @@ typedef struct { /* SetDesktopSize client -> server message */ #define rfbSetDesktopSize 251 +#define rfbQemuClientMessage 255 @@ -467,6 +469,9 @@ typedef struct { /* Xvp pseudo-encoding */ #define rfbEncodingXvp 0xFFFFFECB + /* QemuKeyEvent pseudo-encoding */ +#define rfbEncodingQEMUKeyEvent 0xFFFFFEFE + /* * Special encoding numbers: * 0xFFFFFD00 .. 0xFFFFFD05 -- subsampling level @@ -1490,6 +1495,16 @@ typedef struct _rfbSetSWMsg { #define sz_rfbSetSWMsg 6 +typedef struct _rfbQemuClientMsg { + uint8_t type; /* always rfbQemuClientMessage */ + uint8_t subtype; + uint16_t down_flag; + uint32_t keysym; + uint32_t keycode; +} rfbQemuClientMsg; + +#define sz_rfbQemuClientMsg 12 + /*----------------------------------------------------------------------------- * Union of all client->server messages. @@ -1512,6 +1527,7 @@ typedef union { rfbTextChatMsg tc; rfbXvpMsg xvp; rfbSetDesktopSizeMsg sdm; + rfbQemuClientMsg qcm; } rfbClientToServerMsg; /*