jcs ratpoison hax
at master 617 lines 19 kB view raw
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}