/* 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 <john@dcs.warwick.ac.uk>
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 <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <X11/keysym.h>
#include <X11/Xutil.h>
/* 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 ();
}
syntax highlighted by Code2HTML, v. 0.9.1