/* bindings.c: deal with event bindings, and running the event loop */ /* Copyright (C) 1999 by the Massachusetts Institute of Technology. * * Permission to use, copy, modify, and distribute this * software and its documentation for any purpose and without * fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting * documentation, and that the name of M.I.T. not be used in * advertising or publicity pertaining to distribution of the * software without specific, written prior permission. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" * without express or implied warranty. */ #include #include #include #include "nawm.h" #include "lang.h" #include "parser.h" bindlist *current_bindings; bindlist *anymode; extern Window root; int quit; extern Display *dpy; extern variable CURRENTWINDOW, PX, PY; extern long options; long eventmask = SubstructureNotifyMask | EnterWindowMask | LeaveWindowMask; event_handler *eventhandlers = NULL; bindlist *findmode(char *name); int interactive(bindlist *mode); typedef struct _mode { char *name; bindlist *bindings; struct _mode *next; } mode; mode *modes; void initbindings(void) { anymode = NULL; current_bindings = NULL; } bindlist *mkbinding(int type, char *data, node *cmds) { bindlist *ans = xmalloc(sizeof(bindlist)); char *p = data; ans->cmds = cmds; switch(type) { case KEYPRESS: case KEYRELEASE: ans->type = (type == KEYPRESS) ? KeyPress : KeyRelease; ans->u.key.mods = parse_mods(&p); ans->u.key.kc = parse_key(p); free(data); break; case BUTTONPRESS: case BUTTONRELEASE: ans->type = (type == BUTTONPRESS) ? ButtonPress : ButtonRelease; ans->u.button.mods = parse_mods(&p); ans->u.button.button = parse_button(p); free(data); break; case ENTER: case LEAVE: ans->type = (type == ENTER) ? EnterNotify : LeaveNotify; ans->u.name = data; break; case MOTION: ans->type = MotionNotify; break; default: ans->type = type; break; } return ans; } int parse_mods(char **str) { int mods = 0; do { if (!strncasecmp(*str, "shift", 5) && isspace(*(*str + 5))) { mods |= ShiftMask; *str += 5; } else if (!strncasecmp(*str, "lock", 4) && isspace(*(*str + 4))) { mods |= LockMask; *str += 4; } else if (!strncasecmp(*str, "control", 7) && isspace(*(*str + 7))) { mods |= ControlMask; *str += 7; } else if ((!strncasecmp(*str, "mod1", 4) || !strncasecmp(*str, "meta", 4)) && isspace(*(*str + 4))) { mods |= Mod1Mask; *str += 4; } else if (!strncasecmp(*str, "mod2", 4) && isspace(*(*str + 4))) { mods |= Mod2Mask; *str += 4; } else if (!strncasecmp(*str, "mod3", 4) && isspace(*(*str + 4))) { mods |= Mod3Mask; *str += 4; } else if (!strncasecmp(*str, "mod4", 4) && isspace(*(*str + 4))) { mods |= Mod4Mask; *str += 4; } else if (!strncasecmp(*str, "mod5", 4) && isspace(*(*str + 4))) { mods |= Mod5Mask; *str += 4; } else if (!strncasecmp(*str, "any", 3) && isspace(*(*str + 3))) { mods = AnyModifier; *str += 3; } else break; while (isspace(**str)) (*str)++; } while (**str); return mods; } KeyCode parse_key(char *name) { KeySym ks; KeyCode kc = 0; char *ndup, *p; ndup = xstrdup(name); for (p = strtok(ndup, "| "); p && !kc; p = strtok(NULL, "| ")) { ks = XStringToKeysym(p); if (ks == NoSymbol) continue; kc = XKeysymToKeycode(dpy, ks); } free(ndup); if (ks == NoSymbol) die("Bad keysym name: %s", name); else if (!kc) die("No keycode for keysym: %s", name); return kc; } int parse_button(char *name) { if (!strcasecmp(name, "left")) return 1; else if (!strcasecmp(name, "right")) return 3; else if (!strcasecmp(name, "middle")) return 2; else if (*name > '0' && *name < '6' && !*(name + 1)) return *name - '0'; die("Bad button name: %s", name); } void add_to_anymode(bindlist *b) { b->next = anymode; anymode = b; } void defmode(char *name, bindlist *bindings) { mode *tmp; tmp = xmalloc(sizeof(mode)); tmp->name = name; tmp->bindings = bindings; tmp->next = modes; modes = tmp; } bindlist *findmode(char *name) { mode *m; for (m = modes; m; m = m->next) { if (!strcmp(name, m->name)) return m->bindings; } die("No such mode: %s", name); } void set_mode(char *mode) { undo_bindings(current_bindings); current_bindings = findmode(mode); do_bindings(current_bindings); } void do_bindings(bindlist *mode) { for (; mode; mode = mode->next) { switch (mode->type) { case BEGIN_: eval_cmds(mode->cmds); break; case ButtonPress: case ButtonRelease: XGrabButton(dpy, mode->u.button.button, mode->u.button.mods, root, False, ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None); if (mode->u.button.mods != AnyModifier && options & NAWM_OPT_NOCAPSLOCK) { XGrabButton(dpy, mode->u.button.button, mode->u.button.mods ^ LockMask, root, False, ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None); } break; case KeyPress: case KeyRelease: XGrabKey(dpy, mode->u.key.kc, mode->u.key.mods, root, False, GrabModeAsync, GrabModeAsync); if (mode->u.key.mods != AnyModifier && options & NAWM_OPT_NOCAPSLOCK) { XGrabKey(dpy, mode->u.key.kc, mode->u.key.mods ^ LockMask, root, False, GrabModeAsync, GrabModeAsync); } break; case MotionNotify: XGrabPointer(dpy, root, False, ButtonPressMask | ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); break; } } } void undo_bindings(bindlist *mode) { for (; mode; mode = mode->next) { switch (mode->type) { case END: eval_cmds(mode->cmds); break; case ButtonPress: case ButtonRelease: XUngrabButton(dpy, mode->u.button.button, mode->u.button.mods, root); break; case KeyPress: case KeyRelease: XUngrabKey(dpy, mode->u.key.kc, mode->u.key.mods, root); break; case MotionNotify: XUngrabPointer(dpy, CurrentTime); break; } } } int interactive(bindlist *mode) { for (; mode; mode = mode->next) { if (mode->type != BEGIN_ && mode->type != END) return 1; } return 0; } void run(void) { XEvent ev; bindlist *binding; Window rr, cr; int rx, ry, cx, cy; unsigned int mr; event_handler *eh; quit = 0; do_bindings(anymode); XSelectInput(dpy, root, eventmask); while (!quit && (interactive(anymode) || interactive(current_bindings))) { XNextEvent(dpy, &ev); XQueryPointer(dpy, root, &rr, &cr, &rx, &ry, &cx, &cy, &mr); if (cr) assign_var(&CURRENTWINDOW, (nawmval)client_window(cr)); else assign_var(&CURRENTWINDOW, 0); switch (ev.type) { case MappingNotify: update_keymap(); break; case CreateNotify: case DestroyNotify: case ConfigureNotify: case UnmapNotify: case MapNotify: update_cache(&ev); break; case MotionNotify: for (binding = current_bindings; binding; binding = binding->next) { if (binding->type == ev.type) eval_cmds(binding->cmds); } for (binding = anymode; binding; binding = binding->next) { if (binding->type == ev.type) eval_cmds(binding->cmds); } break; case KeyPress: case KeyRelease: if (options & NAWM_OPT_NOCAPSLOCK) ev.xkey.state &= ~LockMask; for (binding = current_bindings; binding; binding = binding->next) { if (binding->type == ev.type && binding->u.key.kc == ev.xkey.keycode && (binding->u.key.mods == ev.xkey.state || binding->u.key.mods == AnyModifier)) eval_cmds(binding->cmds); } for (binding = anymode; binding; binding = binding->next) { if(binding->type == ev.type && binding->u.key.kc == ev.xkey.keycode && (binding->u.key.mods == ev.xkey.state || binding->u.key.mods == AnyModifier)) eval_cmds(binding->cmds); } break; case ButtonPress: case ButtonRelease: if (ev.type == ButtonRelease) ev.xkey.state &= ~(1 << (ev.xbutton.button + 7)); if (options & NAWM_OPT_NOCAPSLOCK) ev.xkey.state &= ~LockMask; for (binding = current_bindings; binding; binding = binding->next) { if (binding->type == ev.type && binding->u.button.button == ev.xbutton.button && (binding->u.button.mods == ev.xbutton.state || binding->u.button.mods == AnyModifier)) eval_cmds(binding->cmds); } for (binding = anymode; binding; binding = binding->next) { if (binding->type == ev.type && binding->u.button.button == ev.xbutton.button && (binding->u.button.mods == ev.xbutton.state || binding->u.button.mods == AnyModifier)) eval_cmds(binding->cmds); } break; case EnterNotify: for (binding = current_bindings; binding; binding = binding->next) { if (binding->type == ev.type && (!binding->u.name || hasname(ev.xcrossing.window, binding->u.name))) eval_cmds(binding->cmds); } for (binding = anymode; binding; binding = binding->next) { if (binding->type == ev.type && (!binding->u.name || hasname(ev.xcrossing.window, binding->u.name))) eval_cmds(binding->cmds); } break; case LeaveNotify: for (binding = current_bindings; binding; binding = binding->next) { if (binding->type == ev.type && (!binding->u.name || hasname(ev.xcrossing.window, binding->u.name))) { assign_var(&CURRENTWINDOW, (nawmval)ev.xcrossing.window); eval_cmds(binding->cmds); } } for (binding = anymode; binding; binding = binding->next) { if (binding->type == ev.type && (!binding->u.name || hasname(ev.xcrossing.window, binding->u.name))) { assign_var(&CURRENTWINDOW, (nawmval)ev.xcrossing.window); eval_cmds(binding->cmds); } } break; } for (eh = eventhandlers; eh; eh = eh->next) eh->handler(&ev); } undo_bindings(anymode); }