From dc2da2dfefbfd4e67dd3733a917a61dc2a901b79 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/Viewport.cxx | 62 ++++++++++++++++++++++++++++++++++++++++++ vncviewer/Viewport.h | 1 + vncviewer/keysym2ucs.c | 4 +-- 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx index e29c877cf5..7a26a168de 100644 --- a/vncviewer/Viewport.cxx +++ b/vncviewer/Viewport.cxx @@ -120,6 +120,7 @@ Viewport::Viewport(int w, int h, const rfb::PixelFormat& /*serverPF*/, CConn* cc lastPointerPos(0, 0), lastButtonMask(0), #ifdef WIN32 altGrArmed(false), + vkPacketHighSurrogate(0), #endif firstLEDState(true), pendingClientClipboard(false), menuCtrlKey(false), menuAltKey(false), cursor(nullptr) @@ -971,6 +972,12 @@ int Viewport::handleSystemEvent(void *event, void *data) keyCode = 0x38; } + // VK_PACKET handling: Translate to WM_*CHAR message, handled below. + if (vKey == VK_PACKET) { + TranslateMessage(msg); + return 1; + } + // Windows doesn't have a proper AltGr, but handles it using fake // Ctrl+Alt. However the remote end might not be Windows, so we need // to merge those in to a single AltGr event. We detect this case @@ -1086,6 +1093,12 @@ int Viewport::handleSystemEvent(void *event, void *data) keyCode = 0x38; } + // VK_PACKET handling: Translate to WM_*CHAR message, handled below. + if (vKey == VK_PACKET) { + TranslateMessage(msg); + return 1; + } + // We can't get a release in the middle of an AltGr sequence, so // abort that detection if (self->altGrArmed) @@ -1118,6 +1131,55 @@ int Viewport::handleSystemEvent(void *event, void *data) self->handleKeyRelease(0x36); } + return 1; + } else if ((msg->message == WM_CHAR) || (msg->message == WM_DEADCHAR) || + (msg->message == WM_SYSCHAR) || (msg->message == WM_SYSDEADCHAR)) { + // Windows will send VK_PACKET key events if it doesn't have a physical key + // associated with the event (e.g. from a mobile device virtual keyboard). + // After translating a keydown/keyup event pair with VK_PACKET as vKey, a + // WM_*CHAR message will be issued containing a UTF-16 code unit. Unicode + // characters outside of the range U+0000 - U+ffff (Basic Multilingual + // Plane, BMP), are encoded as a pair of UTF-16 code units that will need to + // be synthesized into one Unicode code point. + uint32_t ucsCode = msg->wParam; + if ((ucsCode & 0xfc00) == 0xd800) { + // We have received a high surrogate code unit. Remember it and wait for + // the low surrogate which should come immediately after. + if (self->vkPacketHighSurrogate) { + vlog.error(_("Unmatched UTF-16 surrogate pair through VK_PACKET, " + "codes: 0x%04x, 0x%04x"), + self->vkPacketHighSurrogate, ucsCode); + } + self->vkPacketHighSurrogate = ucsCode; + return 1; + } + uint32_t codePoint = 0; + if ((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 (!self->vkPacketHighSurrogate) { + vlog.error(_("Unmatched UTF-16 surrogate pair through VK_PACKET, " + "code: 0x%04x"), + ucsCode); + return 1; + } + codePoint = (((self->vkPacketHighSurrogate & 0x03ff) << 10) | + (ucsCode & 0x03ff)) + 0x010000; + } else { + // BMP character. Unicode characters in this range are encoded as a + // single UTF-16 code unit. + if (self->vkPacketHighSurrogate) { + vlog.error(_("Unmatched UTF-16 surrogate pair through VK_PACKET, " + "codes: 0x%04x, U+%04x"), + self->vkPacketHighSurrogate, ucsCode); + self->vkPacketHighSurrogate = 0; + } + codePoint = ucsCode; + } + uint32_t keySym = ucs2keysym(codePoint); + uint32_t keyCode = 0x100 + keySym; // Fake key code + self->handleKeyPress(keyCode, keySym); + self->handleKeyRelease(keyCode); return 1; } #elif defined(__APPLE__) diff --git a/vncviewer/Viewport.h b/vncviewer/Viewport.h index 5f4c1ca7f1..54dded094d 100644 --- a/vncviewer/Viewport.h +++ b/vncviewer/Viewport.h @@ -118,6 +118,7 @@ class Viewport : public Fl_Widget, public EmulateMB { #ifdef WIN32 bool altGrArmed; + uint32_t vkPacketHighSurrogate; unsigned int altGrCtrlTime; #endif diff --git a/vncviewer/keysym2ucs.c b/vncviewer/keysym2ucs.c index 6607e3065c..3731482a33 100644 --- a/vncviewer/keysym2ucs.c +++ b/vncviewer/keysym2ucs.c @@ -167,8 +167,8 @@ unsigned ucs2keysym(unsigned ucs) if (keysym != NoSymbol) return keysym; - /* us the directly encoded 24-bit UCS character */ - if ((ucs & 0xff000000) == 0) + /* ucs is a directly encoded 21-bit Unicode character */ + if (ucs <= 0x10ffff && ((ucs & 0xfff800) != 0x00d800)) return ucs | 0x01000000; /* no matching keysym value found */ From d7956cad50d80ec666291fae725067afa086c4a4 Mon Sep 17 00:00:00 2001 From: Axel Svensson Date: Tue, 22 Oct 2024 17:10:40 +0200 Subject: [PATCH 2/2] Review fixes --- vncviewer/Viewport.cxx | 2 +- vncviewer/keysym2ucs.c | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/vncviewer/Viewport.cxx b/vncviewer/Viewport.cxx index 7a26a168de..0d84154de1 100644 --- a/vncviewer/Viewport.cxx +++ b/vncviewer/Viewport.cxx @@ -1177,7 +1177,7 @@ int Viewport::handleSystemEvent(void *event, void *data) codePoint = ucsCode; } uint32_t keySym = ucs2keysym(codePoint); - uint32_t keyCode = 0x100 + keySym; // Fake key code + uint32_t keyCode = 0x01000000 | codePoint; // Fake key code self->handleKeyPress(keyCode, keySym); self->handleKeyRelease(keyCode); return 1; diff --git a/vncviewer/keysym2ucs.c b/vncviewer/keysym2ucs.c index 3731482a33..c2dc05f6b0 100644 --- a/vncviewer/keysym2ucs.c +++ b/vncviewer/keysym2ucs.c @@ -167,8 +167,18 @@ unsigned ucs2keysym(unsigned ucs) if (keysym != NoSymbol) return keysym; + /* 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 && ((ucs & 0xfff800) != 0x00d800)) + if (ucs <= 0x10ffff) return ucs | 0x01000000; /* no matching keysym value found */