You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SInput.cxx 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. *
  3. * This is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This software is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this software; if not, write to the Free Software
  15. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  16. * USA.
  17. */
  18. // -=- SInput.cxx
  19. //
  20. // A number of routines that accept VNC input event data and perform
  21. // the appropriate actions under Win32
  22. #ifdef HAVE_CONFIG_H
  23. #include <config.h>
  24. #endif
  25. #define XK_MISCELLANY
  26. #define XK_LATIN1
  27. #define XK_CURRENCY
  28. #include <rfb/keysymdef.h>
  29. #include <tchar.h>
  30. #include <rfb_win32/SInput.h>
  31. #include <rfb_win32/MonitorInfo.h>
  32. #include <rfb_win32/Service.h>
  33. #include <rfb_win32/keymap.h>
  34. #include <rdr/Exception.h>
  35. #include <rfb/LogWriter.h>
  36. using namespace rfb;
  37. static LogWriter vlog("SInput");
  38. //
  39. // -=- Pointer implementation for Win32
  40. //
  41. static DWORD buttonDownMapping[8] = {
  42. MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_RIGHTDOWN,
  43. MOUSEEVENTF_WHEEL, MOUSEEVENTF_WHEEL, 0, 0, 0
  44. };
  45. static DWORD buttonUpMapping[8] = {
  46. MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_RIGHTUP,
  47. MOUSEEVENTF_WHEEL, MOUSEEVENTF_WHEEL, 0, 0, 0
  48. };
  49. static DWORD buttonDataMapping[8] = {
  50. 0, 0, 0, 120, (DWORD)-120, 0, 0, 0
  51. };
  52. win32::SPointer::SPointer()
  53. : last_buttonmask(0)
  54. {
  55. }
  56. void
  57. win32::SPointer::pointerEvent(const Point& pos, int buttonmask)
  58. {
  59. // - We are specifying absolute coordinates
  60. DWORD flags = MOUSEEVENTF_ABSOLUTE;
  61. // - Has the pointer moved since the last event?
  62. if (!last_position.equals(pos))
  63. flags |= MOUSEEVENTF_MOVE;
  64. // - If the system swaps left and right mouse buttons then we must
  65. // swap them here to negate the effect, so that we do the actual
  66. // action we mean to do
  67. if (::GetSystemMetrics(SM_SWAPBUTTON)) {
  68. bool leftDown = buttonmask & 1;
  69. bool rightDown = buttonmask & 4;
  70. buttonmask = (buttonmask & ~(1 | 4));
  71. if (leftDown) buttonmask |= 4;
  72. if (rightDown) buttonmask |= 1;
  73. }
  74. DWORD data = 0;
  75. for (int i = 0; i < 8; i++) {
  76. if ((buttonmask & (1<<i)) != (last_buttonmask & (1<<i))) {
  77. if (buttonmask & (1<<i)) {
  78. flags |= buttonDownMapping[i];
  79. if (buttonDataMapping[i]) {
  80. if (data) vlog.info("warning - two buttons set mouse_event data field");
  81. data = buttonDataMapping[i];
  82. }
  83. } else {
  84. flags |= buttonUpMapping[i];
  85. }
  86. }
  87. }
  88. last_position = pos;
  89. last_buttonmask = buttonmask;
  90. Rect primaryDisplay(0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN));
  91. if (primaryDisplay.contains(pos)) {
  92. // mouse_event wants coordinates specified as a proportion of the
  93. // primary display's size, scaled to the range 0 to 65535
  94. Point scaled;
  95. scaled.x = (pos.x * 65535) / (primaryDisplay.width()-1);
  96. scaled.y = (pos.y * 65535) / (primaryDisplay.height()-1);
  97. ::mouse_event(flags, scaled.x, scaled.y, data, 0);
  98. } else {
  99. // The event lies outside the primary monitor. Under Win2K, we can just use
  100. // SendInput, which allows us to provide coordinates scaled to the virtual desktop.
  101. // SendInput is available on all multi-monitor-aware platforms.
  102. INPUT evt;
  103. evt.type = INPUT_MOUSE;
  104. Point vPos(pos.x-GetSystemMetrics(SM_XVIRTUALSCREEN),
  105. pos.y-GetSystemMetrics(SM_YVIRTUALSCREEN));
  106. evt.mi.dx = (vPos.x * 65535) / (GetSystemMetrics(SM_CXVIRTUALSCREEN)-1);
  107. evt.mi.dy = (vPos.y * 65535) / (GetSystemMetrics(SM_CYVIRTUALSCREEN)-1);
  108. evt.mi.dwFlags = flags | MOUSEEVENTF_VIRTUALDESK;
  109. evt.mi.dwExtraInfo = 0;
  110. evt.mi.mouseData = data;
  111. evt.mi.time = 0;
  112. if (SendInput(1, &evt, sizeof(evt)) != 1)
  113. throw rdr::SystemException("SendInput", GetLastError());
  114. }
  115. }
  116. //
  117. // -=- Keyboard implementation
  118. //
  119. BoolParameter rfb::win32::SKeyboard::deadKeyAware("DeadKeyAware",
  120. "Whether to assume the viewer has already interpreted dead key sequences "
  121. "into latin-1 characters", true);
  122. BoolParameter rfb::win32::SKeyboard::rawKeyboard("RawKeyboard",
  123. "Send keyboard events straight through and avoid mapping them to the "
  124. "current keyboard layout", false);
  125. // The keysymToAscii table transforms a couple of awkward keysyms into their
  126. // ASCII equivalents.
  127. struct keysymToAscii_t {
  128. rdr::U32 keysym;
  129. rdr::U8 ascii;
  130. };
  131. keysymToAscii_t keysymToAscii[] = {
  132. { XK_KP_Space, ' ' },
  133. { XK_KP_Equal, '=' },
  134. };
  135. rdr::U8 latin1DeadChars[] = {
  136. XK_grave, XK_acute, XK_asciicircum, XK_diaeresis, XK_degree, XK_cedilla,
  137. XK_asciitilde
  138. };
  139. struct latin1ToDeadChars_t {
  140. rdr::U8 latin1Char;
  141. rdr::U8 deadChar;
  142. rdr::U8 baseChar;
  143. };
  144. latin1ToDeadChars_t latin1ToDeadChars[] = {
  145. { XK_Agrave, XK_grave, XK_A },
  146. { XK_Egrave, XK_grave, XK_E },
  147. { XK_Igrave, XK_grave, XK_I },
  148. { XK_Ograve, XK_grave, XK_O },
  149. { XK_Ugrave, XK_grave, XK_U },
  150. { XK_agrave, XK_grave, XK_a },
  151. { XK_egrave, XK_grave, XK_e },
  152. { XK_igrave, XK_grave, XK_i },
  153. { XK_ograve, XK_grave, XK_o},
  154. { XK_ugrave, XK_grave, XK_u },
  155. { XK_Aacute, XK_acute, XK_A },
  156. { XK_Eacute, XK_acute, XK_E },
  157. { XK_Iacute, XK_acute, XK_I },
  158. { XK_Oacute, XK_acute, XK_O },
  159. { XK_Uacute, XK_acute, XK_U },
  160. { XK_Yacute, XK_acute, XK_Y },
  161. { XK_aacute, XK_acute, XK_a },
  162. { XK_eacute, XK_acute, XK_e },
  163. { XK_iacute, XK_acute, XK_i },
  164. { XK_oacute, XK_acute, XK_o},
  165. { XK_uacute, XK_acute, XK_u },
  166. { XK_yacute, XK_acute, XK_y },
  167. { XK_Acircumflex, XK_asciicircum, XK_A },
  168. { XK_Ecircumflex, XK_asciicircum, XK_E },
  169. { XK_Icircumflex, XK_asciicircum, XK_I },
  170. { XK_Ocircumflex, XK_asciicircum, XK_O },
  171. { XK_Ucircumflex, XK_asciicircum, XK_U },
  172. { XK_acircumflex, XK_asciicircum, XK_a },
  173. { XK_ecircumflex, XK_asciicircum, XK_e },
  174. { XK_icircumflex, XK_asciicircum, XK_i },
  175. { XK_ocircumflex, XK_asciicircum, XK_o},
  176. { XK_ucircumflex, XK_asciicircum, XK_u },
  177. { XK_Adiaeresis, XK_diaeresis, XK_A },
  178. { XK_Ediaeresis, XK_diaeresis, XK_E },
  179. { XK_Idiaeresis, XK_diaeresis, XK_I },
  180. { XK_Odiaeresis, XK_diaeresis, XK_O },
  181. { XK_Udiaeresis, XK_diaeresis, XK_U },
  182. { XK_adiaeresis, XK_diaeresis, XK_a },
  183. { XK_ediaeresis, XK_diaeresis, XK_e },
  184. { XK_idiaeresis, XK_diaeresis, XK_i },
  185. { XK_odiaeresis, XK_diaeresis, XK_o},
  186. { XK_udiaeresis, XK_diaeresis, XK_u },
  187. { XK_ydiaeresis, XK_diaeresis, XK_y },
  188. { XK_Aring, XK_degree, XK_A },
  189. { XK_aring, XK_degree, XK_a },
  190. { XK_Ccedilla, XK_cedilla, XK_C },
  191. { XK_ccedilla, XK_cedilla, XK_c },
  192. { XK_Atilde, XK_asciitilde, XK_A },
  193. { XK_Ntilde, XK_asciitilde, XK_N },
  194. { XK_Otilde, XK_asciitilde, XK_O },
  195. { XK_atilde, XK_asciitilde, XK_a },
  196. { XK_ntilde, XK_asciitilde, XK_n },
  197. { XK_otilde, XK_asciitilde, XK_o },
  198. };
  199. // doKeyboardEvent wraps the system keybd_event function and attempts to find
  200. // the appropriate scancode corresponding to the supplied virtual keycode.
  201. inline void doKeyboardEvent(BYTE vkCode, DWORD flags) {
  202. vlog.debug("vkCode 0x%x flags 0x%lx", vkCode, flags);
  203. keybd_event(vkCode, MapVirtualKey(vkCode, 0), flags, 0);
  204. }
  205. inline void doScanCodeEvent(BYTE scancode, bool down) {
  206. INPUT evt;
  207. evt.type = INPUT_KEYBOARD;
  208. evt.ki.wVk = 0;
  209. evt.ki.dwFlags = KEYEVENTF_SCANCODE;
  210. if (!down)
  211. evt.ki.dwFlags |= KEYEVENTF_KEYUP;
  212. if (scancode & 0x80) {
  213. evt.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
  214. scancode &= ~0x80;
  215. }
  216. evt.ki.wScan = scancode;
  217. evt.ki.dwExtraInfo = 0;
  218. evt.ki.time = 0;
  219. vlog.debug("SendInput ScanCode: 0x%x Flags: 0x%lx %s", scancode,
  220. evt.ki.dwFlags, down ? "Down" : "Up");
  221. // Windows has some bug where it doesn't look up scan code 0x45
  222. // properly, so we need to help it out
  223. if (evt.ki.wScan == 0x45) {
  224. evt.ki.dwFlags &= ~KEYEVENTF_SCANCODE;
  225. if (evt.ki.dwFlags & KEYEVENTF_EXTENDEDKEY)
  226. evt.ki.wVk = VK_NUMLOCK;
  227. else
  228. evt.ki.wVk = VK_PAUSE;
  229. }
  230. if (SendInput(1, &evt, sizeof(evt)) != 1)
  231. vlog.error("SendInput %lu", GetLastError());
  232. }
  233. // KeyStateModifier is a class which helps simplify generating a "fake" press
  234. // or release of shift, ctrl, alt, etc. An instance of the class is created
  235. // for every key which may need to be pressed or released. Then either press()
  236. // or release() may be called to make sure that the corresponding key is in the
  237. // right state. The destructor of the class automatically reverts to the
  238. // previous state.
  239. class KeyStateModifier {
  240. public:
  241. KeyStateModifier(int vkCode_, int flags_=0)
  242. : vkCode(vkCode_), flags(flags_), pressed(false), released(false)
  243. {}
  244. void press() {
  245. if (!(GetAsyncKeyState(vkCode) & 0x8000)) {
  246. doKeyboardEvent(vkCode, flags);
  247. pressed = true;
  248. }
  249. }
  250. void release() {
  251. if (GetAsyncKeyState(vkCode) & 0x8000) {
  252. doKeyboardEvent(vkCode, flags | KEYEVENTF_KEYUP);
  253. released = true;
  254. }
  255. }
  256. ~KeyStateModifier() {
  257. if (pressed) {
  258. doKeyboardEvent(vkCode, flags | KEYEVENTF_KEYUP);
  259. } else if (released) {
  260. doKeyboardEvent(vkCode, flags);
  261. }
  262. }
  263. int vkCode;
  264. int flags;
  265. bool pressed;
  266. bool released;
  267. };
  268. // doKeyEventWithModifiers() generates a key event having first "pressed" or
  269. // "released" the shift, ctrl or alt modifiers if necessary.
  270. void doKeyEventWithModifiers(BYTE vkCode, BYTE modifierState, bool down)
  271. {
  272. KeyStateModifier ctrl(VK_CONTROL);
  273. KeyStateModifier alt(VK_MENU);
  274. KeyStateModifier shift(VK_SHIFT);
  275. if (down) {
  276. if (modifierState & 2) ctrl.press();
  277. if (modifierState & 4) alt.press();
  278. if (modifierState & 1) {
  279. shift.press();
  280. } else {
  281. shift.release();
  282. }
  283. }
  284. doKeyboardEvent(vkCode, down ? 0 : KEYEVENTF_KEYUP);
  285. }
  286. win32::SKeyboard::SKeyboard()
  287. {
  288. for (unsigned int i = 0; i < sizeof(keymap) / sizeof(keymap_t); i++) {
  289. vkMap[keymap[i].keysym] = keymap[i].vk;
  290. extendedMap[keymap[i].keysym] = keymap[i].extended;
  291. }
  292. // Find dead characters for the current keyboard layout
  293. // XXX how could we handle the keyboard layout changing?
  294. BYTE keystate[256];
  295. memset(keystate, 0, 256);
  296. for (unsigned int j = 0; j < sizeof(latin1DeadChars); j++) {
  297. SHORT s = VkKeyScan(latin1DeadChars[j]);
  298. if (s != -1) {
  299. BYTE vkCode = LOBYTE(s);
  300. BYTE modifierState = HIBYTE(s);
  301. keystate[VK_SHIFT] = (modifierState & 1) ? 0x80 : 0;
  302. keystate[VK_CONTROL] = (modifierState & 2) ? 0x80 : 0;
  303. keystate[VK_MENU] = (modifierState & 4) ? 0x80 : 0;
  304. rdr::U8 chars[2];
  305. int nchars = ToAscii(vkCode, 0, keystate, (WORD*)&chars, 0);
  306. if (nchars < 0) {
  307. vlog.debug("Found dead key 0x%x '%c'",
  308. latin1DeadChars[j], latin1DeadChars[j]);
  309. deadChars.push_back(latin1DeadChars[j]);
  310. ToAscii(vkCode, 0, keystate, (WORD*)&chars, 0);
  311. }
  312. }
  313. }
  314. }
  315. void win32::SKeyboard::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
  316. {
  317. // If scan code is available use that directly as windows uses
  318. // compatible scancodes
  319. if (keycode && rawKeyboard) {
  320. // However NumLock incorrectly has the extended bit set
  321. if (keycode == 0x45)
  322. keycode = 0xc5;
  323. // And Pause uses NumLock's proper code, except when Control is
  324. // also pressed (i.e. when it is generating Break)
  325. if ((keycode == 0xc6) && !(GetAsyncKeyState(VK_CONTROL) & 0x8000))
  326. keycode = 0x45;
  327. // And PrintScreen uses a different code than Alt+PrintScreen (SysRq)
  328. if ((keycode == 0x54) && !(GetAsyncKeyState(VK_MENU) & 0x8000))
  329. keycode = 0xb7;
  330. if (down && (keycode == 0xd3) &&
  331. ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0) &&
  332. ((GetAsyncKeyState(VK_MENU) & 0x8000) != 0))
  333. {
  334. rfb::win32::emulateCtrlAltDel();
  335. return;
  336. }
  337. doScanCodeEvent(keycode, down);
  338. return;
  339. }
  340. for (unsigned int i = 0; i < sizeof(keysymToAscii) / sizeof(keysymToAscii_t); i++) {
  341. if (keysymToAscii[i].keysym == keysym) {
  342. keysym = keysymToAscii[i].ascii;
  343. break;
  344. }
  345. }
  346. if ((keysym >= 32 && keysym <= 126) ||
  347. (keysym >= 160 && keysym <= 255))
  348. {
  349. // ordinary Latin-1 character
  350. if (deadKeyAware) {
  351. // Detect dead chars and generate the dead char followed by space so
  352. // that we'll end up with the original char.
  353. for (unsigned int i = 0; i < deadChars.size(); i++) {
  354. if (keysym == deadChars[i]) {
  355. SHORT dc = VkKeyScan(keysym);
  356. if (dc != -1) {
  357. if (down) {
  358. vlog.info("latin-1 dead key: 0x%x vkCode 0x%x mod 0x%x "
  359. "followed by space", keysym, LOBYTE(dc), HIBYTE(dc));
  360. doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), true);
  361. doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), false);
  362. doKeyEventWithModifiers(VK_SPACE, 0, true);
  363. doKeyEventWithModifiers(VK_SPACE, 0, false);
  364. }
  365. return;
  366. }
  367. }
  368. }
  369. }
  370. SHORT s = VkKeyScan(keysym);
  371. if (s == -1) {
  372. if (down) {
  373. // not a single keypress - try synthesizing dead chars.
  374. for (unsigned int j = 0;
  375. j < sizeof(latin1ToDeadChars) / sizeof(latin1ToDeadChars_t);
  376. j++) {
  377. if (keysym == latin1ToDeadChars[j].latin1Char) {
  378. for (unsigned int i = 0; i < deadChars.size(); i++) {
  379. if (deadChars[i] == latin1ToDeadChars[j].deadChar) {
  380. SHORT dc = VkKeyScan(latin1ToDeadChars[j].deadChar);
  381. SHORT bc = VkKeyScan(latin1ToDeadChars[j].baseChar);
  382. if (dc != -1 && bc != -1) {
  383. vlog.info("latin-1 key: 0x%x dead key vkCode 0x%x mod 0x%x "
  384. "followed by vkCode 0x%x mod 0x%x",
  385. keysym, LOBYTE(dc), HIBYTE(dc),
  386. LOBYTE(bc), HIBYTE(bc));
  387. doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), true);
  388. doKeyEventWithModifiers(LOBYTE(dc), HIBYTE(dc), false);
  389. doKeyEventWithModifiers(LOBYTE(bc), HIBYTE(bc), true);
  390. doKeyEventWithModifiers(LOBYTE(bc), HIBYTE(bc), false);
  391. return;
  392. }
  393. break;
  394. }
  395. }
  396. break;
  397. }
  398. }
  399. vlog.info("ignoring unrecognised Latin-1 keysym 0x%x",keysym);
  400. }
  401. return;
  402. }
  403. BYTE vkCode = LOBYTE(s);
  404. BYTE modifierState = HIBYTE(s);
  405. vlog.debug("latin-1 key: 0x%x vkCode 0x%x mod 0x%x down %d",
  406. keysym, vkCode, modifierState, down);
  407. doKeyEventWithModifiers(vkCode, modifierState, down);
  408. } else {
  409. // see if it's a recognised keyboard key, otherwise ignore it
  410. if (vkMap.find(keysym) == vkMap.end()) {
  411. vlog.info("ignoring unknown keysym 0x%x",keysym);
  412. return;
  413. }
  414. BYTE vkCode = vkMap[keysym];
  415. DWORD flags = 0;
  416. if (extendedMap[keysym]) flags |= KEYEVENTF_EXTENDEDKEY;
  417. if (!down) flags |= KEYEVENTF_KEYUP;
  418. vlog.debug("keyboard key: keysym 0x%x vkCode 0x%x ext %d down %d",
  419. keysym, vkCode, extendedMap[keysym], down);
  420. if (down && (vkCode == VK_DELETE) &&
  421. ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0) &&
  422. ((GetAsyncKeyState(VK_MENU) & 0x8000) != 0))
  423. {
  424. rfb::win32::emulateCtrlAltDel();
  425. return;
  426. }
  427. doKeyboardEvent(vkCode, flags);
  428. }
  429. }