Blame SOURCES/tigervnc-add-new-keycodes-for-unknown-keysyms.patch

8a9f08
From ccbd491fa48f1c43daeb1a6c5ee91a1a8fa3db88 Mon Sep 17 00:00:00 2001
8a9f08
From: Jan Grulich <jgrulich@redhat.com>
8a9f08
Date: Tue, 9 Aug 2022 14:31:07 +0200
8a9f08
Subject: [PATCH] x0vncserver: add new keysym in case we don't find a matching
8a9f08
 keycode
8a9f08
8a9f08
We might often fail to find a matching X11 keycode when the client has
8a9f08
a different keyboard layout and end up with no key event. To avoid a
8a9f08
failure we add it as a new keysym/keycode pair so the next time a keysym
8a9f08
from the client that is unknown to the server is send, we will find a
8a9f08
match and proceed with key event. This is same behavior used in Xvnc or
8a9f08
x11vnc, although Xvnc has more advanced mapping from keysym to keycode.
8a9f08
---
8a9f08
 unix/x0vncserver/XDesktop.cxx | 121 +++++++++++++++++++++++++++++++++-
8a9f08
 unix/x0vncserver/XDesktop.h   |   4 ++
8a9f08
 2 files changed, 122 insertions(+), 3 deletions(-)
8a9f08
8a9f08
diff --git a/unix/x0vncserver/XDesktop.cxx b/unix/x0vncserver/XDesktop.cxx
8a9f08
index f2046e43e..933998f05 100644
8a9f08
--- a/unix/x0vncserver/XDesktop.cxx
8a9f08
+++ b/unix/x0vncserver/XDesktop.cxx
8a9f08
@@ -31,6 +31,7 @@
8a9f08
 #include <x0vncserver/XDesktop.h>
8a9f08
8a9f08
 #include <X11/XKBlib.h>
8a9f08
+#include <X11/Xutil.h>
8a9f08
 #ifdef HAVE_XTEST
8a9f08
 #include <X11/extensions/XTest.h>
8a9f08
 #endif
8a9f08
@@ -50,6 +51,7 @@ void vncSetGlueContext(Display *dpy, void *res);
8a9f08
 #include <x0vncserver/Geometry.h>
8a9f08
 #include <x0vncserver/XPixelBuffer.h>
8a9f08
8a9f08
+using namespace std;
8a9f08
 using namespace rfb;
8a9f08
8a9f08
 extern const unsigned short code_map_qnum_to_xorgevdev[];
