VirtualBox

source: vbox/trunk/src/VBox/RDP/client-1.8.3/xkeymap.c@ 55121

Last change on this file since 55121 was 55121, checked in by vboxsync, 10 years ago

rdesktop 1.8.3 unmodified

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

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