From 08802167a96b4023e9d68af1a0df5e36a83bc9c9 Mon Sep 17 00:00:00 2001 From: Axel Svensson Date: Fri, 18 Oct 2024 01:07:25 +0200 Subject: [PATCH 1/2] Support VK_PACKET in vncviewer on Windows Fixes #1847 Co-authored-by: Pierre Ossman --- vncviewer/KeyboardWin32.cxx | 74 ++++++++++++++++++++++++++++++++++++- vncviewer/KeyboardWin32.h | 2 + 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/vncviewer/KeyboardWin32.cxx b/vncviewer/KeyboardWin32.cxx index aefebeff26..3eac5bd2fc 100644 --- a/vncviewer/KeyboardWin32.cxx +++ b/vncviewer/KeyboardWin32.cxx @@ -53,6 +53,9 @@ // Used to detect fake input (0xaa is not a real key) static const WORD SCAN_FAKE = 0xaa; +// Fake scan code to represent VK_PACKET +static const int SCAN_VK_PACKET = 0x1ff; + static core::LogWriter vlog("KeyboardWin32"); // Layout independent keys @@ -171,7 +174,8 @@ static const UINT vkey_map_ko[][3] = { KeyboardWin32::KeyboardWin32(KeyboardHandler* handler_) : Keyboard(handler_), cachedHasAltGr(false), currentLayout(nullptr), - altGrArmed(false), leftShiftDown(false), rightShiftDown(false) + altGrArmed(false), vkPacketHighSurrogate(0), leftShiftDown(false), + rightShiftDown(false) { } @@ -239,6 +243,9 @@ bool KeyboardWin32::handleEvent(const void* event) return true; } + if (vKey == VK_PACKET) + systemKeyCode = SCAN_VK_PACKET; + // Windows sets the scan code to 0x00 for multimedia keys, so we // have to do a reverse lookup based on the vKey. if (systemKeyCode == 0x00) { @@ -252,7 +259,7 @@ bool KeyboardWin32::handleEvent(const void* event) } } - if (systemKeyCode & ~0x7f) { + if (systemKeyCode != SCAN_VK_PACKET && (systemKeyCode & ~0x7f)) { vlog.error(_("Invalid scan code 0x%02x"), (int)systemKeyCode); return true; } @@ -273,6 +280,48 @@ bool KeyboardWin32::handleEvent(const void* event) keySym = translateVKey(vKey, isExtended, state); + // VK_PACKET Surrogate pair handling + if (vKey == VK_PACKET && ((keySym | 0x7ff) == 0x0100dfff)) { + unsigned ucsCode = keySym & 0xffff; // keySym == 0b11011xxxxxxxxxxx + if ((ucsCode & 0xfc00) == 0xd800) { // keySym == 0b110110xxxxxxxxxx + // We have received a high surrogate code unit. Remember it and wait for + // the low surrogate which should come immediately after. + if (vkPacketHighSurrogate) { + // High surrogate followed by another high surrogate + vlog.error("%s: 0x%04x 0x%04x", + _("Unmatched UTF-16 surrogate pair through VK_PACKET"), + vkPacketHighSurrogate, ucsCode); + } + vkPacketHighSurrogate = ucsCode; + return true; + } else { + // keySym == 0b11011xxxxxxxxxxx && + // keySym != 0b110110xxxxxxxxxx implies + // keySym == 0b110111xxxxxxxxxx. + // Let's assert this provable invariant. + assert((ucsCode & 0xfc00) == 0xdc00); + // We have received a low surrogate code unit. We should have a high + // surrogate saved that we can use to calculate the code point. + if (!vkPacketHighSurrogate) { + // Low surrogate not directly preceded by a high surrogate + vlog.error("%s: 0x%04x", + _("Unmatched UTF-16 surrogate pair through VK_PACKET"), + ucsCode); + return true; + } + uint32_t codePoint = (((vkPacketHighSurrogate & 0x03ff) << 10) | + (ucsCode & 0x03ff)) + 0x010000; + vkPacketHighSurrogate = 0; + keySym = ucs2keysym(codePoint); + } + } else if (vkPacketHighSurrogate) { + // High surrogate not directly followed by a low surrogate + vlog.error("%s: 0x%04x", + _("Unmatched UTF-16 surrogate pair through VK_PACKET"), + vkPacketHighSurrogate); + vkPacketHighSurrogate = 0; + } + if (keySym == NoSymbol) { // Most Ctrl+Alt combinations will fail to produce a symbol, so // try it again with Ctrl unconditionally disabled. @@ -308,6 +357,11 @@ bool KeyboardWin32::handleEvent(const void* event) handler->handleKeyPress(systemKeyCode, keyCode, keySym); + // While VK_PACKET does deliver a key release immediately after key down, we + // gain nothing from relying on it. + if (systemKeyCode == SCAN_VK_PACKET) + handler->handleKeyRelease(systemKeyCode); + // We don't get reliable WM_KEYUP for these switch (keySym) { case XK_Zenkaku_Hankaku: @@ -351,6 +405,11 @@ bool KeyboardWin32::handleEvent(const void* event) return 1; } + if (vKey == VK_PACKET) { + // Release of VK_PACKET handled in WM_KEYDOWN branch above. + return true; + } + if (systemKeyCode == 0x00) systemKeyCode = MapVirtualKey(vKey, MAPVK_VK_TO_VSC); if (isExtended) @@ -586,6 +645,10 @@ uint32_t KeyboardWin32::translateSystemKeyCode(int systemKeyCode) if (systemKeyCode == 0xc5) return 0x45; + // VK_PACKET only has a fake key code, and should be sent to server as 0. + if (systemKeyCode == SCAN_VK_PACKET) + return 0; + return systemKeyCode; } @@ -670,6 +733,13 @@ uint32_t KeyboardWin32::translateVKey(unsigned vkey, bool extended, // on Arabic layout ret = ToUnicode(vkey, 0, state, wstr, sizeof(wstr)/sizeof(wstr[0]), 0); + if (vkey == VK_PACKET && ret == 1 && ((wstr[0] | 0x7ff) == 0xdfff)) { + // ucs2keysym correctly refuses to translate surrogate code units. They are + // invalid code points and invalid keysyms. Here, they are used only as + // intermediate values to be picked up by handleEvent(). + return (unsigned)wstr[0] | 0x01000000; + } + if (ret == 1) return ucs2keysym(wstr[0]); diff --git a/vncviewer/KeyboardWin32.h b/vncviewer/KeyboardWin32.h index dd1f64c3d1..f1881773f8 100644 --- a/vncviewer/KeyboardWin32.h +++ b/vncviewer/KeyboardWin32.h @@ -54,6 +54,8 @@ class KeyboardWin32 : public Keyboard bool altGrArmed; unsigned int altGrCtrlTime; + uint32_t vkPacketHighSurrogate; + bool leftShiftDown; bool rightShiftDown; }; From 01d30f8db7acafbd9ad2310375e3318c394e7ac2 Mon Sep 17 00:00:00 2001 From: Axel Svensson Date: Fri, 7 Aug 2026 00:28:14 +0200 Subject: [PATCH 2/2] Fix ucs2keysym() to reject surrogate and private use code points --- vncviewer/keysym2ucs.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/vncviewer/keysym2ucs.c b/vncviewer/keysym2ucs.c index 6607e3065c..c2dc05f6b0 100644 --- a/vncviewer/keysym2ucs.c +++ b/vncviewer/keysym2ucs.c @@ -167,8 +167,18 @@ unsigned ucs2keysym(unsigned ucs) if (keysym != NoSymbol) return keysym; - /* us the directly encoded 24-bit UCS character */ - if ((ucs & 0xff000000) == 0) + /* surrogates? */ + if (0xd800 <= ucs && ucs <= 0xdfff) + return NoSymbol; + + /* private use? */ + if ((0xe000 <= ucs && ucs <= 0xf8ff) || + (0x0f0000 <= ucs && ucs <= 0x0ffffd) || + (0x100000 <= ucs && ucs <= 0x10fffd)) + return NoSymbol; + + /* ucs is a directly encoded 21-bit Unicode character */ + if (ucs <= 0x10ffff) return ucs | 0x01000000; /* no matching keysym value found */