+++ /dev/null
-/* 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 <dix-config.h>
-#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 <X11/keysym.h>
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-
-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<<i)) ?
- ButtonPress : ButtonRelease;
- valuator_mask_set_range(&mask, 0, 0, NULL);
- QueuePointerEvents(vncPointerDev, action, i + 1,
- POINTER_RELATIVE, &mask);
- }
- }
-
- oldButtonMask = buttonMask;
-}
-
-void vncPointerMove(int x, int y)
-{
- int valuators[2];
- ValuatorMask mask;
-
- if (cursorPosX == x && cursorPosY == y)
- return;
-
- valuators[0] = x;
- valuators[1] = y;
- valuator_mask_set_range(&mask, 0, 2, valuators);
- QueuePointerEvents(vncPointerDev, MotionNotify, 0,
- POINTER_ABSOLUTE, &mask);
-
- cursorPosX = x;
- cursorPosY = y;
-}
-
-void vncGetPointerPos(int *x, int *y)
-{
- if (vncPointerDev != NULL) {
- ScreenPtr ptrScreen;
-
- miPointerGetPosition(vncPointerDev, &cursorPosX, &cursorPosY);
-
- /* Pointer coordinates are screen relative */
- ptrScreen = miPointerGetScreen(vncPointerDev);
- cursorPosX += ptrScreen->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();
-}
+++ /dev/null
-/* 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 <stdlib.h>
-#include <X11/X.h>
-
-#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
+++ /dev/null
-/* 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 <dix-config.h>
-#endif
-
-#include "xorg-version.h"
-
-#include <stdio.h>
-
-#include <X11/keysym.h>
-#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-
-#include "xkbsrv.h"
-#include "xkbstr.h"
-#include "eventstr.h"
-#include "scrnintstr.h"
-#include "mi.h"
-
-#include "Input.h"
-
-#ifndef KEYBOARD_OR_FLOAT
-#define KEYBOARD_OR_FLOAT MASTER_KEYBOARD
-#endif
-
-#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;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; // 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;
-}
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 \
#include "vncHooks.h"
#include "vncSelection.h"
#include "XorgGlue.h"
-#include "Input.h"
+#include "vncInput.h"
extern "C" {
void vncSetGlueContext(int screenIndex);
#include <rfb/Configuration.h>
#include <rfb/Timer.h>
#include <unixcommon.h>
-#include "Input.h"
+#include "vncInput.h"
namespace rfb {
class VNCServerST;
--- /dev/null
+/* 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 <dix-config.h>
+#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 <X11/keysym.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+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<<i)) ?
+ ButtonPress : ButtonRelease;
+ valuator_mask_set_range(&mask, 0, 0, NULL);
+ QueuePointerEvents(vncPointerDev, action, i + 1,
+ POINTER_RELATIVE, &mask);
+ }
+ }
+
+ oldButtonMask = buttonMask;
+}
+
+void vncPointerMove(int x, int y)
+{
+ int valuators[2];
+ ValuatorMask mask;
+
+ if (cursorPosX == x && cursorPosY == y)
+ return;
+
+ valuators[0] = x;
+ valuators[1] = y;
+ valuator_mask_set_range(&mask, 0, 2, valuators);
+ QueuePointerEvents(vncPointerDev, MotionNotify, 0,
+ POINTER_ABSOLUTE, &mask);
+
+ cursorPosX = x;
+ cursorPosY = y;
+}
+
+void vncGetPointerPos(int *x, int *y)
+{
+ if (vncPointerDev != NULL) {
+ ScreenPtr ptrScreen;
+
+ miPointerGetPosition(vncPointerDev, &cursorPosX, &cursorPosY);
+
+ /* Pointer coordinates are screen relative */
+ ptrScreen = miPointerGetScreen(vncPointerDev);
+ cursorPosX += ptrScreen->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();
+}
--- /dev/null
+/* 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 <stdlib.h>
+#include <X11/X.h>
+
+#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
--- /dev/null
+/* 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 <dix-config.h>
+#endif
+
+#include "xorg-version.h"
+
+#include <stdio.h>
+
+#include <X11/keysym.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include "xkbsrv.h"
+#include "xkbstr.h"
+#include "eventstr.h"
+#include "scrnintstr.h"
+#include "mi.h"
+
+#include "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;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; // 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;
+}