Skip to content

Commit

Permalink
Handle unknown keysyms
Browse files Browse the repository at this point in the history
  • Loading branch information
gujjwal00 committed Mar 4, 2024
1 parent e9d7851 commit c8008e5
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 8 deletions.
174 changes: 174 additions & 0 deletions unix/common/CInput.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@


#include <list>
#include <rfb/LogWriter.h>

#include "CInput.h"


static rfb::LogWriter vlog("CInput");

/*************************************
* TODO
* - Double check "changes" struct type
* - Double check all "changes" are retrieved/propagated to server (name & map)
*/

/************************* Custom key list ****************************/
typedef struct {
KeyCode keyCode;
KeySym keySym;
}AssignedKey;


/**
* Values are stored in 'most recently used' order
*/
static std::list<AssignedKey> assignedKeys;


static Bool isAssignmentStillValid(AssignedKey assignment, XkbDescPtr xkb)
{
return XkbKeyNumGroups(xkb, assignment.keyCode) > 0 &&
XkbKeySymsPtr(xkb, assignment.keyCode)[0] == assignment.keySym &&
(xkb->names == NULL || xkb->names->keys[assignment.keyCode].name[0] == 'T');
}

static void addAssignment(KeyCode code, KeySym sym)
{
vlog.info("Setting custom key assignment: [%d] -> %ld", code, sym);
assignedKeys.push_front({ code, sym });
}

/**
* Returns the least recently used and still valid assignment.
* If no valid assignment found, returns an asssignment with keyCode = 0.
* Returned and invalid assignments are removed from list.
*/
static AssignedKey getReusableAssignment(XkbDescPtr xkb)
{
while (!assignedKeys.empty()) {
AssignedKey last = assignedKeys.back();
assignedKeys.pop_back();
if (isAssignmentStillValid(last, xkb))
return last;
else
vlog.info("Assingnment no longer valid: %d", last.keyCode);
}
return { 0, 0 };
}



static KeyCode getUnusedKey(XkbDescPtr xkb)
{
for (KeyCode key = xkb->max_key_code; key >= xkb->min_key_code; key--)
if (XkbKeyNumGroups(xkb, key) == 0)
return key;
return 0;
}

static void assignKeysymToKey(KeySym keysym, KeyCode key, XkbDescPtr xkb, XkbChangesPtr changes)
{
int types[1];
KeySym* syms;
KeySym upper, lower;

// Assign a custom name
if (xkb->names) {
xkb->names->keys[key].name[0] = 'T';
xkb->names->keys[key].name[1] = '0' + (key / 100) % 10;
xkb->names->keys[key].name[2] = '0' + (key / 10) % 10;
xkb->names->keys[key].name[3] = '0' + (key / 1) % 10;
changes->names.changed |= XkbKeyNamesMask;
changes->names.first_key = key;
changes->names.num_keys = 1;
}

// Update group
XConvertCase(keysym, &lower, &upper);
if (upper == lower)
types[XkbGroup1Index] = XkbOneLevelIndex;
else
types[XkbGroup1Index] = XkbAlphabeticIndex;

XkbChangeTypesOfKey(xkb, key, 1, XkbGroup1Mask, types, &changes->map);

// Assign symbols
syms = XkbKeySymsPtr(xkb, key);
if (upper == lower)
syms[0] = keysym;
else {
syms[0] = lower;
syms[1] = upper;
}

changes->map.changed |= XkbKeySymsMask;
changes->map.first_key_sym = key;
changes->map.num_key_syms = 1;

// Remember assignment
addAssignment(key, syms[0]);
}

KeyCode addKeysymToMap(KeySym keysym, XkbDescPtr xkb, XkbChangesPtr changes)
{
//vlog.info("Trying to add unknown keysym: %ld", keysym);

KeyCode key = getUnusedKey(xkb);
if (!key) {
key = getReusableAssignment(xkb).keyCode;
vlog.info("Got reusable key: %d", key);
}
if (!key)
return 0;

assignKeysymToKey(keysym, key, xkb, changes);

return key;
}

void removeAddedKeysymsFromMap(XkbDescPtr xkb, XkbMapChangesPtr changes) {
KeyCode lowestKeyCode = xkb->max_key_code;
KeyCode highestKeyCode = xkb->min_key_code;

AssignedKey assignment = getReusableAssignment(xkb);
while (assignment.keyCode) {
KeyCode keyCode = assignment.keyCode;

XkbChangeTypesOfKey(xkb, keyCode, 0, XkbGroup1Mask, NULL, changes);

if (keyCode < lowestKeyCode)
lowestKeyCode = keyCode;

if (keyCode > highestKeyCode)
highestKeyCode = keyCode;

// Get next
assignment = getReusableAssignment(xkb);
}

// Did we actually find something to remove?
if (highestKeyCode >= lowestKeyCode) {
changes->changed |= XkbKeySymsMask;
changes->first_key_sym = lowestKeyCode;
changes->num_key_syms = highestKeyCode - lowestKeyCode + 1;
}
}


