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
74 changes: 72 additions & 2 deletions vncviewer/KeyboardWin32.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
}

Expand Down Expand Up @@ -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) {
Expand All @@ -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;
}
Expand All @@ -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);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this comes from Windows? We should be prepared for bugs there and not just crash. A check and return is more appropriate.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is a provable invariant. Added comments.

// 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.
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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]);

Expand Down
2 changes: 2 additions & 0 deletions vncviewer/KeyboardWin32.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ class KeyboardWin32 : public Keyboard
bool altGrArmed;
unsigned int altGrCtrlTime;

uint32_t vkPacketHighSurrogate;

bool leftShiftDown;
bool rightShiftDown;
};
Expand Down
14 changes: 12 additions & 2 deletions vncviewer/keysym2ucs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down