From bb305ca3dbbc316c9742d36b0919226e15120add Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 11 Dec 2016 12:41:26 +0100 Subject: [PATCH] Add server side lock key sync heuristic Based on QEMU's behaviour. --- common/rfb/VNCSConnectionST.cxx | 69 +++++++++++++++++++++++++++++++++ common/rfb/VNCServer.h | 4 ++ common/rfb/VNCServerST.cxx | 8 +++- common/rfb/VNCServerST.h | 2 + unix/xserver/hw/vnc/Input.c | 6 --- unix/xserver/hw/vnc/Input.h | 2 - unix/xserver/hw/vnc/InputXKB.c | 17 -------- 7 files changed, 82 insertions(+), 26 deletions(-) diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index 53dd364a..de41e8fc 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -34,10 +34,12 @@ #include #include #include +#include #include #include #include #include +#define XK_LATIN1 #define XK_MISCELLANY #define XK_XKB_KEYS #include @@ -560,6 +562,73 @@ void VNCSConnectionST::keyEvent(rdr::U32 key, bool down) { } } + // Avoid lock keys if we don't know the server state + if ((server->ledState == ledUnknown) && + ((key == XK_Caps_Lock) || + (key == XK_Num_Lock) || + (key == XK_Scroll_Lock))) { + vlog.debug("Ignoring lock key (e.g. caps lock)"); + return; + } + // Always ignore ScrollLock though as we don't have a heuristic + // for that + if (key == XK_Scroll_Lock) { + vlog.debug("Ignoring lock key (e.g. caps lock)"); + return; + } + + if (down && (server->ledState != ledUnknown)) { + // CapsLock synchronisation heuristic + // (this assumes standard interaction between CapsLock the Shift + // keys and normal characters) + if (((key >= XK_A) && (key <= XK_Z)) || + ((key >= XK_a) && (key <= XK_z))) { + bool uppercase, shift, lock; + + uppercase = (key >= XK_A) && (key <= XK_Z); + shift = pressedKeys.find(XK_Shift_L) != pressedKeys.end() || + pressedKeys.find(XK_Shift_R) != pressedKeys.end(); + lock = server->ledState & ledCapsLock; + + if (lock == (uppercase == shift)) { + vlog.debug("Inserting fake CapsLock to get in sync with client"); + server->desktop->keyEvent(XK_Caps_Lock, true); + server->desktop->keyEvent(XK_Caps_Lock, false); + } + } + + // NumLock synchronisation heuristic + // (this is more cautious because of the differences between Unix, + // Windows and macOS) + if (((key >= XK_KP_Home) && (key <= XK_KP_Delete)) || + ((key >= XK_KP_0) && (key <= XK_KP_9)) || + (key == XK_KP_Separator) || (key == XK_KP_Decimal)) { + bool number, shift, lock; + + number = ((key >= XK_KP_0) && (key <= XK_KP_9)) || + (key == XK_KP_Separator) || (key == XK_KP_Decimal); + shift = pressedKeys.find(XK_Shift_L) != pressedKeys.end() || + pressedKeys.find(XK_Shift_R) != pressedKeys.end(); + lock = server->ledState & ledNumLock; + + if (shift) { + // We don't know the appropriate NumLock state for when Shift + // is pressed as it could be one of: + // + // a) A Unix client where Shift negates NumLock + // + // b) A Windows client where Shift only cancels NumLock + // + // c) A macOS client where Shift doesn't have any effect + // + } else if (lock == (number == shift)) { + vlog.debug("Inserting fake NumLock to get in sync with client"); + server->desktop->keyEvent(XK_Num_Lock, true); + server->desktop->keyEvent(XK_Num_Lock, false); + } + } + } + // Turn ISO_Left_Tab into shifted Tab. VNCSConnectionSTShiftPresser shiftPresser(server->desktop); if (key == XK_ISO_Left_Tab) { diff --git a/common/rfb/VNCServer.h b/common/rfb/VNCServer.h index 982a4ff5..c5335ad2 100644 --- a/common/rfb/VNCServer.h +++ b/common/rfb/VNCServer.h @@ -74,6 +74,10 @@ namespace rfb { // setName() tells the server what desktop title to supply to clients virtual void setName(const char* name) = 0; + + // setLEDState() tells the server what the current lock keys LED + // state is + virtual void setLEDState(unsigned int state) = 0; }; } #endif diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index ec5e962f..28f6a62b 100644 --- a/common/rfb/VNCServerST.cxx +++ b/common/rfb/VNCServerST.cxx @@ -58,6 +58,7 @@ #include #include #include +#include #include @@ -74,7 +75,7 @@ LogWriter VNCServerST::connectionsLog("Connections"); VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_) : blHosts(&blacklist), desktop(desktop_), desktopStarted(false), - blockCounter(0), pb(0), + blockCounter(0), pb(0), ledState(ledUnknown), name(strDup(name_)), pointerClient(0), comparer(0), cursor(new Cursor(0, 0, Point(), NULL)), renderedCursorInvalid(false), @@ -458,6 +459,11 @@ void VNCServerST::setCursorPos(const Point& pos) } } +void VNCServerST::setLEDState(unsigned int state) +{ + ledState = state; +} + // Other public methods void VNCServerST::approveConnection(network::Socket* sock, bool accept, diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h index 00f77c73..2dfdbbdb 100644 --- a/common/rfb/VNCServerST.h +++ b/common/rfb/VNCServerST.h @@ -101,6 +101,7 @@ namespace rfb { virtual void setCursor(int width, int height, const Point& hotspot, const rdr::U8* data); virtual void setCursorPos(const Point& p); + virtual void setLEDState(unsigned state); virtual void bell(); @@ -209,6 +210,7 @@ namespace rfb { int blockCounter; PixelBuffer* pb; ScreenSet screenLayout; + unsigned int ledState; CharArray name; diff --git a/unix/xserver/hw/vnc/Input.c b/unix/xserver/hw/vnc/Input.c index 33e8604b..1d7183b9 100644 --- a/unix/xserver/hw/vnc/Input.c +++ b/unix/xserver/hw/vnc/Input.c @@ -437,12 +437,6 @@ void vncKeyboardEvent(KeySym keysym, int down) } } - /* We don't have lock synchronisation... */ - if (vncIsLockModifier(keycode, new_state)) { - LOG_DEBUG("Ignoring lock key (e.g. caps lock)"); - return; - } - /* No matches. Will have to add a new entry... */ if (keycode == 0) { keycode = vncAddKeysym(keysym, state); diff --git a/unix/xserver/hw/vnc/Input.h b/unix/xserver/hw/vnc/Input.h index 11e88710..67b9c8db 100644 --- a/unix/xserver/hw/vnc/Input.h +++ b/unix/xserver/hw/vnc/Input.h @@ -53,8 +53,6 @@ size_t vncReleaseLevelThree(KeyCode *keys, size_t maxKeys); KeyCode vncKeysymToKeycode(KeySym keysym, unsigned state, unsigned *new_state); -int vncIsLockModifier(KeyCode keycode, unsigned state); - int vncIsAffectedByNumLock(KeyCode keycode); KeyCode vncAddKeysym(KeySym keysym, unsigned state); diff --git a/unix/xserver/hw/vnc/InputXKB.c b/unix/xserver/hw/vnc/InputXKB.c index 2a3f7afb..a9bd11d1 100644 --- a/unix/xserver/hw/vnc/InputXKB.c +++ b/unix/xserver/hw/vnc/InputXKB.c @@ -485,23 +485,6 @@ KeyCode vncKeysymToKeycode(KeySym keysym, unsigned state, unsigned *new_state) return 0; } -int vncIsLockModifier(KeyCode keycode, unsigned state) -{ - XkbDescPtr xkb; - XkbAction *act; - - xkb = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc; - - act = XkbKeyActionPtr(xkb, keycode, state); - if (act == NULL) - return 0; - - if (act->type != XkbSA_LockMods) - return 0; - - return 1; -} - int vncIsAffectedByNumLock(KeyCode keycode) { unsigned state; -- 2.39.5