aboutsummaryrefslogtreecommitdiffstats
path: root/xevents.c
diff options
context:
space:
mode:
authorbernd2007-04-27 17:58:48 +0000
committerbernd2007-04-27 17:58:48 +0000
commit055eec93ac495df814cc5585fd55712a6cac06c4 (patch)
treea7f9e9b6a1231b28b552199ac2e27c45d64a9ba7 /xevents.c
downloadcwm-055eec93ac495df814cc5585fd55712a6cac06c4.tar.gz
Initial import of cwm-3.
tested by sturm@, ok matthieu@
Diffstat (limited to 'xevents.c')
-rw-r--r--xevents.c603
1 files changed, 603 insertions, 0 deletions
diff --git a/xevents.c b/xevents.c
new file mode 100644
index 0000000..3e9ac8c
--- /dev/null
+++ b/xevents.c
@@ -0,0 +1,603 @@
+/*
+ * calmwm - the calm window manager
+ *
+ * Copyright (c) 2004 Marius Aamodt Eriksen <marius@monkey.org>
+ * All rights reserved.
+ *
+ * $Id: xevents.c,v 1.1.1.1 2007/04/27 17:58:48 bernd Exp $
+ */
+
+/*
+ * NOTE:
+ * It is the responsibility of the caller to deal with memory
+ * management of the xevent's.
+ */
+
+#include "headers.h"
+#include "calmwm.h"
+
+void _sendxmsg(Window, Atom, long);
+
+/*
+ * 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(G_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;
+ struct screen_ctx *sc;
+ int wascurrent;
+
+ if ((cc = client_find(e->window)) != NULL) {
+ sc = CCTOSC(cc);
+ wascurrent = cc == client_current();
+ client_delete(cc, e->send_event, 0);
+
+#ifdef notyet
+ /* XXX disable the ptrwarp until we handle it
+ * better. */
+ if (!client_delete(cc, e->send_event, 0) && wascurrent)
+ ;/* client_ptrwarp(new_cc); */
+#endif
+ }
+
+ 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(G_dpy, sc->which))
+ cc->geom.x -= cc->bwidth;
+
+ if (cc->geom.y == 0 &&
+ cc->geom.height >= DisplayHeight(G_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(G_dpy, cc->pwin, e->value_mask, &wc);
+ xev_reconfig(cc);
+ }
+
+ wc.x = cc != NULL ? 0 : e->x;
+ wc.y = cc != NULL ? 0 : 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(G_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(G_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(G_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, &G_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;
+ if (conf_cmd_changed(G_conf.menu_path)) {
+ conf_cmd_clear(&G_conf);
+ conf_cmd_populate(&G_conf, G_conf.menu_path);
+ }
+ TAILQ_FOREACH(cmd, &G_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 = (struct menu *)grab_menu(e, &menuq);
+ 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 && !G_groupmode)
+ group_sticky_toggle_enter(cc);
+ else {
+ grab_drag(cc);
+ client_move(cc);
+ }
+ break;
+ case Button2:
+ /* XXXSIGH!!! */
+ if (G_groupmode)
+ group_click(cc);
+ else {
+ 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 && !G_groupmode)
+ 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(G_dpy, e->keycode, 0);
+ skeysym = XKeycodeToKeysym(G_dpy, e->keycode, 1);
+
+ TAILQ_FOREACH(kb, &G_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 && e->window == screen_current()->groupwin)
+ group_display_keypress(e->keycode);
+
+ if (kb == NULL)
+ goto out;
+
+ if ((kb->flags & (KBFLAG_NEEDCLIENT|KBFLAG_FINDCLIENT)) &&
+ (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 supression detection.
+ */
+void
+xev_handle_keyrelease(struct xevent *xev, XEvent *ee)
+{
+ XKeyEvent *e = &ee->xkey;
+ struct screen_ctx *sc = screen_fromroot(e->root);
+ int keysym;
+
+ keysym = XKeycodeToKeysym(G_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);
+ client_altrelease();
+
+ 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(G_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;
+
+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 screen_ctx *sc = screen_current();
+ struct client_ctx *cc;
+
+ if ((cc = client_find(e->window)) != NULL)
+ client_draw_border(cc);
+
+ if (sc->groupwin == e->window)
+ group_display_draw(sc);
+
+ 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;
+
+ for (;;) {
+#ifdef DIAGNOSTIC
+ if (TAILQ_EMPTY(&_xevq))
+ errx(1, "X event queue empty");
+#endif
+
+ XNextEvent(G_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