/[rdesktop]/sourceforge.net/trunk/rdesktop/xkeymap.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /sourceforge.net/trunk/rdesktop/xkeymap.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 212 - (show annotations)
Sun Oct 6 13:25:30 2002 UTC (21 years, 7 months ago) by matthewc
File MIME type: text/plain
File size: 14691 byte(s)
On Solaris Break is Shift-Pause not Alt-Pause - so when checking for
Ctrl-Alt-Break, accept both Break and Pause and check state of Ctrl
and Alt.

1 /*
2 rdesktop: A Remote Desktop Protocol client.
3 User interface services - X keyboard mapping
4 Copyright (C) Matthew Chapman 1999-2002
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include <X11/Xlib.h>
22 #define XK_MISCELLANY
23 #include <X11/keysymdef.h>
24 #include <ctype.h>
25 #include <limits.h>
26 #include <time.h>
27 #include "rdesktop.h"
28 #include "scancodes.h"
29
30 #define KEYMAP_SIZE 4096
31 #define KEYMAP_MASK (KEYMAP_SIZE - 1)
32 #define KEYMAP_MAX_LINE_LENGTH 80
33
34 extern Display *display;
35 extern char keymapname[16];
36 extern int keylayout;
37 extern BOOL enable_compose;
38
39 static BOOL keymap_loaded;
40 static key_translation keymap[KEYMAP_SIZE];
41 static int min_keycode;
42 static uint16 remote_modifier_state = 0;
43
44 static void update_modifier_state(uint8 scancode, BOOL pressed);
45
46 static void
47 add_to_keymap(char *keyname, uint8 scancode, uint16 modifiers, char *mapname)
48 {
49 KeySym keysym;
50
51 keysym = XStringToKeysym(keyname);
52 if (keysym == NoSymbol)
53 {
54 error("Bad keysym %s in keymap %s\n", keyname, mapname);
55 return;
56 }
57
58 DEBUG_KBD(("Adding translation, keysym=0x%x, scancode=0x%x, "
59 "modifiers=0x%x\n", (unsigned int) keysym, scancode, modifiers));
60
61 keymap[keysym & KEYMAP_MASK].scancode = scancode;
62 keymap[keysym & KEYMAP_MASK].modifiers = modifiers;
63
64 return;
65 }
66
67
68 static BOOL
69 xkeymap_read(char *mapname)
70 {
71 FILE *fp;
72 char line[KEYMAP_MAX_LINE_LENGTH], path[PATH_MAX];
73 unsigned int line_num = 0;
74 unsigned int line_length = 0;
75 char *keyname, *p;
76 char *line_rest;
77 uint8 scancode;
78 uint16 modifiers;
79
80
81 strcpy(path, KEYMAP_PATH);
82 strncat(path, mapname, sizeof(path) - sizeof(KEYMAP_PATH));
83
84 fp = fopen(path, "r");
85 if (fp == NULL)
86 {
87 error("Failed to open keymap %s\n", path);
88 return False;
89 }
90
91 /* FIXME: More tolerant on white space */
92 while (fgets(line, sizeof(line), fp) != NULL)
93 {
94 line_num++;
95
96 /* Replace the \n with \0 */
97 p = strchr(line, '\n');
98 if (p != NULL)
99 *p = 0;
100
101 line_length = strlen(line);
102
103 /* Completely empty line */
104 if (strspn(line, " \t\n\r\f\v") == line_length)
105 {
106 continue;
107 }
108
109 /* Include */
110 if (strncmp(line, "include ", 8) == 0)
111 {
112 if (!xkeymap_read(line + 8))
113 return False;
114 continue;
115 }
116
117 /* map */
118 if (strncmp(line, "map ", 4) == 0)
119 {
120 keylayout = strtol(line + 4, NULL, 16);
121 DEBUG_KBD(("Keylayout 0x%x\n", keylayout));
122 continue;
123 }
124
125 /* compose */
126 if (strncmp(line, "enable_compose", 15) == 0)
127 {
128 DEBUG_KBD(("Enabling compose handling\n"));
129 enable_compose = True;
130 continue;
131 }
132
133 /* Comment */
134 if (line[0] == '#')
135 {
136 continue;
137 }
138
139 /* Normal line */
140 keyname = line;
141 p = strchr(line, ' ');
142 if (p == NULL)
143 {
144 error("Bad line %d in keymap %s\n", line_num, mapname);
145 continue;
146 }
147 else
148 {
149 *p = 0;
150 }
151
152 /* scancode */
153 p++;
154 scancode = strtol(p, &line_rest, 16);
155
156 /* flags */
157 /* FIXME: Should allow case-insensitive flag names.
158 Fix by using lex+yacc... */
159 modifiers = 0;
160 if (strstr(line_rest, "altgr"))
161 {
162 MASK_ADD_BITS(modifiers, MapAltGrMask);
163 }
164
165 if (strstr(line_rest, "shift"))
166 {
167 MASK_ADD_BITS(modifiers, MapLeftShiftMask);
168 }
169
170 if (strstr(line_rest, "numlock"))
171 {
172 MASK_ADD_BITS(modifiers, MapNumLockMask);
173 }
174
175 if (strstr(line_rest, "localstate"))
176 {
177 MASK_ADD_BITS(modifiers, MapLocalStateMask);
178 }
179
180 if (strstr(line_rest, "inhibit"))
181 {
182 MASK_ADD_BITS(modifiers, MapInhibitMask);
183 }
184
185 add_to_keymap(keyname, scancode, modifiers, mapname);
186
187 if (strstr(line_rest, "addupper"))
188 {
189 /* Automatically add uppercase key, with same modifiers
190 plus shift */
191 for (p = keyname; *p; p++)
192 *p = toupper(*p);
193 MASK_ADD_BITS(modifiers, MapLeftShiftMask);
194 add_to_keymap(keyname, scancode, modifiers, mapname);
195 }
196 }
197
198 fclose(fp);
199 return True;
200 }
201
202
203 /* Before connecting and creating UI */
204 void
205 xkeymap_init(void)
206 {
207 unsigned int max_keycode;
208
209 if (strcmp(keymapname, "none"))
210 {
211 if (xkeymap_read(keymapname))
212 keymap_loaded = True;
213 }
214
215 XDisplayKeycodes(display, &min_keycode, (int *) &max_keycode);
216 }
217
218 /* Handles, for example, multi-scancode keypresses (which is not
219 possible via keymap-files) */
220 BOOL
221 handle_special_keys(uint32 keysym, unsigned int state, uint32 ev_time, BOOL pressed)
222 {
223 switch (keysym)
224 {
225 case XK_Break:
226 case XK_Pause:
227 if ((get_key_state(state, XK_Alt_L) || get_key_state(state, XK_Alt_R))
228 && (get_key_state(state, XK_Control_L) || get_key_state(state, XK_Control_R)))
229 {
230 /* Ctrl-Alt-Break: toggle full screen */
231 if (pressed)
232 xwin_toggle_fullscreen();
233
234 }
235 else if (keysym == XK_Break)
236 {
237 /* Send Break sequence E0 46 E0 C6 */
238 if (pressed)
239 {
240 rdp_send_scancode(ev_time, RDP_KEYPRESS,
241 (SCANCODE_EXTENDED | 0x46));
242 rdp_send_scancode(ev_time, RDP_KEYPRESS,
243 (SCANCODE_EXTENDED | 0xc6));
244 }
245 /* No break sequence */
246 }
247 else /* XK_Pause */
248 {
249 /* According to MS Keyboard Scan Code
250 Specification, pressing Pause should result
251 in E1 1D 45 E1 9D C5. I'm not exactly sure
252 of how this is supposed to be sent via
253 RDP. The code below seems to work, but with
254 the side effect that Left Ctrl stays
255 down. Therefore, we release it when Pause
256 is released. */
257 if (pressed)
258 {
259 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0xe1, 0);
260 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0x1d, 0);
261 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0x45, 0);
262 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0xe1, 0);
263 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0x9d, 0);
264 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYPRESS, 0xc5, 0);
265 }
266 else
267 {
268 /* Release Left Ctrl */
269 rdp_send_input(ev_time, RDP_INPUT_SCANCODE, RDP_KEYRELEASE, 0x1d,
270 0);
271 }
272 }
273 return True;
274
275 case XK_Meta_L: /* Windows keys */
276 case XK_Super_L:
277 case XK_Hyper_L:
278 case XK_Meta_R:
279 case XK_Super_R:
280 case XK_Hyper_R:
281 if (pressed)
282 {
283 rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_LCTRL);
284 rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_ESC);
285 }
286 else
287 {
288 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_ESC);
289 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LCTRL);
290 }
291 return True;
292 }
293 return False;
294 }
295
296
297 key_translation
298 xkeymap_translate_key(uint32 keysym, unsigned int keycode, unsigned int state)
299 {
300 key_translation tr = { 0, 0 };
301
302 tr = keymap[keysym & KEYMAP_MASK];
303
304 if (tr.modifiers & MapInhibitMask)
305 {
306 DEBUG_KBD(("Inhibiting key\n"));
307 tr.scancode = 0;
308 return tr;
309 }
310
311 if (tr.modifiers & MapLocalStateMask)
312 {
313 /* The modifiers to send for this key should be obtained
314 from the local state. Currently, only shift is implemented. */
315 if (state & ShiftMask)
316 {
317 tr.modifiers = MapLeftShiftMask;
318 }
319 }
320
321 if (tr.scancode != 0)
322 {
323 DEBUG_KBD(("Found key translation, scancode=0x%x, modifiers=0x%x\n",
324 tr.scancode, tr.modifiers));
325 return tr;
326 }
327
328 if (keymap_loaded)
329 error("No translation for (keysym 0x%lx, %s)\n", keysym, get_ksname(keysym));
330
331 /* not in keymap, try to interpret the raw scancode */
332 if ((keycode >= min_keycode) && (keycode <= 0x60))
333 {
334 tr.scancode = keycode - min_keycode;
335
336 /* The modifiers to send for this key should be
337 obtained from the local state. Currently, only
338 shift is implemented. */
339 if (state & ShiftMask)
340 {
341 tr.modifiers = MapLeftShiftMask;
342 }
343
344 DEBUG_KBD(("Sending guessed scancode 0x%x\n", tr.scancode));
345 }
346 else
347 {
348 DEBUG_KBD(("No good guess for keycode 0x%x found\n", keycode));
349 }
350
351 return tr;
352 }
353
354 uint16
355 xkeymap_translate_button(unsigned int button)
356 {
357 switch (button)
358 {
359 case Button1: /* left */
360 return MOUSE_FLAG_BUTTON1;
361 case Button2: /* middle */
362 return MOUSE_FLAG_BUTTON3;
363 case Button3: /* right */
364 return MOUSE_FLAG_BUTTON2;
365 case Button4: /* wheel up */
366 return MOUSE_FLAG_BUTTON4;
367 case Button5: /* wheel down */
368 return MOUSE_FLAG_BUTTON5;
369 }
370
371 return 0;
372 }
373
374 char *
375 get_ksname(uint32 keysym)
376 {
377 char *ksname = NULL;
378
379 if (keysym == NoSymbol)
380 ksname = "NoSymbol";
381 else if (!(ksname = XKeysymToString(keysym)))
382 ksname = "(no name)";
383
384 return ksname;
385 }
386
387
388 void
389 ensure_remote_modifiers(uint32 ev_time, key_translation tr)
390 {
391 /* If this key is a modifier, do nothing */
392 switch (tr.scancode)
393 {
394 case SCANCODE_CHAR_LSHIFT:
395 case SCANCODE_CHAR_RSHIFT:
396 case SCANCODE_CHAR_LCTRL:
397 case SCANCODE_CHAR_RCTRL:
398 case SCANCODE_CHAR_LALT:
399 case SCANCODE_CHAR_RALT:
400 case SCANCODE_CHAR_LWIN:
401 case SCANCODE_CHAR_RWIN:
402 case SCANCODE_CHAR_NUMLOCK:
403 return;
404 default:
405 break;
406 }
407
408 /* Shift. Left shift and right shift are treated as equal; either is fine. */
409 if (MASK_HAS_BITS(tr.modifiers, MapShiftMask)
410 != MASK_HAS_BITS(remote_modifier_state, MapShiftMask))
411 {
412 /* The remote modifier state is not correct */
413 if (MASK_HAS_BITS(tr.modifiers, MapLeftShiftMask))
414 {
415 /* Needs left shift. Send down. */
416 rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_LSHIFT);
417 }
418 else if (MASK_HAS_BITS(tr.modifiers, MapRightShiftMask))
419 {
420 /* Needs right shift. Send down. */
421 rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_RSHIFT);
422 }
423 else
424 {
425 /* Should not use this modifier. Send up for shift currently pressed. */
426 if (MASK_HAS_BITS(remote_modifier_state, MapLeftShiftMask))
427 /* Left shift is down */
428 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LSHIFT);
429 else
430 /* Right shift is down */
431 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_RSHIFT);
432 }
433 }
434
435 /* AltGr */
436 if (MASK_HAS_BITS(tr.modifiers, MapAltGrMask)
437 != MASK_HAS_BITS(remote_modifier_state, MapAltGrMask))
438 {
439 /* The remote modifier state is not correct */
440 if (MASK_HAS_BITS(tr.modifiers, MapAltGrMask))
441 {
442 /* Needs this modifier. Send down. */
443 rdp_send_scancode(ev_time, RDP_KEYPRESS, SCANCODE_CHAR_RALT);
444 }
445 else
446 {
447 /* Should not use this modifier. Send up. */
448 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_RALT);
449 }
450 }
451
452 /* NumLock */
453 if (MASK_HAS_BITS(tr.modifiers, MapNumLockMask)
454 != MASK_HAS_BITS(remote_modifier_state, MapNumLockMask))
455 {
456 /* The remote modifier state is not correct */
457 uint16 new_remote_state = 0;
458
459 if (MASK_HAS_BITS(tr.modifiers, MapNumLockMask))
460 {
461 DEBUG_KBD(("Remote NumLock state is incorrect, activating NumLock.\n"));
462 new_remote_state |= KBD_FLAG_NUMLOCK;
463 }
464 else
465 {
466 DEBUG_KBD(("Remote NumLock state is incorrect, deactivating NumLock.\n"));
467 }
468
469 rdp_send_input(0, RDP_INPUT_SYNCHRONIZE, 0, new_remote_state, 0);
470 update_modifier_state(SCANCODE_CHAR_NUMLOCK, True);
471 }
472 }
473
474
475 void
476 reset_modifier_keys(unsigned int state)
477 {
478 /* reset keys */
479 uint32 ev_time;
480 ev_time = time(NULL);
481
482 if (MASK_HAS_BITS(remote_modifier_state, MapLeftShiftMask) && !get_key_state(state, XK_Shift_L))
483 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LSHIFT);
484
485 if (MASK_HAS_BITS(remote_modifier_state, MapRightShiftMask) && !get_key_state(state, XK_Shift_R))
486 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_RSHIFT);
487
488 if (MASK_HAS_BITS(remote_modifier_state, MapLeftCtrlMask) && !get_key_state(state, XK_Control_L))
489 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LCTRL);
490
491 if (MASK_HAS_BITS(remote_modifier_state, MapRightCtrlMask) && !get_key_state(state, XK_Control_R))
492 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_RCTRL);
493
494 if (MASK_HAS_BITS(remote_modifier_state, MapLeftAltMask) && !get_key_state(state, XK_Alt_L))
495 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_LALT);
496
497 if (MASK_HAS_BITS(remote_modifier_state, MapRightAltMask) &&
498 !get_key_state(state, XK_Alt_R) && !get_key_state(state, XK_Mode_switch))
499 rdp_send_scancode(ev_time, RDP_KEYRELEASE, SCANCODE_CHAR_RALT);
500 }
501
502
503 static void
504 update_modifier_state(uint8 scancode, BOOL pressed)
505 {
506 #ifdef WITH_DEBUG_KBD
507 uint16 old_modifier_state;
508
509 old_modifier_state = remote_modifier_state;
510 #endif
511
512 switch (scancode)
513 {
514 case SCANCODE_CHAR_LSHIFT:
515 MASK_CHANGE_BIT(remote_modifier_state, MapLeftShiftMask, pressed);
516 break;
517 case SCANCODE_CHAR_RSHIFT:
518 MASK_CHANGE_BIT(remote_modifier_state, MapRightShiftMask, pressed);
519 break;
520 case SCANCODE_CHAR_LCTRL:
521 MASK_CHANGE_BIT(remote_modifier_state, MapLeftCtrlMask, pressed);
522 break;
523 case SCANCODE_CHAR_RCTRL:
524 MASK_CHANGE_BIT(remote_modifier_state, MapRightCtrlMask, pressed);
525 break;
526 case SCANCODE_CHAR_LALT:
527 MASK_CHANGE_BIT(remote_modifier_state, MapLeftAltMask, pressed);
528 break;
529 case SCANCODE_CHAR_RALT:
530 MASK_CHANGE_BIT(remote_modifier_state, MapRightAltMask, pressed);
531 break;
532 case SCANCODE_CHAR_LWIN:
533 MASK_CHANGE_BIT(remote_modifier_state, MapLeftWinMask, pressed);
534 break;
535 case SCANCODE_CHAR_RWIN:
536 MASK_CHANGE_BIT(remote_modifier_state, MapRightWinMask, pressed);
537 break;
538 case SCANCODE_CHAR_NUMLOCK:
539 /* KeyReleases for NumLocks are sent immediately. Toggle the
540 modifier state only on Keypress */
541 if (pressed)
542 {
543 BOOL newNumLockState;
544 newNumLockState =
545 (MASK_HAS_BITS
546 (remote_modifier_state, MapNumLockMask) == False);
547 MASK_CHANGE_BIT(remote_modifier_state,
548 MapNumLockMask, newNumLockState);
549 }
550 break;
551 }
552
553 #ifdef WITH_DEBUG_KBD
554 if (old_modifier_state != remote_modifier_state)
555 {
556 DEBUG_KBD(("Before updating modifier_state:0x%x, pressed=0x%x\n",
557 old_modifier_state, pressed));
558 DEBUG_KBD(("After updating modifier_state:0x%x\n", remote_modifier_state));
559 }
560 #endif
561
562 }
563
564 /* Send keyboard input */
565 void
566 rdp_send_scancode(uint32 time, uint16 flags, uint8 scancode)
567 {
568 update_modifier_state(scancode, !(flags & RDP_KEYRELEASE));
569
570 if (scancode & SCANCODE_EXTENDED)
571 {
572 DEBUG_KBD(("Sending extended scancode=0x%x, flags=0x%x\n",
573 scancode & ~SCANCODE_EXTENDED, flags));
574 rdp_send_input(time, RDP_INPUT_SCANCODE, flags | KBD_FLAG_EXT,
575 scancode & ~SCANCODE_EXTENDED, 0);
576 }
577 else
578 {
579 DEBUG_KBD(("Sending scancode=0x%x, flags=0x%x\n", scancode, flags));
580 rdp_send_input(time, RDP_INPUT_SCANCODE, flags, scancode, 0);
581 }
582 }

  ViewVC Help
Powered by ViewVC 1.1.26