VirtualBox

source: vbox/trunk/src/VBox/RDP/client/xkeymap.c@ 38836

Last change on this file since 38836 was 38836, checked in by vboxsync, 13 years ago

rdesktop: Quick virtual desktop usability hack, disabled by default.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.9 KB
Line 
1/* -*- c-basic-offset: 8 -*-
2 rdesktop: A Remote Desktop Protocol client.
3 User interface services - X keyboard mapping
4
5 Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
6 Copyright (C) 2003-2008 Peter Astrand <astrand@cendio.se> for Cendio AB
7
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20*/
21
22/*
23 * Oracle GPL Disclaimer: For the avoidance of doubt, except that if any license choice
24 * other than GPL or LGPL is available it will apply instead, Oracle elects to use only
25 * the General Public License version 2 (GPLv2) at this time for any software where
26 * a choice of GPL license versions is made available with the language indicating
27 * that GPLv2 or any later version may be used, or where a choice of which version
28 * of the GPL is applied is otherwise unspecified.
29 */
30
31#ifdef RDP2VNC
32#include "vnc/x11stubs.h"
33#else
34#include <X11/Xlib.h>
35#include <X11/keysym.h>
36#endif
37
38#include <ctype.h>
39#include <limits.h>
40#include <time.h>
41#include <string.h>
42#include <assert.h>
43#include "rdesktop.h"
44#include "scancodes.h"
45
46#define KEYMAP_SIZE 0xffff+1
47#define KEYMAP_MASK 0xffff
48#define KEYMAP_MAX_LINE_LENGTH 80
49
50extern Display *g_display;
51extern Window g_wnd;
52extern char g_keymapname[16];
53extern unsigned int g_keylayout;
54extern int g_keyboard_type;
55extern int g_keyboard_subtype;
56extern int g_keyboard_functionkeys;
57extern int g_win_button_size;
58extern RD_BOOL g_enable_compose;
59extern RD_BOOL g_use_rdp5;
60extern RD_BOOL g_numlock_sync;
61
62static RD_BOOL keymap_loaded;
63static key_translation *keymap[KEYMAP_SIZE];
64static KeySym keypress_keysyms[256];
65static int min_keycode;
66static uint16 remote_modifier_state = 0;
67static uint16 saved_remote_modifier_state = 0;
68
69static void update_modifier_state(uint8 scancode, RD_BOOL pressed);
70
71/* Free key_translation structure, including linked list */
72static void
73free_key_translation(key_translation * ptr)
74{
75 key_translation *next;
76
77 while (ptr)
78 {
79 next = ptr->next;
80 xfree(ptr);
81 ptr = next;
82 }
83}
84
85static void
86add_to_keymap(char *keyname, uint8 scancode, uint16 modifiers, char *mapname)
87{
88 KeySym keysym;
89 key_translation *tr;
90
91 keysym = XStringToKeysym(keyname);
92 if (keysym == NoSymbol)
93 {
94 DEBUG_KBD(("Bad keysym \"%s\" in keymap %s (ignoring)\n", keyname, mapname));
95 return;
96 }
97
98 DEBUG_KBD(("Adding translation, keysym=0x%x, scancode=0x%x, "
99 "modifiers=0x%x\n", (unsigned int) keysym, scancode, modifiers));
100
101 tr = (key_translation *) xmalloc(sizeof(key_translation));
102 memset(tr, 0, sizeof(key_translation));
103 tr->scancode = scancode;
104 tr->modifiers = modifiers;
105 free_key_translation(keymap[keysym & KEYMAP_MASK]);
106 keymap[keysym & KEYMAP_MASK] = tr;
107
108 return;
109}
110
111static void
112add_sequence(char *rest, char *mapname)
113{
114 KeySym keysym;
115 key_translation *tr, **prev_next;
116 size_t chars;
117 char keyname[KEYMAP_MAX_LINE_LENGTH];
118
119 /* Skip over whitespace after the sequence keyword */
120 chars = strspn(rest, " \t");
121 rest += chars;
122
123 /* Fetch the keysym name */
124 chars = strcspn(rest, " \t\0");
125 STRNCPY(keyname, rest, chars + 1);
126 rest += chars;
127
128 keysym = XStringToKeysym(keyname);
129 if (keysym == NoSymbol)
130 {
131 DEBUG_KBD(("Bad keysym \"%s\" in keymap %s (ignoring line)\n", keyname, mapname));
132 return;
133 }
134
135
136 DEBUG_KBD(("Adding sequence for keysym (0x%lx, %s) -> ", keysym, keyname));
137
138 free_key_translation(keymap[keysym & KEYMAP_MASK]);
139 prev_next = &keymap[keysym & KEYMAP_MASK];
140
141 while (*rest)
142 {
143 /* Skip whitespace */
144 chars = strspn(rest, " \t");
145 rest += chars;
146
147 /* Fetch the keysym name */
148 chars = strcspn(rest, " \t\0");
149 STRNCPY(keyname, rest, chars + 1);
150 rest += chars;
151
152 keysym = XStringToKeysym(keyname);
153 if (keysym == NoSymbol)
154 {
155 DEBUG_KBD(("Bad keysym \"%s\" in keymap %s (ignoring line)\n", keyname,
156 mapname));
157 return;
158 }
159
160 /* Allocate space for key_translation structure */
161 tr = (key_translation *) xmalloc(sizeof(key_translation));
162 memset(tr, 0, sizeof(key_translation));
163 *prev_next = tr;
164 prev_next = &tr->next;
165 tr->seq_keysym = keysym;
166
167 DEBUG_KBD(("0x%x, ", (unsigned int) keysym));
168 }
169 DEBUG_KBD(("\n"));
170}
171
172RD_BOOL
173xkeymap_from_locale(const char *locale)
174{
175 char *str, *ptr;
176 FILE *fp;
177
178 /* Create a working copy */
179 str = xstrdup(locale);
180
181 /* Truncate at dot and at */
182 ptr = strrchr(str, '.');
183 if (ptr)
184 *ptr = '\0';
185 ptr = strrchr(str, '@');
186 if (ptr)
187 *ptr = '\0';
188
189 /* Replace _ with - */
190 ptr = strrchr(str, '_');
191 if (ptr)
192 *ptr = '-';
193
194 /* Convert to lowercase */
195 ptr = str;
196 while (*ptr)
197 {
198 *ptr = tolower((int) *ptr);
199 ptr++;
200 }
201
202 /* Try to open this keymap (da-dk) */
203 fp = xkeymap_open(str);
204 if (fp == NULL)
205 {
206 /* Truncate at dash */
207 ptr = strrchr(str, '-');
208 if (ptr)
209 *ptr = '\0';
210
211 /* Try the short name (da) */
212 fp = xkeymap_open(str);
213 }
214
215 if (fp)
216 {
217 fclose(fp);
218 STRNCPY(g_keymapname, str, sizeof(g_keymapname));
219 xfree(str);
220 return True;
221 }
222
223 xfree(str);
224 return False;
225}
226
227
228/* Joins two path components. The result should be freed with
229 xfree(). */
230static char *
231pathjoin(const char *a, const char *b)
232{
233 char *result;
234 result = xmalloc(PATH_MAX * 2 + 1);
235
236 if (b[0] == '/')
237 {
238 strncpy(result, b, PATH_MAX);
239 }
240 else
241 {
242 strncpy(result, a, PATH_MAX);
243 strcat(result, "/");
244 strncat(result, b, PATH_MAX);
245 }
246 return result;
247}
248
249/* Try to open a keymap with fopen() */
250FILE *
251xkeymap_open(const char *filename)
252{
253 char *path1, *path2;
254 char *home;
255 FILE *fp;
256
257 /* Try ~/.rdesktop/keymaps */
258 home = getenv("HOME");
259 if (home)
260 {
261 path1 = pathjoin(home, ".rdesktop/keymaps");
262 path2 = pathjoin(path1, filename);
263 xfree(path1);
264 fp = fopen(path2, "r");
265 xfree(path2);
266 if (fp)
267 return fp;
268 }
269
270 /* Try KEYMAP_PATH */
271 path1 = pathjoin(KEYMAP_PATH, filename);
272 fp = fopen(path1, "r");
273 xfree(path1);
274 if (fp)
275 return fp;
276
277 /* Try current directory, in case we are running from the source
278 tree */
279 path1 = pathjoin("keymaps", filename);
280 fp = fopen(path1, "r");
281 xfree(path1);
282 if (fp)
283 return fp;
284
285 return NULL;
286}
287
288static RD_BOOL
289xkeymap_read(char *mapname)
290{
291 FILE *fp;
292 char line[KEYMAP_MAX_LINE_LENGTH];
293 unsigned int line_num = 0;
294 unsigned int line_length = 0;
295 char *keyname, *p;
296 char *line_rest;
297 uint8 scancode;
298 uint16 modifiers;
299
300 fp = xkeymap_open(mapname);
301 if (fp == NULL)
302 {
303 error("Failed to open keymap %s\n", mapname);
304 return False;
305 }
306
307 /* FIXME: More tolerant on white space */
308 while (fgets(line, sizeof(line), fp) != NULL)
309 {
310 line_num++;
311
312 /* Replace the \n with \0 */
313 p = strchr(line, '\n');
314 if (p != NULL)
315 *p = 0;
316
317 line_length = strlen(line);
318
319 /* Completely empty line */
320 if (strspn(line, " \t\n\r\f\v") == line_length)
321 {
322 continue;
323 }
324
325 /* Include */
326 if (str_startswith(line, "include "))
327 {
328 if (!xkeymap_read(line + sizeof("include ") - 1))
329 return False;
330 continue;
331 }
332
333 /* map */
334 if (str_startswith(line, "map "))
335 {
336 g_keylayout = strtoul(line + sizeof("map ") - 1, NULL, 16);
337 DEBUG_KBD(("Keylayout 0x%x\n", g_keylayout));
338 continue;
339 }
340
341 /* compose */
342 if (str_startswith(line, "enable_compose"))
343 {
344 DEBUG_KBD(("Enabling compose handling\n"));
345 g_enable_compose = True;
346 continue;
347 }
348
349 /* sequence */
350 if (str_startswith(line, "sequence"))
351 {
352 add_sequence(line + sizeof("sequence") - 1, mapname);
353 continue;
354 }
355
356 /* keyboard_type */
357 if (str_startswith(line, "keyboard_type "))
358 {
359 g_keyboard_type = strtol(line + sizeof("keyboard_type ") - 1, NULL, 16);
360 DEBUG_KBD(("keyboard_type 0x%x\n", g_keyboard_type));
361 continue;
362 }
363
364 /* keyboard_subtype */
365 if (str_startswith(line, "keyboard_subtype "))
366 {
367 g_keyboard_subtype =
368 strtol(line + sizeof("keyboard_subtype ") - 1, NULL, 16);
369 DEBUG_KBD(("keyboard_subtype 0x%x\n", g_keyboard_subtype));
370 continue;
371 }
372
373 /* keyboard_functionkeys */
374 if (str_startswith(line, "keyboard_functionkeys "))
375 {
376 g_keyboard_functionkeys =
377 strtol(line + sizeof("keyboard_functionkeys ") - 1, NULL, 16);
378 DEBUG_KBD(("keyboard_functionkeys 0x%x\n", g_keyboard_functionkeys));
379 continue;
380 }
381
382 /* Comment */
383 if (line[0] == '#')
384 {
385 continue;
386 }
387
388 /* Normal line */
389 keyname = line;
390 p = strchr(line, ' ');
391 if (p == NULL)
392 {
393 error("Bad line %d in keymap %s\n", line_num, mapname);
394 continue;
395 }
396 else
397 {
398 *p = 0;
399 }
400
401 /* scancode */
402 p++;
403 scancode = strtol(p, &line_rest, 16);
404
405 /* flags */
406 /* FIXME: Should allow case-insensitive flag names.
407 Fix by using lex+yacc... */
408 modifiers = 0;
409 if (strstr(line_rest, "altgr"))
410 {
411 MASK_ADD_BITS(modifiers, MapAltGrMask);
412 }
413
414 if (strstr(line_rest, "shift"))
415 {
416 MASK_ADD_BITS(modifiers, MapLeftShiftMask);
417 }
418
419 if (strstr(line_rest, "numlock"))
420 {
421 MASK_ADD_BITS(modifiers, MapNumLockMask);
422 }
423
424 if (strstr(line_rest, "localstate"))
425 {
426 MASK_ADD_BITS(modifiers, MapLocalStateMask);
427 }
428
429 if (strstr(line_rest, "inhibit"))
430 {
431 MASK_ADD_BITS(modifiers, MapInhibitMask);
432 }
433
434 add_to_keymap(keyname, scancode, modifiers, mapname);
435
436 if (strstr(line_rest, "addupper"))
437 {
438 /* Automatically add uppercase key, with same modifiers
439 plus shift */
440 for (p = keyname; *p; p++)
441 *p = toupper((int) *p);
442 MASK_ADD_BITS(modifiers, MapLeftShiftMask);
443 add_to_keymap(keyname, scancode, modifiers, mapname);
444 }
445 }
446
447 fclose(fp);
448 return True;
449}
450
451
452/* Before connecting and creating UI */
453void
454xkeymap_init(void)
455{
456 unsigned int max_keycode;
457
458 if (strcmp(g_keymapname, "none"))
459 {
460 if (xkeymap_read(g_keymapname))
461 keymap_loaded = True;
462 }
463
464 XDisplayKeycodes(g_display, &min_keycode, (int *) &max_keycode);
465}
466
467static void
468send_winkey(uint32 ev_time, RD_BOOL pressed, RD_BOOL leftkey)
469{
470 uint8 winkey;
471
472 if (leftkey)
473 winkey = SCANCODE_CHAR_LWIN;
474 else
475 winkey = SCANCODE_CHAR_RWIN;
476
477 if (pressed)
478 {
479 if (g_use_rdp5)
480 {
481 rdp_send_scancode(ev_time, RDP_KEYPRESS, winkey);
482 }
483 else
484 {
485 /* RDP4 doesn't support winkey. Fake with Ctrl-Esc */
486 rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_LCTRL);
487 rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_ESC);
488 }
489 }
490 else
491 {
492 /* key released */
493 if (g_use_rdp5)
494 {
495 rdp_send_scancode(ev_time, RDP_KEYRELEASE, winkey);
496 }
497 else
498 {
499 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_ESC);
500 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LCTRL);
501 }
502 }
503}
504
505static void
506reset_winkey(uint32 ev_time)
507{
508 if (g_use_rdp5)
509 {
510 /* For some reason, it seems to suffice to release
511 *either* the left or right winkey. */
512 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LWIN);
513 }
514}
515
516
517void
518set_keypress_keysym(unsigned int keycode, KeySym keysym)
519{
520 if (keycode < 8 || keycode > 255)
521 return;
522 keypress_keysyms[keycode] = keysym;
523}
524
525
526KeySym
527reset_keypress_keysym(unsigned int keycode, KeySym keysym)
528{
529 KeySym ks;
530 if (keycode < 8 || keycode > 255)
531 return keysym;
532 ks = keypress_keysyms[keycode];
533 if (ks != 0)
534 {
535 keypress_keysyms[keycode] = 0;
536 }
537 else
538 {
539 ks = keysym;
540 }
541
542 return ks;
543}
544
545
546/* Handle special key combinations */
547RD_BOOL
548handle_special_keys(uint32 keysym, unsigned int state, uint32 ev_time, RD_BOOL pressed)
549{
550 switch (keysym)
551 {
552 case XK_Return:
553 if ((get_key_state(state, XK_Alt_L) || get_key_state(state, XK_Alt_R))
554 && (get_key_state(state, XK_Control_L)
555 || get_key_state(state, XK_Control_R)))
556 {
557 /* Ctrl-Alt-Enter: toggle full screen */
558 if (pressed)
559 xwin_toggle_fullscreen();
560 return True;
561 }
562 break;
563
564 case XK_Break:
565#ifdef RDESKTOP_KBD_CODE
566 /* Send Break sequence E0 46 E0 C6 */
567 if (pressed)
568 {
569 rdp_send_scancode(ev_time, RDP_KEYPRESS,
570 (SCANCODE_EXTENDED | 0x46));
571 rdp_send_scancode(ev_time, RDP_KEYPRESS,
572 (SCANCODE_EXTENDED | 0xc6));
573 }
574 /* No release sequence */
575 return True;
576#else
577 /* Send Break sequence E0 46 E0 C6 */
578 if (pressed)
579 {
580 /* VirtualBox code begin */
581 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, KBD_FLAG_EXT, 0x46, 0);
582 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, KBD_FLAG_EXT | KBD_FLAG_UP, 0x46, 0);
583 /* VirtualBox code end */
584 }
585 /* No release sequence */
586 return True;
587#endif /* RDESKTOP_KBD_CODE */
588 break;
589
590 case XK_Pause:
591#ifdef RDESKTOP_KBD_CODE
592 /* According to MS Keyboard Scan Code
593 Specification, pressing Pause should result
594 in E1 1D 45 E1 9D C5. I'm not exactly sure
595 of how this is supposed to be sent via
596 RDP. The code below seems to work, but with
597 the side effect that Left Ctrl stays
598 down. Therefore, we release it when Pause
599 is released. */
600 if (pressed)
601 {
602 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0xe1, 0);
603 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0x1d, 0);
604 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0x45, 0);
605 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0xe1, 0);
606 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0x9d, 0);
607 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0xc5, 0);
608 }
609 else
610 {
611 /* Release Left Ctrl */
612 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYRELEASE,
613 0x1d, 0);
614 }
615 return True;
616#else
617 /* Send Break sequence E1 1D 45 E1 9D C5 */
618 if (pressed)
619 {
620 /* VirtualBox code begin */
621 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, KBD_FLAG_EXT2, 0x1d, 0);
622 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0x45, 0);
623 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, KBD_FLAG_EXT2 | KBD_FLAG_UP, 0x1d, 0);
624 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, KBD_FLAG_UP, 0x45, 0);
625 /* VirtualBox code end */
626 }
627 /* No release sequence */
628 return True;
629#endif /* RDESKTOP_KBD_CODE */
630 break;
631
632 case XK_Meta_L: /* Windows keys */
633 case XK_Super_L:
634 case XK_Hyper_L:
635 send_winkey(ev_time, pressed, True);
636 return True;
637 break;
638
639 case XK_Meta_R:
640 case XK_Super_R:
641 case XK_Hyper_R:
642 send_winkey(ev_time, pressed, False);
643 return True;
644 break;
645
646 case XK_space:
647 /* Prevent access to the Windows system menu in single app mode */
648 if (g_win_button_size
649 && (get_key_state(state, XK_Alt_L) || get_key_state(state, XK_Alt_R)))
650 return True;
651 break;
652
653 case XK_Num_Lock:
654 /* Synchronize on key release */
655 if (g_numlock_sync && !pressed)
656 rdp_send_input(0, RDP_INPUT_SYNCHRONIZE, 0,
657 ui_get_numlock_state(read_keyboard_state()), 0);
658
659 /* Inhibit */
660 return True;
661 break;
662 case XK_Overlay1_Enable:
663 /* Toggle SeamlessRDP */
664 if (pressed)
665 ui_seamless_toggle();
666 break;
667#ifdef WITH_BIRD_VD_HACKS
668 case XK_Left:
669 case XK_Right:
670 {
671 /* Check for typical virtual desktop switching hotkeys:
672 Ctrl-Alt-Left and Ctrl-Alt-Right.
673 Needs to be pressed twice to have any effect... */
674 extern RD_BOOL g_keep_virtual_desktop_shortcuts;
675 extern RD_BOOL g_fullscreen;
676 if ( g_keep_virtual_desktop_shortcuts
677 && ( ( get_key_state(state, XK_Alt_L)
678 || get_key_state(state, XK_Alt_R))
679 && ( get_key_state(state, XK_Control_L)
680 || get_key_state(state, XK_Control_R))
681 )
682 )
683 {
684 if (g_fullscreen)
685 xwin_toggle_fullscreen();
686 XUngrabKeyboard(g_display, CurrentTime);
687 return True;
688 }
689 break;
690 }
691#endif
692 }
693 return False;
694}
695
696
697key_translation
698xkeymap_translate_key(uint32 keysym, unsigned int keycode, unsigned int state)
699{
700 key_translation tr = { 0, 0, 0, 0 };
701 key_translation *ptr;
702
703 ptr = keymap[keysym & KEYMAP_MASK];
704 if (ptr)
705 {
706 tr = *ptr;
707 if (tr.seq_keysym == 0) /* Normal scancode translation */
708 {
709 if (MASK_HAS_BITS(tr.modifiers, MapInhibitMask))
710 {
711 DEBUG_KBD(("Inhibiting key\n"));
712 tr.scancode = 0;
713 return tr;
714 }
715
716 if (MASK_HAS_BITS(tr.modifiers, MapLocalStateMask))
717 {
718 /* The modifiers to send for this key should be obtained
719 from the local state. Currently, only shift is implemented. */
720 if (MASK_HAS_BITS(state, ShiftMask))
721 {
722 tr.modifiers = MapLeftShiftMask;
723 }
724 }
725
726 /* Windows interprets CapsLock+Ctrl+key
727 differently from Shift+Ctrl+key. Since we
728 are simulating CapsLock with Shifts, things
729 like Ctrl+f with CapsLock on breaks. To
730 solve this, we are releasing Shift if Ctrl
731 is on, but only if Shift isn't physically pressed. */
732 if (MASK_HAS_BITS(tr.modifiers, MapShiftMask)
733 && MASK_HAS_BITS(remote_modifier_state, MapCtrlMask)
734 && !MASK_HAS_BITS(state, ShiftMask))
735 {
736 DEBUG_KBD(("Non-physical Shift + Ctrl pressed, releasing Shift\n"));
737 MASK_REMOVE_BITS(tr.modifiers, MapShiftMask);
738 }
739
740 DEBUG_KBD(("Found scancode translation, scancode=0x%x, modifiers=0x%x\n",
741 tr.scancode, tr.modifiers));
742 }
743 }
744 else
745 {
746 if (keymap_loaded)
747 warning("No translation for (keysym 0x%lx, %s)\n", keysym,
748 get_ksname(keysym));
749
750 /* not in keymap, try to interpret the raw scancode */
751 if (((int) keycode >= min_keycode) && (keycode <= 0x60))
752 {
753 tr.scancode = keycode - min_keycode;
754
755 /* The modifiers to send for this key should be
756 obtained from the local state. Currently, only
757 shift is implemented. */
758 if (MASK_HAS_BITS(state, ShiftMask))
759 {
760 tr.modifiers = MapLeftShiftMask;
761 }
762
763 DEBUG_KBD(("Sending guessed scancode 0x%x\n", tr.scancode));
764 }
765 else
766 {
767 DEBUG_KBD(("No good guess for keycode 0x%x found\n", keycode));
768 }
769 }
770
771 return tr;
772}
773
774static RD_BOOL
775is_modifier(uint8 scancode)
776{
777 switch (scancode)
778 {
779 case SCANCODE_CHAR_LSHIFT:
780 case SCANCODE_CHAR_RSHIFT:
781 case SCANCODE_CHAR_LCTRL:
782 case SCANCODE_CHAR_RCTRL:
783 case SCANCODE_CHAR_LALT:
784 case SCANCODE_CHAR_RALT:
785 case SCANCODE_CHAR_LWIN:
786 case SCANCODE_CHAR_RWIN:
787 case SCANCODE_CHAR_NUMLOCK:
788 return True;
789 default:
790 break;
791 }
792 return False;
793}
794
795
796void
797xkeymap_send_keys(uint32 keysym, unsigned int keycode, unsigned int state, uint32 ev_time,
798 RD_BOOL pressed, uint8 nesting)
799{
800 key_translation tr, *ptr;
801 tr = xkeymap_translate_key(keysym, keycode, state);
802
803 if (tr.seq_keysym == 0)
804 {
805 /* Scancode translation */
806 if (tr.scancode == 0)
807 return;
808
809 save_remote_modifiers(tr.scancode);
810 ensure_remote_modifiers(ev_time, tr);
811 rdp_send_scancode(ev_time, pressed ? RDP_KEYPRESS : RDP_KEYRELEASE, tr.scancode);
812 restore_remote_modifiers(ev_time, tr.scancode);
813 return;
814 }
815
816 /* Sequence, only on key down */
817 if (pressed)
818 {
819 ptr = &tr;
820 do
821 {
822 DEBUG_KBD(("Handling sequence element, keysym=0x%x\n",
823 (unsigned int) ptr->seq_keysym));
824
825 if (nesting++ > 32)
826 {
827 error("Sequence nesting too deep\n");
828 return;
829 }
830
831 xkeymap_send_keys(ptr->seq_keysym, keycode, state, ev_time, True, nesting);
832 xkeymap_send_keys(ptr->seq_keysym, keycode, state, ev_time, False, nesting);
833 ptr = ptr->next;
834 }
835 while (ptr);
836 }
837}
838
839uint16
840xkeymap_translate_button(unsigned int button)
841{
842 switch (button)
843 {
844 case Button1: /* left */
845 return MOUSE_FLAG_BUTTON1;
846 case Button2: /* middle */
847 return MOUSE_FLAG_BUTTON3;
848 case Button3: /* right */
849 return MOUSE_FLAG_BUTTON2;
850 case Button4: /* wheel up */
851 return MOUSE_FLAG_BUTTON4;
852 case Button5: /* wheel down */
853 return MOUSE_FLAG_BUTTON5;
854 }
855
856 return 0;
857}
858
859char *
860get_ksname(uint32 keysym)
861{
862 char *ksname = NULL;
863
864 if (keysym == NoSymbol)
865 ksname = "NoSymbol";
866 else if (!(ksname = XKeysymToString(keysym)))
867 ksname = "(no name)";
868
869 return ksname;
870}
871
872void
873save_remote_modifiers(uint8 scancode)
874{
875 if (is_modifier(scancode))
876 return;
877
878 saved_remote_modifier_state = remote_modifier_state;
879}
880
881void
882restore_remote_modifiers(uint32 ev_time, uint8 scancode)
883{
884 key_translation dummy = { };
885
886 if (is_modifier(scancode))
887 return;
888
889 dummy.scancode = 0;
890 dummy.modifiers = saved_remote_modifier_state;
891 ensure_remote_modifiers(ev_time, dummy);
892}
893
894void
895ensure_remote_modifiers(uint32 ev_time, key_translation tr)
896{
897 /* If this key is a modifier, do nothing */
898 if (is_modifier(tr.scancode))
899 return;
900
901 if (!g_numlock_sync)
902 {
903 /* NumLock */
904 if (MASK_HAS_BITS(tr.modifiers, MapNumLockMask)
905 != MASK_HAS_BITS(remote_modifier_state, MapNumLockMask))
906 {
907 /* The remote modifier state is not correct */
908 uint16 new_remote_state;
909
910 if (MASK_HAS_BITS(tr.modifiers, MapNumLockMask))
911 {
912 DEBUG_KBD(("Remote NumLock state is incorrect, activating NumLock.\n"));
913 new_remote_state = KBD_FLAG_NUMLOCK;
914 remote_modifier_state = MapNumLockMask;
915 }
916 else
917 {
918 DEBUG_KBD(("Remote NumLock state is incorrect, deactivating NumLock.\n"));
919 new_remote_state = 0;
920 remote_modifier_state = 0;
921 }
922
923 rdp_send_input(0, RDP_INPUT_SYNCHRONIZE, 0, new_remote_state, 0);
924 }
925 }
926
927
928 /* Shift. Left shift and right shift are treated as equal; either is fine. */
929 if (MASK_HAS_BITS(tr.modifiers, MapShiftMask)
930 != MASK_HAS_BITS(remote_modifier_state, MapShiftMask))
931 {
932 /* The remote modifier state is not correct */
933 if (MASK_HAS_BITS(tr.modifiers, MapLeftShiftMask))
934 {
935 /* Needs left shift. Send down. */
936 rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_LSHIFT);
937 }
938 else if (MASK_HAS_BITS(tr.modifiers, MapRightShiftMask))
939 {
940 /* Needs right shift. Send down. */
941 rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_RSHIFT);
942 }
943 else
944 {
945 /* Should not use this modifier. Send up for shift currently pressed. */
946 if (MASK_HAS_BITS(remote_modifier_state, MapLeftShiftMask))
947 /* Left shift is down */
948 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LSHIFT);
949 else
950 /* Right shift is down */
951 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_RSHIFT);
952 }
953 }
954
955 /* AltGr */
956 if (MASK_HAS_BITS(tr.modifiers, MapAltGrMask)
957 != MASK_HAS_BITS(remote_modifier_state, MapAltGrMask))
958 {
959 /* The remote modifier state is not correct */
960 if (MASK_HAS_BITS(tr.modifiers, MapAltGrMask))
961 {
962 /* Needs this modifier. Send down. */
963 rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_RALT);
964 }
965 else
966 {
967 /* Should not use this modifier. Send up. */
968 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_RALT);
969 }
970 }
971
972
973}
974
975
976unsigned int
977read_keyboard_state()
978{
979#ifdef RDP2VNC
980 return 0;
981#else
982 unsigned int state;
983 Window wdummy;
984 int dummy;
985
986 XQueryPointer(g_display, g_wnd, &wdummy, &wdummy, &dummy, &dummy, &dummy, &dummy, &state);
987 return state;
988#endif
989}
990
991
992uint16
993ui_get_numlock_state(unsigned int state)
994{
995 uint16 numlock_state = 0;
996
997 if (get_key_state(state, XK_Num_Lock))
998 numlock_state = KBD_FLAG_NUMLOCK;
999
1000 return numlock_state;
1001}
1002
1003
1004void
1005reset_modifier_keys()
1006{
1007 unsigned int state = read_keyboard_state();
1008
1009 /* reset keys */
1010 uint32 ev_time;
1011 ev_time = time(NULL);
1012
1013 if (MASK_HAS_BITS(remote_modifier_state, MapLeftShiftMask)
1014 && !get_key_state(state, XK_Shift_L))
1015 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LSHIFT);
1016
1017 if (MASK_HAS_BITS(remote_modifier_state, MapRightShiftMask)
1018 && !get_key_state(state, XK_Shift_R))
1019 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_RSHIFT);
1020
1021 if (MASK_HAS_BITS(remote_modifier_state, MapLeftCtrlMask)
1022 && !get_key_state(state, XK_Control_L))
1023 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LCTRL);
1024
1025 if (MASK_HAS_BITS(remote_modifier_state, MapRightCtrlMask)
1026 && !get_key_state(state, XK_Control_R))
1027 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_RCTRL);
1028
1029 if (MASK_HAS_BITS(remote_modifier_state, MapLeftAltMask) && !get_key_state(state, XK_Alt_L))
1030 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LALT);
1031
1032 if (MASK_HAS_BITS(remote_modifier_state, MapRightAltMask) &&
1033 !get_key_state(state, XK_Alt_R) && !get_key_state(state, XK_Mode_switch)
1034 && !get_key_state(state, XK_ISO_Level3_Shift))
1035 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_RALT);
1036
1037 reset_winkey(ev_time);
1038
1039 if (g_numlock_sync)
1040 rdp_send_input(ev_time, RDP_INPUT_SYNCHRONIZE, 0, ui_get_numlock_state(state), 0);
1041}
1042
1043
1044static void
1045update_modifier_state(uint8 scancode, RD_BOOL pressed)
1046{
1047#ifdef WITH_DEBUG_KBD
1048 uint16 old_modifier_state;
1049
1050 old_modifier_state = remote_modifier_state;
1051#endif
1052
1053 switch (scancode)
1054 {
1055 case SCANCODE_CHAR_LSHIFT:
1056 MASK_CHANGE_BIT(remote_modifier_state, MapLeftShiftMask, pressed);
1057 break;
1058 case SCANCODE_CHAR_RSHIFT:
1059 MASK_CHANGE_BIT(remote_modifier_state, MapRightShiftMask, pressed);
1060 break;
1061 case SCANCODE_CHAR_LCTRL:
1062 MASK_CHANGE_BIT(remote_modifier_state, MapLeftCtrlMask, pressed);
1063 break;
1064 case SCANCODE_CHAR_RCTRL:
1065 MASK_CHANGE_BIT(remote_modifier_state, MapRightCtrlMask, pressed);
1066 break;
1067 case SCANCODE_CHAR_LALT:
1068 MASK_CHANGE_BIT(remote_modifier_state, MapLeftAltMask, pressed);
1069 break;
1070 case SCANCODE_CHAR_RALT:
1071 MASK_CHANGE_BIT(remote_modifier_state, MapRightAltMask, pressed);
1072 break;
1073 case SCANCODE_CHAR_LWIN:
1074 MASK_CHANGE_BIT(remote_modifier_state, MapLeftWinMask, pressed);
1075 break;
1076 case SCANCODE_CHAR_RWIN:
1077 MASK_CHANGE_BIT(remote_modifier_state, MapRightWinMask, pressed);
1078 break;
1079 case SCANCODE_CHAR_NUMLOCK:
1080 /* KeyReleases for NumLocks are sent immediately. Toggle the
1081 modifier state only on Keypress */
1082 if (pressed && !g_numlock_sync)
1083 {
1084 RD_BOOL newNumLockState;
1085 newNumLockState =
1086 (MASK_HAS_BITS
1087 (remote_modifier_state, MapNumLockMask) == False);
1088 MASK_CHANGE_BIT(remote_modifier_state,
1089 MapNumLockMask, newNumLockState);
1090 }
1091 }
1092
1093#ifdef WITH_DEBUG_KBD
1094 if (old_modifier_state != remote_modifier_state)
1095 {
1096 DEBUG_KBD(("Before updating modifier_state:0x%x, pressed=0x%x\n",
1097 old_modifier_state, pressed));
1098 DEBUG_KBD(("After updating modifier_state:0x%x\n", remote_modifier_state));
1099 }
1100#endif
1101
1102}
1103
1104/* Send keyboard input */
1105void
1106rdp_send_scancode(uint32 time, uint16 flags, uint8 scancode)
1107{
1108 update_modifier_state(scancode, !(flags & RDP_KEYRELEASE));
1109
1110 if (scancode & SCANCODE_EXTENDED)
1111 {
1112 DEBUG_KBD(("Sending extended scancode=0x%x, flags=0x%x\n",
1113 scancode & ~SCANCODE_EXTENDED, flags));
1114 rdp_send_input(time, RDP_INPUT_SCANCODE, flags | KBD_FLAG_EXT,
1115 scancode & ~SCANCODE_EXTENDED, 0);
1116 }
1117 else
1118 {
1119 DEBUG_KBD(("Sending scancode=0x%x, flags=0x%x\n", scancode, flags));
1120 rdp_send_input(time, RDP_INPUT_SCANCODE, flags, scancode, 0);
1121 }
1122}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette