123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494 |
- /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- *
- * 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.
- */
-
- // -=- SInput.cxx
- //
- // A number of routines that accept VNC input event data and perform
- // the appropriate actions under Win32
-
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
-
- #define XK_MISCELLANY
- #define XK_LATIN1
- #define XK_CURRENCY
- #include <rfb/keysymdef.h>
-
- #include <tchar.h>
- #include <rfb_win32/SInput.h>
- #include <rfb_win32/MonitorInfo.h>
- #include <rfb_win32/Service.h>
- #include <rfb_win32/keymap.h>
- #include <rdr/Exception.h>
- #include <rfb/LogWriter.h>
-
- using namespace rfb;
-
- static LogWriter vlog("SInput");
-
- //
- // -=- Pointer implementation for Win32
- //
-
- static DWORD buttonDownMapping[8] = {
- MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_RIGHTDOWN,
- MOUSEEVENTF_WHEEL, MOUSEEVENTF_WHEEL, 0, 0, 0
- };
-
- static DWORD buttonUpMapping[8] = {
- MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_RIGHTUP,
- MOUSEEVENTF_WHEEL, MOUSEEVENTF_WHEEL, 0, 0, 0
- };
-
- static DWORD buttonDataMapping[8] = {
- 0, 0, 0, 120, (DWORD)-120, 0, 0, 0
- };
-
- win32::SPointer::SPointer()
- : last_buttonmask(0)
- {
- }
-
- void
- win32::SPointer::pointerEvent(const Point& pos, int buttonmask)
- {
- // - We are specifying absolute coordinates
- DWORD flags = MOUSEEVENTF_ABSOLUTE;
-
- // - Has the pointer moved since the last event?
- if (!last_position.equals(pos))
- flags |= MOUSEEVENTF_MOVE;
-
- // - If the system swaps left and right mouse buttons then we must
- // swap them here to negate the effect, so that we do the actual
- // action we mean to do
- if (::GetSystemMetrics(SM_SWAPBUTTON)) {
- bool leftDown = buttonmask & 1;
- bool rightDown = buttonmask & 4;
- buttonmask = (buttonmask & ~(1 | 4));
- if (leftDown) buttonmask |= 4;
- if (rightDown) buttonmask |= 1;
- }
-
- DWORD data = 0;
- for (int i = 0; i < 8; i++) {
- if ((buttonmask & (1<<i)) != (last_buttonmask & (1<<i))) {
- if (buttonmask & (1<<i)) {
- flags |= buttonDownMapping[i];
- if (buttonDataMapping[i]) {
- if (data) vlog.info("warning - two buttons set mouse_event data field");
- data = buttonDataMapping[i];
- }
- } else {
- flags |= buttonUpMapping[i];
- }
- }
- }
-
- last_position = pos;
- last_buttonmask = buttonmask;
-
- Rect primaryDisplay(0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN));
- if (primaryDisplay.contains(pos)) {
- // mouse_event wants coordinates specified as a proportion of the
- // primary display's size, scaled to the range 0 to 65535
- Point scaled;
- scaled.x = (pos.x * 65535) / (primaryDisplay.width()-1);
- scaled.y = (pos.y * 65535) / (primaryDisplay.height()-1);
- ::mouse_event(flags, scaled.x, scaled.y, data, 0);
- } else {
- // The event lies outside the primary monitor. Under Win2K, we can just use
- // SendInput, which allows us to provide coordinates scaled to the virtual desktop.
- // SendInput is available on all multi-monitor-aware platforms.
- INPUT evt;
- evt.type = INPUT_MOUSE;
- Point vPos(pos.x-GetSystemMetrics(SM_XVIRTUALSCREEN),
- pos.y-GetSystemMetrics(SM_YVIRTUALSCREEN));
- evt.mi.dx = (vPos.x * 65535) / (GetSystemMetrics(SM_CXVIRTUALSCREEN)-1);
- evt.mi.dy = (vPos.y * 65535) / (GetSystemMetrics(SM_CYVIRTUALSCREEN)-1);
- evt.mi.dwFlags = flags | MOUSEEVENTF_VIRTUALDESK;
- evt.mi.dwExtraInfo = 0;
- evt.mi.mouseData = data;
- evt.mi.time = 0;
- if (SendInput(1, &evt, sizeof(evt)) != 1)
- throw rdr::SystemException("SendInput", GetLastError());
- }
- }
-
- //
- // -=- Keyboard implementation
- //
-
- BoolParameter rfb::win32::SKeyboard::deadKeyAware("DeadKeyAware",
- "Whether to assume the viewer has already interpreted dead key sequences "
- "into latin-1 characters", true);
- BoolParameter rfb::win32::SKeyboard::rawKeyboard("RawKeyboard",
- "Send keyboard events straight through and avoid mapping them to the "
- "current keyboard layout", false);
-
- // The keysymToAscii table transforms a couple of awkward keysyms into their
- // ASCII equivalents.
- struct keysymToAscii_t {
- rdr::U32 keysym;
- rdr::U8 ascii;
- };
-
- keysymToAscii_t keysymToAscii[] = {
- { XK_KP_Space, ' ' },
- { XK_KP_Equal, '=' },
- };
-
- rdr::U8 latin1DeadChars[] = {
- XK_grave, XK_acute, XK_asciicircum, XK_diaeresis, XK_degree, XK_cedilla,
- XK_asciitilde
- };
-
- struct latin1ToDeadChars_t {
- rdr::U8 latin1Char;
- rdr::U8 deadChar;
- rdr::U8 baseChar;
- };
-
- latin1ToDeadChars_t latin1ToDeadChars[] = {
-
- { XK_Agrave, XK_grave, XK_A },
- { XK_Egrave, XK_grave, XK_E },
- { XK_Igrave, XK_grave, XK_I },
- { XK_Ograve, XK_grave, XK_O },
- { XK_Ugrave, XK_grave, XK_U },
- { XK_agrave, XK_grave, XK_a },
- { XK_egrave, XK_grave, XK_e },
- { XK_igrave, XK_grave, XK_i },
- { XK_ograve, XK_grave, XK_o},
- { XK_ugrave, XK_grave, XK_u },
-
- { XK_Aacute, XK_acute, XK_A },
- { XK_Eacute, XK_acute, XK_E },
- { XK_Iacute, XK_acute, XK_I },
- { XK_Oacute, XK_acute, XK_O },
- { XK_Uacute, XK_acute, XK_U },
- { XK_Yacute, XK_acute, XK_Y },
- { XK_aacute, XK_acute, XK_a },
- { XK_eacute, XK_acute, XK_e },
- { XK_iacute, XK_acute, XK_i },
- { XK_oacute, XK_acute, XK_o},
- { XK_uacute, XK_acute, XK_u },
- { XK_yacute, XK_acute, XK_y },
-
- { XK_Acircumflex, XK_asciicircum, XK_A },
- { XK_Ecircumflex, XK_asciicircum, XK_E },
- { XK_Icircumflex, XK_asciicircum, XK_I },
- { XK_Ocircumflex, XK_asciicircum, XK_O },
- { XK_Ucircumflex, XK_asciicircum, XK_U },
- { XK_acircumflex, XK_asciicircum, XK_a },
- { XK_ecircumflex, XK_asciicircum, XK_e },
- { XK_icircumflex, XK_asciicircum, XK_i },
- { XK_ocircumflex, XK_asciicircum, XK_o},
- { XK_ucircumflex, XK_asciicircum, XK_u },
-
- { XK_Adiaeresis, XK_diaeresis, XK_A },
- { XK_Ediaeresis, XK_diaeresis, XK_E },
- { XK_Idiaeresis, XK_diaeresis, XK_I },
- { XK_Odiaeresis, XK_diaeresis, XK_O },
- { XK_Udiaeresis, XK_diaeresis, XK_U },
- { XK_adiaeresis, XK_diaeresis, XK_a },
- { XK_ediaeresis, XK_diaeresis, XK_e },
- { XK_idiaeresis, XK_diaeresis, XK_i },
- { XK_odiaeresis, XK_diaeresis, XK_o},
- { XK_udiaeresis, XK_diaeresis, XK_u },
- { XK_ydiaeresis, XK_diaeresis, XK_y },
-
- { XK_Aring, XK_degree, XK_A },
- { XK_aring, XK_degree, XK_a },
-
- { XK_Ccedilla, XK_cedilla, XK_C },
- { XK_ccedilla, XK_cedilla, XK_c },
-
- { XK_Atilde, XK_asciitilde, XK_A },
- { XK_Ntilde, XK_asciitilde, XK_N },
- { XK_Otilde, XK_asciitilde, XK_O },
- { XK_atilde, XK_asciitilde, XK_a },
- { XK_ntilde, XK_asciitilde, XK_n },
- { XK_otilde, XK_asciitilde, XK_o },
- };
-
- // doKeyboardEvent wraps the system keybd_event function and attempts to find
- // the appropriate scancode corresponding to the supplied virtual keycode.
-
- inline void doKeyboardEvent(BYTE vkCode, DWORD flags) {
- vlog.debug("vkCode 0x%x flags 0x%lx", vkCode, flags);
- keybd_event(vkCode, MapVirtualKey(vkCode, 0), flags, 0);
- }
-
- inline void doScanCodeEvent(BYTE scancode, bool down) {
- INPUT evt;
-
- evt.type = INPUT_KEYBOARD;
- evt.ki.wVk = 0;
- evt.ki.dwFlags = KEYEVENTF_SCANCODE;
-
- if (!down)
- evt.ki.dwFlags |= KEYEVENTF_KEYUP;
-
- if (scancode & 0x80) {
- evt.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
- scancode &= ~0x80;
- }
-
- evt.ki.wScan = scancode;
- evt.ki.dwExtraInfo = 0;
- evt.ki.time = 0;
- vlog.debug("SendInput ScanCode: 0x%x Flags: 0x%lx %s", scancode,
- evt.ki.dwFlags, down ? "Down" : "Up");
-
- // Windows has some bug where it doesn't look up scan code 0x45
- // properly, so we need to help it out
- if (evt.ki.wScan == 0x45) {
- evt.ki.dwFlags &= ~KEYEVENTF_SCANCODE;
- if (evt.ki.dwFlags & KEYEVENTF_EXTENDEDKEY)
- evt.ki.wVk = VK_NUMLOCK;
- else
- evt.ki.wVk = VK_PAUSE;
- }
-
- if (SendInput(1, &evt, sizeof(evt)) != 1)
- vlog.error("SendInput %lu", GetLastError());
- }
-
- // KeyStateModifier is a class which helps simplify generating a "fake" press
- // or release of shift, ctrl, alt, etc. An instance of the class is created
- // for every key which may need to be pressed or released. Then either press()
- // or release() may be called to make sure that the corresponding key is in the
- // right state. The destructor of the class automatically reverts to the
- // previous state.
-
- class KeyStateModifier {
- public:
- KeyStateModifier(int vkCode_, int flags_=0)
- : vkCode(vkCode_), flags(flags_), pressed(false), released(false)
- {}
- void press() {
- if (!(GetAsyncKeyState(vkCode) & 0x8000)) {
- doKeyboardEvent(vkCode, flags);
- pressed = true;
- }
- }
- void release() {
- if (GetAsyncKeyState(vkCode) & 0x8000) {
- doKeyboardEvent(vkCode, flags | KEYEVENTF_KEYUP);
- released = true;
- }
- }
- ~KeyStateModifier() {
- if (pressed) {
- doKeyboardEvent(vkCode, flags | KEYEVENTF_KEYUP);
- } else if (released) {
- doKeyboardEvent(vkCode, flags);
- }
- }
- int vkCode;
- int flags;
- bool pressed;
- bool released;
- };
-
-
- // doKeyEventWithModifiers() generates a key event having first "pressed" or
- // "released" the shift, ctrl or alt modifiers if necessary.
-
- void doKeyEventWithModifiers(BYTE vkCode, BYTE modifierState, bool down)
- {
- KeyStateModifier ctrl(VK_CONTROL);
- KeyStateModifier alt(VK_MENU);
- KeyStateModifier shift(VK_SHIFT);
-
- if (down) {
- if (modifierState & 2) ctrl.press();
- if (modifierState & 4) alt.press();
- if (modifierState & 1) {
- shift.press();
- } else {
- shift.release();
- }
- }
- doKeyboardEvent(vkCode, down ? 0 : KEYEVENTF_KEYUP);
- }
-
-
- win32::SKeyboard::SKeyboard()
- {
- for (unsigned int i = 0; i < sizeof(keymap) / sizeof(keymap_t); i++) {
- vkMap[keymap[i].keysym] = keymap[i].vk;
- extendedMap[keymap[i].keysym] = keymap[i].extended;
- }
-
- // Find dead characters for the current keyboard layout
- // XXX how could we handle the keyboard layout changing?
- BYTE keystate[256];
- memset(keystate, 0, 256);
- for (unsigned int j = 0; j < sizeof(latin1DeadChars); j++) {
- SHORT s = VkKeyScan(latin1DeadChars[j]);
- if (s != -1) {
- BYTE vkCode = LOBYTE(s);
- BYTE modifierState = HIBYTE(s);
- keystate[VK_SHIFT] = (modifierState & 1) ? 0x80 : 0;
- keystate[VK_CONTROL] = (modifierState & 2) ? 0x80 : 0;
- keystate[VK_MENU] = (modifierState & 4) ? 0x80 : 0;
- rdr::U8 chars[2];
- int nchars = ToAscii(vkCode, 0, keystate, (WORD*)&chars, 0);
- if (nchars < 0) {
- vlog.debug("Found dead key 0x%x '%c'",
- latin1DeadChars[j], latin1DeadChars[j]);
- deadChars.push_back(latin1DeadChars[j]);
- ToAscii(vkCode, 0, keystate, (WORD*)&chars, 0);
- }
- }
- }
- }
-
-
- void win32::SKeyboard::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
- {
- // If scan code is available use that directly as windows uses
- // compatible scancodes
- if (keycode && rawKeyboard) {
- // However NumLock incorrectly has the extended bit set
- if (keycode == 0x45)
- keycode = 0xc5;
-
- // And Pause uses NumLock's proper code, except when Control is
- // also pressed (i.e. when it is generating Break)
- if ((keycode == 0xc6) && !(GetAsyncKeyState(VK_CONTROL) & 0x8000))
- keycode = 0x45;
-
- // And PrintScreen uses a different code than Alt+PrintScreen (SysRq)
- if ((keycode == 0x54) && !(GetAsyncKeyState(VK_MENU) & 0x8000))
- keycode = 0xb7;
-
- if (down && (keycode == 0xd3) &&
- ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0) &&
- ((GetAsyncKeyState(VK_MENU) & 0x8000) != 0))
- {
- rfb::win32::emulateCtrlAltDel();
- return;
- }
-
- doScanCodeEvent(keycode, down);
- return;
- }
-
- for (unsigned int i = 0; i < sizeof(keysymToAscii) / sizeof(keysymToAscii_t); i++) {
- if (keysymToAscii[i].keysym == keysym) {
- keysym = keysymToAscii[i].ascii;
- break;
- }
- }
-
- if ((keysym >= 32 && keysym <= 126) ||
- (keysym >= 160 && keysym <= 255))
- {
- // ordinary Latin-1 character
-
- if (deadKeyAware) {
- // Detect dead chars and generate the dead char followed by space so
- // that we'll end up with the original char.
- for (unsigned int i = 0; i < deadChars.size(); i++) {
- if (keysym == deadChars[i]) {
- SHORT dc = VkKeyScan(keysym);
- if (dc != -1) {
- if (down) {
- vlog.info("latin-1 dead key: 0x%x vkCode 0x%x mod 0x%x "
- "followed by space", keysym, LOBYTE(dc), HIBYTE(dc));
- doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), true);
- doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), false);
- doKeyEventWithModifiers(VK_SPACE, 0, true);
- doKeyEventWithModifiers(VK_SPACE, 0, false);
- }
- return;
- }
- }
- }
- }
-
- SHORT s = VkKeyScan(keysym);
- if (s == -1) {
- if (down) {
- // not a single keypress - try synthesizing dead chars.
- for (unsigned int j = 0;
- j < sizeof(latin1ToDeadChars) / sizeof(latin1ToDeadChars_t);
- j++) {
- if (keysym == latin1ToDeadChars[j].latin1Char) {
- for (unsigned int i = 0; i < deadChars.size(); i++) {
- if (deadChars[i] == latin1ToDeadChars[j].deadChar) {
- SHORT dc = VkKeyScan(latin1ToDeadChars[j].deadChar);
- SHORT bc = VkKeyScan(latin1ToDeadChars[j].baseChar);
- if (dc != -1 && bc != -1) {
- vlog.info("latin-1 key: 0x%x dead key vkCode 0x%x mod 0x%x "
- "followed by vkCode 0x%x mod 0x%x",
- keysym, LOBYTE(dc), HIBYTE(dc),
- LOBYTE(bc), HIBYTE(bc));
- doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), true);
- doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), false);
- doKeyEventWithModifiers(LOBYTE(bc), HIBYTE(bc), true);
- doKeyEventWithModifiers(LOBYTE(bc), HIBYTE(bc), false);
- return;
- }
- break;
- }
- }
- break;
- }
- }
- vlog.info("ignoring unrecognised Latin-1 keysym 0x%x",keysym);
- }
- return;
- }
-
- BYTE vkCode = LOBYTE(s);
- BYTE modifierState = HIBYTE(s);
- vlog.debug("latin-1 key: 0x%x vkCode 0x%x mod 0x%x down %d",
- keysym, vkCode, modifierState, down);
- doKeyEventWithModifiers(vkCode, modifierState, down);
-
- } else {
-
- // see if it's a recognised keyboard key, otherwise ignore it
-
- if (vkMap.find(keysym) == vkMap.end()) {
- vlog.info("ignoring unknown keysym 0x%x",keysym);
- return;
- }
- BYTE vkCode = vkMap[keysym];
- DWORD flags = 0;
- if (extendedMap[keysym]) flags |= KEYEVENTF_EXTENDEDKEY;
- if (!down) flags |= KEYEVENTF_KEYUP;
-
- vlog.debug("keyboard key: keysym 0x%x vkCode 0x%x ext %d down %d",
- keysym, vkCode, extendedMap[keysym], down);
- if (down && (vkCode == VK_DELETE) &&
- ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0) &&
- ((GetAsyncKeyState(VK_MENU) & 0x8000) != 0))
- {
- rfb::win32::emulateCtrlAltDel();
- return;
- }
-
- doKeyboardEvent(vkCode, flags);
- }
- }
|