void onKeyUsed(KeyCode usedKey) {
if (assignedKeys.empty())
return;

// If there a custom assignment for this key, move it to the front
std::list<AssignedKey>::iterator it = assignedKeys.begin();
for (; it != assignedKeys.end(); ++it) {
AssignedKey assignment = *it;
if (assignment.keyCode == usedKey && it != assignedKeys.begin()) {
assignedKeys.erase(it);
assignedKeys.push_front(assignment);
break;
}
}
}
22 changes: 22 additions & 0 deletions unix/common/CInput.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef INPUTCOMMON_H
#define INPUTCOMMON_H

#include <X11/Xlib.h>
#include <X11/XKBlib.h>
#include <X11/Xutil.h>

#ifdef __cplusplus
extern "C" {
#endif
KeyCode addKeysymToMap(KeySym keysym, XkbDescPtr xkb, XkbChangesPtr changes);
void removeAddedKeysymsFromMap(XkbDescPtr xkb, XkbMapChangesPtr changes);

/**
* Should be called whenever client uses a key.
*/
void onKeyUsed(KeyCode key);

#ifdef __cplusplus
}
#endif
#endif /* INPUTCOMMON_H */
3 changes: 2 additions & 1 deletion unix/common/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
add_library(unixcommon STATIC
randr.cxx)
randr.cxx
CInput.cxx)

target_include_directories(unixcommon PUBLIC ${CMAKE_SOURCE_DIR}/common)
target_include_directories(unixcommon PUBLIC ${CMAKE_SOURCE_DIR}/unix/common)
Expand Down
28 changes: 21 additions & 7 deletions unix/x0vncserver/XDesktop.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#ifdef HAVE_XRANDR
#include <X11/extensions/Xrandr.h>
#include <RandrGlue.h>
#include <CInput.h>
extern "C" {
void vncSetGlueContext(Display *dpy, void *res);
}
Expand Down Expand Up @@ -404,19 +405,20 @@ KeyCode XDesktop::XkbKeysymToKeycode(Display* dpy, KeySym keysym) {

KeyCode XDesktop::addKeysym(Display* dpy, KeySym keysym)
{
int types[1];
//int types[1];
unsigned int key;
XkbDescPtr xkb;
XkbMapChangesRec changes;
KeySym *syms;
KeySym upper, lower;
XkbChangesRec changes;
//KeySym *syms;
//KeySym upper, lower;

xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
xkb = XkbGetMap(dpy, XkbAllComponentsMask|XkbNamesMask, XkbUseCoreKbd);

if (!xkb)
return 0;

for (key = xkb->max_key_code; key >= xkb->min_key_code; key--) {
memset(&changes, 0, sizeof(changes));
/* for (key = xkb->max_key_code; key >= xkb->min_key_code; key--) {
if (XkbKeyNumGroups(xkb, key) == 0)
break;
}
Expand Down Expand Up @@ -446,8 +448,11 @@ KeyCode XDesktop::addKeysym(Display* dpy, KeySym keysym)
changes.changed |= XkbKeySymsMask;
changes.first_key_sym = key;
changes.num_key_syms = 1;
*/

if (XkbChangeMap(dpy, xkb, &changes)) {
key = addKeysymToMap(keysym, xkb, &changes);

if (key && XkbChangeMap(dpy, xkb, &changes.map)) {
vlog.info("Added unknown keysym %s to keycode %d", XKeysymToString(keysym), key);
addedKeysyms[keysym] = key;
return key;
Expand All @@ -466,6 +471,7 @@ void XDesktop::deleteAddedKeysyms(Display* dpy) {
XkbMapChangesRec changes;
memset(&changes, 0, sizeof(changes));

/*
KeyCode lowestKeyCode = xkb->max_key_code;
KeyCode highestKeyCode = xkb->min_key_code;
std::map<KeySym, KeyCode>::iterator it;
Expand Down Expand Up @@ -493,6 +499,11 @@ void XDesktop::deleteAddedKeysyms(Display* dpy) {
changes.first_key_sym = lowestKeyCode;
changes.num_key_syms = highestKeyCode - lowestKeyCode + 1;
XkbChangeMap(dpy, xkb, &changes);
*/

removeAddedKeysymsFromMap(xkb, &changes);
if (changes.num_key_syms > 0)
XkbChangeMap(dpy, xkb, &changes);

addedKeysyms.clear();
}
Expand Down Expand Up @@ -550,6 +561,9 @@ void XDesktop::keyEvent(uint32_t keysym, uint32_t xtcode, bool down) {

vlog.debug("%d %s", keycode, down ? "down" : "up");

if(down)
onKeyUsed(keycode);

XTestFakeKeyEvent(dpy, keycode, down, CurrentTime);
#else
(void)keysym;
Expand Down

0 comments on commit c8008e5

Please sign in to comment.