aboutsummaryrefslogtreecommitdiffstats
path: root/unix/xserver/hw/vnc/InputXKB.c
diff options
context:
space:
mode:
authorPierre Ossman <ossman@cendio.se>2015-01-27 13:24:29 +0100
committerPierre Ossman <ossman@cendio.se>2015-01-27 13:24:29 +0100
commit7e4da45422ecfb0746c5c79593a642a275142643 (patch)
treedbd4860596318af73f2cc85d1f1cd7b82444c46e /unix/xserver/hw/vnc/InputXKB.c
parent50a0427925d50804ec66d0d11a0ee10dcb572c83 (diff)
parent9018af44dadebb5ed50ba9336007fc9da653a9a0 (diff)
downloadtigervnc-7e4da45422ecfb0746c5c79593a642a275142643.tar.gz
tigervnc-7e4da45422ecfb0746c5c79593a642a275142643.zip
Merge branch 'xorgheaders' of https://github.com/CendioOssman/tigervnc
Diffstat (limited to 'unix/xserver/hw/vnc/InputXKB.c')
-rw-r--r--unix/xserver/hw/vnc/InputXKB.c662
1 files changed, 662 insertions, 0 deletions
diff --git a/unix/xserver/hw/vnc/InputXKB.c b/unix/xserver/hw/vnc/InputXKB.c
new file mode 100644
index 00000000..e639d5ee
--- /dev/null
+++ b/unix/xserver/hw/vnc/InputXKB.c
@@ -0,0 +1,662 @@
+/* Copyright (C) 2009 TightVNC Team
+ * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright 2013-2015 Pierre Ossman for Cendio AB
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifdef HAVE_DIX_CONFIG_H
+#include <dix-config.h>
+#endif
+
+#include "xorg-version.h"
+
+#if XORG >= 17
+
+#include <stdio.h>
+
+#include <X11/keysym.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include "xkbsrv.h"
+#include "xkbstr.h"
+#include "eventstr.h"
+#include "scrnintstr.h"
+#include "mi.h"
+
+#include "Input.h"
+
+#ifndef KEYBOARD_OR_FLOAT
+#define KEYBOARD_OR_FLOAT MASTER_KEYBOARD
+#endif
+
+extern DeviceIntPtr vncKeyboardDev;
+
+static void vncXkbProcessDeviceEvent(int screenNum,
+ InternalEvent *event,
+ DeviceIntPtr dev);
+
+/* Stolen from libX11 */
+static Bool
+XkbTranslateKeyCode(register XkbDescPtr xkb, KeyCode key,
+ register unsigned int mods, unsigned int *mods_rtrn,
+ KeySym *keysym_rtrn)
+{
+ XkbKeyTypeRec *type;
+ int col,nKeyGroups;
+ unsigned preserve,effectiveGroup;
+ KeySym *syms;
+
+ if (mods_rtrn!=NULL)
+ *mods_rtrn = 0;
+
+ nKeyGroups= XkbKeyNumGroups(xkb,key);
+ if ((!XkbKeycodeInRange(xkb,key))||(nKeyGroups==0)) {
+ if (keysym_rtrn!=NULL)
+ *keysym_rtrn = NoSymbol;
+ return False;
+ }
+
+ syms = XkbKeySymsPtr(xkb,key);
+
+ /* find the offset of the effective group */
+ col = 0;
+ effectiveGroup= XkbGroupForCoreState(mods);
+ if ( effectiveGroup>=nKeyGroups ) {
+ unsigned groupInfo= XkbKeyGroupInfo(xkb,key);
+ switch (XkbOutOfRangeGroupAction(groupInfo)) {
+ default:
+ effectiveGroup %= nKeyGroups;
+ break;
+ case XkbClampIntoRange:
+ effectiveGroup = nKeyGroups-1;
+ break;
+ case XkbRedirectIntoRange:
+ effectiveGroup = XkbOutOfRangeGroupNumber(groupInfo);
+ if (effectiveGroup>=nKeyGroups)
+ effectiveGroup= 0;
+ break;
+ }
+ }
+ col= effectiveGroup*XkbKeyGroupsWidth(xkb,key);
+ type = XkbKeyKeyType(xkb,key,effectiveGroup);
+
+ preserve= 0;
+ if (type->map) { /* find the column (shift level) within the group */
+ register int i;
+ register XkbKTMapEntryPtr entry;
+ for (i=0,entry=type->map;i<type->map_count;i++,entry++) {
+ if ((entry->active)&&((mods&type->mods.mask)==entry->mods.mask)) {
+ col+= entry->level;
+ if (type->preserve)
+ preserve= type->preserve[i].mask;
+ break;
+ }
+ }
+ }
+
+ if (keysym_rtrn!=NULL)
+ *keysym_rtrn= syms[col];
+ if (mods_rtrn)
+ *mods_rtrn= type->mods.mask&(~preserve);
+
+ return (syms[col]!=NoSymbol);
+}
+
+static XkbAction *XkbKeyActionPtr(XkbDescPtr xkb, KeyCode key, unsigned int mods)
+{
+ XkbKeyTypeRec *type;
+ int col,nKeyGroups;
+ unsigned effectiveGroup;
+ XkbAction *acts;
+
+ if (!XkbKeyHasActions(xkb, key))
+ return NULL;
+
+ nKeyGroups= XkbKeyNumGroups(xkb,key);
+ if ((!XkbKeycodeInRange(xkb,key))||(nKeyGroups==0))
+ return NULL;
+
+ acts = XkbKeyActionsPtr(xkb,key);
+
+ /* find the offset of the effective group */
+ col = 0;
+ effectiveGroup= XkbGroupForCoreState(mods);
+ if ( effectiveGroup>=nKeyGroups ) {
+ unsigned groupInfo= XkbKeyGroupInfo(xkb,key);
+ switch (XkbOutOfRangeGroupAction(groupInfo)) {
+ default:
+ effectiveGroup %= nKeyGroups;
+ break;
+ case XkbClampIntoRange:
+ effectiveGroup = nKeyGroups-1;
+ break;
+ case XkbRedirectIntoRange:
+ effectiveGroup = XkbOutOfRangeGroupNumber(groupInfo);
+ if (effectiveGroup>=nKeyGroups)
+ effectiveGroup= 0;
+ break;
+ }
+ }
+ col= effectiveGroup*XkbKeyGroupsWidth(xkb,key);
+ type = XkbKeyKeyType(xkb,key,effectiveGroup);
+
+ if (type->map) { /* find the column (shift level) within the group */
+ register int i;
+ register XkbKTMapEntryPtr entry;
+ for (i=0,entry=type->map;i<type->map_count;i++,entry++) {
+ if ((entry->active)&&((mods&type->mods.mask)==entry->mods.mask)) {
+ col+= entry->level;
+ break;
+ }
+ }
+ }
+
+ return &acts[col];
+}
+
+static unsigned XkbKeyEffectiveGroup(XkbDescPtr xkb, KeyCode key, unsigned int mods)
+{
+ int nKeyGroups;
+ unsigned effectiveGroup;
+
+ nKeyGroups= XkbKeyNumGroups(xkb,key);
+ if ((!XkbKeycodeInRange(xkb,key))||(nKeyGroups==0))
+ return 0;
+
+ effectiveGroup= XkbGroupForCoreState(mods);
+ if ( effectiveGroup>=nKeyGroups ) {
+ unsigned groupInfo= XkbKeyGroupInfo(xkb,key);
+ switch (XkbOutOfRangeGroupAction(groupInfo)) {
+ default:
+ effectiveGroup %= nKeyGroups;
+ break;
+ case XkbClampIntoRange:
+ effectiveGroup = nKeyGroups-1;
+ break;
+ case XkbRedirectIntoRange:
+ effectiveGroup = XkbOutOfRangeGroupNumber(groupInfo);
+ if (effectiveGroup>=nKeyGroups)
+ effectiveGroup= 0;
+ break;
+ }
+ }
+
+ return effectiveGroup;
+}
+
+void vncPrepareInputDevices(void)
+{
+ /*
+ * Not ideal since these callbacks do not stack, but it's the only
+ * decent way we can reliably catch events for both the slave and
+ * master device.
+ */
+ mieqSetHandler(ET_KeyPress, vncXkbProcessDeviceEvent);
+ mieqSetHandler(ET_KeyRelease, vncXkbProcessDeviceEvent);
+}
+
+unsigned vncGetKeyboardState(void)
+{
+ DeviceIntPtr master;
+
+ master = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT);
+ return XkbStateFieldFromRec(&master->key->xkbInfo->state);
+}
+
+unsigned vncGetLevelThreeMask(void)
+{
+ unsigned state;
+ KeyCode keycode;
+ XkbDescPtr xkb;
+ XkbAction *act;
+
+ /* Group state is still important */
+ state = vncGetKeyboardState();
+ state &= ~0xff;
+
+ keycode = vncKeysymToKeycode(XK_ISO_Level3_Shift, state, NULL);
+ if (keycode == 0) {
+ keycode = vncKeysymToKeycode(XK_Mode_switch, state, NULL);
+ if (keycode == 0)
+ return 0;
+ }
+
+ xkb = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc;
+
+ act = XkbKeyActionPtr(xkb, keycode, state);
+ if (act == NULL)
+ return 0;
+ if (act->type != XkbSA_SetMods)
+ return 0;
+
+ if (act->mods.flags & XkbSA_UseModMapMods)
+ return xkb->map->modmap[keycode];
+ else
+ return act->mods.mask;
+}
+
+KeyCode vncPressShift(void)
+{
+ unsigned state;
+
+ XkbDescPtr xkb;
+ unsigned int key;
+
+ state = vncGetKeyboardState();
+ if (state & ShiftMask)
+ return 0;
+
+ xkb = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc;
+ for (key = xkb->min_key_code; key <= xkb->max_key_code; key++) {
+ XkbAction *act;
+ unsigned char mask;
+
+ act = XkbKeyActionPtr(xkb, key, state);
+ if (act == NULL)
+ continue;
+
+ if (act->type != XkbSA_SetMods)
+ continue;
+
+ if (act->mods.flags & XkbSA_UseModMapMods)
+ mask = xkb->map->modmap[key];
+ else
+ mask = act->mods.mask;
+
+ if ((mask & ShiftMask) == ShiftMask)
+ return key;
+ }
+
+ return 0;
+}
+
+size_t vncReleaseShift(KeyCode *keys, size_t maxKeys)
+{
+ size_t count;
+
+ unsigned state;
+
+ DeviceIntPtr master;
+ XkbDescPtr xkb;
+ unsigned int key;
+
+ state = vncGetKeyboardState();
+ if (!(state & ShiftMask))
+ return 0;
+
+ count = 0;
+
+ master = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT);
+ xkb = master->key->xkbInfo->desc;
+ for (key = xkb->min_key_code; key <= xkb->max_key_code; key++) {
+ XkbAction *act;
+ unsigned char mask;
+
+ if (!key_is_down(master, key, KEY_PROCESSED))
+ continue;
+
+ act = XkbKeyActionPtr(xkb, key, state);
+ if (act == NULL)
+ continue;
+
+ if (act->type != XkbSA_SetMods)
+ continue;
+
+ if (act->mods.flags & XkbSA_UseModMapMods)
+ mask = xkb->map->modmap[key];
+ else
+ mask = act->mods.mask;
+
+ if (!(mask & ShiftMask))
+ continue;
+
+ if (count >= maxKeys)
+ return 0;
+
+ keys[count++] = key;
+ }
+
+ return count;
+}
+
+KeyCode vncPressLevelThree(void)
+{
+ unsigned state, mask;
+
+ KeyCode keycode;
+ XkbDescPtr xkb;
+ XkbAction *act;
+
+ mask = vncGetLevelThreeMask();
+ if (mask == 0)
+ return 0;
+
+ state = vncGetKeyboardState();
+ if (state & mask)
+ return 0;
+
+ keycode = vncKeysymToKeycode(XK_ISO_Level3_Shift, state, NULL);
+ if (keycode == 0) {
+ keycode = vncKeysymToKeycode(XK_Mode_switch, state, NULL);
+ if (keycode == 0)
+ return 0;
+ }
+
+ xkb = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc;
+
+ act = XkbKeyActionPtr(xkb, keycode, state);
+ if (act == NULL)
+ return 0;
+ if (act->type != XkbSA_SetMods)
+ return 0;
+
+ return keycode;
+}
+
+size_t vncReleaseLevelThree(KeyCode *keys, size_t maxKeys)
+{
+ size_t count;
+
+ unsigned state, mask;
+
+ DeviceIntPtr master;
+ XkbDescPtr xkb;
+ unsigned int key;
+
+ mask = vncGetLevelThreeMask();
+ if (mask == 0)
+ return 0;
+
+ state = vncGetKeyboardState();
+ if (!(state & mask))
+ return 0;
+
+ count = 0;
+
+ master = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT);
+ xkb = master->key->xkbInfo->desc;
+ for (key = xkb->min_key_code; key <= xkb->max_key_code; key++) {
+ XkbAction *act;
+ unsigned char key_mask;
+
+ if (!key_is_down(master, key, KEY_PROCESSED))
+ continue;
+
+ act = XkbKeyActionPtr(xkb, key, state);
+ if (act == NULL)
+ continue;
+
+ if (act->type != XkbSA_SetMods)
+ continue;
+
+ if (act->mods.flags & XkbSA_UseModMapMods)
+ key_mask = xkb->map->modmap[key];
+ else
+ key_mask = act->mods.mask;
+
+ if (!(key_mask & mask))
+ continue;
+
+ if (count >= maxKeys)
+ return 0;
+
+ keys[count++] = key;
+ }
+
+ return count;
+}
+
+KeyCode vncKeysymToKeycode(KeySym keysym, unsigned state, unsigned *new_state)
+{
+ XkbDescPtr xkb;
+ unsigned int key;
+ KeySym ks;
+ unsigned level_three_mask;
+
+ if (new_state != NULL)
+ *new_state = state;
+
+ xkb = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc;
+ for (key = xkb->min_key_code; key <= xkb->max_key_code; key++) {
+ unsigned int state_out;
+ KeySym dummy;
+
+ XkbTranslateKeyCode(xkb, key, state, &state_out, &ks);
+ if (ks == NoSymbol)
+ continue;
+
+ /*
+ * Despite every known piece of documentation on
+ * XkbTranslateKeyCode() stating that mods_rtrn returns
+ * the unconsumed modifiers, in reality it always
+ * returns the _potentially consumed_ modifiers.
+ */
+ state_out = state & ~state_out;
+ if (state_out & LockMask)
+ XkbConvertCase(ks, &dummy, &ks);
+
+ if (ks == keysym)
+ return key;
+ }
+
+ if (new_state == NULL)
+ return 0;
+
+ *new_state = (state & ~ShiftMask) |
+ ((state & ShiftMask) ? 0 : ShiftMask);
+ key = vncKeysymToKeycode(keysym, *new_state, NULL);
+ if (key != 0)
+ return key;
+
+ level_three_mask = vncGetLevelThreeMask();
+ if (level_three_mask == 0)
+ return 0;
+
+ *new_state = (state & ~level_three_mask) |
+ ((state & level_three_mask) ? 0 : level_three_mask);
+ key = vncKeysymToKeycode(keysym, *new_state, NULL);
+ if (key != 0)
+ return key;
+
+ *new_state = (state & ~(ShiftMask | level_three_mask)) |
+ ((state & ShiftMask) ? 0 : ShiftMask) |
+ ((state & level_three_mask) ? 0 : level_three_mask);
+ key = vncKeysymToKeycode(keysym, *new_state, NULL);
+ if (key != 0)
+ return key;
+
+ 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;
+
+ KeyCode numlock_keycode;
+ unsigned numlock_mask;
+
+ XkbDescPtr xkb;
+ XkbAction *act;
+
+ unsigned group;
+ XkbKeyTypeRec *type;
+
+ /* Group state is still important */
+ state = vncGetKeyboardState();
+ state &= ~0xff;
+
+ /*
+ * Not sure if hunting for a virtual modifier called "NumLock",
+ * or following the keysym Num_Lock is the best approach. We
+ * try the latter.
+ */
+ numlock_keycode = vncKeysymToKeycode(XK_Num_Lock, state, NULL);
+ if (numlock_keycode == 0)
+ return 0;
+
+ xkb = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT)->key->xkbInfo->desc;
+
+ act = XkbKeyActionPtr(xkb, numlock_keycode, state);
+ if (act == NULL)
+ return 0;
+ if (act->type != XkbSA_LockMods)
+ return 0;
+
+ if (act->mods.flags & XkbSA_UseModMapMods)
+ numlock_mask = xkb->map->modmap[keycode];
+ else
+ numlock_mask = act->mods.mask;
+
+ group = XkbKeyEffectiveGroup(xkb, keycode, state);
+ type = XkbKeyKeyType(xkb, keycode, group);
+ if ((type->mods.mask & numlock_mask) == 0)
+ return 0;
+
+ return 1;
+}
+
+KeyCode vncAddKeysym(KeySym keysym, unsigned state)
+{
+ DeviceIntPtr master;
+ XkbDescPtr xkb;
+ unsigned int key;
+
+ XkbEventCauseRec cause;
+ XkbChangesRec changes;
+
+ int types[1];
+ KeySym *syms;
+ KeySym upper, lower;
+
+ master = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT);
+ xkb = master->key->xkbInfo->desc;
+ for (key = xkb->max_key_code; key >= xkb->min_key_code; key--) {
+ if (XkbKeyNumGroups(xkb, key) == 0)
+ break;
+ }
+
+ if (key < xkb->min_key_code)
+ return 0;
+
+ memset(&changes, 0, sizeof(changes));
+ memset(&cause, 0, sizeof(cause));
+
+ XkbSetCauseUnknown(&cause);
+
+ /*
+ * 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';
+ 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;
+ }
+
+ /* FIXME: Verify that ONE_LEVEL/ALPHABETIC isn't screwed up */
+
+ /*
+ * For keysyms that are affected by Lock, we are better off
+ * using ALPHABETIC rather than ONE_LEVEL as the latter
+ * generally cannot produce lower case when Lock is active.
+ */
+ XkbConvertCase(keysym, &lower, &upper);
+ if (upper == lower)
+ types[XkbGroup1Index] = XkbOneLevelIndex;
+ else
+ types[XkbGroup1Index] = XkbAlphabeticIndex;
+
+ XkbChangeTypesOfKey(xkb, key, 1, XkbGroup1Mask, types, &changes.map);
+
+ 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;
+
+ XkbSendNotification(master, &changes, &cause);
+
+ return key;
+}
+
+static void vncXkbProcessDeviceEvent(int screenNum,
+ InternalEvent *event,
+ DeviceIntPtr dev)
+{
+ unsigned int backupctrls;
+
+ if (event->device_event.sourceid == vncKeyboardDev->id) {
+ XkbControlsPtr ctrls;
+
+ /*
+ * We need to bypass AccessX since it is timing sensitive and
+ * the network can cause fake event delays.
+ */
+ ctrls = dev->key->xkbInfo->desc->ctrls;
+ backupctrls = ctrls->enabled_ctrls;
+ ctrls->enabled_ctrls &= ~XkbAllFilteredEventsMask;
+
+ /*
+ * This flag needs to be set for key repeats to be properly
+ * respected.
+ */
+ if ((event->device_event.type == ET_KeyPress) &&
+ key_is_down(dev, event->device_event.detail.key, KEY_PROCESSED))
+ event->device_event.key_repeat = TRUE;
+ }
+
+ dev->public.processInputProc(event, dev);
+
+ if (event->device_event.sourceid == vncKeyboardDev->id) {
+ XkbControlsPtr ctrls;
+
+ ctrls = dev->key->xkbInfo->desc->ctrls;
+ ctrls->enabled_ctrls = backupctrls;
+ }
+}
+
+#endif /* XORG >= 117 */