KasmVNC/unix/xserver/hw/vnc/InputXKB.c

727 lines
17 KiB
C
Raw Normal View History

2020-09-20 14:16:44 +02:00
/* 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"
#include <stdio.h>
#include <limits.h>
2020-09-20 14:16:44 +02:00
#include <X11/keysym.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "RFBGlue.h"
2020-09-20 14:16:44 +02:00
#include "xkbsrv.h"
#include "xkbstr.h"
#include "eventstr.h"
#include "scrnintstr.h"
#include "mi.h"
#include "stdbool.h"
2020-09-20 14:16:44 +02:00
#include "Input.h"
#ifndef KEYBOARD_OR_FLOAT
#define KEYBOARD_OR_FLOAT MASTER_KEYBOARD
#endif
#if XORG < 118
#if XORG < 110
#define GetMaster(dev, type) ((dev)->u.master)
#else
#define GetMaster(dev, type) ((dev)->master)
#endif
#endif
#define LOG_NAME "Input"
#define LOG_ERROR(...) vncLogError(LOG_NAME, __VA_ARGS__)
#define LOG_STATUS(...) vncLogStatus(LOG_NAME, __VA_ARGS__)
#define LOG_INFO(...) vncLogInfo(LOG_NAME, __VA_ARGS__)
#define LOG_DEBUG(...) vncLogDebug(LOG_NAME, __VA_ARGS__)
2020-09-20 14:16:44 +02:00
extern DeviceIntPtr vncKeyboardDev;
static unsigned int MAX_MAPPINGS = UINT_MAX - 1;
2020-09-20 14:16:44 +02:00
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 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, unsigned int *needFree, bool freeKeys)
2020-09-20 14:16:44 +02:00
{
DeviceIntPtr master;
XkbDescPtr xkb;
unsigned int key = 0;
unsigned int i;
unsigned int newest_k = 0;
unsigned int oldest_k = 0;
unsigned int freeCnt = 0;
2020-09-20 14:16:44 +02:00
XkbEventCauseRec cause;
XkbChangesRec changes;
int types[1];
KeySym *syms;
KeySym upper, lower;
master = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT);
xkb = master->key->xkbInfo->desc;
//find the first free key to map
for (i = xkb->max_key_code; i >= xkb->min_key_code; i--) {
if (XkbKeyNumGroups(xkb, i) == 0 && *(needFree + i) != UINT_MAX) {
if (++freeCnt == 1)
key = i;
}
2020-09-20 14:16:44 +02:00
}
if (key < xkb->min_key_code)
return 0;
//find the oldest and newest keys that have been remapped
for (i = 1;i < 256;i++) {
//protect from uint rollover
if (*(needFree + i) == MAX_MAPPINGS)
*(needFree + i) = 1;
if (*(needFree + i) > *(needFree + newest_k) && *(needFree + i) != UINT_MAX)
newest_k = i;
if (*(needFree + i) < *(needFree + oldest_k) && *(needFree +i) > 0)
oldest_k = i;
//mark as free to use
if (*(needFree + i) == UINT_MAX)
*(needFree + i) = 0;
}
*(needFree + key) = ++(*(needFree + newest_k));
//if running low on free keys, free the oldest key that was used
if (freeCnt < 3 && *(needFree + oldest_k) > 0 && freeKeys) {
vncRemoveKeycode(oldest_k);
LOG_DEBUG("Removed mapping for keycode %d", oldest_k);
//mark as free to use after one more cycle
*(needFree + oldest_k) = UINT_MAX;
} else if (freeCnt < 3 && freeKeys) {
//this should only happen if the system had fewer than 3 free keys to begin with
LOG_INFO("The system has fewer than 3 unmapped keys with no available keys to free.");
return 0;
}
2020-09-20 14:16:44 +02:00
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;
}
void vncRemoveKeycode(unsigned keycode)
{
DeviceIntPtr master;
XkbDescPtr xkb;
XkbEventCauseRec cause;
XkbChangesRec changes;
master = GetMaster(vncKeyboardDev, KEYBOARD_OR_FLOAT);
xkb = master->key->xkbInfo->desc;
memset(&changes, 0, sizeof(changes));
memset(&cause, 0, sizeof(cause));
XkbSetCauseUnknown(&cause);
KeySym ks = NoSymbol;
KeySymsRec keysyms;
keysyms.minKeyCode = keycode;
keysyms.maxKeyCode = keycode;
keysyms.mapWidth = 1;
keysyms.map = &ks;
unsigned check = 0;
XkbUpdateKeyTypesFromCore(master, &keysyms, keycode, 1, &changes);
XkbUpdateActions(master, keycode, 1, &changes, &check, &cause);
if (check)
XkbCheckSecondaryEffects(master->key->xkbInfo, 1, &changes, &cause);
XkbSendNotification(master, &changes, &cause);
}
2020-09-20 14:16:44 +02:00
static void vncXkbProcessDeviceEvent(int screenNum,
InternalEvent *event,
DeviceIntPtr dev)
{
unsigned int backupctrls;
XkbControlsPtr ctrls;
if (event->device_event.sourceid != vncKeyboardDev->id) {
dev->public.processInputProc(event, dev);
return;
}
/*
* 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);
ctrls->enabled_ctrls = backupctrls;
}