/* * calmwm - the calm window manager * * Copyright (c) 2004 Marius Aamodt Eriksen * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $Id: xevents.c,v 1.18 2008/06/13 03:41:58 oga Exp $ */ /* * NOTE: * It is the responsibility of the caller to deal with memory * management of the xevent's. */ #include "headers.h" #include "calmwm.h" /* * NOTE: in reality, many of these should move to client.c now that * we've got this nice event layer. */ void xev_handle_maprequest(struct xevent *xev, XEvent *ee) { XMapRequestEvent *e = &ee->xmaprequest; struct client_ctx *cc = NULL, *old_cc = client_current(); XWindowAttributes xattr; struct screen_ctx *sc; #ifdef notyet int state; #endif if (old_cc != NULL) client_ptrsave(old_cc); if ((cc = client_find(e->window)) == NULL) { XGetWindowAttributes(X_Dpy, e->window, &xattr); cc = client_new(e->window, screen_fromroot(xattr.root), 1); sc = CCTOSC(cc); } else cc->beepbeep = 1; #ifdef notyet /* XXX - possibly, we shouldn't map if * the window is withdrawn. */ if (xu_getstate(cc, &state) == 0 && state == WithdrawnState) warnx("WITHDRAWNSTATE for %s", cc->name); #endif client_ptrwarp(cc); xev_register(xev); } void xev_handle_unmapnotify(struct xevent *xev, XEvent *ee) { XUnmapEvent *e = &ee->xunmap; struct client_ctx *cc; if ((cc = client_find(e->window)) != NULL) xu_setstate(cc, WithdrawnState); xev_register(xev); } void xev_handle_destroynotify(struct xevent *xev, XEvent *ee) { XDestroyWindowEvent *e = &ee->xdestroywindow; struct client_ctx *cc; if ((cc = client_find(e->window)) != NULL) client_delete(cc, 1, 1); xev_register(xev); } void xev_handle_configurerequest(struct xevent *xev, XEvent *ee) { XConfigureRequestEvent *e = &ee->xconfigurerequest; struct client_ctx *cc; struct screen_ctx *sc; XWindowChanges wc; if ((cc = client_find(e->window)) != NULL) { sc = CCTOSC(cc); client_gravitate(cc, 0); if (e->value_mask & CWWidth) cc->geom.width = e->width; if (e->value_mask & CWHeight) cc->geom.height = e->height; if (e->value_mask & CWX) cc->geom.x = e->x; if (e->value_mask & CWY) cc->geom.y = e->y; if (cc->geom.x == 0 && cc->geom.width >= DisplayWidth(X_Dpy, sc->which)) cc->geom.x -= cc->bwidth; if (cc->geom.y == 0 && cc->geom.height >= DisplayHeight(X_Dpy, sc->which)) cc->geom.y -= cc->bwidth; client_gravitate(cc, 1); wc.x = cc->geom.x - cc->bwidth; wc.y = cc->geom.y - cc->bwidth; wc.width = cc->geom.width + cc->bwidth*2; wc.height = cc->geom.height + cc->bwidth*2; wc.border_width = 0; /* We need to move the parent window, too. */ XConfigureWindow(X_Dpy, cc->pwin, e->value_mask, &wc); xev_reconfig(cc); } wc.x = cc != NULL ? cc->bwidth : e->x; wc.y = cc != NULL ? cc->bwidth : e->y; wc.width = e->width; wc.height = e->height; wc.stack_mode = Above; wc.border_width = 0; e->value_mask &= ~CWStackMode; e->value_mask |= CWBorderWidth; XConfigureWindow(X_Dpy, e->window, e->value_mask, &wc); xev_register(xev); } void xev_handle_propertynotify(struct xevent *xev, XEvent *ee) { XPropertyEvent *e = &ee->xproperty; struct client_ctx *cc; long tmp; if ((cc = client_find(e->window)) != NULL) { switch (e->atom) { case XA_WM_NORMAL_HINTS: XGetWMNormalHints(X_Dpy, cc->win, cc->size, &tmp); break; case XA_WM_NAME: client_setname(cc); break; default: /* do nothing */ break; } } xev_register(xev); } void xev_reconfig(struct client_ctx *cc) { XConfigureEvent ce; ce.type = ConfigureNotify; ce.event = cc->win; ce.window = cc->win; ce.x = cc->geom.x; ce.y = cc->geom.y; ce.width = cc->geom.width; ce.height = cc->geom.height; ce.border_width = 0; ce.above = None; ce.override_redirect = 0; XSendEvent(X_Dpy, cc->win, False, StructureNotifyMask, (XEvent *)&ce); } void xev_handle_enternotify(struct xevent *xev, XEvent *ee) { XCrossingEvent *e = &ee->xcrossing; struct client_ctx *cc; if ((cc = client_find(e->window)) == NULL) { /* * XXX - later. messes up unclutter. but may be * needed when we introduce menu windows and such into * the main event loop. */ #ifdef notyet if (e->window != e->root) client_nocurrent(); #endif } else client_setactive(cc, 1); xev_register(xev); } void xev_handle_leavenotify(struct xevent *xev, XEvent *ee) { client_leave(NULL); xev_register(xev); } /* We can split this into two event handlers. */ void xev_handle_buttonpress(struct xevent *xev, XEvent *ee) { XButtonEvent *e = &ee->xbutton; struct client_ctx *cc, *old_cc = client_current(); struct screen_ctx *sc = screen_fromroot(e->root); char *wname; int altcontrol = e->state == (ControlMask|Mod1Mask); cc = client_find(e->window); if (sc->rootwin == e->window && !altcontrol) { struct menu_q menuq; struct menu *mi; /* XXXSIGH!!!! */ if (e->button == Button2) { group_menu(e); goto out; } TAILQ_INIT(&menuq); switch (e->button) { case Button1: TAILQ_FOREACH(cc, &Clientq, entry) { if (cc->flags & CLIENT_HIDDEN) { if (cc->label != NULL) wname = cc->label; else wname = cc->name; if (wname == NULL) continue; XCALLOC(mi, struct menu); strlcpy(mi->text, wname, sizeof(mi->text)); mi->ctx = cc; TAILQ_INSERT_TAIL(&menuq, mi, entry); } } break; case Button3: { struct cmd *cmd; conf_reload(&Conf); TAILQ_FOREACH(cmd, &Conf.cmdq, entry) { XCALLOC(mi, struct menu); strlcpy(mi->text, cmd->label, sizeof(mi->text)); mi->ctx = cmd; TAILQ_INSERT_TAIL(&menuq, mi, entry); } break; } default: break; } if (TAILQ_EMPTY(&menuq)) goto out; mi = menu_filter(&menuq, NULL, NULL, 0, NULL, NULL); if (mi == NULL) goto cleanup; switch (e->button) { case Button1: cc = (struct client_ctx *)mi->ctx; client_unhide(cc); if (old_cc != NULL) client_ptrsave(old_cc); client_ptrwarp(cc); break; case Button3: u_spawn(((struct cmd *)mi->ctx)->image); break; default: break; } cleanup: while ((mi = TAILQ_FIRST(&menuq)) != NULL) { TAILQ_REMOVE(&menuq, mi, entry); xfree(mi); } goto out; } if (cc == NULL || e->state == 0) goto out; sc = CCTOSC(cc); switch (e->button) { case Button1: if (altcontrol) group_sticky_toggle_enter(cc); else { grab_drag(cc); client_move(cc); } break; case Button2: grab_sweep(cc); client_resize(cc); break; case Button3: client_ptrsave(cc); client_lower(cc); break; } out: xev_register(xev); } void xev_handle_buttonrelease(struct xevent *xev, XEvent *ee) { struct client_ctx *cc = client_current(); if (cc != NULL) group_sticky_toggle_exit(cc); xev_register(xev); } void xev_handle_keypress(struct xevent *xev, XEvent *ee) { XKeyEvent *e = &ee->xkey; struct client_ctx *cc = NULL; /* Make gcc happy. */ struct keybinding *kb; KeySym keysym, skeysym; int modshift; keysym = XKeycodeToKeysym(X_Dpy, e->keycode, 0); skeysym = XKeycodeToKeysym(X_Dpy, e->keycode, 1); TAILQ_FOREACH(kb, &Conf.keybindingq, entry) { if (keysym != kb->keysym && skeysym == kb->keysym) modshift = ShiftMask; else modshift = 0; if ((kb->modmask | modshift) != e->state) continue; if ((kb->keycode != 0 && kb->keysym == NoSymbol && kb->keycode == e->keycode) || kb->keysym == (modshift == 0 ? keysym : skeysym)) break; } if (kb == NULL) goto out; if ((kb->flags & (KBFLAG_NEEDCLIENT)) && (cc = client_find(e->window)) == NULL && (cc = client_current()) == NULL) if (kb->flags & KBFLAG_NEEDCLIENT) goto out; (*kb->callback)(cc, kb->argument); out: xev_register(xev); } /* * This is only used for the alt suppression detection. */ void xev_handle_keyrelease(struct xevent *xev, XEvent *ee) { XKeyEvent *e = &ee->xkey; struct screen_ctx *sc = screen_fromroot(e->root); struct client_ctx *cc = client_current(); int keysym; keysym = XKeycodeToKeysym(X_Dpy, e->keycode, 0); if (keysym != XK_Alt_L && keysym != XK_Alt_R) goto out; sc->altpersist = 0; /* * XXX - better interface... xevents should not know about * how/when to mtf. */ client_mtf(NULL); if (cc != NULL) { group_sticky_toggle_exit(cc); XUngrabKeyboard(X_Dpy, CurrentTime); } out: xev_register(xev); } void xev_handle_clientmessage(struct xevent *xev, XEvent *ee) { XClientMessageEvent *e = &ee->xclient; struct client_ctx *cc = client_find(e->window); Atom xa_wm_change_state = XInternAtom(X_Dpy, "WM_CHANGE_STATE", False); if (cc == NULL) goto out; if (e->message_type == xa_wm_change_state && e->format == 32 && e->data.l[0] == IconicState) client_hide(cc); out: xev_register(xev); } /* * X Event handling */ static struct xevent_q _xevq, _xevq_putaway; static short _xev_q_lock = 0; volatile sig_atomic_t _xev_quit = 0; void xev_init(void) { TAILQ_INIT(&_xevq); TAILQ_INIT(&_xevq_putaway); } struct xevent * xev_new(Window *win, Window *root, int type, void (*cb)(struct xevent *, XEvent *), void *arg) { struct xevent *xev; XMALLOC(xev, struct xevent); xev->xev_win = win; xev->xev_root = root; xev->xev_type = type; xev->xev_cb = cb; xev->xev_arg = arg; return (xev); } void xev_register(struct xevent *xev) { struct xevent_q *xq; xq = _xev_q_lock ? &_xevq_putaway : &_xevq; TAILQ_INSERT_TAIL(xq, xev, entry); } void _xev_reincorporate(void) { struct xevent *xev; while ((xev = TAILQ_FIRST(&_xevq_putaway)) != NULL) { TAILQ_REMOVE(&_xevq_putaway, xev, entry); TAILQ_INSERT_TAIL(&_xevq, xev, entry); } } void xev_handle_expose(struct xevent *xev, XEvent *ee) { XExposeEvent *e = &ee->xexpose; struct client_ctx *cc; if ((cc = client_find(e->window)) != NULL && e->count == 0) client_draw_border(cc); xev_register(xev); } #define ASSIGN(xtype) do { \ root = e. xtype .root; \ win = e. xtype .window; \ } while (0) #define ASSIGN1(xtype) do { \ win = e. xtype .window; \ } while (0) void xev_loop(void) { Window win, root; int type; XEvent e; struct xevent *xev, *nextxev; while (_xev_quit == 0) { #ifdef DIAGNOSTIC if (TAILQ_EMPTY(&_xevq)) errx(1, "X event queue empty"); #endif XNextEvent(X_Dpy, &e); type = e.type; win = root = 0; switch (type) { case MapRequest: ASSIGN1(xmaprequest); break; case UnmapNotify: ASSIGN1(xunmap); break; case ConfigureRequest: ASSIGN1(xconfigurerequest); break; case PropertyNotify: ASSIGN1(xproperty); break; case EnterNotify: case LeaveNotify: ASSIGN(xcrossing); break; case ButtonPress: ASSIGN(xbutton); break; case ButtonRelease: ASSIGN(xbutton); break; case KeyPress: case KeyRelease: ASSIGN(xkey); break; case DestroyNotify: ASSIGN1(xdestroywindow); break; case ClientMessage: ASSIGN1(xclient); break; default: /* XXX - still need shape event support. */ break; } /* * Now, search for matches, and call each of them. */ _xev_q_lock = 1; for (xev = TAILQ_FIRST(&_xevq); xev != NULL; xev = nextxev) { nextxev = TAILQ_NEXT(xev, entry); if ((type != xev->xev_type && xev->xev_type != 0) || (xev->xev_win != NULL && win != *xev->xev_win) || (xev->xev_root != NULL && root != *xev->xev_root)) continue; TAILQ_REMOVE(&_xevq, xev, entry); (*xev->xev_cb)(xev, &e); } _xev_q_lock = 0; _xev_reincorporate(); } } #undef ASSIGN #undef ASSIGN1