/* 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 #endif #include "xorg-version.h" #include #include #include #include #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 #if XORG < 118 #if XORG < 110 #define GetMaster(dev, type) ((dev)->u.master) #else #define GetMaster(dev, type) ((dev)->master) #endif #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;imap_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;imap_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) { 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; 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; }