/* keys.c -- Key binding and evaluating (this should be called events.c) $Id: keys.c,v 1.86 2002/10/20 04:05:40 jsh Exp $ Copyright (C) 1999 John Harper This file is part of sawmill. sawmill is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. sawmill is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with sawmill; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "sawmill.h" #include "keys.h" #include #include #include #include #include /* max number of milliseconds between successive-clicks */ #define DEFAULT_DOUBLE_CLICK_TIME 250 /* current_event holds the event we're processing (or 0s), last_event contains the previously processed event. */ static u_long current_event[2], last_event[2]; /* print_prefix means echo all events upto the end of the key-sequence. printed_this_prefix says the last event has been echoed. */ static bool print_prefix, printed_this_prefix; /* Buffer holding the events making this key-sequence. */ #define EVENT_BUFSIZ 20 static u_long event_buf[EVENT_BUFSIZ]; /* one event = (code,mods) */ static int event_index; /* Data for testing double-clicks */ static Time last_click; static u_long last_click_button; static int click_count; /* These control which types of keyboard events we actually evaluate */ DEFSYM(eval_modifier_events, "eval-modifier-events"); DEFSYM(eval_key_release_events, "eval-key-release-events"); DEFSYM(global_keymap, "global-keymap"); DEFSYM(root_window_keymap, "root-window-keymap"); DEFSYM(override_keymap, "override-keymap"); DEFSYM(unbound_key_hook, "unbound-key-hook"); DEFSYM(keymap, "keymap"); DEFSYM(this_command, "this-command"); DEFSYM(last_command, "last-command"); DEFSYM(prefix_arg, "prefix-arg"); DEFSYM(current_prefix_arg, "current-prefix-arg"); DEFSYM(async_pointer, "async-pointer"); DEFSYM(async_keyboard, "async-keyboard"); DEFSYM(sync_pointer, "sync-pointer"); DEFSYM(sync_keyboard, "sync-keyboard"); DEFSYM(replay_pointer, "replay-pointer"); DEFSYM(replay_keyboard, "replay-keyboard"); DEFSYM(sync_both, "sync-both"); DEFSYM(async_both, "async-both"); DEFSYM(display_message, "display-message"); DEFSYM(call_command, "call-command"); static repv next_keymap_path; DEFSYM(multi_click_delay, "multi-click-delay"); /* The X modifiers being used for Meta, Alt, and Hyper */ static u_long meta_mod, alt_mod, hyper_mod, super_mod; /* The user-customizable modifier; used for default key bindings. This shouldn't include any bits that don't have a fixed meaning. */ static u_long wm_mod = EV_MOD_META; /* The X modifiers bound to the Num_Lock and Scroll_Lock keysyms */ static u_long num_lock_mod, scroll_lock_mod; DEFSYM(meta_keysyms, "meta-keysyms"); DEFSYM(alt_keysyms, "alt-keysyms"); DEFSYM(hyper_keysyms, "hyper-keysyms"); DEFSYM(super_keysyms, "super-keysyms"); static void grab_keymap_event (repv km, long code, long mods, bool grab); static void grab_all_keylist_events (repv map, bool grab); static int all_buttons[7] = { Button1, Button2, Button3, Button4, Button5, Button6, Button7 }; /* locks: currently LockMask, num_lock, and scroll_lock */ static int total_lock_combs, all_lock_mask; static int all_lock_combs[2*2*2]; /* Translate from X events to Lisp events */ static u_long direct_modifiers (u_long mods) { /* Do this first, since it may contain other indirect mods */ if (wm_mod != 0 && (mods & EV_MOD_WM)) mods = (mods & ~EV_MOD_WM) | wm_mod; if (meta_mod != 0 && (mods & EV_MOD_META)) mods = (mods & ~EV_MOD_META) | meta_mod; if (alt_mod != 0 && (mods & EV_MOD_ALT)) mods = (mods & ~EV_MOD_ALT) | alt_mod; if (hyper_mod != 0 && (mods & EV_MOD_HYPER)) mods = (mods & ~EV_MOD_HYPER) | hyper_mod; if (super_mod != 0 && (mods & EV_MOD_SUPER)) mods = (mods & ~EV_MOD_SUPER) | super_mod; return mods; } static u_long indirect_modifiers (u_long mods) { if(mods & meta_mod) mods = (mods & ~meta_mod) | EV_MOD_META; if(mods & alt_mod) mods = (mods & ~alt_mod) | EV_MOD_ALT; if(mods & hyper_mod) mods = (mods & ~hyper_mod) | EV_MOD_HYPER; if(mods & super_mod) mods = (mods & ~super_mod) | EV_MOD_SUPER; return mods; } /* Translate the X key or button event XEV to *CODE and *MODS */ static bool translate_event(u_long *code, u_long *mods, XEvent *xev) { repv multi_click_delay; int delay; bool ret = FALSE; switch(xev->type) { case KeyRelease: if (global_symbol_value (Qeval_key_release_events) == Qnil) break; /* FALL THROUGH */ case KeyPress: *mods = xev->xkey.state & ~all_lock_mask; if (xev->type == KeyRelease) *mods |= EV_MOD_RELEASE; if(*mods & ShiftMask) { KeySym normal, shifted; normal = XKeycodeToKeysym (dpy, xev->xkey.keycode, 0); shifted = XKeycodeToKeysym (dpy, xev->xkey.keycode, 1); /* Some keys don't have keysym at index 1, if not treat it as normal keysym shifted. But if the keysym at index 1 is the same as that in index 0, then preserve the shift modifier */ if (shifted == NoSymbol) *code = normal; else { *code = shifted; if (shifted != normal) *mods &= ~ShiftMask; } } else *code = XKeycodeToKeysym(xev->xany.display, xev->xkey.keycode, 0); if(*code != NoSymbol && (!IsModifierKey (*code) || global_symbol_value (Qeval_modifier_events) == Qt)) { *mods |= EV_TYPE_KEY; ret = TRUE; } break; case ButtonPress: if(xev->xbutton.button == last_click_button) { multi_click_delay = global_symbol_value (Qmulti_click_delay); if (rep_INTP(multi_click_delay)) delay = rep_INT(multi_click_delay); else delay = DEFAULT_DOUBLE_CLICK_TIME; if (xev->xbutton.time < (last_click + delay)) { click_count++; } else click_count = 1; } else click_count = 1; switch (click_count) { case 2: *code = EV_CODE_MOUSE_CLICK2; break; case 3: *code = EV_CODE_MOUSE_CLICK3; break; default: *code = EV_CODE_MOUSE_CLICK1; } last_click = xev->xbutton.time; last_click_button = xev->xbutton.button; goto button; case ButtonRelease: switch (click_count) { case 2: *code = EV_CODE_MOUSE_UP2; break; case 3: *code = EV_CODE_MOUSE_UP3; break; default: *code = EV_CODE_MOUSE_UP1; } button: *mods = xev->xbutton.state & ~all_lock_mask; *mods |= EV_TYPE_MOUSE; switch(xev->xbutton.button) { case Button1: *mods |= Button1Mask; break; case Button2: *mods |= Button2Mask; break; case Button3: *mods |= Button3Mask; break; case Button4: *mods |= Button4Mask; break; case Button5: *mods |= Button5Mask; break; case Button6: *mods |= Button6Mask; break; case Button7: *mods |= Button7Mask; break; } ret = TRUE; break; case MotionNotify: *code = EV_CODE_MOUSE_MOVE; *mods = xev->xmotion.state & ~all_lock_mask; *mods |= EV_TYPE_MOUSE; ret = TRUE; break; } if (ret) *mods = indirect_modifiers (*mods); return ret; } /* Translate the Lisp key event EV to X keycode *KEYCODE and modifier mask *STATE, returning true if successful. */ static bool translate_event_to_x_key (repv ev, u_int *keycode, u_int *state) { if (rep_INT(EVENT_MODS(ev)) & EV_TYPE_KEY) { u_int s = rep_INT(EVENT_MODS(ev)) & EV_MOD_MASK; KeySym sym = rep_INT(EVENT_CODE(ev)); KeySym normal, shifted; u_int k = XKeysymToKeycode (dpy, sym); if (k == 0) return FALSE; s = direct_modifiers (s); /* Check if we need a shift modifier */ normal = XKeycodeToKeysym (dpy, k, 0); shifted = XKeycodeToKeysym (dpy, k, 1); if (sym != normal && sym == shifted) s |= ShiftMask; if (s & EV_MOD_RELEASE) s &= ~EV_MOD_RELEASE; if (s == EV_MOD_ANY) s = AnyModifier; if ((s & EV_VIRT_MOD_MASK) != 0) return FALSE; *state = s; *keycode = k; return TRUE; } else return FALSE; } /* Translate the Lisp button event EV to X button identifier *BUTTON and modifier mask *STATE, returning true if successful. */ static u_int translate_event_to_x_button (repv ev, u_int *button, u_int *state) { if (rep_INT(EVENT_MODS(ev)) & EV_TYPE_MOUSE) { u_long mods = rep_INT(EVENT_MODS(ev)); static struct { u_int button; u_int mask; } buttons[] = { { Button1, Button1Mask }, { Button2, Button2Mask }, { Button3, Button3Mask }, { Button4, Button4Mask }, { Button5, Button5Mask }, { Button6, Button6Mask }, { Button7, Button7Mask }, { 0, 0 } }; int i; for (i = 0; buttons[i].button != 0; i++) { if (mods & buttons[i].mask) { u_int s; mods &= ~buttons[i].mask; s = direct_modifiers (mods & EV_MOD_MASK); if (s == EV_MOD_ANY) s = AnyModifier; if ((s & EV_VIRT_MOD_MASK) == 0) { *button = buttons[i].button; *state = s; return buttons[i].mask; } } } /* no actual button specified. if mod_any is set, then just return this; hope the caller does the Right Thing */ if (mods & EV_MOD_ANY) { *state = AnyModifier; *button = 0; /* anything.. */ return 1; } } return 0; } /* Keymap searching */ static inline bool compare_events (u_long code1, u_long mods1, u_long code2, u_long mods2) { return (code1 == code2 && ((mods1 == mods2) || ((mods2 & wm_mod) == wm_mod && ((mods2 & ~wm_mod) | EV_MOD_WM) == mods1) || (((mods1 & EV_MOD_MASK) & EV_MOD_ANY) /* this allows things like Any-C-x, mapping to C-x _plus_ any other modifiers */ && (((mods1 & ~EV_MOD_ANY) & mods2) == (mods1 & ~EV_MOD_ANY))))); } /* Search the keymap KM for a binding of CODE&MODS. If CALLBACK is non-nil it's a function to call for the binding found. If this function returns true, then this binding is acceptable and is returned from the function. */ static repv search_keymap(repv km, u_long code, u_long mods, bool (*callback)(repv key)) { /* If it's a symbol, dereference it. */ while(rep_SYMBOLP(km) && !rep_NILP(km) && !rep_INTERRUPTP) { repv tem = Fsymbol_value(km, Qt); if(tem == km) break; km = tem; rep_TEST_INT; } /* Find the list of bindings to scan. */ if rep_CONSP(km) km = rep_CDR(km); else return rep_NULL; /* Scan them for a match.. */ while(rep_CONSP(km)) { rep_TEST_INT; if(rep_INTERRUPTP) break; if(rep_CONSP(rep_CAR(km))) { repv ev = KEY_EVENT(rep_CAR(km)); if(compare_events (rep_INT(EVENT_CODE(ev)), rep_INT(EVENT_MODS(ev)), code, mods)) { repv key = rep_CAR(km); if(callback == 0 || callback(key)) return key; } km = rep_CDR(km); } else /* An inherited sub-keymap. Start scanning it */ km = rep_CDR(km); } return rep_NULL; } /* Search for a binding of CODE&MODS. */ static repv lookup_binding(u_long code, u_long mods, bool (*callback)(repv key), repv context_keymap, Lisp_Window *current_window) { repv k = rep_NULL, nkp = next_keymap_path; next_keymap_path = rep_NULL; if(nkp == rep_NULL || nkp == Qglobal_keymap) { repv tem = global_symbol_value (Qoverride_keymap); if (tem != Qnil && !rep_VOIDP(tem)) k = search_keymap (tem, code, mods, callback); else { /* 1. search keymap for active window decoration */ k = search_keymap(context_keymap, code, mods, callback); if (!k && (current_x_event != 0 && (current_x_event->xany.window == root_window || current_x_event->xany.window == no_focus_window))) { /* 2. if event from root, search the root-window-keymap */ k = search_keymap (Qroot_window_keymap, code, mods, callback); } if (!k && current_window) { /* 3. search focused/pointer window keymap property */ tem = Fwindow_get (rep_VAL(current_window), Qkeymap); if (tem && tem != Qnil) k = search_keymap(tem, code, mods, callback); } if(!k) /* 4. search global-keymap */ k = search_keymap(Qglobal_keymap, code, mods, callback); } } else { rep_GC_root gc_nkp; rep_PUSHGC(gc_nkp, nkp); while(!k && rep_CONSP(nkp)) { k = search_keymap(rep_CAR(nkp), code, mods, callback); nkp = rep_CDR(nkp); } rep_POPGC; } return (k != rep_NULL && KEYP(k)) ? KEY_COMMAND(k) : rep_NULL; } static bool eval_input_callback(repv key) { repv cmd = KEY_COMMAND(key); if(rep_SYMBOLP(cmd)) { cmd = Fsymbol_value (cmd, Qt); if (rep_FUNARGP(cmd)) { repv fun = rep_FUNARG(cmd)->fun; if(rep_CONSP(fun) && rep_CAR(fun) == Qautoload) { /* An autoload, try to load it. */ rep_GC_root gc_key; rep_PUSHGC (gc_key, key); cmd = rep_call_with_closure (cmd, rep_load_autoload, cmd); rep_POPGC; if(cmd == rep_NULL) return FALSE; } } } if(Fkeymapp (cmd) != Qnil) { /* A prefix key, add its list to the next-keymap-path. */ next_keymap_path = Fcons(cmd, next_keymap_path ? next_keymap_path : Qnil); /* Look for more prefix keys */ return FALSE; } if (cmd == Qnil) return FALSE; next_keymap_path = rep_NULL; return TRUE; } static repv lookup_event_binding (u_long code, u_long mods, repv context_map) { Lisp_Window *w = 0; if (current_x_event && (mods & EV_TYPE_MOUSE)) { /* If a mouse event, look for bindings in the window that the button was pressed in, not where the input focus is. */ repv tem = Fquery_button_press_window (); if (tem && WINDOWP(tem)) w = VWIN(tem); } else w = focus_window; return lookup_binding(code, mods, eval_input_callback, context_map, w); } /* Process the event CODE+MODS. OS-INPUT-MSG is the raw input event from the window-system, this is only used to cook a string from. */ repv eval_input_event(repv context_map) { u_long code, mods; repv result = Qnil, cmd, orig_next_keymap_path = next_keymap_path; if (!translate_event (&code, &mods, current_x_event)) return Qnil; event_buf[event_index++] = code; event_buf[event_index++] = mods; if(event_index == EVENT_BUFSIZ) event_index = 0; printed_this_prefix = FALSE; current_event[0] = code; current_event[1] = mods; cmd = lookup_event_binding (code, mods, context_map); if (next_keymap_path == rep_NULL) { if (print_prefix) rep_call_lisp1 (global_symbol_value (Qdisplay_message), Qnil); print_prefix = FALSE; event_index = 0; } if(cmd != rep_NULL) { /* Found a binding for this event; evaluate it. */ result = rep_call_lisp1 (Fsymbol_value (Qcall_command, Qt), cmd); } else if(next_keymap_path != rep_NULL) { /* We already handled some prefixes. */ Fset (Qthis_command, Qkeymap); result = Qnil; /* Grab the input devices for the next event */ Fgrab_keyboard (focus_window ? rep_VAL(focus_window) : Qnil, Qt, Qt); } else if(orig_next_keymap_path != rep_NULL && orig_next_keymap_path != Qglobal_keymap) { /* A multi-key binding, but no final step; clear the prefix argument for the next command and beep. */ Fset (Qprefix_arg, Qnil); Fbeep(); } else { /* An unbound key with no prefix keys. */ repv hook = global_symbol_value (Qunbound_key_hook); if (!rep_VOIDP (hook) && hook != Qnil) result = Fcall_hook(Qunbound_key_hook, Qnil, Qor); else if (rep_recurse_depth == 0 && (mods & (EV_TYPE_KEY | EV_MOD_RELEASE)) == EV_TYPE_KEY) { /* We're receiving events that we have no way of handling.. I think this gives us justification in aborting in case we've got stuck with an unbreakable grab somehow.. */ Fungrab_keyboard (); Fungrab_pointer (); result = Fthrow (Qtop_level, Qnil); } } /* Out of a multi-key sequence, ungrab */ if (orig_next_keymap_path && !next_keymap_path) Fungrab_keyboard (); last_event[0] = current_event[0]; last_event[1] = current_event[1]; /* Only lose the current event when in the topmost event loop. */ if (rep_recurse_depth == 0) current_event[0] = current_event[1] = 0; if (print_prefix) print_event_prefix (); return result; } /* Translate text->event and vice versa */ struct key_def { const char *name; u_long mods, code; }; static struct key_def default_mods[] = { { "S", ShiftMask }, { "Shift", ShiftMask }, { "C", ControlMask }, { "Control", ControlMask }, { "M", EV_MOD_META }, { "Meta", EV_MOD_META }, { "A", EV_MOD_ALT }, { "Alt", EV_MOD_ALT }, { "H", EV_MOD_HYPER }, { "Hyper", EV_MOD_HYPER }, { "Super", EV_MOD_SUPER }, { "W", EV_MOD_WM }, { "Mod1", Mod1Mask }, { "Mod2", Mod2Mask }, { "Mod3", Mod3Mask }, { "Mod4", Mod4Mask }, { "Mod5", Mod5Mask }, { "Button1", Button1Mask }, { "Button2", Button2Mask }, { "Button3", Button3Mask }, { "Button4", Button4Mask }, { "Button5", Button5Mask }, { "Button6", Button6Mask }, { "Button7", Button7Mask }, { "Any", EV_MOD_ANY }, { "Release", EV_MOD_RELEASE }, { 0, 0 } }; static struct key_def default_codes[] = { { "Click", EV_TYPE_MOUSE, EV_CODE_MOUSE_CLICK1 }, { "Click1", EV_TYPE_MOUSE, EV_CODE_MOUSE_CLICK1 }, { "Click2", EV_TYPE_MOUSE, EV_CODE_MOUSE_CLICK2 }, { "Click3", EV_TYPE_MOUSE, EV_CODE_MOUSE_CLICK3 }, { "Off", EV_TYPE_MOUSE, EV_CODE_MOUSE_UP1 }, { "Off1", EV_TYPE_MOUSE, EV_CODE_MOUSE_UP1 }, { "Off2", EV_TYPE_MOUSE, EV_CODE_MOUSE_UP2 }, { "Off3", EV_TYPE_MOUSE, EV_CODE_MOUSE_UP3 }, { "Move", EV_TYPE_MOUSE, EV_CODE_MOUSE_MOVE }, { "SPC", EV_TYPE_KEY, XK_space }, { "Space", EV_TYPE_KEY, XK_space }, { "TAB", EV_TYPE_KEY, XK_Tab }, { "RET", EV_TYPE_KEY, XK_Return }, { "ESC", EV_TYPE_KEY, XK_Escape }, { "BS", EV_TYPE_KEY, XK_BackSpace }, { "DEL", EV_TYPE_KEY, XK_Delete }, /* X defines lots of long names for these simple keys... */ { " ", EV_TYPE_KEY, XK_space }, { "!", EV_TYPE_KEY, XK_exclam }, { "\"", EV_TYPE_KEY, XK_quotedbl }, { "#", EV_TYPE_KEY, XK_numbersign }, { "$", EV_TYPE_KEY, XK_dollar }, { "%", EV_TYPE_KEY, XK_percent }, { "&", EV_TYPE_KEY, XK_ampersand }, { "'", EV_TYPE_KEY, XK_quoteright }, { "(", EV_TYPE_KEY, XK_parenleft }, { ")", EV_TYPE_KEY, XK_parenright }, { "*", EV_TYPE_KEY, XK_asterisk }, { "+", EV_TYPE_KEY, XK_plus }, { ",", EV_TYPE_KEY, XK_comma }, { "-", EV_TYPE_KEY, XK_minus }, { ".", EV_TYPE_KEY, XK_period }, { "/", EV_TYPE_KEY, XK_slash }, { ":", EV_TYPE_KEY, XK_colon }, { ";", EV_TYPE_KEY, XK_semicolon }, { "<", EV_TYPE_KEY, XK_less }, { "=", EV_TYPE_KEY, XK_equal }, { ">", EV_TYPE_KEY, XK_greater }, { "?", EV_TYPE_KEY, XK_question }, { "@", EV_TYPE_KEY, XK_at }, { "[", EV_TYPE_KEY, XK_bracketleft }, { "\\", EV_TYPE_KEY, XK_backslash }, { "]", EV_TYPE_KEY, XK_bracketright }, { "^", EV_TYPE_KEY, XK_asciicircum }, { "_", EV_TYPE_KEY, XK_underscore }, { "`", EV_TYPE_KEY, XK_quoteleft }, { "{", EV_TYPE_KEY, XK_braceleft }, { "|", EV_TYPE_KEY, XK_bar }, { "}", EV_TYPE_KEY, XK_braceright }, { "~", EV_TYPE_KEY, XK_asciitilde }, { 0, 0, 0 } }; /* Puts the integers defining the event described in DESC into CODE and MODS. */ static bool lookup_event(u_long *code, u_long *mods, u_char *desc) { char *tem; char buf[100]; *code = *mods = 0; /* First handle all modifiers */ while(*desc && (tem = strchr(desc + 1, '-')) != 0) { struct key_def *x = default_mods; memcpy(buf, desc, tem - (char *)desc); buf[tem - (char *)desc] = 0; while(x->name != 0) { if(strcasecmp(buf, x->name) == 0) { *mods |= x->mods; break; } x++; } if(x->name == 0) goto error; desc = tem + 1; } /* Then go for the code itself */ { struct key_def *x = default_codes; u_int ks; while(x->name != 0) { if(strcasecmp(desc, x->name) == 0) { *mods |= x->mods; *code = x->code; if ((x->mods == EV_TYPE_MOUSE) && (*mods & (EV_MOD_BUTTON_MASK | EV_MOD_ANY)) == 0) { /* Button events must include at least one of the button modifiers (otherwise they can never be generated) */ return FALSE; } else return TRUE; } x++; } ks = XStringToKeysym(desc); if(ks != NoSymbol) { if (*mods & ShiftMask) { KeySym lower, upper; XConvertCase (ks, &lower, &upper); if (ks == lower && upper != lower) { /* canonify `S-x' to `X' */ *mods &= ~ShiftMask; ks = upper; } } *mods |= EV_TYPE_KEY; *code = ks; return TRUE; } else goto error; } error: Fsignal(Qbad_event_desc, rep_LIST_1(rep_string_dup(desc))); return FALSE; } /* Constructs the name of the event defined by CODE and MODS in BUF. */ static bool lookup_event_name(u_char *buf, u_long code, u_long mods) { int i; struct key_def *x; u_long type = mods & EV_TYPE_MASK; char *end = buf, *tem; *buf = 0; mods &= EV_MOD_MASK; for(i = 32; i >= 0 && mods != 0; i--) /* magic numbers!? */ { u_long mask = 1 << i; if(mods & mask) { mods &= ~mask; x = default_mods; while(x->name != 0) { if(x->mods == mask) { strcpy (end, x->name); end += strlen (x->name); break; } x++; } *end++ = '-'; } } x = default_codes; while(x->name != 0) { if(type == x->mods && code == x->code) { strcpy(end, x->name); return TRUE; } x++; } tem = XKeysymToString((KeySym)code); if(tem != 0) { strcpy(end, tem); return TRUE; } return FALSE; } /* If necessary, print the name of the current event prefix. Returns true if in the middle of a multi-key sequence. */ bool print_event_prefix(void) { int i; u_char buf[256]; u_char *bufp = buf; if (next_keymap_path == rep_NULL) return FALSE; else if (printed_this_prefix) return TRUE; print_prefix = TRUE; for (i = 0; i < event_index; i += 2) { if (lookup_event_name (bufp, event_buf[i], event_buf[i+1])) { bufp += strlen (bufp); *bufp++ = ' '; } } if (next_keymap_path != rep_NULL) { if (bufp > buf) bufp--; /* erase the last space */ *bufp++ = '.'; *bufp++ = '.'; *bufp++ = '.'; } rep_call_lisp1 (global_symbol_value (Qdisplay_message), rep_string_dupn (buf, bufp - buf)); printed_this_prefix = TRUE; return TRUE; } /* Lisp functions */ DEFUN("make-keymap", Fmake_keymap, Smake_keymap, (void), rep_Subr0) /* ::doc:sawfish.wm.events#make-keymap:: make-keymap Return a new keymap suitable for storing bindings in. This is a cons cell looking like `(keymap . LIST-OF-BINDINGS)', LIST-OF-BINDINGS is initially nil. ::end:: */ { return Fcons(Qkeymap, Qnil); } DEFUN("bind-keys", Fbind_keys, Sbind_keys, (repv args), rep_SubrN) /* ::doc:sawfish.wm.events#bind-keys:: bind-keys KEYMAP { EVENT-DESCRIPTION COMMAND }... Adds key bindings to KEYMAP. Each EVENT-DESCRIPTION is a string naming an event to bind to the corresponding COMMAND. Returns KEYMAP when successful. ::end:: */ { repv km, arg1; if (!rep_CONSP(args)) return rep_signal_missing_arg (1); km = rep_CAR(args); args = rep_CDR(args); while (rep_CONSP(args) && rep_CONSP(rep_CDR(args))) { u_long code, mods; repv key; arg1 = rep_CAR(args); args = rep_CDR(args); if (rep_STRINGP(arg1)) { if (!lookup_event (&code, &mods, rep_STR(arg1))) return Fsignal (Qbad_event_desc, rep_LIST_1(arg1)); } else if(Feventp(arg1) != Qnil) { code = rep_INT(rep_CAR(arg1)); mods = rep_INT(rep_CDR(arg1)); } else return Fsignal (Qbad_event_desc, rep_LIST_1(arg1)); key = search_keymap (km, code, mods, 0); if (key != rep_NULL && rep_CONSP(key)) KEY_COMMAND(key) = rep_CAR(args); else { key = MAKE_KEY(MAKE_EVENT(rep_MAKE_INT(code), rep_MAKE_INT(mods)), rep_CAR(args)); rep_CDR(km) = Fcons(key, rep_CDR(km)); grab_keymap_event (km, code, mods, TRUE); } args = rep_CDR(args); } return km; } DEFUN("unbind-keys", Funbind_keys, Sunbind_keys, (repv args), rep_SubrN) /* ::doc:sawfish.wm.events#unbind-keys:: unbind-keys KEY-MAP EVENT-DESCRIPTION... ::end:: */ { repv km, arg1; if (!rep_CONSP(args)) return rep_signal_missing_arg (1); km = rep_CAR(args); if (!rep_CONSP(km)) return rep_signal_arg_error(km, 1); args = rep_CDR(args); while (rep_CONSP(args)) { u_long code, mods; repv *keyp; arg1 = rep_CAR(args); if (rep_STRINGP(arg1)) { if (!lookup_event (&code, &mods, rep_STR(arg1))) return Fsignal (Qbad_event_desc, rep_LIST_1(arg1)); } else if(Feventp(arg1) != Qnil) { code = rep_INT(rep_CAR(arg1)); mods = rep_INT(rep_CDR(arg1)); } else return Fsignal(Qbad_event_desc, rep_LIST_1(arg1)); keyp = &rep_CDR(km); while (rep_CONSP(*keyp)) { repv cell = rep_CAR(*keyp); if (rep_CONSP(cell)) { if ((rep_INT(EVENT_MODS(KEY_EVENT(cell))) == mods) && (rep_INT(EVENT_CODE(KEY_EVENT(cell))) == code)) { *keyp = rep_CDR(*keyp); } else keyp = &rep_CDR(*keyp); } rep_TEST_INT; if(rep_INTERRUPTP) return rep_NULL; } grab_keymap_event (km, code, mods, FALSE); args = rep_CDR(args); } return km; } DEFUN("grab-keymap", Fgrab_keymap, Sgrab_keymap, (repv map), rep_Subr1) /* ::doc:sawfish.wm.events#grab-keymap:: grab-keymap KEYMAP Grab any events in KEYMAP that need to be grabbed so that bindings in KEYMAP may be serviced. ::end:: */ { rep_DECLARE1(map, rep_CONSP); grab_all_keylist_events (map, TRUE); return map; } DEFUN("ungrab-keymap", Fungrab_keymap, Sungrab_keymap, (repv map), rep_Subr1)/* ::doc:sawfish.wm.events#ungrab-keymap:: ungrab-keymap KEYMAP Ungrab any events in KEYMAP that would have been grabbed so that bindings in KEYMAP may be serviced. ::end:: */ { rep_DECLARE1(map, rep_CONSP); grab_all_keylist_events (map, FALSE); return map; } DEFSTRING(not_in_handler, "Not in event handler"); DEFUN("current-event-string", Fcurrent_event_string, Scurrent_event_string, (void), rep_Subr0) /* ::doc:sawfish.wm.events#current-event-string:: current-event-string Returns the string which would have been inserted by the current event if a Lisp function hadn't been called instead. ::end:: */ { KeySym ks; u_char buf[256]; int len; if(current_x_event == 0) return Fsignal(Qerror, rep_LIST_1(rep_VAL(¬_in_handler))); len = XLookupString(¤t_x_event->xkey, buf, sizeof (buf) - 1, &ks, NULL); if(len > 0) return rep_string_dupn(buf, len); else return rep_null_string(); } DEFUN("current-event", Fcurrent_event, Scurrent_event, (void), rep_Subr0) /* ::doc:sawfish.wm.events#current-event:: current-event Return the event which caused the current command to be invoked. ::end:: */ { if(current_event[1]) return MAKE_EVENT(rep_MAKE_INT(current_event[0]), rep_MAKE_INT(current_event[1])); else return Qnil; } DEFUN("proxy-current-event", Fproxy_current_event, Sproxy_current_event, (repv win, repv mask, repv prop), rep_Subr3) /* ::doc:sawfish.wm.events#proxy-current-event:: proxy-current-event WINDOW [MASK] [PROPAGATE] Send the current X event to WINDOW, either a window object, a numeric window id, or the symbol `root'. If a ButtonPress event the pointer grab will be released first. ::end:: */ { Window w = x_win_from_arg (win); if (w == 0) return WINDOWP(win) ? Qnil : rep_signal_arg_error (win, 1); if (current_x_event != 0) { long e_mask = (rep_INTP(mask) ? rep_INT(mask) : get_event_mask (current_x_event->type)); if (current_x_event->type == ButtonPress) ungrab_pointer (); XSendEvent (dpy, w, prop == Qnil ? False : True, e_mask, current_x_event); return Qt; } else return Qnil; } DEFUN("allow-events", Fallow_events, Sallow_events, (repv mode), rep_Subr1) /* ::doc:sawfish.wm.events#allow-events:: allow-events MODE ::end:: */ { int x_mode; if (mode == Qasync_pointer) x_mode = AsyncPointer; else if (mode == Qasync_keyboard) x_mode = AsyncKeyboard; else if (mode == Qsync_pointer) x_mode = SyncPointer; else if (mode == Qsync_keyboard) x_mode = SyncKeyboard; else if (mode == Qreplay_pointer) x_mode = ReplayPointer; else if (mode == Qreplay_keyboard) x_mode = ReplayKeyboard; else if (mode == Qsync_both) x_mode = SyncBoth; else if (mode == Qasync_both) x_mode = AsyncBoth; else return rep_signal_arg_error (mode, 1); XAllowEvents (dpy, x_mode, last_event_time); return Qt; } DEFUN("last-event", Flast_event, Slast_event, (void), rep_Subr0) /* ::doc:sawfish.wm.events#last-event:: last-event Return the previous event which occurred. ::end:: */ { if(last_event[1]) return MAKE_EVENT(rep_MAKE_INT(last_event[0]), rep_MAKE_INT(last_event[1])); else return Qnil; } DEFUN("event-name", Fevent_name, Sevent_name, (repv ev), rep_Subr1) /* ::doc:sawfish.wm.events#event-name:: event-name EVENT Returns a string naming the event EVENT. ::end:: */ { u_char buf[256]; if(!EVENTP(ev)) return rep_signal_arg_error(ev, 1); if(lookup_event_name(buf, rep_INT(EVENT_CODE(ev)), rep_INT(EVENT_MODS(ev)))) { return rep_string_dup(buf); } else return Qnil; } DEFUN("lookup-event", Flookup_event, Slookup_event, (repv name), rep_Subr1) /* ::doc:sawfish.wm.events#lookup-event:: lookup-event EVENT-NAME Return the event whose name is EVENT-NAME. ::end:: */ { u_long code, mods; rep_DECLARE1(name, rep_STRINGP); if(lookup_event(&code, &mods, rep_STR(name))) return MAKE_EVENT(rep_MAKE_INT(code), rep_MAKE_INT(mods)); else return Qnil; } DEFUN("lookup-event-binding", Flookup_event_binding, Slookup_event_binding, (repv ev), rep_Subr1) /* ::doc:sawfish.wm.events#lookup-event-binding:: lookup-event-binding EVENT Return the command currently associated with the event EVENT. Note that `currently associated' means that the currently active set of keymaps is used to resolve the binding. This means the window and frame-part that received the current event for pointer events, or the currently focused window for keyboard events. ::end:: */ { repv res, context = Qnil; u_long code, mods; if(!EVENTP(ev)) return(rep_signal_arg_error(ev, 1)); code = rep_INT(EVENT_CODE(ev)); mods = rep_INT(EVENT_MODS(ev)); if (mods & EV_TYPE_MOUSE) { /* look for a context map */ if (clicked_frame_part != 0 && clicked_frame_part->clicked) context = get_keymap_for_frame_part (clicked_frame_part); } res = lookup_event_binding(code, mods, context); return res ? res : Qnil; } DEFUN("search-keymap", Fsearch_keymap, Ssearch_keymap, (repv ev, repv km), rep_Subr2) /* ::doc:sawfish.wm.events#search-keymap:: search-keymap EVENT KEYMAP Return the (COMMAND . EVENT) binding of EVENT in KEYMAP, or nil. ::end:: */ { repv res; rep_DECLARE1(ev, EVENTP); res = search_keymap(km, rep_INT(EVENT_CODE(ev)), rep_INT(EVENT_MODS(ev)), 0); return res ? res : Qnil; } DEFUN("x-lookup-keysym", Fx_lookup_keysym, Sx_lookup_keysym, (repv name), rep_Subr1) /* ::doc:sawfish.wm.events#x-lookup-keysym:: x-lookup-keysym NAME Return the X11 keysym (an integer), named by the Lisp symbol NAME. ::end:: */ { KeySym sym; rep_DECLARE1(name, rep_SYMBOLP); sym = XStringToKeysym (rep_STR(rep_SYM(name)->name)); return sym == NoSymbol ? Qnil : rep_MAKE_INT (sym); } DEFUN("x-keysym-name", Fx_keysym_name, Sx_keysym_name, (repv ks), rep_Subr1) /* ::doc:sawfish.wm.events#x-keysym-name:: x-keysym-name KEYSYM Return the Lisp symbol naming the X11 keysym represented by the integer KEYSYM. ::end:: */ { char *id; rep_DECLARE1(ks, rep_INTP); id = XKeysymToString (rep_INT(ks)); return !id ? Qnil : Fintern (rep_string_dup (id), rep_obarray); } DEFUN("keymapp", Fkeymapp, Skeymapp, (repv arg), rep_Subr1) /* ::doc:sawfish.wm.events#keymapp:: keymapp ARG Returns t if ARG can be used as a keymap. ::end:: */ { return (rep_CONSP(arg) && rep_CAR(arg) == Qkeymap) ? Qt : Qnil; } DEFUN("eventp", Feventp, Seventp, (repv arg), rep_Subr1) /* ::doc:sawfish.wm.events#eventp:: eventp ARG Returns t if the ARG is an input event. ::end:: */ { return EVENTP(arg) ? Qt : Qnil; } DEFUN("event-match", Fevent_match, Sevent_match, (repv ev1, repv ev2), rep_Subr2) /* ::doc:sawfish.wm.events#event-match:: event-match EVENT1 EVENT2 ::end:: */ { rep_DECLARE1 (ev1, EVENTP); rep_DECLARE2 (ev2, EVENTP); return (compare_events (rep_INT (EVENT_CODE (ev1)), rep_INT (EVENT_MODS (ev1)), rep_INT (EVENT_CODE (ev2)), rep_INT (EVENT_MODS (ev2))) || compare_events (rep_INT (EVENT_CODE (ev2)), rep_INT (EVENT_MODS (ev2)), rep_INT (EVENT_CODE (ev1)), rep_INT (EVENT_MODS (ev1)))) ? Qt : Qnil; } DEFUN("forget-button-press", Fforget_button_press, Sforget_button_press, (void), rep_Subr0) { last_click = 0; return Qt; } /* Find the bottom-most window below TOP containing (X,Y) that selects for button events */ static Window window_getting_button_event(Window top, int x, int y) { Window w = top, w2 = w, child; XWindowAttributes wa; /* Find the bottom-most child of W containing (X,Y) */ do { XTranslateCoordinates (dpy, w, w2, x, y, &x, &y, &child); w = w2; if (child) w2 = child; } while (child); /* Then work up until TOP is reached, or a window selecting button events is found */ again: XGetWindowAttributes (dpy, w, &wa); if (w != top && !(wa.all_event_masks & (ButtonPressMask | ButtonReleaseMask))) { Window d1, *d2, parent; u_int d3; XQueryTree (dpy, w, &d1, &parent, &d2, &d3); if (d2 != 0) XFree (d2); if (parent != 0) { w = parent; goto again; } } return w; } DEFUN("synthesize-event", Fsynthesize_event, Ssynthesize_event, (repv event, repv win, repv propagate), rep_Subr3) /* ::doc:sawfish.wm.events#synthesize-event:: synthesize-event EVENT WINDOW [PROPAGATE] ::end:: */ { XEvent ev; repv ptr = Fquery_pointer (Qnil); Window w = x_win_from_arg (win); Window child, dummy; int x_offset, y_offset; if (w == 0) return WINDOWP(win) ? Qnil : rep_signal_arg_error (win, 1); if (rep_STRINGP (event)) event = Flookup_event (event); if (!event || !ptr) return rep_NULL; rep_DECLARE (1, event, EVENTP (event)); ev.xany.display = dpy; ev.xany.window = w; if (WINDOWP(win)) { x_offset = -VWIN(win)->attr.x; y_offset = -VWIN(win)->attr.y; if (VWIN(win)->reparented) { x_offset += VWIN(win)->frame_x; y_offset += VWIN(win)->frame_y; } } else x_offset = y_offset = 0; switch (rep_INT(EVENT_MODS(event)) & EV_TYPE_MASK) { u_int mask; case EV_TYPE_KEY: if (!translate_event_to_x_key (event, &ev.xkey.keycode, &ev.xkey.state)) { return rep_signal_arg_error (event, 1); } ev.xkey.root = root_window; ev.xkey.subwindow = 0; ev.xkey.time = last_event_time; ev.xkey.x_root = rep_INT (rep_CAR (ptr)); ev.xkey.y_root = rep_INT (rep_CDR (ptr)); ev.xkey.x = (ev.xkey.x_root + x_offset); ev.xkey.y = (ev.xkey.y_root + y_offset); ev.xkey.same_screen = True; ev.xany.type = KeyPress; XSendEvent (dpy, w, propagate != Qnil, KeyPressMask, &ev); ev.xany.type = KeyRelease; XSendEvent (dpy, w, propagate != Qnil, KeyReleaseMask, &ev); break; case EV_TYPE_MOUSE: mask = translate_event_to_x_button (event, &ev.xbutton.button, &ev.xbutton.state); if (!mask || ev.xbutton.button == 0) return rep_signal_arg_error (event, 1); ev.xbutton.root = root_window; ev.xbutton.subwindow = 0; ev.xbutton.time = last_event_time; ev.xbutton.x = rep_INT (rep_CAR (ptr)) + x_offset; ev.xbutton.y = rep_INT (rep_CDR (ptr)) + y_offset; XTranslateCoordinates (dpy, w, root_window, ev.xbutton.x, ev.xbutton.y, &ev.xbutton.x_root, &ev.xbutton.y_root, &dummy); child = window_getting_button_event (w, ev.xbutton.x, ev.xbutton.y); XTranslateCoordinates (dpy, w, child, ev.xbutton.x, ev.xbutton.y, &ev.xbutton.x, &ev.xbutton.y, &dummy); ev.xbutton.same_screen = True; ev.xbutton.window = child; ev.xany.type = ButtonPress; XSendEvent (dpy, child, propagate != Qnil, ButtonPressMask, &ev); ev.xany.type = ButtonRelease; ev.xbutton.state |= mask; XSendEvent (dpy, child, propagate != Qnil, ButtonReleaseMask, &ev); break; default: return rep_signal_arg_error (event, 1); } return Qt; } /* Find the lisp modifier mask used by the meta and alt keys. This code shamelessly stolen from Emacs 19. :-) */ DEFSTRING(meta_l, "Meta_L"); DEFSTRING(meta_r, "Meta_R"); DEFSTRING(alt_l, "Alt_L"); DEFSTRING(alt_r, "Alt_R"); DEFSTRING(hyper_l, "Hyper_L"); DEFSTRING(hyper_r, "Hyper_R"); DEFSTRING(super_l, "Super_L"); DEFSTRING(super_r, "Super_R"); static void nconc (repv x, repv y) { repv *ptr = &x; while (rep_CONSP (*ptr)) ptr = rep_CDRLOC (*ptr); *ptr = y; } static void find_meta(void) { int min_code, max_code; KeySym *syms; int syms_per_code; XModifierKeymap *mods; repv meta_syms = Qnil, alt_syms = Qnil; repv hyper_syms = Qnil, super_syms = Qnil; #if defined (XlibSpecificationRelease) && XlibSpecificationRelease >= 4 XDisplayKeycodes(dpy, &min_code, &max_code); #else min_code = dpy->min_keycode; max_code = dpy->max_keycode; #endif Fset (Qmeta_keysyms, Qnil); Fset (Qalt_keysyms, Qnil); Fset (Qhyper_keysyms, Qnil); Fset (Qsuper_keysyms, Qnil); syms = XGetKeyboardMapping(dpy, min_code, max_code - min_code + 1, &syms_per_code); mods = XGetModifierMapping(dpy); { int row, col; for(row = 3; row < 8; row++) { for(col = 0; col < mods->max_keypermod; col++) { KeyCode code = mods->modifiermap[(row * mods->max_keypermod) + col]; int code_col; if(code == 0) continue; for(code_col = 0; code_col < syms_per_code; code_col++) { int sym = syms[((code - min_code) * syms_per_code) + code_col]; switch(sym) { case XK_Meta_L: case XK_Meta_R: meta_mod = 1 << row; meta_syms = Fcons (sym == XK_Meta_L ? rep_VAL(&meta_l) : rep_VAL(&meta_r), meta_syms); break; case XK_Alt_L: case XK_Alt_R: alt_mod = 1 << row; alt_syms = Fcons (sym == XK_Alt_L ? rep_VAL(&alt_l) : rep_VAL(&alt_r), alt_syms); break; case XK_Hyper_L: case XK_Hyper_R: hyper_mod = 1 << row; hyper_syms = Fcons (sym == XK_Hyper_L ? rep_VAL(&hyper_l) : rep_VAL(&hyper_r), hyper_syms); break; case XK_Super_L: case XK_Super_R: super_mod = 1 << row; super_syms = Fcons (sym == XK_Super_L ? rep_VAL(&super_l) : rep_VAL(&super_r), super_syms); break; case XK_Num_Lock: num_lock_mod = 1 << row; break; case XK_Scroll_Lock: scroll_lock_mod = 1 << row; break; } } } } } XFree((char *)syms); XFreeModifiermap(mods); if (meta_mod == 0 && alt_mod == 0 && hyper_mod == 0 && super_mod == 0) return; if (meta_mod == 0) meta_mod = alt_mod; if (meta_mod == 0) meta_mod = hyper_mod; if (meta_mod == 0) meta_mod = super_mod; if (meta_mod == alt_mod) { nconc (meta_syms, alt_syms); alt_syms = meta_syms; } if (meta_mod == hyper_mod) { nconc (meta_syms, hyper_syms); hyper_syms = meta_syms; } if (meta_mod == super_mod) { nconc (meta_syms, super_syms); super_syms = meta_syms; } Fset (Qmeta_keysyms, meta_syms); Fset (Qalt_keysyms, alt_syms); Fset (Qhyper_keysyms, hyper_syms); Fset (Qsuper_keysyms, super_syms); } static void build_lock_mods (void) { int i; total_lock_combs = 2 * (num_lock_mod ? 2 : 1) * (scroll_lock_mod ? 2 : 1); for (i = 0; i < total_lock_combs; i++) { if (i & 1) all_lock_combs[i] |= LockMask; if (i & 2) all_lock_combs[i] |= num_lock_mod ? num_lock_mod : scroll_lock_mod; if (i & 4) all_lock_combs[i] |= scroll_lock_mod; } all_lock_mask = LockMask | num_lock_mod | scroll_lock_mod; } void update_keyboard_mapping (void) { find_meta (); build_lock_mods (); } static void set_wm_modifier (u_long mods) { wm_mod = indirect_modifiers (mods); } DEFUN ("set-wm-modifier", Fset_wm_modifier, Sset_wm_modifier, (repv mods), rep_Subr1) /* ::doc:sawfish.wm.events#set-wm-modifier:: set-wm-modifier MODIFIERS Set the value of the `Window Manager' modifier to MODIFIERS, an integer. ::end:: */ { rep_DECLARE1 (mods, rep_INTP); set_wm_modifier (rep_INT (mods)); return Qt; } DEFUN ("wm-modifier", Fwm_modifier, Swm_modifier, (void), rep_Subr0) /* ::doc:sawfish.wm.events#wm-modifier:: wm-modifier Returns the current value of the `Window Manager' modifier, an integer. ::end:: */ { return rep_MAKE_INT (wm_mod); } /* Key and button grabbing */ static void grab_event (Window grab_win, repv ev) { switch (rep_INT(EVENT_MODS(ev)) & EV_TYPE_MASK) { u_int code, state; int i; case EV_TYPE_KEY: if (translate_event_to_x_key (ev, &code, &state)) { if (state != AnyModifier) { for (i = 0; i < total_lock_combs; i++) { XGrabKey (dpy, code, state | all_lock_combs[i], grab_win, False, GrabModeSync, GrabModeSync); } } else { XGrabKey (dpy, code, state, grab_win, False, GrabModeSync, GrabModeSync); } } break; case EV_TYPE_MOUSE: if (translate_event_to_x_button (ev, &code, &state)) { if (state != AnyModifier) { for (i = 0; i < total_lock_combs; i++) { XGrabButton (dpy, code, state | all_lock_combs[i], grab_win, False, POINTER_GRAB_EVENTS, GrabModeSync, GrabModeSync, None, None); } } else if (code != 0) { XGrabButton (dpy, code, AnyModifier, grab_win, False, POINTER_GRAB_EVENTS, GrabModeSync, GrabModeSync, None, None); } else { /* sawmill treats mouse buttons as modifiers, not as codes, so for us AnyModifier includes all buttons.. */ for (i = 0; i < 7; i++) { XGrabButton (dpy, all_buttons[i], AnyModifier, grab_win, False, POINTER_GRAB_EVENTS, GrabModeSync, GrabModeSync, None, None); } } } } } static void ungrab_event (Window grab_win, repv ev) { switch (rep_INT(EVENT_MODS(ev)) & EV_TYPE_MASK) { u_int code, state; int i; case EV_TYPE_KEY: if (translate_event_to_x_key (ev, &code, &state)) { if (state != AnyModifier) { for (i = 0; i < total_lock_combs; i++) { XUngrabKey (dpy, code, state | all_lock_combs[i], grab_win); } } else XUngrabKey (dpy, code, state, grab_win); } break; case EV_TYPE_MOUSE: if (translate_event_to_x_button (ev, &code, &state)) { if (state != AnyModifier) { for (i = 0; i < total_lock_combs; i++) { XUngrabButton (dpy, code, state | all_lock_combs[i], grab_win); } } else if (code != 0) { XUngrabButton (dpy, code, AnyModifier, grab_win); } else { for (i = 0; i < 7; i++) XUngrabButton (dpy, all_buttons[i], AnyModifier, grab_win); } } } } static void grab_keymap_event (repv km, long code, long mods, bool grab) { Lisp_Window *w; repv ev = MAKE_EVENT(rep_MAKE_INT(code), rep_MAKE_INT(mods)); repv global = Fsymbol_value (Qglobal_keymap, Qt); if (rep_SYMBOLP(km)) km = Fsymbol_value (km, Qt); for (w = window_list; w != 0; w = w->next) { if (!WINDOW_IS_GONE_P (w)) { repv tem = Fwindow_get (rep_VAL(w), Qkeymap); if (rep_SYMBOLP(tem) && tem != Qnil) tem = Fsymbol_value (tem, Qt); if (km == global || tem == km) (grab ? grab_event : ungrab_event) (w->id, ev); } } } static void grab_all_keylist_events (repv map, bool grab) { repv tem = rep_CDR(map); while (rep_CONSP(tem) && !rep_INTERRUPTP) { repv key = KEY_EVENT(rep_CAR(tem)); grab_keymap_event (map, rep_INT(EVENT_CODE(key)), rep_INT(EVENT_MODS(key)), grab); tem = rep_CDR(tem); rep_TEST_INT; } } static void grab_keylist_events (Window grab_win, repv list, bool grab) { while (!rep_INTERRUPTP && rep_CONSP(list)) { (grab ? grab_event : ungrab_event) (grab_win, KEY_EVENT(rep_CAR(list))); list = rep_CDR(list); rep_TEST_INT; } } void grab_keymap_events (Window grab_win, repv keymap, bool grab) { /* If it's a symbol, dereference it. */ while(rep_SYMBOLP(keymap) && !rep_NILP(keymap) && !rep_INTERRUPTP) { repv tem = Fsymbol_value(keymap, Qt); if(tem == keymap) break; keymap = tem; rep_TEST_INT; } if (rep_CONSP(keymap)) grab_keylist_events (grab_win, rep_CDR(keymap), grab); } /* Grab all bound events in client window W. */ void grab_window_events (Lisp_Window *w, bool grab) { repv tem; tem = Fsymbol_value (Qglobal_keymap, Qt); if (tem != Qnil && !rep_VOIDP(tem) && !WINDOW_IS_GONE_P (w)) grab_keymap_events (w->id, tem, grab); tem = Fwindow_get (rep_VAL(w), Qkeymap); if (tem && tem != Qnil && !WINDOW_IS_GONE_P (w)) grab_keymap_events (w->id, tem, grab); } static void keymap_prop_change (Lisp_Window *w, repv prop, repv old, repv new) { if (prop == Qkeymap && !WINDOW_IS_GONE_P (w)) { /* A bit of a hack */ grab_keymap_events (w->id, old, FALSE); grab_keymap_events (w->id, new, TRUE); } } /* initialisation */ void keys_init(void) { repv tem; rep_INTERN_SPECIAL(global_keymap); rep_INTERN_SPECIAL(root_window_keymap); rep_INTERN_SPECIAL(override_keymap); rep_INTERN_SPECIAL(unbound_key_hook); rep_INTERN_SPECIAL(eval_key_release_events); rep_INTERN_SPECIAL(eval_modifier_events); Fset (Qeval_key_release_events, Qnil); Fset (Qeval_modifier_events, Qnil); rep_INTERN(keymap); tem = rep_push_structure ("sawfish.wm.events"); rep_ADD_SUBR(Smake_keymap); rep_ADD_SUBR(Sbind_keys); rep_ADD_SUBR(Sunbind_keys); rep_ADD_SUBR(Sgrab_keymap); rep_ADD_SUBR(Sungrab_keymap); rep_ADD_SUBR(Scurrent_event_string); rep_ADD_SUBR(Scurrent_event); rep_ADD_SUBR(Sproxy_current_event); rep_ADD_SUBR(Sallow_events); rep_ADD_SUBR(Slast_event); rep_ADD_SUBR(Sevent_name); rep_ADD_SUBR(Slookup_event); rep_ADD_SUBR(Slookup_event_binding); rep_ADD_SUBR(Ssearch_keymap); rep_ADD_SUBR(Skeymapp); rep_ADD_SUBR(Seventp); rep_ADD_SUBR(Sevent_match); rep_ADD_SUBR(Sforget_button_press); rep_ADD_SUBR(Sx_lookup_keysym); rep_ADD_SUBR(Sx_keysym_name); rep_ADD_SUBR(Ssynthesize_event); rep_ADD_SUBR(Sset_wm_modifier); rep_ADD_SUBR(Swm_modifier); rep_pop_structure (tem); rep_INTERN(async_pointer); rep_INTERN(async_keyboard); rep_INTERN(sync_pointer); rep_INTERN(sync_keyboard); rep_INTERN(replay_pointer); rep_INTERN(replay_keyboard); rep_INTERN(sync_both); rep_INTERN(async_both); rep_INTERN_SPECIAL(meta_keysyms); Fset (Qmeta_keysyms, Qnil); rep_INTERN_SPECIAL(alt_keysyms); Fset (Qalt_keysyms, Qnil); rep_INTERN_SPECIAL(hyper_keysyms); Fset (Qhyper_keysyms, Qnil); rep_INTERN_SPECIAL(super_keysyms); Fset (Qsuper_keysyms, Qnil); rep_INTERN_SPECIAL(multi_click_delay); Fset (Qmulti_click_delay, rep_MAKE_INT(DEFAULT_DOUBLE_CLICK_TIME)); rep_INTERN_SPECIAL(this_command); rep_INTERN_SPECIAL(last_command); rep_INTERN_SPECIAL(prefix_arg); rep_INTERN_SPECIAL(current_prefix_arg); Fset (Qthis_command, Qnil); Fset (Qlast_command, Qnil); Fset (Qprefix_arg, Qnil); Fset (Qcurrent_prefix_arg, Qnil); rep_INTERN(display_message); rep_INTERN(call_command); rep_mark_static(&next_keymap_path); register_property_monitor (Qkeymap, keymap_prop_change); if (!batch_mode_p ()) update_keyboard_mapping (); }