summary refs log tree commit diff
path: root/xevents.c
diff options
context:
space:
mode:
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..784730a
--- /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$
+ */
+
+/*
+ * 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