From: Alex Richardson Date: Wed, 14 Jul 2021 13:13:54 +0000 (+0100) Subject: Rename Input.h to vncInput.h to fix building on case-insensitive FS X-Git-Tag: v1.11.90~17^4 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=refs%2Fpull%2F1291%2Fhead;p=tigervnc.git Rename Input.h to vncInput.h to fix building on case-insensitive FS I am cross-compiling from macOS for a FreeBSD-derived system so my host file system is case insensitive but the target isn't. Without this change I get the following warnings which show that the vnc "Input.h" is being included from mi/mi.h instead of the xserver "input.h": ``` In file included from /Users/alex/cheri/xvnc-server/hw/vnc/Input.c:33: /Users/alex/cheri/xvnc-server/mi/mi.h:55:10: warning: non-portable path to file '"Input.h"'; specified path differs in case from file name on disk [-Wnonportable-include-path] #include "input.h" ^~~~~~~~~ "Input.h" ``` --- diff --git a/unix/xserver/hw/vnc/Input.c b/unix/xserver/hw/vnc/Input.c deleted file mode 100644 index 2a154ada..00000000 --- a/unix/xserver/hw/vnc/Input.c +++ /dev/null @@ -1,644 +0,0 @@ -/* Copyright (C) 2009 TightVNC Team - * Copyright (C) 2009, 2014 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 "Input.h" -#include "vncExtInit.h" -#include "RFBGlue.h" - -#include "inputstr.h" -#include "inpututils.h" -#include "mi.h" -#include "mipointer.h" -#include "exevents.h" -#include "scrnintstr.h" -#include "xkbsrv.h" -#include "xkbstr.h" -#include "xserver-properties.h" -extern _X_EXPORT DevPrivateKey CoreDevicePrivateKey; -#include -#include -#include - -extern const unsigned short code_map_qnum_to_xorgevdev[]; -extern const unsigned int code_map_qnum_to_xorgevdev_len; -extern const unsigned short code_map_qnum_to_xorgkbd[]; -extern const unsigned int code_map_qnum_to_xorgkbd_len; - -#define BUTTONS 7 - -DeviceIntPtr vncKeyboardDev; -DeviceIntPtr vncPointerDev; - -static int oldButtonMask; -static int cursorPosX, cursorPosY; - -static const unsigned short *codeMap; -static unsigned int codeMapLen; - -static KeySym pressedKeys[256]; - -static int vncPointerProc(DeviceIntPtr pDevice, int onoff); -static void vncKeyboardBell(int percent, DeviceIntPtr device, - void * ctrl, int class); -static int vncKeyboardProc(DeviceIntPtr pDevice, int onoff); - -static void vncKeysymKeyboardEvent(KeySym keysym, int down); - -#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__) - -/* - * Init input device. - * This has to be called after core pointer/keyboard - * initialization which unfortunately is after extensions - * initialization (which means we cannot call it in - * vncExtensionInit(). Check InitExtensions(), - * InitCoreDevices() and InitInput() calls in dix/main.c. - * Instead we call it from XserverDesktop at an appropriate - * time. - */ -void vncInitInputDevice(void) -{ - int i, ret; - - if ((vncPointerDev != NULL) || (vncKeyboardDev != NULL)) - return; - - /* - * On Linux we try to provide the same key codes as Xorg with - * the evdev driver. On other platforms we mimic the older - * Xorg KBD driver. - */ -#ifdef __linux__ - codeMap = code_map_qnum_to_xorgevdev; - codeMapLen = code_map_qnum_to_xorgevdev_len; -#else - codeMap = code_map_qnum_to_xorgkbd; - codeMapLen = code_map_qnum_to_xorgkbd_len; -#endif - - for (i = 0;i < 256;i++) - pressedKeys[i] = NoSymbol; - - ret = AllocDevicePair(serverClient, "TigerVNC", - &vncPointerDev, &vncKeyboardDev, - vncPointerProc, vncKeyboardProc, - FALSE); - - if (ret != Success) - FatalError("Failed to initialize TigerVNC input devices\n"); - - if (ActivateDevice(vncPointerDev, TRUE) != Success || - ActivateDevice(vncKeyboardDev, TRUE) != Success) - FatalError("Failed to activate TigerVNC devices\n"); - - if (!EnableDevice(vncPointerDev, TRUE) || - !EnableDevice(vncKeyboardDev, TRUE)) - FatalError("Failed to activate TigerVNC devices\n"); - - vncPrepareInputDevices(); -} - -void vncPointerButtonAction(int buttonMask) -{ - int i; - ValuatorMask mask; - - for (i = 0; i < BUTTONS; i++) { - if ((buttonMask ^ oldButtonMask) & (1 << i)) { - int action = (buttonMask & (1<x; - cursorPosY += ptrScreen->y; - } - - *x = cursorPosX; - *y = cursorPosY; -} - -static int vncPointerProc(DeviceIntPtr pDevice, int onoff) -{ - BYTE map[BUTTONS + 1]; - DevicePtr pDev = (DevicePtr)pDevice; - int i; - /* - * NOTE: map[] array is one element longer than btn_labels[] array. This - * is not a bug. - */ - Atom btn_labels[BUTTONS]; - Atom axes_labels[2]; - - switch (onoff) { - case DEVICE_INIT: - for (i = 0; i < BUTTONS + 1; i++) - map[i] = i; - - btn_labels[0] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_LEFT); - btn_labels[1] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_MIDDLE); - btn_labels[2] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_RIGHT); - btn_labels[3] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_UP); - btn_labels[4] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_DOWN); - btn_labels[5] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_LEFT); - btn_labels[6] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_RIGHT); - - axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_X); - axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y); - - InitPointerDeviceStruct(pDev, map, BUTTONS, btn_labels, - (PtrCtrlProcPtr)NoopDDA, - GetMotionHistorySize(), - 2, axes_labels); - break; - case DEVICE_ON: - pDev->on = TRUE; - break; - case DEVICE_OFF: - pDev->on = FALSE; - break; - case DEVICE_CLOSE: - vncPointerDev = NULL; - break; - } - - return Success; -} - -static void vncKeyboardBell(int percent, DeviceIntPtr device, - void * ctrl, int class) -{ - if (percent > 0) - vncBell(); -} - -static void vncKeyboardCtrl(DeviceIntPtr pDevice, KeybdCtrl *ctrl) -{ - vncSetLEDState(ctrl->leds); -} - -static int vncKeyboardProc(DeviceIntPtr pDevice, int onoff) -{ - DevicePtr pDev = (DevicePtr)pDevice; - - switch (onoff) { - case DEVICE_INIT: - InitKeyboardDeviceStruct(pDevice, NULL, vncKeyboardBell, - vncKeyboardCtrl); - break; - case DEVICE_ON: - pDev->on = TRUE; - break; - case DEVICE_OFF: - pDev->on = FALSE; - break; - case DEVICE_CLOSE: - vncKeyboardDev = NULL; - break; - } - - return Success; -} - -static inline void pressKey(DeviceIntPtr dev, int kc, Bool down, const char *msg) -{ - int action; - - if (msg != NULL) - LOG_DEBUG("%s %d %s", msg, kc, down ? "down" : "up"); - - action = down ? KeyPress : KeyRelease; -#if XORG_OLDER_THAN(1, 18, 0) - QueueKeyboardEvents(dev, action, kc, NULL); -#else - QueueKeyboardEvents(dev, action, kc); -#endif -} - -/* - * vncKeyboardEvent() - add X11 events for the given RFB key event - */ -void vncKeyboardEvent(KeySym keysym, unsigned xtcode, int down) -{ - /* Simple case: the client has specified the key */ - if (xtcode && xtcode < codeMapLen) { - int keycode; - - keycode = codeMap[xtcode]; - if (!keycode) { - /* - * Figure something out based on keysym if we - * cannot find a mapping. - */ - if (keysym) - vncKeysymKeyboardEvent(keysym, down); - return; - } - - /* - * We update the state table in case we get a mix of - * events with and without key codes. - */ - if (down) - pressedKeys[keycode] = keysym; - else - pressedKeys[keycode] = NoSymbol; - - pressKey(vncKeyboardDev, keycode, down, "raw keycode"); - mieqProcessInputEvents(); - return; - } - - /* - * Advanced case: We have to figure out a sequence of keys that - * result in the given keysym - */ - if (keysym) - vncKeysymKeyboardEvent(keysym, down); -} - -/* altKeysym is a table of alternative keysyms which have the same meaning. */ - -static struct altKeysym_t { - KeySym a, b; -} altKeysym[] = { - { XK_Shift_L, XK_Shift_R }, - { XK_Control_L, XK_Control_R }, - { XK_Meta_L, XK_Meta_R }, - { XK_Alt_L, XK_Alt_R }, - { XK_Super_L, XK_Super_R }, - { XK_Hyper_L, XK_Hyper_R }, - { XK_KP_Space, XK_space }, - { XK_KP_Tab, XK_Tab }, - { XK_KP_Enter, XK_Return }, - { XK_KP_F1, XK_F1 }, - { XK_KP_F2, XK_F2 }, - { XK_KP_F3, XK_F3 }, - { XK_KP_F4, XK_F4 }, - { XK_KP_Home, XK_Home }, - { XK_KP_Left, XK_Left }, - { XK_KP_Up, XK_Up }, - { XK_KP_Right, XK_Right }, - { XK_KP_Down, XK_Down }, - { XK_KP_Page_Up, XK_Page_Up }, - { XK_KP_Page_Down, XK_Page_Down }, - { XK_KP_End, XK_End }, - { XK_KP_Begin, XK_Begin }, - { XK_KP_Insert, XK_Insert }, - { XK_KP_Delete, XK_Delete }, - { XK_KP_Equal, XK_equal }, - { XK_KP_Multiply, XK_asterisk }, - { XK_KP_Add, XK_plus }, - { XK_KP_Separator, XK_comma }, - { XK_KP_Subtract, XK_minus }, - { XK_KP_Decimal, XK_period }, - { XK_KP_Divide, XK_slash }, - { XK_KP_0, XK_0 }, - { XK_KP_1, XK_1 }, - { XK_KP_2, XK_2 }, - { XK_KP_3, XK_3 }, - { XK_KP_4, XK_4 }, - { XK_KP_5, XK_5 }, - { XK_KP_6, XK_6 }, - { XK_KP_7, XK_7 }, - { XK_KP_8, XK_8 }, - { XK_KP_9, XK_9 }, - { XK_ISO_Level3_Shift, XK_Mode_switch }, -}; - -/* - * vncKeysymKeyboardEvent() - work out the best keycode corresponding - * to the keysym sent by the viewer. This is basically impossible in - * the general case, but we make a best effort by assuming that all - * useful keysyms can be reached using just the Shift and - * Level 3 (AltGr) modifiers. For core keyboards this is basically - * always true, and should be true for most sane, western XKB layouts. - */ -static void vncKeysymKeyboardEvent(KeySym keysym, int down) -{ - int i; - unsigned state, new_state; - KeyCode keycode; - - unsigned level_three_mask; - KeyCode shift_press, level_three_press; - KeyCode shift_release[8], level_three_release[8]; - size_t shift_release_count, level_three_release_count; - - /* - * Release events must match the press event, so look up what - * keycode we sent for the press. - */ - if (!down) { - for (i = 0;i < 256;i++) { - if (pressedKeys[i] == keysym) { - pressedKeys[i] = NoSymbol; - pressKey(vncKeyboardDev, i, FALSE, "keycode"); - mieqProcessInputEvents(); - return; - } - } - - /* - * This can happen quite often as we ignore some - * key presses. - */ - LOG_DEBUG("Unexpected release of keysym 0x%x", keysym); - return; - } - - /* - * Since we are checking the current state to determine if we need - * to fake modifiers, we must make sure that everything put on the - * input queue is processed before we start. Otherwise, shift may be - * stuck down. - */ - mieqProcessInputEvents(); - - state = vncGetKeyboardState(); - - keycode = vncKeysymToKeycode(keysym, state, &new_state); - - /* - * Shift+Alt is often mapped to Meta, so try that rather than - * allocating a new entry, faking shift, or using the dummy - * key entries that many layouts have. - */ - if ((state & ShiftMask) && - ((keysym == XK_Alt_L) || (keysym == XK_Alt_R))) { - KeyCode alt, meta; - - if (keysym == XK_Alt_L) { - alt = vncKeysymToKeycode(XK_Alt_L, state & ~ShiftMask, NULL); - meta = vncKeysymToKeycode(XK_Meta_L, state, NULL); - } else { - alt = vncKeysymToKeycode(XK_Alt_R, state & ~ShiftMask, NULL); - meta = vncKeysymToKeycode(XK_Meta_R, state, NULL); - } - - if ((meta != 0) && (alt == meta)) { - LOG_DEBUG("Replacing Shift+Alt with Shift+Meta"); - keycode = meta; - new_state = state; - } - } - - /* Try some equivalent keysyms if we couldn't find a perfect match */ - if (keycode == 0) { - for (i = 0;i < sizeof(altKeysym)/sizeof(altKeysym[0]);i++) { - KeySym altsym; - - if (altKeysym[i].a == keysym) - altsym = altKeysym[i].b; - else if (altKeysym[i].b == keysym) - altsym = altKeysym[i].a; - else - continue; - - keycode = vncKeysymToKeycode(altsym, state, &new_state); - if (keycode != 0) - break; - } - } - - /* No matches. Will have to add a new entry... */ - if (keycode == 0) { - keycode = vncAddKeysym(keysym, state); - if (keycode == 0) { - LOG_ERROR("Failure adding new keysym 0x%x", keysym); - return; - } - - LOG_INFO("Added unknown keysym 0x%x to keycode %d", - keysym, keycode); - - /* - * The state given to addKeysym() is just a hint and - * the actual result might still require some state - * changes. - */ - keycode = vncKeysymToKeycode(keysym, state, &new_state); - if (keycode == 0) { - LOG_ERROR("Newly added keysym 0x%x cannot be generated", keysym); - return; - } - } - - /* - * X11 generally lets shift toggle the keys on the numeric pad - * the same way NumLock does. This is however not the case on - * other systems like Windows. As a result, some applications - * get confused when we do a fake shift to get the same effect - * that having NumLock active would produce. - * - * Not all clients have proper NumLock synchronisation (so we - * can avoid faking shift) so we try to avoid the fake shifts - * if we can use an alternative keysym. - */ - if (((state & ShiftMask) != (new_state & ShiftMask)) && - vncGetAvoidShiftNumLock() && vncIsAffectedByNumLock(keycode)) { - KeyCode keycode2; - unsigned new_state2; - - LOG_DEBUG("Finding alternative to keysym 0x%x to avoid fake shift for numpad", keysym); - - for (i = 0;i < sizeof(altKeysym)/sizeof(altKeysym[0]);i++) { - KeySym altsym; - - if (altKeysym[i].a == keysym) - altsym = altKeysym[i].b; - else if (altKeysym[i].b == keysym) - altsym = altKeysym[i].a; - else - continue; - - keycode2 = vncKeysymToKeycode(altsym, state, &new_state2); - if (keycode2 == 0) - continue; - - if (((state & ShiftMask) != (new_state2 & ShiftMask)) && - vncIsAffectedByNumLock(keycode2)) - continue; - - break; - } - - if (i == sizeof(altKeysym)/sizeof(altKeysym[0])) - LOG_DEBUG("No alternative keysym found"); - else { - keycode = keycode2; - new_state = new_state2; - } - } - - /* - * "Shifted Tab" is a bit of a mess. Some systems have varying, - * special keysyms for this symbol. VNC mandates that clients - * should always send the plain XK_Tab keysym and the server - * should deduce the meaning based on current Shift state. - * To comply with this, we will find the keycode that sends - * XK_Tab, and make sure that Shift isn't cleared. This can - * possibly result in a different keysym than XK_Tab, but that - * is the desired behaviour. - * - * Note: We never get ISO_Left_Tab here because it's already - * been translated in VNCSConnectionST. - */ - if (keysym == XK_Tab && (state & ShiftMask)) - new_state |= ShiftMask; - - /* - * We need a bigger state change than just shift, - * so we need to know what the mask is for level 3 shifts. - */ - if ((new_state & ~ShiftMask) != (state & ~ShiftMask)) - level_three_mask = vncGetLevelThreeMask(); - else - level_three_mask = 0; - - shift_press = level_three_press = 0; - shift_release_count = level_three_release_count = 0; - - /* Need a fake press or release of shift? */ - if (!(state & ShiftMask) && (new_state & ShiftMask)) { - shift_press = vncPressShift(); - if (shift_press == 0) { - LOG_ERROR("Unable to find a modifier key for Shift"); - return; - } - - pressKey(vncKeyboardDev, shift_press, TRUE, "temp shift"); - } else if ((state & ShiftMask) && !(new_state & ShiftMask)) { - shift_release_count = vncReleaseShift(shift_release, - sizeof(shift_release)/sizeof(*shift_release)); - if (shift_release_count == 0) { - LOG_ERROR("Unable to find the modifier key(s) for releasing Shift"); - return; - } - - for (i = 0;i < shift_release_count;i++) - pressKey(vncKeyboardDev, shift_release[i], FALSE, "temp shift"); - } - - /* Need a fake press or release of level three shift? */ - if (!(state & level_three_mask) && (new_state & level_three_mask)) { - level_three_press = vncPressLevelThree(); - if (level_three_press == 0) { - LOG_ERROR("Unable to find a modifier key for ISO_Level3_Shift/Mode_Switch"); - return; - } - - pressKey(vncKeyboardDev, level_three_press, TRUE, "temp level 3 shift"); - } else if ((state & level_three_mask) && !(new_state & level_three_mask)) { - level_three_release_count = vncReleaseLevelThree(level_three_release, - sizeof(level_three_release)/sizeof(*level_three_release)); - if (level_three_release_count == 0) { - LOG_ERROR("Unable to find the modifier key(s) for releasing ISO_Level3_Shift/Mode_Switch"); - return; - } - - for (i = 0;i < level_three_release_count;i++) - pressKey(vncKeyboardDev, level_three_release[i], FALSE, "temp level 3 shift"); - } - - /* Now press the actual key */ - pressKey(vncKeyboardDev, keycode, TRUE, "keycode"); - - /* And store the mapping so that we can do a proper release later */ - for (i = 0;i < 256;i++) { - if (i == keycode) - continue; - if (pressedKeys[i] == keysym) { - LOG_ERROR("Keysym 0x%x generated by both keys %d and %d", keysym, i, keycode); - pressedKeys[i] = NoSymbol; - } - } - - pressedKeys[keycode] = keysym; - - /* Undo any fake level three shift */ - if (level_three_press != 0) - pressKey(vncKeyboardDev, level_three_press, FALSE, "temp level 3 shift"); - else if (level_three_release_count != 0) { - for (i = 0;i < level_three_release_count;i++) - pressKey(vncKeyboardDev, level_three_release[i], TRUE, "temp level 3 shift"); - } - - /* Undo any fake shift */ - if (shift_press != 0) - pressKey(vncKeyboardDev, shift_press, FALSE, "temp shift"); - else if (shift_release_count != 0) { - for (i = 0;i < shift_release_count;i++) - pressKey(vncKeyboardDev, shift_release[i], TRUE, "temp shift"); - } - - /* - * When faking a modifier we are putting a keycode (which can - * currently activate the desired modifier) on the input - * queue. A future modmap change can change the mapping so - * that this keycode means something else entirely. Guard - * against this by processing the queue now. - */ - mieqProcessInputEvents(); -} diff --git a/unix/xserver/hw/vnc/Input.h b/unix/xserver/hw/vnc/Input.h deleted file mode 100644 index a9d067f2..00000000 --- a/unix/xserver/hw/vnc/Input.h +++ /dev/null @@ -1,64 +0,0 @@ -/* Copyright (C) 2009 TightVNC Team - * Copyright (C) 2009, 2010 Red Hat, Inc. - * Copyright (C) 2009, 2010 TigerVNC Team - * 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. - */ - -/* Make sure macro doesn't conflict with macro in include/input.h. */ -#ifndef INPUT_H_ -#define INPUT_H_ - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -void vncInitInputDevice(void); - -void vncPointerButtonAction(int buttonMask); -void vncPointerMove(int x, int y); -void vncGetPointerPos(int *x, int *y); - -void vncKeyboardEvent(KeySym keysym, unsigned xtcode, int down); - -/* Backend dependent functions below here */ - -void vncPrepareInputDevices(void); - -unsigned vncGetKeyboardState(void); -unsigned vncGetLevelThreeMask(void); - -KeyCode vncPressShift(void); -size_t vncReleaseShift(KeyCode *keys, size_t maxKeys); - -KeyCode vncPressLevelThree(void); -size_t vncReleaseLevelThree(KeyCode *keys, size_t maxKeys); - -KeyCode vncKeysymToKeycode(KeySym keysym, unsigned state, unsigned *new_state); - -int vncIsAffectedByNumLock(KeyCode keycode); - -KeyCode vncAddKeysym(KeySym keysym, unsigned state); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/unix/xserver/hw/vnc/InputXKB.c b/unix/xserver/hw/vnc/InputXKB.c deleted file mode 100644 index de6c1d3a..00000000 --- a/unix/xserver/hw/vnc/InputXKB.c +++ /dev/null @@ -1,678 +0,0 @@ -/* Copyright (C) 2009 TightVNC Team - * Copyright (C) 2009 Red Hat, Inc. - * Copyright 2013-2018 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_OLDER_THAN(1, 18, 0) -#define GetMaster(dev, type) ((dev)->master) -#endif - -extern DeviceIntPtr vncKeyboardDev; - -static const KeyCode fakeKeys[] = { -#ifdef __linux__ - 92, 203, 204, 205, 206, 207 -#else - 8, 124, 125, 156, 127, 128 -#endif - }; - -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; // KeyCode has insufficient range for the loop - KeyCode fallback; - KeySym ks; - unsigned level_three_mask; - - if (new_state != NULL) - *new_state = state; - - fallback = 0; - 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; - size_t fakeIdx; - - 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) - continue; - - /* - * Some keys are never sent by a real keyboard and are - * used in the default layouts as a fallback for - * modifiers. Make sure we use them last as some - * applications can be confused by these normally - * unused keys. - */ - for (fakeIdx = 0; - fakeIdx < sizeof(fakeKeys)/sizeof(fakeKeys[0]); - fakeIdx++) { - if (key == fakeKeys[fakeIdx]) { - if (fallback == 0) - fallback = key; - break; - } - } - if (fakeIdx < sizeof(fakeKeys)/sizeof(fakeKeys[0])) - continue; - - return key; - } - - /* Use the fallback key, if one was found */ - if (fallback != 0) - return fallback; - - 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; -} diff --git a/unix/xserver/hw/vnc/Makefile.am b/unix/xserver/hw/vnc/Makefile.am index d2d0265b..f3abd8a0 100644 --- a/unix/xserver/hw/vnc/Makefile.am +++ b/unix/xserver/hw/vnc/Makefile.am @@ -13,12 +13,12 @@ noinst_LTLIBRARIES = libvnccommon.la HDRS = vncExtInit.h vncHooks.h \ vncBlockHandler.h vncSelection.h \ XorgGlue.h XserverDesktop.h xorg-version.h \ - Input.h RFBGlue.h + vncInput.h RFBGlue.h libvnccommon_la_SOURCES = $(HDRS) \ vncExt.c vncExtInit.cc vncHooks.c vncSelection.c \ vncBlockHandler.c XorgGlue.c RandrGlue.c RFBGlue.cc XserverDesktop.cc \ - Input.c InputXKB.c qnum_to_xorgevdev.c qnum_to_xorgkbd.c + vncInput.c vncInputXKB.c qnum_to_xorgevdev.c qnum_to_xorgkbd.c libvnccommon_la_CPPFLAGS = -DVENDOR_RELEASE="$(VENDOR_RELEASE)" -I$(TIGERVNC_SRCDIR)/unix/common \ -DVENDOR_STRING="\"$(VENDOR_STRING)\"" -I$(TIGERVNC_SRCDIR)/common -UHAVE_CONFIG_H \ diff --git a/unix/xserver/hw/vnc/XserverDesktop.cc b/unix/xserver/hw/vnc/XserverDesktop.cc index 25dcd845..d9f47f20 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.cc +++ b/unix/xserver/hw/vnc/XserverDesktop.cc @@ -46,7 +46,7 @@ #include "vncHooks.h" #include "vncSelection.h" #include "XorgGlue.h" -#include "Input.h" +#include "vncInput.h" extern "C" { void vncSetGlueContext(int screenIndex); diff --git a/unix/xserver/hw/vnc/XserverDesktop.h b/unix/xserver/hw/vnc/XserverDesktop.h index 383e0bbf..57ee8083 100644 --- a/unix/xserver/hw/vnc/XserverDesktop.h +++ b/unix/xserver/hw/vnc/XserverDesktop.h @@ -36,7 +36,7 @@ #include #include #include -#include "Input.h" +#include "vncInput.h" namespace rfb { class VNCServerST; diff --git a/unix/xserver/hw/vnc/vncInput.c b/unix/xserver/hw/vnc/vncInput.c new file mode 100644 index 00000000..16ac25ed --- /dev/null +++ b/unix/xserver/hw/vnc/vncInput.c @@ -0,0 +1,644 @@ +/* Copyright (C) 2009 TightVNC Team + * Copyright (C) 2009, 2014 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 "vncInput.h" +#include "vncExtInit.h" +#include "RFBGlue.h" + +#include "inputstr.h" +#include "inpututils.h" +#include "mi.h" +#include "mipointer.h" +#include "exevents.h" +#include "scrnintstr.h" +#include "xkbsrv.h" +#include "xkbstr.h" +#include "xserver-properties.h" +extern _X_EXPORT DevPrivateKey CoreDevicePrivateKey; +#include +#include +#include + +extern const unsigned short code_map_qnum_to_xorgevdev[]; +extern const unsigned int code_map_qnum_to_xorgevdev_len; +extern const unsigned short code_map_qnum_to_xorgkbd[]; +extern const unsigned int code_map_qnum_to_xorgkbd_len; + +#define BUTTONS 7 + +DeviceIntPtr vncKeyboardDev; +DeviceIntPtr vncPointerDev; + +static int oldButtonMask; +static int cursorPosX, cursorPosY; + +static const unsigned short *codeMap; +static unsigned int codeMapLen; + +static KeySym pressedKeys[256]; + +static int vncPointerProc(DeviceIntPtr pDevice, int onoff); +static void vncKeyboardBell(int percent, DeviceIntPtr device, + void * ctrl, int class); +static int vncKeyboardProc(DeviceIntPtr pDevice, int onoff); + +static void vncKeysymKeyboardEvent(KeySym keysym, int down); + +#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__) + +/* + * Init input device. + * This has to be called after core pointer/keyboard + * initialization which unfortunately is after extensions + * initialization (which means we cannot call it in + * vncExtensionInit(). Check InitExtensions(), + * InitCoreDevices() and InitInput() calls in dix/main.c. + * Instead we call it from XserverDesktop at an appropriate + * time. + */ +void vncInitInputDevice(void) +{ + int i, ret; + + if ((vncPointerDev != NULL) || (vncKeyboardDev != NULL)) + return; + + /* + * On Linux we try to provide the same key codes as Xorg with + * the evdev driver. On other platforms we mimic the older + * Xorg KBD driver. + */ +#ifdef __linux__ + codeMap = code_map_qnum_to_xorgevdev; + codeMapLen = code_map_qnum_to_xorgevdev_len; +#else + codeMap = code_map_qnum_to_xorgkbd; + codeMapLen = code_map_qnum_to_xorgkbd_len; +#endif + + for (i = 0;i < 256;i++) + pressedKeys[i] = NoSymbol; + + ret = AllocDevicePair(serverClient, "TigerVNC", + &vncPointerDev, &vncKeyboardDev, + vncPointerProc, vncKeyboardProc, + FALSE); + + if (ret != Success) + FatalError("Failed to initialize TigerVNC input devices\n"); + + if (ActivateDevice(vncPointerDev, TRUE) != Success || + ActivateDevice(vncKeyboardDev, TRUE) != Success) + FatalError("Failed to activate TigerVNC devices\n"); + + if (!EnableDevice(vncPointerDev, TRUE) || + !EnableDevice(vncKeyboardDev, TRUE)) + FatalError("Failed to activate TigerVNC devices\n"); + + vncPrepareInputDevices(); +} + +void vncPointerButtonAction(int buttonMask) +{ + int i; + ValuatorMask mask; + + for (i = 0; i < BUTTONS; i++) { + if ((buttonMask ^ oldButtonMask) & (1 << i)) { + int action = (buttonMask & (1<x; + cursorPosY += ptrScreen->y; + } + + *x = cursorPosX; + *y = cursorPosY; +} + +static int vncPointerProc(DeviceIntPtr pDevice, int onoff) +{ + BYTE map[BUTTONS + 1]; + DevicePtr pDev = (DevicePtr)pDevice; + int i; + /* + * NOTE: map[] array is one element longer than btn_labels[] array. This + * is not a bug. + */ + Atom btn_labels[BUTTONS]; + Atom axes_labels[2]; + + switch (onoff) { + case DEVICE_INIT: + for (i = 0; i < BUTTONS + 1; i++) + map[i] = i; + + btn_labels[0] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_LEFT); + btn_labels[1] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_MIDDLE); + btn_labels[2] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_RIGHT); + btn_labels[3] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_UP); + btn_labels[4] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_WHEEL_DOWN); + btn_labels[5] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_LEFT); + btn_labels[6] = XIGetKnownProperty(BTN_LABEL_PROP_BTN_HWHEEL_RIGHT); + + axes_labels[0] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_X); + axes_labels[1] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_Y); + + InitPointerDeviceStruct(pDev, map, BUTTONS, btn_labels, + (PtrCtrlProcPtr)NoopDDA, + GetMotionHistorySize(), + 2, axes_labels); + break; + case DEVICE_ON: + pDev->on = TRUE; + break; + case DEVICE_OFF: + pDev->on = FALSE; + break; + case DEVICE_CLOSE: + vncPointerDev = NULL; + break; + } + + return Success; +} + +static void vncKeyboardBell(int percent, DeviceIntPtr device, + void * ctrl, int class) +{ + if (percent > 0) + vncBell(); +} + +static void vncKeyboardCtrl(DeviceIntPtr pDevice, KeybdCtrl *ctrl) +{ + vncSetLEDState(ctrl->leds); +} + +static int vncKeyboardProc(DeviceIntPtr pDevice, int onoff) +{ + DevicePtr pDev = (DevicePtr)pDevice; + + switch (onoff) { + case DEVICE_INIT: + InitKeyboardDeviceStruct(pDevice, NULL, vncKeyboardBell, + vncKeyboardCtrl); + break; + case DEVICE_ON: + pDev->on = TRUE; + break; + case DEVICE_OFF: + pDev->on = FALSE; + break; + case DEVICE_CLOSE: + vncKeyboardDev = NULL; + break; + } + + return Success; +} + +static inline void pressKey(DeviceIntPtr dev, int kc, Bool down, const char *msg) +{ + int action; + + if (msg != NULL) + LOG_DEBUG("%s %d %s", msg, kc, down ? "down" : "up"); + + action = down ? KeyPress : KeyRelease; +#if XORG_OLDER_THAN(1, 18, 0) + QueueKeyboardEvents(dev, action, kc, NULL); +#else + QueueKeyboardEvents(dev, action, kc); +#endif +} + +/* + * vncKeyboardEvent() - add X11 events for the given RFB key event + */ +void vncKeyboardEvent(KeySym keysym, unsigned xtcode, int down) +{ + /* Simple case: the client has specified the key */ + if (xtcode && xtcode < codeMapLen) { + int keycode; + + keycode = codeMap[xtcode]; + if (!keycode) { + /* + * Figure something out based on keysym if we + * cannot find a mapping. + */ + if (keysym) + vncKeysymKeyboardEvent(keysym, down); + return; + } + + /* + * We update the state table in case we get a mix of + * events with and without key codes. + */ + if (down) + pressedKeys[keycode] = keysym; + else + pressedKeys[keycode] = NoSymbol; + + pressKey(vncKeyboardDev, keycode, down, "raw keycode"); + mieqProcessInputEvents(); + return; + } + + /* + * Advanced case: We have to figure out a sequence of keys that + * result in the given keysym + */ + if (keysym) + vncKeysymKeyboardEvent(keysym, down); +} + +/* altKeysym is a table of alternative keysyms which have the same meaning. */ + +static struct altKeysym_t { + KeySym a, b; +} altKeysym[] = { + { XK_Shift_L, XK_Shift_R }, + { XK_Control_L, XK_Control_R }, + { XK_Meta_L, XK_Meta_R }, + { XK_Alt_L, XK_Alt_R }, + { XK_Super_L, XK_Super_R }, + { XK_Hyper_L, XK_Hyper_R }, + { XK_KP_Space, XK_space }, + { XK_KP_Tab, XK_Tab }, + { XK_KP_Enter, XK_Return }, + { XK_KP_F1, XK_F1 }, + { XK_KP_F2, XK_F2 }, + { XK_KP_F3, XK_F3 }, + { XK_KP_F4, XK_F4 }, + { XK_KP_Home, XK_Home }, + { XK_KP_Left, XK_Left }, + { XK_KP_Up, XK_Up }, + { XK_KP_Right, XK_Right }, + { XK_KP_Down, XK_Down }, + { XK_KP_Page_Up, XK_Page_Up }, + { XK_KP_Page_Down, XK_Page_Down }, + { XK_KP_End, XK_End }, + { XK_KP_Begin, XK_Begin }, + { XK_KP_Insert, XK_Insert }, + { XK_KP_Delete, XK_Delete }, + { XK_KP_Equal, XK_equal }, + { XK_KP_Multiply, XK_asterisk }, + { XK_KP_Add, XK_plus }, + { XK_KP_Separator, XK_comma }, + { XK_KP_Subtract, XK_minus }, + { XK_KP_Decimal, XK_period }, + { XK_KP_Divide, XK_slash }, + { XK_KP_0, XK_0 }, + { XK_KP_1, XK_1 }, + { XK_KP_2, XK_2 }, + { XK_KP_3, XK_3 }, + { XK_KP_4, XK_4 }, + { XK_KP_5, XK_5 }, + { XK_KP_6, XK_6 }, + { XK_KP_7, XK_7 }, + { XK_KP_8, XK_8 }, + { XK_KP_9, XK_9 }, + { XK_ISO_Level3_Shift, XK_Mode_switch }, +}; + +/* + * vncKeysymKeyboardEvent() - work out the best keycode corresponding + * to the keysym sent by the viewer. This is basically impossible in + * the general case, but we make a best effort by assuming that all + * useful keysyms can be reached using just the Shift and + * Level 3 (AltGr) modifiers. For core keyboards this is basically + * always true, and should be true for most sane, western XKB layouts. + */ +static void vncKeysymKeyboardEvent(KeySym keysym, int down) +{ + int i; + unsigned state, new_state; + KeyCode keycode; + + unsigned level_three_mask; + KeyCode shift_press, level_three_press; + KeyCode shift_release[8], level_three_release[8]; + size_t shift_release_count, level_three_release_count; + + /* + * Release events must match the press event, so look up what + * keycode we sent for the press. + */ + if (!down) { + for (i = 0;i < 256;i++) { + if (pressedKeys[i] == keysym) { + pressedKeys[i] = NoSymbol; + pressKey(vncKeyboardDev, i, FALSE, "keycode"); + mieqProcessInputEvents(); + return; + } + } + + /* + * This can happen quite often as we ignore some + * key presses. + */ + LOG_DEBUG("Unexpected release of keysym 0x%x", keysym); + return; + } + + /* + * Since we are checking the current state to determine if we need + * to fake modifiers, we must make sure that everything put on the + * input queue is processed before we start. Otherwise, shift may be + * stuck down. + */ + mieqProcessInputEvents(); + + state = vncGetKeyboardState(); + + keycode = vncKeysymToKeycode(keysym, state, &new_state); + + /* + * Shift+Alt is often mapped to Meta, so try that rather than + * allocating a new entry, faking shift, or using the dummy + * key entries that many layouts have. + */ + if ((state & ShiftMask) && + ((keysym == XK_Alt_L) || (keysym == XK_Alt_R))) { + KeyCode alt, meta; + + if (keysym == XK_Alt_L) { + alt = vncKeysymToKeycode(XK_Alt_L, state & ~ShiftMask, NULL); + meta = vncKeysymToKeycode(XK_Meta_L, state, NULL); + } else { + alt = vncKeysymToKeycode(XK_Alt_R, state & ~ShiftMask, NULL); + meta = vncKeysymToKeycode(XK_Meta_R, state, NULL); + } + + if ((meta != 0) && (alt == meta)) { + LOG_DEBUG("Replacing Shift+Alt with Shift+Meta"); + keycode = meta; + new_state = state; + } + } + + /* Try some equivalent keysyms if we couldn't find a perfect match */ + if (keycode == 0) { + for (i = 0;i < sizeof(altKeysym)/sizeof(altKeysym[0]);i++) { + KeySym altsym; + + if (altKeysym[i].a == keysym) + altsym = altKeysym[i].b; + else if (altKeysym[i].b == keysym) + altsym = altKeysym[i].a; + else + continue; + + keycode = vncKeysymToKeycode(altsym, state, &new_state); + if (keycode != 0) + break; + } + } + + /* No matches. Will have to add a new entry... */ + if (keycode == 0) { + keycode = vncAddKeysym(keysym, state); + if (keycode == 0) { + LOG_ERROR("Failure adding new keysym 0x%x", keysym); + return; + } + + LOG_INFO("Added unknown keysym 0x%x to keycode %d", + keysym, keycode); + + /* + * The state given to addKeysym() is just a hint and + * the actual result might still require some state + * changes. + */ + keycode = vncKeysymToKeycode(keysym, state, &new_state); + if (keycode == 0) { + LOG_ERROR("Newly added keysym 0x%x cannot be generated", keysym); + return; + } + } + + /* + * X11 generally lets shift toggle the keys on the numeric pad + * the same way NumLock does. This is however not the case on + * other systems like Windows. As a result, some applications + * get confused when we do a fake shift to get the same effect + * that having NumLock active would produce. + * + * Not all clients have proper NumLock synchronisation (so we + * can avoid faking shift) so we try to avoid the fake shifts + * if we can use an alternative keysym. + */ + if (((state & ShiftMask) != (new_state & ShiftMask)) && + vncGetAvoidShiftNumLock() && vncIsAffectedByNumLock(keycode)) { + KeyCode keycode2; + unsigned new_state2; + + LOG_DEBUG("Finding alternative to keysym 0x%x to avoid fake shift for numpad", keysym); + + for (i = 0;i < sizeof(altKeysym)/sizeof(altKeysym[0]);i++) { + KeySym altsym; + + if (altKeysym[i].a == keysym) + altsym = altKeysym[i].b; + else if (altKeysym[i].b == keysym) + altsym = altKeysym[i].a; + else + continue; + + keycode2 = vncKeysymToKeycode(altsym, state, &new_state2); + if (keycode2 == 0) + continue; + + if (((state & ShiftMask) != (new_state2 & ShiftMask)) && + vncIsAffectedByNumLock(keycode2)) + continue; + + break; + } + + if (i == sizeof(altKeysym)/sizeof(altKeysym[0])) + LOG_DEBUG("No alternative keysym found"); + else { + keycode = keycode2; + new_state = new_state2; + } + } + + /* + * "Shifted Tab" is a bit of a mess. Some systems have varying, + * special keysyms for this symbol. VNC mandates that clients + * should always send the plain XK_Tab keysym and the server + * should deduce the meaning based on current Shift state. + * To comply with this, we will find the keycode that sends + * XK_Tab, and make sure that Shift isn't cleared. This can + * possibly result in a different keysym than XK_Tab, but that + * is the desired behaviour. + * + * Note: We never get ISO_Left_Tab here because it's already + * been translated in VNCSConnectionST. + */ + if (keysym == XK_Tab && (state & ShiftMask)) + new_state |= ShiftMask; + + /* + * We need a bigger state change than just shift, + * so we need to know what the mask is for level 3 shifts. + */ + if ((new_state & ~ShiftMask) != (state & ~ShiftMask)) + level_three_mask = vncGetLevelThreeMask(); + else + level_three_mask = 0; + + shift_press = level_three_press = 0; + shift_release_count = level_three_release_count = 0; + + /* Need a fake press or release of shift? */ + if (!(state & ShiftMask) && (new_state & ShiftMask)) { + shift_press = vncPressShift(); + if (shift_press == 0) { + LOG_ERROR("Unable to find a modifier key for Shift"); + return; + } + + pressKey(vncKeyboardDev, shift_press, TRUE, "temp shift"); + } else if ((state & ShiftMask) && !(new_state & ShiftMask)) { + shift_release_count = vncReleaseShift(shift_release, + sizeof(shift_release)/sizeof(*shift_release)); + if (shift_release_count == 0) { + LOG_ERROR("Unable to find the modifier key(s) for releasing Shift"); + return; + } + + for (i = 0;i < shift_release_count;i++) + pressKey(vncKeyboardDev, shift_release[i], FALSE, "temp shift"); + } + + /* Need a fake press or release of level three shift? */ + if (!(state & level_three_mask) && (new_state & level_three_mask)) { + level_three_press = vncPressLevelThree(); + if (level_three_press == 0) { + LOG_ERROR("Unable to find a modifier key for ISO_Level3_Shift/Mode_Switch"); + return; + } + + pressKey(vncKeyboardDev, level_three_press, TRUE, "temp level 3 shift"); + } else if ((state & level_three_mask) && !(new_state & level_three_mask)) { + level_three_release_count = vncReleaseLevelThree(level_three_release, + sizeof(level_three_release)/sizeof(*level_three_release)); + if (level_three_release_count == 0) { + LOG_ERROR("Unable to find the modifier key(s) for releasing ISO_Level3_Shift/Mode_Switch"); + return; + } + + for (i = 0;i < level_three_release_count;i++) + pressKey(vncKeyboardDev, level_three_release[i], FALSE, "temp level 3 shift"); + } + + /* Now press the actual key */ + pressKey(vncKeyboardDev, keycode, TRUE, "keycode"); + + /* And store the mapping so that we can do a proper release later */ + for (i = 0;i < 256;i++) { + if (i == keycode) + continue; + if (pressedKeys[i] == keysym) { + LOG_ERROR("Keysym 0x%x generated by both keys %d and %d", keysym, i, keycode); + pressedKeys[i] = NoSymbol; + } + } + + pressedKeys[keycode] = keysym; + + /* Undo any fake level three shift */ + if (level_three_press != 0) + pressKey(vncKeyboardDev, level_three_press, FALSE, "temp level 3 shift"); + else if (level_three_release_count != 0) { + for (i = 0;i < level_three_release_count;i++) + pressKey(vncKeyboardDev, level_three_release[i], TRUE, "temp level 3 shift"); + } + + /* Undo any fake shift */ + if (shift_press != 0) + pressKey(vncKeyboardDev, shift_press, FALSE, "temp shift"); + else if (shift_release_count != 0) { + for (i = 0;i < shift_release_count;i++) + pressKey(vncKeyboardDev, shift_release[i], TRUE, "temp shift"); + } + + /* + * When faking a modifier we are putting a keycode (which can + * currently activate the desired modifier) on the input + * queue. A future modmap change can change the mapping so + * that this keycode means something else entirely. Guard + * against this by processing the queue now. + */ + mieqProcessInputEvents(); +} diff --git a/unix/xserver/hw/vnc/vncInput.h b/unix/xserver/hw/vnc/vncInput.h new file mode 100644 index 00000000..08cab6cb --- /dev/null +++ b/unix/xserver/hw/vnc/vncInput.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2009 TightVNC Team + * Copyright (C) 2009, 2010 Red Hat, Inc. + * Copyright (C) 2009, 2010 TigerVNC Team + * 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. + */ + +#ifndef __VNCINPUT_H__ +#define __VNCINPUT_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void vncInitInputDevice(void); + +void vncPointerButtonAction(int buttonMask); +void vncPointerMove(int x, int y); +void vncGetPointerPos(int *x, int *y); + +void vncKeyboardEvent(KeySym keysym, unsigned xtcode, int down); + +/* Backend dependent functions below here */ + +void vncPrepareInputDevices(void); + +unsigned vncGetKeyboardState(void); +unsigned vncGetLevelThreeMask(void); + +KeyCode vncPressShift(void); +size_t vncReleaseShift(KeyCode *keys, size_t maxKeys); + +KeyCode vncPressLevelThree(void); +size_t vncReleaseLevelThree(KeyCode *keys, size_t maxKeys); + +KeyCode vncKeysymToKeycode(KeySym keysym, unsigned state, unsigned *new_state); + +int vncIsAffectedByNumLock(KeyCode keycode); + +KeyCode vncAddKeysym(KeySym keysym, unsigned state); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/unix/xserver/hw/vnc/vncInputXKB.c b/unix/xserver/hw/vnc/vncInputXKB.c new file mode 100644 index 00000000..d5fe286a --- /dev/null +++ b/unix/xserver/hw/vnc/vncInputXKB.c @@ -0,0 +1,678 @@ +/* Copyright (C) 2009 TightVNC Team + * Copyright (C) 2009 Red Hat, Inc. + * Copyright 2013-2018 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 "vncInput.h" + +#ifndef KEYBOARD_OR_FLOAT +#define KEYBOARD_OR_FLOAT MASTER_KEYBOARD +#endif + +#if XORG_OLDER_THAN(1, 18, 0) +#define GetMaster(dev, type) ((dev)->master) +#endif + +extern DeviceIntPtr vncKeyboardDev; + +static const KeyCode fakeKeys[] = { +#ifdef __linux__ + 92, 203, 204, 205, 206, 207 +#else + 8, 124, 125, 156, 127, 128 +#endif + }; + +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; // KeyCode has insufficient range for the loop + KeyCode fallback; + KeySym ks; + unsigned level_three_mask; + + if (new_state != NULL) + *new_state = state; + + fallback = 0; + 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; + size_t fakeIdx; + + 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) + continue; + + /* + * Some keys are never sent by a real keyboard and are + * used in the default layouts as a fallback for + * modifiers. Make sure we use them last as some + * applications can be confused by these normally + * unused keys. + */ + for (fakeIdx = 0; + fakeIdx < sizeof(fakeKeys)/sizeof(fakeKeys[0]); + fakeIdx++) { + if (key == fakeKeys[fakeIdx]) { + if (fallback == 0) + fallback = key; + break; + } + } + if (fakeIdx < sizeof(fakeKeys)/sizeof(fakeKeys[0])) + continue; + + return key; + } + + /* Use the fallback key, if one was found */ + if (fallback != 0) + return fallback; + + 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; +}