jcs ratpoison hax
1/* Read keyboard input from the user.
2 * Copyright (C) 2000, 2001, 2002, 2003, 2004 Shawn Betts <sabetts@vcn.bc.ca>
3 *
4 * This file is part of ratpoison.
5 *
6 * ratpoison 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, or (at your option)
9 * any later version.
10 *
11 * ratpoison 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 software; see the file COPYING. If not, write to
18 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19 * Boston, MA 02111-1307 USA
20 */
21
22#include <stdlib.h>
23#include <stdio.h>
24#include <string.h>
25#include <unistd.h>
26#include <X11/Xlib.h>
27#include <X11/keysym.h>
28#include <X11/Xutil.h>
29
30#include "ratpoison.h"
31
32#ifdef HAVE_X11_XKBLIB_H
33#include <X11/XKBlib.h>
34#endif
35
36/* Convert an X11 modifier mask to the rp modifier mask equivalent, as
37 best it can (the X server may not have a hyper key defined, for
38 instance). */
39unsigned int
40x11_mask_to_rp_mask (unsigned int mask)
41{
42 unsigned int result = 0;
43
44 PRINT_DEBUG (("x11 mask = %x\n", mask));
45
46 result |= mask & ShiftMask ? RP_SHIFT_MASK:0;
47 result |= mask & ControlMask ? RP_CONTROL_MASK:0;
48 result |= mask & rp_modifier_info.meta_mod_mask ? RP_META_MASK:0;
49 result |= mask & rp_modifier_info.alt_mod_mask ? RP_ALT_MASK:0;
50 result |= mask & rp_modifier_info.hyper_mod_mask ? RP_HYPER_MASK:0;
51 result |= mask & rp_modifier_info.super_mod_mask ? RP_SUPER_MASK:0;
52
53 PRINT_DEBUG (("rp mask = %x\n", mask));
54
55 return result;
56}
57
58/* Convert an rp modifier mask to the x11 modifier mask equivalent, as
59 best it can (the X server may not have a hyper key defined, for
60 instance). */
61unsigned int
62rp_mask_to_x11_mask (unsigned int mask)
63{
64 unsigned int result = 0;
65
66 PRINT_DEBUG (("rp mask = %x\n", mask));
67
68 result |= mask & RP_SHIFT_MASK ? ShiftMask:0;
69 result |= mask & RP_CONTROL_MASK ? ControlMask:0;
70 result |= mask & RP_META_MASK ? rp_modifier_info.meta_mod_mask:0;
71 result |= mask & RP_ALT_MASK ? rp_modifier_info.alt_mod_mask:0;
72 result |= mask & RP_HYPER_MASK ? rp_modifier_info.hyper_mod_mask:0;
73 result |= mask & RP_SUPER_MASK ? rp_modifier_info.super_mod_mask:0;
74
75 PRINT_DEBUG (("x11 mask = %x\n", result));
76
77 return result;
78}
79
80static Bool use_xkb;
81
82void
83init_xkb (void)
84{
85#if defined (WANT_XKB) && defined (HAVE_X11_XKBLIB_H) && defined (HAVE_XKBKEYCODETOKEYSYM)
86 int error, event, major, minor, opcode;
87
88 major = XkbMajorVersion;
89 minor = XkbMajorVersion;
90
91 use_xkb = XkbLibraryVersion (&major, &minor);
92 if (!use_xkb)
93 {
94 PRINT_ERROR (("Not using XKB, compile and load time version mismatch:"));
95 PRINT_ERROR ((" (%d, %d) vs. (%d, %d)\n", XkbMajorVersion,
96 XkbMajorVersion, major, minor));
97 return;
98 }
99
100 use_xkb = XkbQueryExtension (dpy, &opcode, &event, &error, &major, &minor);
101 if (!use_xkb)
102 PRINT_DEBUG (("Not using XKB, XkbQueryExtension failed\n"));
103#else
104 PRINT_DEBUG (("Built with no XKB support."));
105 use_xkb = False;
106#endif
107}
108
109KeySym
110keycode_to_keysym(Display *dpy, KeyCode kc, int group, int level)
111{
112 /*
113 * XKeycodeToKeysym has been deprecated upstream, however we still use
114 * it since XKB may not be available at build time or and not
115 * functional at runtime. The problems in XKeycodeToKeysym don't seem
116 * to matter in the ratpoison case anyway.
117 * https://bugs.freedesktop.org/show_bug.cgi?id=5349
118 */
119#if defined (WANT_XKB) && defined (HAVE_X11_XKBLIB_H) && defined (HAVE_XKBKEYCODETOKEYSYM)
120 if (use_xkb)
121 return XkbKeycodeToKeysym (dpy, kc, group, level);
122#endif
123 (void) group;
124 return XKeycodeToKeysym (dpy, kc, level);
125}
126
127/* Figure out what keysyms are attached to what modifiers */
128void
129update_modifier_map (void)
130{
131 unsigned int modmasks[] =
132 { Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask };
133 int row, col; /* The row and column in the modifier table. */
134 int found_alt_or_meta;
135 XModifierKeymap *mods;
136 int min_code, max_code;
137 int syms_per_code;
138 KeySym *syms;
139
140 rp_modifier_info.meta_mod_mask = 0;
141 rp_modifier_info.alt_mod_mask = 0;
142 rp_modifier_info.super_mod_mask = 0;
143 rp_modifier_info.hyper_mod_mask = 0;
144 rp_modifier_info.num_lock_mask = 0;
145 rp_modifier_info.scroll_lock_mask = 0;
146
147 XDisplayKeycodes (dpy, &min_code, &max_code);
148 syms = XGetKeyboardMapping (dpy,
149 min_code, max_code - min_code + 1,
150 &syms_per_code);
151 mods = XGetModifierMapping (dpy);
152
153 for (row=3; row < 8; row++)
154 {
155 found_alt_or_meta = 0;
156 for (col=0; col < mods->max_keypermod; col++)
157 {
158 KeyCode code = mods->modifiermap[(row * mods->max_keypermod) + col];
159
160 PRINT_DEBUG (("row: %d col: %d code: %d\n", row, col, code));
161
162 if (code == 0) continue;
163
164 /* Are any of this keycode's keysyms a meta key? */
165 {
166 int code_col;
167
168 for (code_col = 0; code_col < syms_per_code; code_col++)
169 {
170 int sym = syms[((code - min_code) * syms_per_code) + code_col];
171
172 switch (sym)
173 {
174 case XK_Meta_L:
175 case XK_Meta_R:
176 found_alt_or_meta = 1;
177 rp_modifier_info.meta_mod_mask |= modmasks[row - 3];
178 PRINT_DEBUG (("Found Meta on %d\n",
179 rp_modifier_info.meta_mod_mask));
180 break;
181
182 case XK_Alt_L:
183 case XK_Alt_R:
184 found_alt_or_meta = 1;
185 rp_modifier_info.alt_mod_mask |= modmasks[row - 3];
186 PRINT_DEBUG (("Found Alt on %d\n",
187 rp_modifier_info.alt_mod_mask));
188 break;
189
190 case XK_Super_L:
191 case XK_Super_R:
192 if (!found_alt_or_meta)
193 {
194 rp_modifier_info.super_mod_mask |= modmasks[row - 3];
195 PRINT_DEBUG (("Found Super on %d\n",
196 rp_modifier_info.super_mod_mask));
197 }
198 code_col = syms_per_code;
199 col = mods->max_keypermod;
200 break;
201
202 case XK_Hyper_L:
203 case XK_Hyper_R:
204 if (!found_alt_or_meta)
205 {
206 rp_modifier_info.hyper_mod_mask |= modmasks[row - 3];
207 PRINT_DEBUG (("Found Hyper on %d\n",
208 rp_modifier_info.hyper_mod_mask));
209 }
210 code_col = syms_per_code;
211 col = mods->max_keypermod;
212
213 break;
214
215 case XK_Num_Lock:
216 rp_modifier_info.num_lock_mask |= modmasks[row - 3];
217 PRINT_DEBUG (("Found NumLock on %d\n",
218 rp_modifier_info.num_lock_mask));
219 break;
220
221 case XK_Scroll_Lock:
222 rp_modifier_info.scroll_lock_mask |= modmasks[row - 3];
223 PRINT_DEBUG (("Found ScrollLock on %d\n",
224 rp_modifier_info.scroll_lock_mask));
225 break;
226 default:
227 break;
228 }
229 }
230 }
231 }
232 }
233
234 /* Stolen from Emacs 21.0.90 - xterm.c */
235 /* If we couldn't find any meta keys, accept any alt keys as meta keys. */
236 if (! rp_modifier_info.meta_mod_mask)
237 {
238 rp_modifier_info.meta_mod_mask = rp_modifier_info.alt_mod_mask;
239 rp_modifier_info.alt_mod_mask = 0;
240 }
241
242 /* If some keys are both alt and meta,
243 make them just meta, not alt. */
244 if (rp_modifier_info.alt_mod_mask & rp_modifier_info.meta_mod_mask)
245 {
246 rp_modifier_info.alt_mod_mask &= ~rp_modifier_info.meta_mod_mask;
247 }
248
249 XFree ((char *) syms);
250 XFreeModifiermap (mods);
251}
252
253/* we need a keycode + modifier to generate the proper keysym (such as
254 @). Return 1 if successful, 0 otherwise. This function can fail if a
255 keysym doesn't map to a keycode. */
256static int
257keysym_to_keycode_mod (KeySym keysym, KeyCode *code, unsigned int *mod)
258{
259 KeySym lower, upper;
260
261 *mod = 0;
262 *code = XKeysymToKeycode (dpy, keysym);
263 lower = keycode_to_keysym (dpy, *code, 0, 0);
264 upper = keycode_to_keysym (dpy, *code, 0, 1);
265 /* If you need to press shift to get the keysym, add the shift
266 mask. */
267 if (upper == keysym && lower != keysym)
268 *mod = ShiftMask;
269
270 return *code != 0;
271}
272
273/* Grab the key while ignoring annoying modifier keys including
274 caps lock, num lock, and scroll lock. */
275void
276grab_key (KeySym keysym, unsigned int modifiers, Window grab_window)
277{
278 unsigned int mod_list[8];
279 int i;
280 KeyCode keycode;
281 unsigned int mod;
282
283 /* Convert to a modifier mask that X Windows will understand. */
284 modifiers = rp_mask_to_x11_mask (modifiers);
285 if (!keysym_to_keycode_mod (keysym, &keycode, &mod))
286 return;
287 PRINT_DEBUG (("keycode_mod: %ld %d %d\n", keysym, keycode, mod));
288 modifiers |= mod;
289
290 /* Create a list of all possible combinations of ignored
291 modifiers. Assumes there are only 3 ignored modifiers. */
292 mod_list[0] = 0;
293 mod_list[1] = LockMask;
294 mod_list[2] = rp_modifier_info.num_lock_mask;
295 mod_list[3] = mod_list[1] | mod_list[2];
296 mod_list[4] = rp_modifier_info.scroll_lock_mask;
297 mod_list[5] = mod_list[1] | mod_list[4];
298 mod_list[6] = mod_list[2] | mod_list[4];
299 mod_list[7] = mod_list[1] | mod_list[2] | mod_list[4];
300
301 /* Grab every combination of ignored modifiers. */
302 for (i=0; i<8; i++)
303 {
304 XGrabKey(dpy, keycode, modifiers | mod_list[i],
305 grab_window, True, GrabModeAsync, GrabModeAsync);
306 }
307}
308
309
310/* Return the name of the keysym. caller must free returned pointer */
311char *
312keysym_to_string (KeySym keysym, unsigned int modifier)
313{
314 struct sbuf *name;
315 char *tmp;
316
317 name = sbuf_new (0);
318
319 if (modifier & RP_SHIFT_MASK) sbuf_concat (name, "S-");
320 if (modifier & RP_CONTROL_MASK) sbuf_concat (name, "C-");
321 if (modifier & RP_META_MASK) sbuf_concat (name, "M-");
322 if (modifier & RP_ALT_MASK) sbuf_concat (name, "A-");
323 if (modifier & RP_HYPER_MASK) sbuf_concat (name, "H-");
324 if (modifier & RP_SUPER_MASK) sbuf_concat (name, "s-");
325
326 /* On solaris machines (perhaps other machines as well) this call
327 can return NULL. In this case use the "NULL" string. */
328 tmp = XKeysymToString (keysym);
329 if (tmp == NULL)
330 tmp = "NULL";
331
332 sbuf_concat (name, tmp);
333
334 return sbuf_free_struct (name);
335}
336
337/* Cooks a keycode + modifier into a keysym + modifier. This should be
338 used anytime meaningful key information is to be extracted from a
339 KeyPress or KeyRelease event.
340
341 returns the number of bytes in keysym_name. If you are not
342 interested in the keysym name pass in NULL for keysym_name and 0
343 for len. */
344int
345cook_keycode (XKeyEvent *ev, KeySym *keysym, unsigned int *mod, char *keysym_name, int len, int ignore_bad_mods)
346{
347 int nbytes;
348 int shift = 0;
349 KeySym lower, upper;
350
351 if (ignore_bad_mods)
352 {
353 ev->state &= ~(LockMask
354 | rp_modifier_info.num_lock_mask
355 | rp_modifier_info.scroll_lock_mask);
356 }
357
358 if (len > 0) len--;
359 nbytes = XLookupString (ev, keysym_name, len, keysym, NULL);
360
361 /* Null terminate the string (not all X servers do it for us). */
362 if (keysym_name) {
363 keysym_name[nbytes] = '\0';
364 }
365
366 /* Find out if XLookupString gobbled the shift modifier */
367 if (ev->state & ShiftMask)
368 {
369 lower = keycode_to_keysym (dpy, ev->keycode, 0, 0);
370 upper = keycode_to_keysym (dpy, ev->keycode, 0, 1);
371 /* If the keysym isn't affected by the shift key, then keep the
372 shift modifier. */
373 if (lower == upper)
374 shift = ShiftMask;
375 }
376
377 *mod = ev->state;
378 *mod &= (rp_modifier_info.meta_mod_mask
379 | rp_modifier_info.alt_mod_mask
380 | rp_modifier_info.hyper_mod_mask
381 | rp_modifier_info.super_mod_mask
382 | ControlMask
383 | shift);
384
385 return nbytes;
386}
387
388/* Wait for a key and discard it. */
389void
390read_any_key (void)
391{
392 char buffer[513];
393 unsigned int mod;
394 KeySym c;
395
396 read_single_key (&c, &mod, buffer, sizeof (buffer));
397}
398
399/* The same as read_key, but handle focusing the key_window and reverting focus. */
400int
401read_single_key (KeySym *keysym, unsigned int *modifiers, char *keysym_name, int len)
402{
403 Window focus;
404 int revert;
405 int nbytes;
406
407 XGetInputFocus (dpy, &focus, &revert);
408 set_window_focus (rp_current_screen->key_window);
409 nbytes = read_key (keysym, modifiers, keysym_name, len);
410 set_window_focus (focus);
411
412 return nbytes;
413}
414
415int
416read_key (KeySym *keysym, unsigned int *modifiers, char *keysym_name, int len)
417{
418 XEvent ev;
419 int nbytes;
420
421 /* Read a key from the keyboard. */
422 do
423 {
424 XMaskEvent (dpy, KeyPressMask|KeyRelease, &ev);
425 *modifiers = ev.xkey.state;
426 nbytes = cook_keycode (&ev.xkey, keysym, modifiers, keysym_name, len, 0);
427 } while (IsModifierKey (*keysym) || ev.xkey.type == KeyRelease);
428
429 return nbytes;
430}
431
432static void
433update_input_window (rp_screen *s, rp_input_line *line)
434{
435 int prompt_width, input_width, total_width;
436 int char_len = 0, height;
437 GC lgc;
438 XGCValues gcv;
439
440 prompt_width = rp_text_width (s, line->prompt, -1);
441 input_width = rp_text_width (s, line->buffer, line->length);
442 total_width = defaults.bar_x_padding * 2 + prompt_width + input_width + MAX_FONT_WIDTH (defaults.font);
443 height = (FONT_HEIGHT (s) + defaults.bar_y_padding * 2);
444
445 if (isu8start (line->buffer[line->position]))
446 do
447 char_len++;
448 while (isu8cont (line->buffer[line->position + char_len]));
449 else
450 char_len = 1;
451
452 if (total_width < defaults.input_window_size + prompt_width)
453 {
454 total_width = defaults.input_window_size + prompt_width;
455 }
456
457 XMoveResizeWindow (dpy, s->input_window,
458 bar_x (s, total_width), bar_y (s, height), total_width,
459 (FONT_HEIGHT (s) + defaults.bar_y_padding * 2));
460
461 XClearWindow (dpy, s->input_window);
462 XSync (dpy, False);
463
464 rp_draw_string (s, s->input_window, STYLE_NORMAL,
465 defaults.bar_x_padding,
466 defaults.bar_y_padding + FONT_ASCENT(s),
467 line->prompt,
468 -1);
469
470 rp_draw_string (s, s->input_window, STYLE_NORMAL,
471 defaults.bar_x_padding + prompt_width,
472 defaults.bar_y_padding + FONT_ASCENT(s),
473 line->buffer,
474 line->length);
475
476 gcv.function = GXxor;
477 gcv.foreground = rp_glob_screen.fg_color ^ rp_glob_screen.bg_color;
478 lgc = XCreateGC (dpy, s->input_window, GCFunction | GCForeground, &gcv);
479
480 /* Draw a cheap-o cursor - MkIII */
481 XFillRectangle (dpy, s->input_window, lgc,
482 defaults.bar_x_padding + prompt_width +
483 rp_text_width (s, line->buffer, line->position),
484 defaults.bar_y_padding,
485 rp_text_width (s, &line->buffer[line->position], char_len),
486 FONT_HEIGHT (s));
487
488 XFlush (dpy);
489 XFreeGC (dpy, lgc);
490}
491
492void
493ring_bell (void)
494{
495#ifdef VISUAL_BELL
496 GC lgc;
497 XGCValues gcv;
498 XWindowAttributes attr;
499 rp_screen *s = rp_current_screen;
500
501 XGetWindowAttributes (dpy, s->input_window, &attr);
502
503 gcv.function = GXxor;
504 gcv.foreground = rp_glob_screen.fg_color ^ rp_glob_screen.bg_color;
505 lgc = XCreateGC (dpy, s->input_window, GCFunction | GCForeground, &gcv);
506
507 XFillRectangle (dpy, s->input_window, lgc, 0, 0, attr.width, attr.height);
508 XFlush (dpy);
509
510 usleep (15000);
511
512 XFillRectangle (dpy, s->input_window, lgc, 0, 0, attr.width, attr.height);
513 XFlush (dpy);
514 XFreeGC (dpy, lgc);
515#else
516 XBell (dpy, 0);
517#endif
518}
519
520char *
521get_input (char *prompt, int history_id, completion_fn fn)
522{
523 return get_more_input (prompt, "", history_id, BASIC, fn);
524}
525
526char *
527get_more_input (char *prompt, char *preinput, int history_id,
528 enum completion_styles style, completion_fn compl_fn)
529{
530 /* Emacs 21 uses a 513 byte string to store the keysym name. */
531 char keysym_buf[513];
532 rp_screen *s = rp_current_screen;
533 KeySym ch;
534 unsigned int modifier;
535 rp_input_line *line;
536 char *final_input;
537 edit_status status;
538 Window focus;
539 int revert, done = 0;
540
541 history_reset();
542
543 /* Create our line structure */
544 line = input_line_new (prompt, preinput, history_id, style, compl_fn);
545
546 /* We don't want to draw overtop of the program bar. */
547 hide_bar (s);
548
549 /* Switch to the default colormap. */
550 if (current_window())
551 XUninstallColormap (dpy, current_window()->colormap);
552 XInstallColormap (dpy, s->def_cmap);
553
554 XMapWindow (dpy, s->input_window);
555 XRaiseWindow (dpy, s->input_window);
556 XClearWindow (dpy, s->input_window);
557 /* Switch focus to our input window to read the next key events. */
558 XGetInputFocus (dpy, &focus, &revert);
559 set_window_focus (s->input_window);
560 XSync (dpy, False);
561
562 update_input_window (s, line);
563
564 while (!done)
565 {
566 read_key (&ch, &modifier, keysym_buf, sizeof (keysym_buf));
567 modifier = x11_mask_to_rp_mask (modifier);
568 PRINT_DEBUG (("ch = %ld, modifier = %d, keysym_buf = %s",
569 ch, modifier, keysym_buf));
570 status = execute_edit_action (line, ch, modifier, keysym_buf);
571
572 switch (status)
573 {
574 case EDIT_COMPLETE:
575 case EDIT_DELETE:
576 case EDIT_INSERT:
577 case EDIT_MOVE:
578 /* If the text changed (and we didn't just complete
579 something) then set the virgin bit. */
580 if (status != EDIT_COMPLETE)
581 line->compl->virgin = 1;
582 /* In all cases, we need to redisplay the input string. */
583 update_input_window (s, line);
584 break;
585 case EDIT_NO_OP:
586 ring_bell ();
587 break;
588 case EDIT_ABORT:
589 final_input = NULL;
590 done = 1;
591 break;
592 case EDIT_DONE:
593 final_input = xstrdup (line->buffer);
594 done = 1;
595 break;
596 default:
597 PRINT_ERROR (("Unhandled status %d; this is a *BUG*\n", status));
598 exit (EXIT_FAILURE);
599 }
600 }
601
602 /* Clean up our line structure */
603 input_line_free (line);
604
605 /* Revert focus. */
606 set_window_focus (focus);
607 XUnmapWindow (dpy, s->input_window);
608
609 /* Possibly restore colormap. */
610 if (current_window())
611 {
612 XUninstallColormap (dpy, s->def_cmap);
613 XInstallColormap (dpy, current_window()->colormap);
614 }
615
616 return final_input;
617}