return keycode;
}
+/*
+ * Keeps the list in LRU order by moving the used key to front of the list.
+ */
+static void onKeyUsed(std::list<AddedKeySym> &list, KeyCode usedKeycode) {
+ if (list.empty() || list.front().keycode == usedKeycode)
+ return;
+
+ std::list<AddedKeySym>::iterator it = list.begin();
+ ++it;
+ for (; it != list.end(); ++it) {
+ AddedKeySym item = *it;
+ if (item.keycode == usedKeycode) {
+ list.erase(it);
+ list.push_front(item);
+ break;
+ }
+ }
+}
+
+/*
+ * Returns keycode of oldest item from list of manually added keysyms.
+ * The item is removed from the list.
+ * Returns 0 if no usable keycode is found.
+ */
+KeyCode XDesktop::getReusableKeycode(XkbDescPtr xkb) {
+ while (!addedKeysyms.empty()) {
+ AddedKeySym last = addedKeysyms.back();
+ addedKeysyms.pop_back();
+
+ // Make sure someone else hasn't modified the key
+ if (XkbKeyNumGroups(xkb, last.keycode) > 0 &&
+ XkbKeySymsPtr(xkb, last.keycode)[0] == last.keysym)
+ return last.keycode;
+ }
+ return 0;
+}
KeyCode XDesktop::addKeysym(Display* dpy, KeySym keysym)
{
}
if (key < xkb->min_key_code)
+ key = getReusableKeycode(xkb);
+
+ if (!key)
return 0;
memset(&changes, 0, sizeof(changes));
if (XkbChangeMap(dpy, xkb, &changes)) {
vlog.info("Added unknown keysym %s to keycode %d", XKeysymToString(keysym), key);
- addedKeysyms[keysym] = key;
+ addedKeysyms.push_front({ syms[0], (KeyCode)key });
return key;
}
KeyCode lowestKeyCode = xkb->max_key_code;
KeyCode highestKeyCode = xkb->min_key_code;
- std::map<KeySym, KeyCode>::iterator it;
- for (it = addedKeysyms.begin(); it != addedKeysyms.end(); it++) {
- if (XkbKeyNumGroups(xkb, it->second) != 0) {
- // Check if we are removing keysym we added ourself
- if (XkbKeysymToKeycode(dpy, it->first) != it->second)
- continue;
+ KeyCode keyCode = getReusableKeycode(xkb);
+ while (keyCode != 0) {
+ XkbChangeTypesOfKey(xkb, keyCode, 0, XkbGroup1Mask, NULL, &changes);
- XkbChangeTypesOfKey(xkb, it->second, 0, XkbGroup1Mask, NULL, &changes);
+ if (keyCode < lowestKeyCode)
+ lowestKeyCode = keyCode;
- if (it->second < lowestKeyCode)
- lowestKeyCode = it->second;
+ if (keyCode > highestKeyCode)
+ highestKeyCode = keyCode;
- if (it->second > highestKeyCode)
- highestKeyCode = it->second;
- }
+ keyCode = getReusableKeycode(xkb);
}
// Did we actually find something to remove?
changes.first_key_sym = lowestKeyCode;
changes.num_key_syms = highestKeyCode - lowestKeyCode + 1;
XkbChangeMap(dpy, xkb, &changes);
-
- addedKeysyms.clear();
}
KeyCode XDesktop::keysymToKeycode(Display* dpy, KeySym keysym) {
else
pressedKeys.erase(keysym);
+ if (down)
+ onKeyUsed(addedKeysyms, keycode);
+
vlog.debug("%d %s", keycode, down ? "down" : "up");
XTestFakeKeyEvent(dpy, keycode, down, CurrentTime);
class Geometry;
class XPixelBuffer;
+struct AddedKeySym
+{
+ KeySym keysym;
+ KeyCode keycode;
+};
+
// number of XKb indicator leds to handle
#define XDESKTOP_N_LEDS 3
bool haveXtest;
bool haveDamage;
int maxButtons;
- std::map<KeySym, KeyCode> addedKeysyms;
+ std::list<AddedKeySym> addedKeysyms;
std::map<KeySym, KeyCode> pressedKeys;
bool running;
#ifdef HAVE_XDAMAGE
protected:
#ifdef HAVE_XTEST
KeyCode XkbKeysymToKeycode(Display* dpy, KeySym keysym);
+ KeyCode getReusableKeycode(XkbDescPtr xkb);
KeyCode addKeysym(Display* dpy, KeySym keysym);
void deleteAddedKeysyms(Display* dpy);
KeyCode keysymToKeycode(Display* dpy, KeySym keysym);
/* Now press the actual key */
pressKey(vncKeyboardDev, keycode, TRUE, "keycode");
+ if(down)
+ vncOnKeyUsed(keycode);
+
/* And store the mapping so that we can do a proper release later */
for (i = 0;i < 256;i++) {
if (i == keycode)
int vncIsAffectedByNumLock(KeyCode keycode);
KeyCode vncAddKeysym(KeySym keysym, unsigned state);
+void vncOnKeyUsed(KeyCode usedKeycode);
#ifdef __cplusplus
}
#include <X11/Xlib.h>
#include <X11/Xutil.h>
+#include "list.h"
#include "xkbsrv.h"
#include "xkbstr.h"
#include "eventstr.h"
#endif
};
+typedef struct
+{
+ KeySym keysym;
+ KeyCode keycode;
+ struct xorg_list entry;
+} AddedKeySym;
+
+/*
+ * If a KeySym recieved from client is not mapped to any KeyCode, it needs to be
+ * mapped to an unused KeyCode to generate required key events.
+ *
+ * This list tracks such assignments. A KeyCode from this list can be reused if
+ * we run out of unused KeyCodes.
+ *
+ * Items in this list are maintained in LRU order, with most recently used key
+ * in front.
+ */
+static struct xorg_list addedKeysyms;
+
static void vncXkbProcessDeviceEvent(int screenNum,
InternalEvent *event,
DeviceIntPtr dev);
*/
mieqSetHandler(ET_KeyPress, vncXkbProcessDeviceEvent);
mieqSetHandler(ET_KeyRelease, vncXkbProcessDeviceEvent);
+
+ xorg_list_init(&addedKeysyms);
}
unsigned vncGetKeyboardState(void)
return 1;
}
+static void saveAddedKeysym(KeyCode code, KeySym sym)
+{
+ AddedKeySym* item;
+
+ item = malloc(sizeof(AddedKeySym));
+ if (!item)
+ return;
+
+ item->keycode = code;
+ item->keysym = sym;
+ xorg_list_add(&item->entry, &addedKeysyms);
+}
+
+/*
+ * Keeps the list in LRU order by moving the used key to front of the list.
+ */
+void vncOnKeyUsed(KeyCode usedKeycode)
+{
+ AddedKeySym* it;
+
+ if (xorg_list_is_empty(&addedKeysyms))
+ return;
+
+ it = xorg_list_first_entry(&addedKeysyms, AddedKeySym, entry);
+ if (it->keycode == usedKeycode)
+ return;
+
+ xorg_list_for_each_entry(it, &addedKeysyms, entry) {
+ if (it->keycode == usedKeycode) {
+ xorg_list_del(&it->entry);
+ xorg_list_add(&it->entry, &addedKeysyms);
+ break;
+ }
+ }
+}
+
+/*
+ * Returns keycode of oldest item from list of manually added keysyms.
+ * The item is removed from the list.
+ * Returns 0 if no usable keycode is found.
+ */
+static KeyCode getReusableKeycode(XkbDescPtr xkb)
+{
+ AddedKeySym* last;
+ KeyCode result;
+
+ result = 0;
+ while (result == 0 && !xorg_list_is_empty(&addedKeysyms)) {
+ last = xorg_list_last_entry(&addedKeysyms, AddedKeySym, entry);
+
+ // Make sure someone else hasn't modified the key
+ if (XkbKeyNumGroups(xkb, last->keycode) > 0 &&
+ XkbKeySymsPtr(xkb, last->keycode)[0] == last->keysym &&
+ (xkb->names == NULL || xkb->names->keys[last->keycode].name[0] == 'T'))
+ result = last->keycode;
+
+ xorg_list_del(&last->entry);
+ free(last);
+ }
+ return result;
+}
+
KeyCode vncAddKeysym(KeySym keysym, unsigned state)
{
DeviceIntPtr master;
}
if (key < xkb->min_key_code)
+ key = getReusableKeycode(xkb);
+
+ if (!key)
return 0;
memset(&changes, 0, sizeof(changes));
* Tools like xkbcomp get confused if there isn't a name
* assigned to the keycode we're trying to use.
*/
- if (xkb->names && xkb->names->keys &&
- (xkb->names->keys[key].name[0] == '\0')) {
- xkb->names->keys[key].name[0] = 'I';
+ if (xkb->names && xkb->names->keys) {
+ 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;
XkbSendNotification(master, &changes, &cause);
+ saveAddedKeysym(key, syms[0]);
+
return key;
}