/* Copyright (C) 2009 TightVNC Team * Copyright (C) 2009, 2014 Red Hat, Inc. * Copyright 2013 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ #ifdef HAVE_DIX_CONFIG_H #include #endif #include #include "Input.h" #include "xorg-version.h" #include "vncExtInit.h" extern "C" { #define public c_public #define class c_class #include "inputstr.h" #if XORG >= 110 #include "inpututils.h" #endif #include "mi.h" #if XORG >= 16 #include "exevents.h" #endif #if XORG == 16 extern void CopyKeyClass(DeviceIntPtr device, DeviceIntPtr master); #endif #if XORG >= 17 #include "xkbsrv.h" #include "xkbstr.h" #include "xserver-properties.h" extern _X_EXPORT DevPrivateKey CoreDevicePrivateKey; #endif #include #include #include #undef public #undef class } #if XORG >= 110 #define Xfree free #endif using namespace rdr; using namespace rfb; static LogWriter vlog("Input"); rfb::BoolParameter avoidShiftNumLock("AvoidShiftNumLock", "Avoid fake Shift presses for keys affected by NumLock.", true); #define BUTTONS 7 class InputDevice *vncInputDevice; InputDevice InputDevice::singleton; /* Event queue is shared between all devices. */ #if XORG == 15 static xEvent *eventq = NULL; #elif XORG < 111 static EventList *eventq = NULL; #endif #if XORG < 111 static void initEventq(void) { /* eventq is never free()-ed because it exists during server life. */ if (eventq == NULL) { #if XORG == 15 eventq = (xEvent *)xcalloc(sizeof(xEvent), GetMaximumEventsNum()); if (!eventq) FatalError("Couldn't allocate eventq\n"); #else GetEventList(&eventq); #endif } } #endif /* XORG < 111 */ #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, #if XORG == 15 eventq + i #elif XORG == 16 (eventq + i)->event #else (InternalEvent *) (eventq + i)->event #endif ); } } #endif /* XORG < 111 */ InputDevice::InputDevice() : oldButtonMask(0) { int i; vncInputDevice = this; #if XORG < 111 initEventq(); #endif for (i = 0;i < 256;i++) pressedKeys[i] = NoSymbol; } void InputDevice::PointerButtonAction(int buttonMask) { 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<= 110 ValuatorMask mask; #endif if (pos.equals(cursorPos)) return; valuators[0] = pos.x; valuators[1] = pos.y; #if XORG < 110 n = GetPointerEvents(eventq, pointerDev, MotionNotify, 0, POINTER_ABSOLUTE, 0, 2, valuators); enqueueEvents(pointerDev, n); #elif XORG < 111 valuator_mask_set_range(&mask, 0, 2, valuators); n = GetPointerEvents(eventq, pointerDev, MotionNotify, 0, POINTER_ABSOLUTE, &mask); enqueueEvents(pointerDev, n); #else valuator_mask_set_range(&mask, 0, 2, valuators); QueuePointerEvents(pointerDev, MotionNotify, 0, POINTER_ABSOLUTE, &mask); #endif cursorPos = pos; } const rfb::Point &InputDevice::getPointerPos(void) { if (pointerDev != NULL) { int x, y; GetSpritePosition (pointerDev, &x, &y); cursorPos.x = x; cursorPos.y = y; } return cursorPos; } int InputDevice::pointerProc(DeviceIntPtr pDevice, int onoff) { BYTE map[BUTTONS + 1]; DevicePtr pDev = (DevicePtr)pDevice; int i; #if XORG >= 17 /* * NOTE: map[] array is one element longer than btn_labels[] array. This * is not a bug. */ Atom btn_labels[BUTTONS]; Atom axes_labels[2]; #endif switch (onoff) { case DEVICE_INIT: for (i = 0; i < BUTTONS + 1; i++) map[i] = i; #if XORG >= 17 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); #endif InitPointerDeviceStruct(pDev, map, BUTTONS, #if XORG == 15 GetMotionHistory, #elif XORG >= 17 btn_labels, #endif (PtrCtrlProcPtr)NoopDDA, GetMotionHistorySize(), 2 #if XORG >= 17 , axes_labels #endif ); break; case DEVICE_ON: pDev->on = TRUE; break; case DEVICE_OFF: pDev->on = FALSE; break; case DEVICE_CLOSE: singleton.pointerDev = NULL; break; } return Success; } static void keyboardBell(int percent, DeviceIntPtr device, pointer ctrl, int class_) { if (percent > 0) vncBell(); } int InputDevice::keyboardProc(DeviceIntPtr pDevice, int onoff) { #if XORG < 17 KeySymsRec keySyms; CARD8 modMap[MAP_LENGTH]; #endif DevicePtr pDev = (DevicePtr)pDevice; switch (onoff) { case DEVICE_INIT: #if XORG < 17 GetInitKeyboardMap(&keySyms, modMap); #endif InitKeyboardDeviceStruct( #if XORG >= 17 pDevice, NULL, #else pDev, &keySyms, modMap, #endif keyboardBell, (KbdCtrlProcPtr)NoopDDA); break; case DEVICE_ON: pDev->on = TRUE; break; case DEVICE_OFF: pDev->on = FALSE; break; case DEVICE_CLOSE: singleton.keyboardDev = NULL; break; } return Success; } void InputDevice::InitInputDevice(void) { if ((pointerDev != NULL) || (keyboardDev != NULL)) return; #if XORG < 17 pointerDev = AddInputDevice( #if XORG >= 16 serverClient, #endif pointerProc, TRUE); RegisterPointerDevice(pointerDev); keyboardDev = AddInputDevice( #if XORG >= 16 serverClient, #endif keyboardProc, TRUE); RegisterKeyboardDevice(keyboardDev); if (ActivateDevice(pointerDev) != Success || ActivateDevice(keyboardDev) != Success) FatalError("Failed to activate TigerVNC devices\n"); if (!EnableDevice(pointerDev) || !EnableDevice(keyboardDev)) FatalError("Failed to enable TigerVNC devices\n"); #else /* < 17 */ int ret; ret = AllocDevicePair(serverClient, "TigerVNC", &pointerDev, &keyboardDev, pointerProc, keyboardProc, FALSE); if (ret != Success) FatalError("Failed to initialize TigerVNC input devices\n"); if (ActivateDevice(pointerDev, TRUE) != Success || ActivateDevice(keyboardDev, TRUE) != Success) FatalError("Failed to activate TigerVNC devices\n"); if (!EnableDevice(pointerDev, TRUE) || !EnableDevice(keyboardDev, TRUE)) FatalError("Failed to activate TigerVNC devices\n"); #endif /* 17 */ PrepareInputDevices(); } 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) vlog.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); #else QueueKeyboardEvents(dev, action, kc, NULL); #endif } /* 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 }, }; /* * keyEvent() - 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. */ void InputDevice::keyEvent(rdr::U32 keysym, bool down) { int i; unsigned state, new_state; KeyCode keycode; unsigned level_three_mask; KeyCode shift_press, level_three_press; std::list shift_release, level_three_release; /* * 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(keyboardDev, i, false, "keycode"); mieqProcessInputEvents(); return; } } /* * This can happen quite often as we ignore some * key presses. */ vlog.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 = getKeyboardState(); keycode = keysymToKeycode(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 = keysymToKeycode(altsym, state, &new_state); if (keycode != 0) break; } } /* We don't have lock synchronisation... */ if (isLockModifier(keycode, new_state)) { vlog.debug("Ignoring lock key (e.g. caps lock)"); return; } /* No matches. Will have to add a new entry... */ if (keycode == 0) { keycode = addKeysym(keysym, state); if (keycode == 0) { vlog.error("Failure adding new keysym 0x%x", keysym); return; } vlog.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 = keysymToKeycode(keysym, state, &new_state); if (keycode == 0) { vlog.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)) && avoidShiftNumLock && isAffectedByNumLock(keycode)) { KeyCode keycode2; unsigned new_state2; vlog.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 = keysymToKeycode(altsym, state, &new_state2); if (keycode2 == 0) continue; if (((state & ShiftMask) != (new_state2 & ShiftMask)) && isAffectedByNumLock(keycode2)) continue; break; } if (i == sizeof(altKeysym)/sizeof(altKeysym[0])) vlog.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 = getLevelThreeMask(); else level_three_mask = 0; shift_press = level_three_press = 0; /* Need a fake press or release of shift? */ if (!(state & ShiftMask) && (new_state & ShiftMask)) { shift_press = pressShift(); if (shift_press == 0) { vlog.error("Unable to find a modifier key for Shift"); return; } pressKey(keyboardDev, shift_press, true, "temp shift"); } else if ((state & ShiftMask) && !(new_state & ShiftMask)) { std::list::const_iterator iter; shift_release = releaseShift(); if (shift_release.empty()) { vlog.error("Unable to find the modifier key(s) for releasing Shift"); return; } for (iter = shift_release.begin();iter != shift_release.end();++iter) pressKey(keyboardDev, *iter, 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 = pressLevelThree(); if (level_three_press == 0) { vlog.error("Unable to find a modifier key for ISO_Level3_Shift/Mode_Switch"); return; } pressKey(keyboardDev, level_three_press, true, "temp level 3 shift"); } else if ((state & level_three_mask) && !(new_state & level_three_mask)) { std::list::const_iterator iter; level_three_release = releaseLevelThree(); if (level_three_release.empty()) { vlog.error("Unable to find the modifier key(s) for releasing ISO_Level3_Shift/Mode_Switch"); return; } for (iter = level_three_release.begin();iter != level_three_release.end();++iter) pressKey(keyboardDev, *iter, false, "temp level 3 shift"); } /* Now press the actual key */ pressKey(keyboardDev, 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) { vlog.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(keyboardDev, level_three_press, false, "temp level 3 shift"); else if (!level_three_release.empty()) { std::list::const_iterator iter; for (iter = level_three_release.begin();iter != level_three_release.end();++iter) pressKey(keyboardDev, *iter, true, "temp level 3 shift"); } /* Undo any fake shift */ if (shift_press != 0) pressKey(keyboardDev, shift_press, false, "temp shift"); else if (!shift_release.empty()) { std::list::const_iterator iter; for (iter = shift_release.begin();iter != shift_release.end();++iter) pressKey(keyboardDev, *iter, 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(); }