8a9f08
@@ -264,6 +266,9 @@ void XDesktop::start(VNCServer* vs) {
8a9f08
 void XDesktop::stop() {
8a9f08
   running = false;
8a9f08
8a9f08
+  // Delete added keycodes
8a9f08
+  deleteAddedKeysyms(dpy);
8a9f08
+
8a9f08
 #ifdef HAVE_XDAMAGE
8a9f08
   if (haveDamage)
8a9f08
     XDamageDestroy(dpy, damage);
8a9f08
@@ -383,6 +388,118 @@ KeyCode XDesktop::XkbKeysymToKeycode(Display* dpy, KeySym keysym) {
8a9f08
 }
8a9f08
 #endif
8a9f08
8a9f08
+KeyCode XDesktop::addKeysym(Display* dpy, KeySym keysym)
8a9f08
+{
8a9f08
+  int types[1];
8a9f08
+  unsigned int key;
8a9f08
+  XkbDescPtr xkb;
8a9f08
+  XkbMapChangesRec changes;
8a9f08
+  KeySym *syms;
8a9f08
+  KeySym upper, lower;
8a9f08
+
8a9f08
+  xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
8a9f08
+
8a9f08
+  if (!xkb)
8a9f08
+    return 0;
8a9f08
+
8a9f08
+  for (key = xkb->max_key_code; key >= xkb->min_key_code; key--) {
8a9f08
+    if (XkbKeyNumGroups(xkb, key) == 0)
8a9f08
+      break;
8a9f08
+  }
8a9f08
+
8a9f08
+  if (key < xkb->min_key_code)
8a9f08
+    return 0;
8a9f08
+
8a9f08
+  memset(&changes, 0, sizeof(changes));
8a9f08
+
8a9f08
+  XConvertCase(keysym, &lower, &upper);
8a9f08
+
8a9f08
+  if (upper == lower)
8a9f08
+    types[XkbGroup1Index] = XkbOneLevelIndex;
8a9f08
+  else
8a9f08
+    types[XkbGroup1Index] = XkbAlphabeticIndex;
8a9f08
+
8a9f08
+  XkbChangeTypesOfKey(xkb, key, 1, XkbGroup1Mask, types, &changes);
8a9f08
+
8a9f08
+  syms = XkbKeySymsPtr(xkb,key);
8a9f08
+  if (upper == lower)
8a9f08
+    syms[0] = keysym;
8a9f08
+  else {
8a9f08
+    syms[0] = lower;
8a9f08
+    syms[1] = upper;
8a9f08
+  }
8a9f08
+
8a9f08
+  changes.changed |= XkbKeySymsMask;
8a9f08
+  changes.first_key_sym = key;
8a9f08
+  changes.num_key_syms = 1;
8a9f08
+
8a9f08
+  if (XkbChangeMap(dpy, xkb, &changes)) {
8a9f08
+    vlog.info("Added unknown keysym %s to keycode %d", XKeysymToString(keysym), key);
8a9f08
+    addedKeysyms[keysym] = key;
8a9f08
+    return key;
8a9f08
+  }
8a9f08
+
8a9f08
+  return 0;
8a9f08
+}
8a9f08
+
8a9f08
+void XDesktop::deleteAddedKeysyms(Display* dpy) {
8a9f08
+  XkbDescPtr xkb;
8a9f08
+  xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
8a9f08
+
8a9f08
+  if (!xkb)
8a9f08
+    return;
8a9f08
+
8a9f08
+  XkbMapChangesRec changes;
8a9f08
+  memset(&changes, 0, sizeof(changes));
8a9f08
+
8a9f08
+  KeyCode lowestKeyCode = xkb->max_key_code;
8a9f08
+  KeyCode highestKeyCode = xkb->min_key_code;
8a9f08
+  std::map<KeySym, KeyCode>::iterator it;
8a9f08
+  for (it = addedKeysyms.begin(); it != addedKeysyms.end(); it++) {
8a9f08
+    if (XkbKeyNumGroups(xkb, it->second) != 0) {
8a9f08
+      // Check if we are removing keysym we added ourself
8a9f08
+      if (XkbKeysymToKeycode(dpy, it->first) != it->second)
8a9f08
+        continue;
8a9f08
+
8a9f08
+      XkbChangeTypesOfKey(xkb, it->second, 0, XkbGroup1Mask, NULL, &changes);
8a9f08
+
8a9f08
+      if (it->second < lowestKeyCode)
8a9f08
+        lowestKeyCode = it->second;
8a9f08
+
8a9f08
+      if (it->second > highestKeyCode)
8a9f08
+        highestKeyCode = it->second;
8a9f08
+    }
8a9f08
+  }
8a9f08
+
8a9f08
+  changes.changed |= XkbKeySymsMask;
8a9f08
+  changes.first_key_sym = lowestKeyCode;
8a9f08
+  changes.num_key_syms = highestKeyCode - lowestKeyCode + 1;
8a9f08
+  XkbChangeMap(dpy, xkb, &changes);
8a9f08
+
8a9f08
+  addedKeysyms.clear();
8a9f08
+}
8a9f08
+
8a9f08
+KeyCode XDesktop::keysymToKeycode(Display* dpy, KeySym keysym) {
8a9f08
+  int keycode = 0;
8a9f08
+
8a9f08
+  // XKeysymToKeycode() doesn't respect state, so we have to use
8a9f08
+  // something slightly more complex
8a9f08
+  keycode = XkbKeysymToKeycode(dpy, keysym);
8a9f08
+
8a9f08
+  if (keycode != 0)
8a9f08
+    return keycode;
8a9f08
+
8a9f08
+  // TODO: try to further guess keycode with all possible mods as Xvnc does
8a9f08
+
8a9f08
+  keycode = addKeysym(dpy, keysym);
8a9f08
+
8a9f08
+  if (keycode == 0)
8a9f08
+    vlog.error("Failure adding new keysym 0x%lx", keysym);
8a9f08
+
8a9f08
+  return keycode;
8a9f08
+}
8a9f08
+
8a9f08
+
8a9f08
 void XDesktop::keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down) {
8a9f08
 #ifdef HAVE_XTEST
8a9f08
   int keycode = 0;
8a9f08
@@ -398,9 +515,7 @@ void XDesktop::keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down) {
8a9f08
     if (pressedKeys.find(keysym) != pressedKeys.end())
8a9f08
       keycode = pressedKeys[keysym];
8a9f08
     else {
8a9f08
-      // XKeysymToKeycode() doesn't respect state, so we have to use
8a9f08
-      // something slightly more complex
8a9f08
-      keycode = XkbKeysymToKeycode(dpy, keysym);
8a9f08
+      keycode = keysymToKeycode(dpy, keysym);
8a9f08
     }
8a9f08
   }
8a9f08
8a9f08
diff --git a/unix/x0vncserver/XDesktop.h b/unix/x0vncserver/XDesktop.h
8a9f08
index 840d43316..6ebcd9f8a 100644
8a9f08
--- a/unix/x0vncserver/XDesktop.h
8a9f08
+++ b/unix/x0vncserver/XDesktop.h
8a9f08
@@ -55,6 +55,9 @@ class XDesktop : public rfb::SDesktop,
8a9f08
                                const char* userName);
8a9f08
   virtual void pointerEvent(const rfb::Point& pos, int buttonMask);
8a9f08
   KeyCode XkbKeysymToKeycode(Display* dpy, KeySym keysym);
8a9f08
+  KeyCode addKeysym(Display* dpy, KeySym keysym);
8a9f08
+  void deleteAddedKeysyms(Display* dpy);
8a9f08
+  KeyCode keysymToKeycode(Display* dpy, KeySym keysym);
8a9f08
   virtual void keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down);
8a9f08
   virtual void clientCutText(const char* str);
8a9f08
   virtual unsigned int setScreenLayout(int fb_width, int fb_height,
8a9f08
@@ -78,6 +81,7 @@ class XDesktop : public rfb::SDesktop,
8a9f08
   bool haveXtest;
8a9f08
   bool haveDamage;
8a9f08
   int maxButtons;
8a9f08
+  std::map<KeySym, KeyCode> addedKeysyms;
8a9f08
   std::map<KeySym, KeyCode> pressedKeys;
8a9f08
   bool running;
8a9f08
 #ifdef HAVE_XDAMAGE