mirror of
https://github.com/kasmtech/KasmVNC.git
synced 2024-12-02 21:13:50 +01:00
0a45fcc700
Previously all scrolling relied on "clicking" the up/down or left/right scroll buttons which made it unprecise and to always scroll at the same speed. Now we pass the scroll delta directly to the xorg input driver so the scroll is more responsinve and adaptive.
734 lines
20 KiB
C
734 lines
20 KiB
C
/* 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"
|
|
#if XORG >= 110
|
|
#include "inpututils.h"
|
|
#endif
|
|
#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
|
|
|
|
/* Event queue is shared between all devices. */
|
|
#if XORG < 111
|
|
static EventList *eventq = NULL;
|
|
#endif
|
|
|
|
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, "KasmVNC",
|
|
&vncPointerDev, &vncKeyboardDev,
|
|
vncPointerProc, vncKeyboardProc,
|
|
FALSE);
|
|
|
|
if (ret != Success)
|
|
FatalError("Failed to initialize KasmVNC input devices\n");
|
|
|
|
if (ActivateDevice(vncPointerDev, TRUE) != Success ||
|
|
ActivateDevice(vncKeyboardDev, TRUE) != Success)
|
|
FatalError("Failed to activate KasmVNC devices\n");
|
|
|
|
if (!EnableDevice(vncPointerDev, TRUE) ||
|
|
!EnableDevice(vncKeyboardDev, TRUE))
|
|
FatalError("Failed to activate KasmVNC devices\n");
|
|
|
|
#if XORG < 111
|
|
/* eventq is never free()-ed because it exists during server life. */
|
|
if (eventq == NULL)
|
|
GetEventList(&eventq);
|
|
#endif
|
|
|
|
vncPrepareInputDevices();
|
|
}
|
|
|
|
#if XORG < 111
|
|
static void enqueueEvents(DeviceIntPtr dev, int n)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
/*
|
|
* Passing arguments in global variable eventq is probably not
|
|
* good programming practise but in this case it is safe and
|
|
* clear.
|
|
*/
|
|
mieqEnqueue(dev, (InternalEvent *) (eventq + i)->event);
|
|
}
|
|
}
|
|
#endif /* XORG < 111 */
|
|
|
|
void vncPointerButtonAction(int buttonMask, const unsigned char skipclick,
|
|
const unsigned char skiprelease)
|
|
{
|
|
int i;
|
|
#if XORG < 111
|
|
int n;
|
|
#endif
|
|
#if XORG >= 110
|
|
ValuatorMask mask;
|
|
#endif
|
|
|
|
for (i = 0; i < BUTTONS; i++) {
|
|
if ((buttonMask ^ oldButtonMask) & (1 << i)) {
|
|
int action = (buttonMask & (1<<i)) ?
|
|
ButtonPress : ButtonRelease;
|
|
|
|
if (action == ButtonPress && skipclick) {
|
|
buttonMask &= ~(1<<i);
|
|
continue;
|
|
} else if (action == ButtonRelease && skiprelease) {
|
|
buttonMask |= (1<<i);
|
|
continue;
|
|
}
|
|
#if XORG < 110
|
|
n = GetPointerEvents(eventq, vncPointerDev,
|
|
action, i + 1,
|
|
POINTER_RELATIVE, 0, 0, NULL);
|
|
enqueueEvents(vncPointerDev, n);
|
|
#elif XORG < 111
|
|
valuator_mask_set_range(&mask, 0, 0, NULL);
|
|
n = GetPointerEvents(eventq, vncPointerDev,
|
|
action, i + 1,
|
|
POINTER_RELATIVE, &mask);
|
|
enqueueEvents(vncPointerDev, n);
|
|
#else
|
|
valuator_mask_set_range(&mask, 0, 0, NULL);
|
|
QueuePointerEvents(vncPointerDev, action, i + 1,
|
|
POINTER_RELATIVE, &mask);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
oldButtonMask = buttonMask;
|
|
}
|
|
|
|
void vncPointerMove(int x, int y)
|
|
{
|
|
int valuators[2];
|
|
#if XORG < 111
|
|
int n;
|
|
#endif
|
|
#if XORG >= 110
|
|
ValuatorMask mask;
|
|
#endif
|
|
|
|
if (cursorPosX == x && cursorPosY == y)
|
|
return;
|
|
|
|
valuators[0] = x;
|
|
valuators[1] = y;
|
|
#if XORG < 110
|
|
n = GetPointerEvents(eventq, vncPointerDev, MotionNotify, 0,
|
|
POINTER_ABSOLUTE, 0, 2, valuators);
|
|
enqueueEvents(vncPointerDev, n);
|
|
#elif XORG < 111
|
|
valuator_mask_set_range(&mask, 0, 2, valuators);
|
|
n = GetPointerEvents(eventq, vncPointerDev, MotionNotify, 0,
|
|
POINTER_ABSOLUTE, &mask);
|
|
enqueueEvents(vncPointerDev, n);
|
|
#else
|
|
valuator_mask_set_range(&mask, 0, 2, valuators);
|
|
QueuePointerEvents(vncPointerDev, MotionNotify, 0,
|
|
POINTER_ABSOLUTE, &mask);
|
|
#endif
|
|
|
|
cursorPosX = x;
|
|
cursorPosY = y;
|
|
}
|
|
|
|
void vncScroll(int x, int y) {
|
|
ValuatorMask mask;
|
|
valuator_mask_zero(&mask);
|
|
valuator_mask_set(&mask, 2, x);
|
|
valuator_mask_set(&mask, 3, y);
|
|
QueuePointerEvents(vncPointerDev, MotionNotify, 0, POINTER_RELATIVE, &mask);
|
|
}
|
|
|
|
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[4];
|
|
|
|
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);
|
|
axes_labels[2] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_HSCROLL);
|
|
axes_labels[3] = XIGetKnownProperty(AXIS_LABEL_PROP_REL_VSCROLL);
|
|
|
|
|
|
InitPointerDeviceStruct(pDev, map, BUTTONS, btn_labels,
|
|
(PtrCtrlProcPtr)NoopDDA,
|
|
GetMotionHistorySize(),
|
|
4, axes_labels);
|
|
|
|
InitValuatorAxisStruct(pDevice, 2, axes_labels[2], NO_AXIS_LIMITS, NO_AXIS_LIMITS, 0, 0, 0, Relative);
|
|
InitValuatorAxisStruct(pDevice, 3, axes_labels[3], NO_AXIS_LIMITS, NO_AXIS_LIMITS, 0, 0, 0, Relative);
|
|
|
|
char* envScrollFactorH = getenv("SCROLL_FACTOR_H");
|
|
char* envScrollFactorV = getenv("SCROLL_FACTOR_V");
|
|
|
|
float scrollFactorH = envScrollFactorH ? atof(envScrollFactorH) : 50.0;
|
|
float scrollFactorV = envScrollFactorV ? atof(envScrollFactorV) : 50.0;
|
|
|
|
LOG_INFO("Mouse horizonatl scroll factor: %f", scrollFactorH);
|
|
LOG_INFO("Mouse vertical scroll factor: %f", scrollFactorV);
|
|
|
|
SetScrollValuator(pDevice, 2, SCROLL_TYPE_HORIZONTAL, scrollFactorH, SCROLL_FLAG_NONE);
|
|
SetScrollValuator(pDevice, 3, SCROLL_TYPE_VERTICAL, scrollFactorV, SCROLL_FLAG_PREFERRED);
|
|
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 XORG < 111
|
|
unsigned int n;
|
|
#endif
|
|
|
|
if (msg != NULL)
|
|
LOG_DEBUG("%s %d %s", msg, kc, down ? "down" : "up");
|
|
|
|
action = down ? KeyPress : KeyRelease;
|
|
#if XORG < 111
|
|
n = GetKeyboardEvents(eventq, dev, action, kc);
|
|
enqueueEvents(dev, n);
|
|
#elif XORG < 118
|
|
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);
|
|
|
|
/* 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.
|
|
*
|
|
* Until we have proper NumLock synchronisation (so we can
|
|
* avoid faking shift), 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();
|
|
}
|
|
|
|
#if INPUTTHREAD
|
|
/** This function is called in Xserver/os/inputthread.c when starting
|
|
the input thread. */
|
|
void
|
|
ddxInputThreadInit(void)
|
|
{
|
|
}
|
|
#endif
|