|
|
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
|