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