about summary refs log tree commit diff
diff options
context:
space:
mode:
authorokan <okan>2012-07-18 21:53:22 +0000
committerokan <okan>2012-07-18 21:53:22 +0000
commit1545eb0837814fa024c0de85e64da262f6d1e209 (patch)
tree1ffbb683959e0b0f16de86d8b414f20e0de464e0
parent8f587e6c8052543125ad4b03cc89560d05a4aca4 (diff)
parent013497c79cdb17d8a40f53d5535b4ba8362f0ef5 (diff)
downloadcwm-1545eb0837814fa024c0de85e64da262f6d1e209.tar.gz
cwm-1545eb0837814fa024c0de85e64da262f6d1e209.tar.xz
cwm-1545eb0837814fa024c0de85e64da262f6d1e209.zip
cvsimport
-rw-r--r--calmwm.c16
-rw-r--r--calmwm.h95
-rw-r--r--client.c218
-rw-r--r--cwmrc.56
-rw-r--r--group.c58
-rw-r--r--kbfunc.c56
-rw-r--r--menu.c4
-rw-r--r--mousefunc.c38
-rw-r--r--screen.c60
-rw-r--r--xevents.c17
-rw-r--r--xutil.c210
11 files changed, 429 insertions, 349 deletions
diff --git a/calmwm.c b/calmwm.c
index 2a52f6a..911c280 100644
--- a/calmwm.c
+++ b/calmwm.c
@@ -44,7 +44,7 @@ Cursor				 Cursor_resize;
 struct screen_ctx_q		 Screenq = TAILQ_HEAD_INITIALIZER(Screenq);
 struct client_ctx_q		 Clientq = TAILQ_HEAD_INITIALIZER(Clientq);
 
-int				 HasXinerama, HasRandr, Randr_ev;
+int				 HasRandr, Randr_ev;
 struct conf			 Conf;
 
 static void	sigchld_cb(int);
@@ -157,7 +157,6 @@ x_setupscreen(struct screen_ctx *sc, u_int which)
 	Window			*wins, w0, w1;
 	XWindowAttributes	 winattr;
 	XSetWindowAttributes	 rootattr;
-	int			 fake;
 	u_int			 nwins, i;
 
 	sc->which = which;
@@ -167,8 +166,8 @@ x_setupscreen(struct screen_ctx *sc, u_int which)
 	xu_ewmh_net_supported_wm_check(sc);
 
 	conf_gap(&Conf, sc);
-	screen_update_geometry(sc, DisplayWidth(X_Dpy, sc->which),
-	    DisplayHeight(X_Dpy, sc->which));
+
+	screen_update_geometry(sc);
 
 	conf_color(&Conf, sc);
 
@@ -201,17 +200,8 @@ x_setupscreen(struct screen_ctx *sc, u_int which)
 
 	screen_updatestackingorder(sc);
 
-	if (XineramaQueryExtension(X_Dpy, &fake, &fake) == 1 &&
-	    ((HasXinerama = XineramaIsActive(X_Dpy)) == 1))
-		HasXinerama = 1;
 	if (HasRandr)
 		XRRSelectInput(X_Dpy, sc->rootwin, RRScreenChangeNotifyMask);
-	/*
-	 * initial setup of xinerama screens, if we're using RandR then we'll
-	 * redo this whenever the screen changes since a CTRC may have been
-	 * added or removed
-	 */
-	screen_init_xinerama(sc);
 
 	XSync(X_Dpy, False);
 }
diff --git a/calmwm.h b/calmwm.h
index bf08c93..8dffc68 100644
--- a/calmwm.h
+++ b/calmwm.h
@@ -108,6 +108,13 @@ struct color {
 	unsigned long	 pixel;
 };
 
+struct geom {
+	int		 x;
+	int		 y;
+	int		 w;
+	int		 h;
+};
+
 struct gap {
 	int		 top;
 	int		 bottom;
@@ -130,12 +137,7 @@ struct client_ctx {
 	XSizeHints		*size;
 	Colormap		 cmap;
 	u_int			 bwidth; /* border width */
-	struct {
-		int		 x;	/* x position */
-		int		 y;	/* y position */
-		int		 width;	/* width */
-		int		 height;/* height */
-	} geom, savegeom;
+	struct geom		 geom, savegeom;
 	struct {
 		int		 basew;	/* desired width */
 		int		 baseh;	/* desired height */
@@ -216,8 +218,8 @@ struct screen_ctx {
 	struct color		 color[CWM_COLOR_MAX];
 	GC			 gc;
 	int			 cycling;
-	int			 xmax;
-	int			 ymax;
+	struct geom		 view; /* viewable area */
+	struct geom		 work; /* workable area, gap-applied */
 	struct gap		 gap;
 	struct cycle_entry_q	 mruq;
 	XftColor		 xftcolor;
@@ -372,8 +374,7 @@ void			 search_print_client(struct menu *, int);
 
 XineramaScreenInfo	*screen_find_xinerama(struct screen_ctx *, int, int);
 struct screen_ctx	*screen_fromroot(Window);
-void			 screen_init_xinerama(struct screen_ctx *);
-void			 screen_update_geometry(struct screen_ctx *, int, int);
+void			 screen_update_geometry(struct screen_ctx *);
 void			 screen_updatestackingorder(struct screen_ctx *);
 
 void			 kbfunc_client_cycle(struct client_ctx *, union arg *);
@@ -478,6 +479,19 @@ void			 xu_setstate(struct client_ctx *, int);
 
 void			 xu_ewmh_net_supported(struct screen_ctx *);
 void			 xu_ewmh_net_supported_wm_check(struct screen_ctx *);
+void			 xu_ewmh_net_desktop_geometry(struct screen_ctx *);
+void			 xu_ewmh_net_workarea(struct screen_ctx *);
+void			 xu_ewmh_net_client_list(struct screen_ctx *);
+void			 xu_ewmh_net_active_window(struct screen_ctx *, Window);
+void			 xu_ewmh_net_wm_desktop_viewport(struct screen_ctx *);
+void			 xu_ewmh_net_wm_number_of_desktops(struct screen_ctx *);
+void			 xu_ewmh_net_showing_desktop(struct screen_ctx *);
+void			 xu_ewmh_net_virtual_roots(struct screen_ctx *);
+void			 xu_ewmh_net_current_desktop(struct screen_ctx *, long);
+void			 xu_ewmh_net_desktop_names(struct screen_ctx *, unsigned char *, int);
+
+void			 xu_ewmh_net_wm_desktop(struct client_ctx *);
+
 
 void			 u_exec(char *);
 void			 u_spawn(char *);
@@ -502,34 +516,37 @@ extern struct conf			 Conf;
 
 extern int				 HasXinerama, HasRandr, Randr_ev;
 
-#define	WM_STATE			 cwm_atoms[0]
-#define WM_DELETE_WINDOW		 cwm_atoms[1]
-#define WM_TAKE_FOCUS			 cwm_atoms[2]
-#define WM_PROTOCOLS			 cwm_atoms[3]
-#define _MOTIF_WM_HINTS			 cwm_atoms[4]
-#define	UTF8_STRING			 cwm_atoms[5]
-/*
- * please make all hints below this point netwm hints, starting with
- * _NET_SUPPORTED. If you change other hints make sure you update
- * CWM_NETWM_START
- */
-#define	_NET_SUPPORTED			 cwm_atoms[6]
-#define	_NET_SUPPORTING_WM_CHECK	 cwm_atoms[7]
-#define	_NET_WM_NAME			 cwm_atoms[8]
-#define	_NET_ACTIVE_WINDOW		 cwm_atoms[9]
-#define	_NET_CLIENT_LIST		 cwm_atoms[10]
-#define	_NET_NUMBER_OF_DESKTOPS		 cwm_atoms[11]
-#define	_NET_CURRENT_DESKTOP		 cwm_atoms[12]
-#define	_NET_DESKTOP_VIEWPORT		 cwm_atoms[13]
-#define	_NET_DESKTOP_GEOMETRY		 cwm_atoms[14]
-#define	_NET_VIRTUAL_ROOTS		 cwm_atoms[15]
-#define	_NET_SHOWING_DESKTOP		 cwm_atoms[16]
-#define	_NET_DESKTOP_NAMES		 cwm_atoms[17]
-#define	_NET_WM_DESKTOP			 cwm_atoms[18]
-#define	_NET_WORKAREA			 cwm_atoms[19]
-#define CWM_NO_ATOMS			 20
-#define CWM_NETWM_START			 6
-
-extern Atom				 cwm_atoms[CWM_NO_ATOMS];
+enum {
+	WM_STATE,
+	WM_DELETE_WINDOW,
+	WM_TAKE_FOCUS,
+	WM_PROTOCOLS,
+	_MOTIF_WM_HINTS,
+	UTF8_STRING,
+	CWMH_NITEMS
+};
+enum {
+	_NET_SUPPORTED,
+	_NET_SUPPORTING_WM_CHECK,
+	_NET_ACTIVE_WINDOW,
+	_NET_CLIENT_LIST,
+	_NET_NUMBER_OF_DESKTOPS,
+	_NET_CURRENT_DESKTOP,
+	_NET_DESKTOP_VIEWPORT,
+	_NET_DESKTOP_GEOMETRY,
+	_NET_VIRTUAL_ROOTS,
+	_NET_SHOWING_DESKTOP,
+	_NET_DESKTOP_NAMES,
+	_NET_WORKAREA,
+	_NET_WM_NAME,
+	_NET_WM_DESKTOP,
+	EWMH_NITEMS
+};
+struct atom_ctx {
+	char	*name;
+	Atom	 atom;
+};
+extern struct atom_ctx			 cwmh[CWMH_NITEMS];
+extern struct atom_ctx			 ewmh[EWMH_NITEMS];
 
 #endif /* _CALMWM_H_ */
diff --git a/client.c b/client.c
index 5fe7aaf..15052eb 100644
--- a/client.c
+++ b/client.c
@@ -89,8 +89,8 @@ client_new(Window win, struct screen_ctx *sc, int mapped)
 	XGetWindowAttributes(X_Dpy, cc->win, &wattr);
 	cc->geom.x = wattr.x;
 	cc->geom.y = wattr.y;
-	cc->geom.width = wattr.width;
-	cc->geom.height = wattr.height;
+	cc->geom.w = wattr.width;
+	cc->geom.h = wattr.height;
 	cc->cmap = wattr.colormap;
 
 	if (wattr.map_state != IsViewable) {
@@ -126,9 +126,8 @@ client_new(Window win, struct screen_ctx *sc, int mapped)
 
 	TAILQ_INSERT_TAIL(&sc->mruq, cc, mru_entry);
 	TAILQ_INSERT_TAIL(&Clientq, cc, entry);
-	/* append to the client list */
-	XChangeProperty(X_Dpy, sc->rootwin, _NET_CLIENT_LIST, XA_WINDOW, 32,
-	    PropModeAppend,  (unsigned char *)&cc->win, 1);
+
+	xu_ewmh_net_client_list(sc);
 
 	client_gethints(cc);
 	client_update(cc);
@@ -143,10 +142,7 @@ void
 client_delete(struct client_ctx *cc)
 {
 	struct screen_ctx	*sc = cc->sc;
-	struct client_ctx	*tcc;
 	struct winname		*wn;
-	Window			*winlist;
-	int			 i, j;
 
 	group_client_delete(cc);
 
@@ -159,23 +155,8 @@ client_delete(struct client_ctx *cc)
 
 	TAILQ_REMOVE(&sc->mruq, cc, mru_entry);
 	TAILQ_REMOVE(&Clientq, cc, entry);
-	/*
-	 * Sadly we can't remove just one entry from a property, so we must
-	 * redo the whole thing from scratch. this is the stupid way, the other
-	 * way incurs many roundtrips to the server.
-	 */
-	i = j = 0;
-	TAILQ_FOREACH(tcc, &Clientq, entry)
-		i++;
-	if (i > 0) {
-		winlist = xmalloc(i * sizeof(*winlist));
-		TAILQ_FOREACH(tcc, &Clientq, entry)
-			winlist[j++] = tcc->win;
-		XChangeProperty(X_Dpy, sc->rootwin, _NET_CLIENT_LIST,
-		    XA_WINDOW, 32, PropModeReplace,
-		    (unsigned char *)winlist, i);
-		xfree(winlist);
-	}
+
+	xu_ewmh_net_client_list(sc);
 
 	if (_curcc == cc)
 		client_none(sc);
@@ -236,9 +217,7 @@ client_setactive(struct client_ctx *cc, int fg)
 	if (fg && _curcc != cc) {
 		client_setactive(NULL, 0);
 		_curcc = cc;
-		XChangeProperty(X_Dpy, sc->rootwin, _NET_ACTIVE_WINDOW,
-		    XA_WINDOW, 32, PropModeReplace,
-		    (unsigned char *)&cc->win, 1);
+		xu_ewmh_net_active_window(sc, cc->win);
 	}
 
 	cc->active = fg;
@@ -253,8 +232,8 @@ client_none(struct screen_ctx *sc)
 {
 	Window none = None;
 
-	XChangeProperty(X_Dpy, sc->rootwin, _NET_ACTIVE_WINDOW,
-	    XA_WINDOW, 32, PropModeReplace, (unsigned char *)&none, 1);
+	xu_ewmh_net_active_window(sc, none);
+
 	_curcc = NULL;
 }
 
@@ -277,8 +256,8 @@ void
 client_maximize(struct client_ctx *cc)
 {
 	struct screen_ctx	*sc = cc->sc;
-	int			 xmax = sc->xmax, ymax = sc->ymax;
-	int			 x_org = 0, y_org = 0;
+	int			 x_org, y_org, xmax, ymax;
+	XineramaScreenInfo	*xine;
 
 	if (cc->flags & CLIENT_FREEZE)
 		return;
@@ -291,37 +270,38 @@ client_maximize(struct client_ctx *cc)
 	}
 
 	if ((cc->flags & CLIENT_VMAXIMIZED) == 0) {
-		cc->savegeom.height = cc->geom.height;
+		cc->savegeom.h = cc->geom.h;
 		cc->savegeom.y = cc->geom.y;
 	}
 
 	if ((cc->flags & CLIENT_HMAXIMIZED) == 0) {
-		cc->savegeom.width = cc->geom.width;
+		cc->savegeom.w = cc->geom.w;
 		cc->savegeom.x = cc->geom.x;
 	}
 
-	if (HasXinerama) {
-		XineramaScreenInfo *xine;
-		/*
-		 * pick screen that the middle of the window is on.
-		 * that's probably more fair than if just the origin of
-		 * a window is poking over a boundary
-		 */
-		xine = screen_find_xinerama(sc,
-		    cc->geom.x + cc->geom.width / 2,
-		    cc->geom.y + cc->geom.height / 2);
-		if (xine == NULL)
-			goto calc;
+	/*
+	 * pick screen that the middle of the window is on.
+	 * that's probably more fair than if just the origin of
+	 * a window is poking over a boundary
+	 */
+	xine = screen_find_xinerama(sc,
+	    cc->geom.x + cc->geom.w / 2,
+	    cc->geom.y + cc->geom.h / 2);
+	if (xine) {
 		x_org = xine->x_org;
 		y_org = xine->y_org;
 		xmax = xine->width;
 		ymax = xine->height;
+	} else {
+		x_org = y_org = 0;
+		xmax = sc->view.w;
+		ymax = sc->view.h;
 	}
-calc:
+
 	cc->geom.x = x_org + sc->gap.left;
 	cc->geom.y = y_org + sc->gap.top;
-	cc->geom.height = ymax - (sc->gap.top + sc->gap.bottom);
-	cc->geom.width = xmax - (sc->gap.left + sc->gap.right);
+	cc->geom.h = ymax - (sc->gap.top + sc->gap.bottom);
+	cc->geom.w = xmax - (sc->gap.left + sc->gap.right);
 	cc->bwidth = 0;
 	cc->flags |= CLIENT_MAXIMIZED;
 
@@ -333,43 +313,44 @@ void
 client_vertmaximize(struct client_ctx *cc)
 {
 	struct screen_ctx	*sc = cc->sc;
-	int			 y_org = 0, ymax = sc->ymax;
+	int			 y_org, ymax;
+	XineramaScreenInfo	*xine;
 
 	if (cc->flags & CLIENT_FREEZE)
 		return;
 
 	if (cc->flags & CLIENT_VMAXIMIZED) {
 		cc->geom.y = cc->savegeom.y;
-		cc->geom.height = cc->savegeom.height;
+		cc->geom.h = cc->savegeom.h;
 		cc->bwidth = Conf.bwidth;
 		if (cc->flags & CLIENT_HMAXIMIZED)
-			cc->geom.width -= cc->bwidth * 2;
+			cc->geom.w -= cc->bwidth * 2;
 		cc->flags &= ~CLIENT_VMAXIMIZED;
 		goto resize;
 	}
 
 	cc->savegeom.y = cc->geom.y;
-	cc->savegeom.height = cc->geom.height;
+	cc->savegeom.h = cc->geom.h;
 
 	/* if this will make us fully maximized then remove boundary */
 	if ((cc->flags & CLIENT_MAXFLAGS) == CLIENT_HMAXIMIZED) {
-		cc->geom.width += Conf.bwidth * 2;
+		cc->geom.w += Conf.bwidth * 2;
 		cc->bwidth = 0;
 	}
 
-	if (HasXinerama) {
-		XineramaScreenInfo *xine;
-		xine = screen_find_xinerama(sc,
-		    cc->geom.x + cc->geom.width / 2,
-		    cc->geom.y + cc->geom.height / 2);
-		if (xine == NULL)
-			goto calc;
+	xine = screen_find_xinerama(sc,
+	    cc->geom.x + cc->geom.w / 2,
+	    cc->geom.y + cc->geom.h / 2);
+	if (xine) {
 		y_org = xine->y_org;
 		ymax = xine->height;
+	} else {
+		y_org = 0;
+		ymax = sc->view.h;
 	}
-calc:
+
 	cc->geom.y = y_org + sc->gap.top;
-	cc->geom.height = ymax - (cc->bwidth * 2) - (sc->gap.top +
+	cc->geom.h = ymax - (cc->bwidth * 2) - (sc->gap.top +
 	    sc->gap.bottom);
 	cc->flags |= CLIENT_VMAXIMIZED;
 
@@ -381,43 +362,44 @@ void
 client_horizmaximize(struct client_ctx *cc)
 {
 	struct screen_ctx	*sc = cc->sc;
-	int			 x_org = 0, xmax = sc->xmax;
+	int			 x_org, xmax;
+	XineramaScreenInfo	*xine;
 
 	if (cc->flags & CLIENT_FREEZE)
 		return;
 
 	if (cc->flags & CLIENT_HMAXIMIZED) {
 		cc->geom.x = cc->savegeom.x;
-		cc->geom.width = cc->savegeom.width;
+		cc->geom.w = cc->savegeom.w;
 		cc->bwidth = Conf.bwidth;
 		if (cc->flags & CLIENT_VMAXIMIZED)
-			cc->geom.height -= cc->bwidth * 2;
+			cc->geom.h -= cc->bwidth * 2;
 		cc->flags &= ~CLIENT_HMAXIMIZED;
 		goto resize;
 	}
 
 	cc->savegeom.x = cc->geom.x;
-	cc->savegeom.width = cc->geom.width;
+	cc->savegeom.w = cc->geom.w;
 
 	/* if this will make us fully maximized then remove boundary */
 	if ((cc->flags & CLIENT_MAXFLAGS) == CLIENT_VMAXIMIZED) {
-		cc->geom.height += cc->bwidth * 2;
+		cc->geom.h += cc->bwidth * 2;
 		cc->bwidth = 0;
 	}
 
-	if (HasXinerama) {
-		XineramaScreenInfo *xine;
-		xine = screen_find_xinerama(sc,
-		    cc->geom.x + cc->geom.width / 2,
-		    cc->geom.y + cc->geom.height / 2);
-		if (xine == NULL)
-			goto calc;
+	xine = screen_find_xinerama(sc,
+	    cc->geom.x + cc->geom.w / 2,
+	    cc->geom.y + cc->geom.h / 2);
+	if (xine) {
 		x_org = xine->x_org;
 		xmax = xine->width;
+	} else {
+		x_org = 0;
+		xmax = sc->view.w;
 	}
-calc:
+
 	cc->geom.x = x_org + sc->gap.left;
-	cc->geom.width = xmax - (cc->bwidth * 2) - (sc->gap.left +
+	cc->geom.w = xmax - (cc->bwidth * 2) - (sc->gap.left +
 	    sc->gap.right);
 	cc->flags |= CLIENT_HMAXIMIZED;
 
@@ -431,7 +413,7 @@ client_resize(struct client_ctx *cc)
 	client_draw_border(cc);
 
 	XMoveResizeWindow(X_Dpy, cc->win, cc->geom.x,
-	    cc->geom.y, cc->geom.width, cc->geom.height);
+	    cc->geom.y, cc->geom.w, cc->geom.h);
 	xu_configure(cc);
 }
 
@@ -460,8 +442,8 @@ client_ptrwarp(struct client_ctx *cc)
 	int	 x = cc->ptr.x, y = cc->ptr.y;
 
 	if (x == -1 || y == -1) {
-		x = cc->geom.width / 2;
-		y = cc->geom.height / 2;
+		x = cc->geom.w / 2;
+		y = cc->geom.h / 2;
 	}
 
 	(cc->state == IconicState) ? client_unhide(cc) : client_raise(cc);
@@ -540,14 +522,14 @@ client_update(struct client_ctx *cc)
 	int	 i;
 	long	 n;
 
-	if ((n = xu_getprop(cc->win, WM_PROTOCOLS,
+	if ((n = xu_getprop(cc->win, cwmh[WM_PROTOCOLS].atom,
 		 XA_ATOM, 20L, (u_char **)&p)) <= 0)
 		return;
 
 	for (i = 0; i < n; i++)
-		if (p[i] == WM_DELETE_WINDOW)
+		if (p[i] == cwmh[WM_DELETE_WINDOW].atom)
 			cc->xproto |= CLIENT_PROTO_DELETE;
-		else if (p[i] == WM_TAKE_FOCUS)
+		else if (p[i] == cwmh[WM_TAKE_FOCUS].atom)
 			cc->xproto |= CLIENT_PROTO_TAKEFOCUS;
 
 	XFree(p);
@@ -557,7 +539,8 @@ void
 client_send_delete(struct client_ctx *cc)
 {
 	if (cc->xproto & CLIENT_PROTO_DELETE)
-		xu_sendmsg(cc->win, WM_PROTOCOLS, WM_DELETE_WINDOW);
+		xu_sendmsg(cc->win,
+		    cwmh[WM_PROTOCOLS].atom, cwmh[WM_DELETE_WINDOW].atom);
 	else
 		XKillClient(X_Dpy, cc->win);
 }
@@ -568,7 +551,7 @@ client_setname(struct client_ctx *cc)
 	struct winname	*wn;
 	char		*newname;
 
-	if (!xu_getstrprop(cc->win, _NET_WM_NAME, &newname))
+	if (!xu_getstrprop(cc->win, ewmh[_NET_WM_NAME].atom, &newname))
 		if (!xu_getstrprop(cc->win, XA_WM_NAME, &newname))
 			newname = emptystring;
 
@@ -690,8 +673,8 @@ client_placecalc(struct client_ctx *cc)
 		 * XRandR bits mean that {x,y}max shouldn't be outside what's
 		 * currently there.
 		 */
-		xslack = sc->xmax - cc->geom.width - cc->bwidth * 2;
-		yslack = sc->ymax - cc->geom.height - cc->bwidth * 2;
+		xslack = sc->view.w - cc->geom.w - cc->bwidth * 2;
+		yslack = sc->view.h - cc->geom.h - cc->bwidth * 2;
 		if (cc->size->x > 0)
 			cc->geom.x = MIN(cc->size->x, xslack);
 		if (cc->size->y > 0)
@@ -702,28 +685,25 @@ client_placecalc(struct client_ctx *cc)
 		int			 xmax, ymax;
 
 		xu_ptr_getpos(sc->rootwin, &xmouse, &ymouse);
-		if (HasXinerama) {
-			info = screen_find_xinerama(sc, xmouse, ymouse);
-			if (info == NULL)
-				goto noxine;
+		info = screen_find_xinerama(sc, xmouse, ymouse);
+		if (info) {
 			xorig = info->x_org;
 			yorig = info->y_org;
 			xmax = xorig + info->width;
 			ymax = yorig + info->height;
 		} else {
-noxine:
 			xorig = yorig = 0;
-			xmax = sc->xmax;
-			ymax = sc->ymax;
+			xmax = sc->view.w;
+			ymax = sc->view.h;
 		}
-		xmouse = MAX(xmouse, xorig) - cc->geom.width / 2;
-		ymouse = MAX(ymouse, yorig) - cc->geom.height / 2;
+		xmouse = MAX(xmouse, xorig) - cc->geom.w / 2;
+		ymouse = MAX(ymouse, yorig) - cc->geom.h / 2;
 
 		xmouse = MAX(xmouse, xorig);
 		ymouse = MAX(ymouse, yorig);
 
-		xslack = xmax - cc->geom.width - cc->bwidth * 2;
-		yslack = ymax - cc->geom.height - cc->bwidth * 2;
+		xslack = xmax - cc->geom.w - cc->bwidth * 2;
+		yslack = ymax - cc->geom.h - cc->bwidth * 2;
 
 		if (xslack >= xorig) {
 			cc->geom.x = MAX(MIN(xmouse, xslack),
@@ -732,7 +712,7 @@ noxine:
 				cc->geom.x -= sc->gap.right;
 		} else {
 			cc->geom.x = xorig + sc->gap.left;
-			cc->geom.width = xmax - sc->gap.left;
+			cc->geom.w = xmax - sc->gap.left;
 		}
 		if (yslack >= yorig) {
 			cc->geom.y = MAX(MIN(ymouse, yslack),
@@ -741,7 +721,7 @@ noxine:
 				cc->geom.y -= sc->gap.bottom;
 		} else {
 			cc->geom.y = yorig + sc->gap.top;
-			cc->geom.height = ymax - sc->gap.top;
+			cc->geom.h = ymax - sc->gap.top;
 		}
 	}
 }
@@ -816,43 +796,43 @@ client_applysizehints(struct client_ctx *cc)
 
 	/* temporarily remove base dimensions, ICCCM 4.1.2.3 */
 	if (!baseismin) {
-		cc->geom.width -= cc->hint.basew;
-		cc->geom.height -= cc->hint.baseh;
+		cc->geom.w -= cc->hint.basew;
+		cc->geom.h -= cc->hint.baseh;
 	}
 
 	/* adjust for aspect limits */
 	if (cc->hint.mina > 0 && cc->hint.maxa > 0) {
 		if (cc->hint.maxa <
-		    (float)cc->geom.width / cc->geom.height)
-			cc->geom.width = cc->geom.height * cc->hint.maxa;
+		    (float)cc->geom.w / cc->geom.h)
+			cc->geom.w = cc->geom.h * cc->hint.maxa;
 		else if (cc->hint.mina <
-		    (float)cc->geom.height / cc->geom.width)
-			cc->geom.height = cc->geom.width * cc->hint.mina;
+		    (float)cc->geom.h / cc->geom.w)
+			cc->geom.h = cc->geom.w * cc->hint.mina;
 	}
 
 	/* remove base dimensions for increment */
 	if (baseismin) {
-		cc->geom.width -= cc->hint.basew;
-		cc->geom.height -= cc->hint.baseh;
+		cc->geom.w -= cc->hint.basew;
+		cc->geom.h -= cc->hint.baseh;
 	}
 
 	/* adjust for increment value */
-	cc->geom.width -= cc->geom.width % cc->hint.incw;
-	cc->geom.height -= cc->geom.height % cc->hint.inch;
+	cc->geom.w -= cc->geom.w % cc->hint.incw;
+	cc->geom.h -= cc->geom.h % cc->hint.inch;
 
 	/* restore base dimensions */
-	cc->geom.width += cc->hint.basew;
-	cc->geom.height += cc->hint.baseh;
+	cc->geom.w += cc->hint.basew;
+	cc->geom.h += cc->hint.baseh;
 
 	/* adjust for min width/height */
-	cc->geom.width = MAX(cc->geom.width, cc->hint.minw);
-	cc->geom.height = MAX(cc->geom.height, cc->hint.minh);
+	cc->geom.w = MAX(cc->geom.w, cc->hint.minw);
+	cc->geom.h = MAX(cc->geom.h, cc->hint.minh);
 
 	/* adjust for max width/height */
 	if (cc->hint.maxw)
-		cc->geom.width = MIN(cc->geom.width, cc->hint.maxw);
+		cc->geom.w = MIN(cc->geom.w, cc->hint.maxw);
 	if (cc->hint.maxh)
-		cc->geom.height = MIN(cc->geom.height, cc->hint.maxh);
+		cc->geom.h = MIN(cc->geom.h, cc->hint.maxh);
 }
 
 static void
@@ -868,7 +848,7 @@ client_gethints(struct client_ctx *cc)
 			cc->app_class = xch.res_class;
 	}
 
-	if (xu_getprop(cc->win, _MOTIF_WM_HINTS, _MOTIF_WM_HINTS,
+	if (xu_getprop(cc->win, cwmh[_MOTIF_WM_HINTS].atom, _MOTIF_WM_HINTS,
 	    PROP_MWM_HINTS_ELEMENTS, (u_char **)&mwmh) == MWM_NUMHINTS)
 		if (mwmh->flags & MWM_HINTS_DECORATIONS &&
 		    !(mwmh->decorations & MWM_DECOR_ALL) &&
@@ -903,8 +883,8 @@ client_transient(struct client_ctx *cc)
 static int
 client_inbound(struct client_ctx *cc, int x, int y)
 {
-	return (x < cc->geom.width && x >= 0 &&
-	    y < cc->geom.height && y >= 0);
+	return (x < cc->geom.w && x >= 0 &&
+	    y < cc->geom.h && y >= 0);
 }
 
 int
diff --git a/cwmrc.5 b/cwmrc.5
index 2bb1d53..e09e35b 100644
--- a/cwmrc.5
+++ b/cwmrc.5
@@ -14,7 +14,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: September 8 2011 $
+.Dd $Mdocdate: November 6 2011 $
 .Dt CWMRC 5
 .Os
 .Sh NAME
@@ -244,6 +244,10 @@ ignore xclock
 bind CM-r	label
 bind CS-Return	"xterm -e top"
 bind 4-o	unmap
+bind CM-equal	unmap
+bind CMS-equal	unmap
+bind C4-equal	vmaximize
+bind C4S-equal	hmaximize
 bind M-1	grouponly1
 bind M-2	grouponly2
 bind M-3	grouponly3
diff --git a/group.c b/group.c
index 85a0e67..f28640c 100644
--- a/group.c
+++ b/group.c
@@ -48,38 +48,31 @@ const char *shortcut_to_name[] = {
 static void
 group_add(struct group_ctx *gc, struct client_ctx *cc)
 {
-	long	no;
 	if (cc == NULL || gc == NULL)
 		errx(1, "group_add: a ctx is NULL");
 
-	no = gc->shortcut - 1;
-
 	if (cc->group == gc)
 		return;
 
 	if (cc->group != NULL)
 		TAILQ_REMOVE(&cc->group->clients, cc, group_entry);
 
-	XChangeProperty(X_Dpy, cc->win, _NET_WM_DESKTOP, XA_CARDINAL,
-	    32, PropModeReplace, (unsigned char *)&no, 1);
-
 	TAILQ_INSERT_TAIL(&gc->clients, cc, group_entry);
 	cc->group = gc;
+
+	xu_ewmh_net_wm_desktop(cc);
 }
 
 static void
 group_remove(struct client_ctx *cc)
 {
-	long	no = 0xffffffff;
-
 	if (cc == NULL || cc->group == NULL)
 		errx(1, "group_remove: a ctx is NULL");
 
-	XChangeProperty(X_Dpy, cc->win, _NET_WM_DESKTOP, XA_CARDINAL,
-	    32, PropModeReplace, (unsigned char *)&no, 1);
-
 	TAILQ_REMOVE(&cc->group->clients, cc, group_entry);
 	cc->group = NULL;
+
+	xu_ewmh_net_wm_desktop(cc);
 }
 
 static void
@@ -146,8 +139,6 @@ void
 group_init(struct screen_ctx *sc)
 {
 	int	 i;
-	long	 viewports[2] = {0, 0};
-	long	 ndesks = CALMWM_NGROUPS, zero = 0;
 
 	TAILQ_INIT(&sc->groupq);
 	sc->group_hideall = 0;
@@ -164,23 +155,11 @@ group_init(struct screen_ctx *sc)
 		TAILQ_INSERT_TAIL(&sc->groupq, &sc->groups[i], entry);
 	}
 
-	/* we don't support large desktops, so this is always (0, 0) */
-	XChangeProperty(X_Dpy, sc->rootwin, _NET_DESKTOP_VIEWPORT,
-	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)viewports, 2);
-	XChangeProperty(X_Dpy, sc->rootwin, _NET_NUMBER_OF_DESKTOPS,
-	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&ndesks, 1);
-	/*
-	 * we don't use virtual roots, so make sure it's not there from a 
-	 * previous wm.
-	 */
-	XDeleteProperty(X_Dpy, sc->rootwin, _NET_VIRTUAL_ROOTS);
-	/*
-	 * We don't really have a ``showing desktop'' mode, so this is zero
-	 * always. XXX Note that when we hide all groups, or when all groups
-	 * are hidden we could technically set this later on.
-	 */
-	XChangeProperty(X_Dpy, sc->rootwin, _NET_SHOWING_DESKTOP,
-	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&zero, 1);
+	xu_ewmh_net_wm_desktop_viewport(sc);
+	xu_ewmh_net_wm_number_of_desktops(sc);
+	xu_ewmh_net_showing_desktop(sc);
+	xu_ewmh_net_virtual_roots(sc);
+
 	group_setactive(sc, 0);
 }
 
@@ -209,8 +188,8 @@ static void
 group_setactive(struct screen_ctx *sc, long idx)
 {
 	sc->group_active = &sc->groups[idx];
-	XChangeProperty(X_Dpy, sc->rootwin, _NET_CURRENT_DESKTOP,
-	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&idx, 1);
+
+	xu_ewmh_net_current_desktop(sc, idx);
 }
 
 void
@@ -439,8 +418,8 @@ group_autogroup(struct client_ctx *cc)
 	if (cc->app_class == NULL || cc->app_name == NULL)
 		return;
 
-	if (xu_getprop(cc->win, _NET_WM_DESKTOP, XA_CARDINAL,
-	    1, (unsigned char **)&grpno) > 0) {
+	if (xu_getprop(cc->win, ewmh[_NET_WM_DESKTOP].atom,
+	    XA_CARDINAL, 1, (unsigned char **)&grpno) > 0) {
 		if (*grpno == 0xffffffff)
 			no = 0;
 		else if (*grpno > CALMWM_NGROUPS || *grpno < 0)
@@ -480,11 +459,12 @@ group_update_names(struct screen_ctx *sc)
 	char		**strings, *p;
 	unsigned char	*prop_ret;
 	Atom		 type_ret;
-	int		 format_ret, i = 0, nstrings = 0, n, setnames = 0;
+	int		 format_ret, i = 0, nstrings = 0, n = 0, setnames = 0;
 	unsigned long	 bytes_after, num_ret;
 
-	if (XGetWindowProperty(X_Dpy, sc->rootwin, _NET_DESKTOP_NAMES, 0,
-	    0xffffff, False, UTF8_STRING, &type_ret, &format_ret,
+	if (XGetWindowProperty(X_Dpy, sc->rootwin,
+	    ewmh[_NET_DESKTOP_NAMES].atom, 0, 0xffffff, False,
+	    cwmh[UTF8_STRING].atom, &type_ret, &format_ret,
 	    &num_ret, &bytes_after, &prop_ret) == Success &&
 	    prop_ret != NULL && format_ret == 8) {
 		/* failure, just set defaults */
@@ -498,7 +478,6 @@ group_update_names(struct screen_ctx *sc)
 	strings = xmalloc((nstrings < CALMWM_NGROUPS ? CALMWM_NGROUPS :
 	    nstrings) * sizeof(*strings));
 
-	i = n = 0;
 	p = prop_ret;
 	while (n < nstrings) {
 		strings[n++] = xstrdup(p);
@@ -545,6 +524,5 @@ group_set_names(struct screen_ctx *sc)
 		q += slen;
 	}
 
-	XChangeProperty(X_Dpy, sc->rootwin, _NET_DESKTOP_NAMES,
-	    UTF8_STRING, 8, PropModeReplace, p, len);
+	xu_ewmh_net_desktop_names(sc, p, len);
 }
diff --git a/kbfunc.c b/kbfunc.c
index 106bf95..f76bd92 100644
--- a/kbfunc.c
+++ b/kbfunc.c
@@ -55,14 +55,13 @@ kbfunc_client_raise(struct client_ctx *cc, union arg *arg)
 void
 kbfunc_moveresize(struct client_ctx *cc, union arg *arg)
 {
-	struct screen_ctx	*sc;
+	struct screen_ctx	*sc = cc->sc;
 	int			 x, y, flags, amt;
 	u_int			 mx, my;
 
 	if (cc->flags & CLIENT_FREEZE)
 		return;
 
-	sc = cc->sc;
 	mx = my = 0;
 
 	flags = arg->i;
@@ -90,22 +89,22 @@ kbfunc_moveresize(struct client_ctx *cc, union arg *arg)
 	switch (flags & TYPEMASK) {
 	case CWM_MOVE:
 		cc->geom.y += my;
-		if (cc->geom.y + cc->geom.height < 0)
-			cc->geom.y = -cc->geom.height;
-		if (cc->geom.y > cc->sc->ymax - 1)
-			cc->geom.y = cc->sc->ymax - 1;
+		if (cc->geom.y + cc->geom.h < 0)
+			cc->geom.y = -cc->geom.h;
+		if (cc->geom.y > sc->view.h - 1)
+			cc->geom.y = sc->view.h - 1;
 
 		cc->geom.x += mx;
-		if (cc->geom.x + cc->geom.width < 0)
-			cc->geom.x = -cc->geom.width;
-		if (cc->geom.x > cc->sc->xmax - 1)
-			cc->geom.x = cc->sc->xmax - 1;
+		if (cc->geom.x + cc->geom.w < 0)
+			cc->geom.x = -cc->geom.w;
+		if (cc->geom.x > sc->view.w - 1)
+			cc->geom.x = sc->view.w - 1;
 
 		cc->geom.x += client_snapcalc(cc->geom.x,
-		    cc->geom.width, cc->sc->xmax,
+		    cc->geom.w, sc->view.w,
 		    cc->bwidth, Conf.snapdist);
 		cc->geom.y += client_snapcalc(cc->geom.y,
-		    cc->geom.height, cc->sc->ymax,
+		    cc->geom.h, sc->view.h,
 		    cc->bwidth, Conf.snapdist);
 
 		client_move(cc);
@@ -115,18 +114,18 @@ kbfunc_moveresize(struct client_ctx *cc, union arg *arg)
 		client_ptrwarp(cc);
 		break;
 	case CWM_RESIZE:
-		if ((cc->geom.height += my) < 1)
-			cc->geom.height = 1;
-		if ((cc->geom.width += mx) < 1)
-			cc->geom.width = 1;
+		if ((cc->geom.h += my) < 1)
+			cc->geom.h = 1;
+		if ((cc->geom.w += mx) < 1)
+			cc->geom.w = 1;
 		client_resize(cc);
 
 		/* Make sure the pointer stays within the window. */
 		xu_ptr_getpos(cc->win, &cc->ptr.x, &cc->ptr.y);
-		if (cc->ptr.x > cc->geom.width)
-			cc->ptr.x = cc->geom.width - cc->bwidth;
-		if (cc->ptr.y > cc->geom.height)
-			cc->ptr.y = cc->geom.height - cc->bwidth;
+		if (cc->ptr.x > cc->geom.w)
+			cc->ptr.x = cc->geom.w - cc->bwidth;
+		if (cc->ptr.y > cc->geom.h)
+			cc->ptr.y = cc->geom.h - cc->bwidth;
 		client_ptrwarp(cc);
 		break;
 	case CWM_PTRMOVE:
@@ -141,12 +140,11 @@ kbfunc_moveresize(struct client_ctx *cc, union arg *arg)
 void
 kbfunc_client_search(struct client_ctx *cc, union arg *arg)
 {
-	struct screen_ctx	*sc;
+	struct screen_ctx	*sc = cc->sc;
 	struct client_ctx	*old_cc;
 	struct menu		*mi;
 	struct menu_q		 menuq;
 
-	sc = cc->sc;
 	old_cc = client_current();
 
 	TAILQ_INIT(&menuq);
@@ -178,12 +176,11 @@ kbfunc_client_search(struct client_ctx *cc, union arg *arg)
 void
 kbfunc_menu_search(struct client_ctx *cc, union arg *arg)
 {
-	struct screen_ctx	*sc;
+	struct screen_ctx	*sc = cc->sc;
 	struct cmd		*cmd;
 	struct menu		*mi;
 	struct menu_q		 menuq;
 
-	sc = cc->sc;
 	TAILQ_INIT(&menuq);
 
 	TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
@@ -206,9 +203,7 @@ kbfunc_menu_search(struct client_ctx *cc, union arg *arg)
 void
 kbfunc_client_cycle(struct client_ctx *cc, union arg *arg)
 {
-	struct screen_ctx	*sc;
-
-	sc = cc->sc;
+	struct screen_ctx	*sc = cc->sc;
 
 	/* XXX for X apps that ignore events */
 	XGrabKeyboard(X_Dpy, sc->rootwin, True,
@@ -245,7 +240,7 @@ void
 kbfunc_exec(struct client_ctx *cc, union arg *arg)
 {
 #define NPATHS 256
-	struct screen_ctx	*sc;
+	struct screen_ctx	*sc = cc->sc;
 	char			**ap, *paths[NPATHS], *path, *pathcpy, *label;
 	char			 tpath[MAXPATHLEN];
 	DIR			*dirp;
@@ -254,7 +249,6 @@ kbfunc_exec(struct client_ctx *cc, union arg *arg)
 	struct menu_q		 menuq;
 	int			 l, i, cmd = arg->i;
 
-	sc = cc->sc;
 	switch (cmd) {
 		case CWM_EXEC_PROGRAM:
 			label = "exec";
@@ -333,7 +327,7 @@ out:
 void
 kbfunc_ssh(struct client_ctx *cc, union arg *arg)
 {
-	struct screen_ctx	*sc;
+	struct screen_ctx	*sc = cc->sc;
 	struct menu		*mi;
 	struct menu_q		 menuq;
 	FILE			*fp;
@@ -343,8 +337,6 @@ kbfunc_ssh(struct client_ctx *cc, union arg *arg)
 	int			 l;
 	size_t			 len;
 
-	sc = cc->sc;
-
 	if ((home = getenv("HOME")) == NULL)
 		return;
 
diff --git a/menu.c b/menu.c
index 17c665a..58ed618 100644
--- a/menu.c
+++ b/menu.c
@@ -349,8 +349,8 @@ menu_draw(struct screen_ctx *sc, struct menu_ctx *mc, struct menu_q *menuq,
 		ymax = xine->y_org + xine->height;
 	} else {
 		xmin = ymin = 0;
-		xmax = sc->xmax;
-		ymax = sc->ymax;
+		xmax = sc->view.w;
+		ymax = sc->view.h;
 	}
 
 	xsave = mc->x;
diff --git a/mousefunc.c b/mousefunc.c
index c165977..b0bd40f 100644
--- a/mousefunc.c
+++ b/mousefunc.c
@@ -37,17 +37,17 @@ static void	mousefunc_sweep_draw(struct client_ctx *);
 static int
 mousefunc_sweep_calc(struct client_ctx *cc, int x, int y, int mx, int my)
 {
-	int	 width = cc->geom.width, height = cc->geom.height;
+	int	 width = cc->geom.w, height = cc->geom.h;
 
-	cc->geom.width = abs(x - mx) - cc->bwidth;
-	cc->geom.height = abs(y - my) - cc->bwidth;
+	cc->geom.w = abs(x - mx) - cc->bwidth;
+	cc->geom.h = abs(y - my) - cc->bwidth;
 
 	client_applysizehints(cc);
 
-	cc->geom.x = x <= mx ? x : x - cc->geom.width;
-	cc->geom.y = y <= my ? y : y - cc->geom.height;
+	cc->geom.x = x <= mx ? x : x - cc->geom.w;
+	cc->geom.y = y <= my ? y : y - cc->geom.h;
 
-	return (width != cc->geom.width || height != cc->geom.height);
+	return (width != cc->geom.w || height != cc->geom.h);
 }
 
 static void
@@ -58,8 +58,8 @@ mousefunc_sweep_draw(struct client_ctx *cc)
 	int			 width, width_size, width_name;
 
 	(void)snprintf(asize, sizeof(asize), "%dx%d",
-	    (cc->geom.width - cc->hint.basew) / cc->hint.incw,
-	    (cc->geom.height - cc->hint.baseh) / cc->hint.inch);
+	    (cc->geom.w - cc->hint.basew) / cc->hint.incw,
+	    (cc->geom.h - cc->hint.baseh) / cc->hint.inch);
 	width_size = font_width(sc, asize, strlen(asize)) + 4;
 	width_name = font_width(sc, cc->name, strlen(cc->name)) + 4;
 	width = MAX(width_size, width_name);
@@ -91,7 +91,7 @@ mousefunc_window_resize(struct client_ctx *cc, void *arg)
 	if (xu_ptr_grab(cc->win, MOUSEMASK, Cursor_resize) < 0)
 		return;
 
-	xu_ptr_setpos(cc->win, cc->geom.width, cc->geom.height);
+	xu_ptr_setpos(cc->win, cc->geom.w, cc->geom.h);
 	mousefunc_sweep_draw(cc);
 
 	for (;;) {
@@ -121,10 +121,10 @@ mousefunc_window_resize(struct client_ctx *cc, void *arg)
 			xu_ptr_ungrab();
 
 			/* Make sure the pointer stays within the window. */
-			if (cc->ptr.x > cc->geom.width)
-				cc->ptr.x = cc->geom.width - cc->bwidth;
-			if (cc->ptr.y > cc->geom.height)
-				cc->ptr.y = cc->geom.height - cc->bwidth;
+			if (cc->ptr.x > cc->geom.w)
+				cc->ptr.x = cc->geom.w - cc->bwidth;
+			if (cc->ptr.y > cc->geom.h)
+				cc->ptr.y = cc->geom.h - cc->bwidth;
 			client_ptrwarp(cc);
 			return;
 		}
@@ -137,6 +137,7 @@ mousefunc_window_move(struct client_ctx *cc, void *arg)
 {
 	XEvent			 ev;
 	Time			 ltime = 0;
+	struct screen_ctx	*sc = cc->sc;
 	int			 px, py;
 
 	client_raise(cc);
@@ -161,10 +162,10 @@ mousefunc_window_move(struct client_ctx *cc, void *arg)
 			cc->geom.y = ev.xmotion.y_root - py - cc->bwidth;
 
 			cc->geom.x += client_snapcalc(cc->geom.x,
-			    cc->geom.width, cc->sc->xmax,
+			    cc->geom.w, sc->view.w,
 			    cc->bwidth, Conf.snapdist);
 			cc->geom.y += client_snapcalc(cc->geom.y,
-			    cc->geom.height, cc->sc->ymax,
+			    cc->geom.h, sc->view.h,
 			    cc->bwidth, Conf.snapdist);
 
 			/* don't move more than 60 times / second */
@@ -217,13 +218,12 @@ mousefunc_menu_group(struct client_ctx *cc, void *arg)
 void
 mousefunc_menu_unhide(struct client_ctx *cc, void *arg)
 {
-	struct screen_ctx	*sc;
+	struct screen_ctx	*sc = cc->sc;
 	struct client_ctx	*old_cc;
 	struct menu		*mi;
 	struct menu_q		 menuq;
 	char			*wname;
 
-	sc = cc->sc;
 	old_cc = client_current();
 
 	TAILQ_INIT(&menuq);
@@ -261,13 +261,11 @@ mousefunc_menu_unhide(struct client_ctx *cc, void *arg)
 void
 mousefunc_menu_cmd(struct client_ctx *cc, void *arg)
 {
-	struct screen_ctx	*sc;
+	struct screen_ctx	*sc = cc->sc;
 	struct menu		*mi;
 	struct menu_q		 menuq;
 	struct cmd		*cmd;
 
-	sc = cc->sc;
-
 	TAILQ_INIT(&menuq);
 	TAILQ_FOREACH(cmd, &Conf.cmdq, entry) {
 		mi = xcalloc(1, sizeof(*mi));
diff --git a/screen.c b/screen.c
index ecd8104..69a638a 100644
--- a/screen.c
+++ b/screen.c
@@ -30,6 +30,8 @@
 
 #include "calmwm.h"
 
+static void	 screen_init_xinerama(struct screen_ctx *);
+
 struct screen_ctx *
 screen_fromroot(Window rootwin)
 {
@@ -65,24 +67,18 @@ screen_updatestackingorder(struct screen_ctx *sc)
 	XFree(wins);
 }
 
+/*
+ * If we're using RandR then we'll redo this whenever the screen
+ * changes since a CTRC may have been added or removed
+ */
 void
 screen_init_xinerama(struct screen_ctx *sc)
 {
-	XineramaScreenInfo	*info;
-	int			 no;
-
-	if (HasXinerama == 0 || XineramaIsActive(X_Dpy) == 0) {
-		HasXinerama = 0;
-		sc->xinerama_no = 0;
-	}
+	XineramaScreenInfo	*info = NULL;
+	int			 no = 0;
 
-	info = XineramaQueryScreens(X_Dpy, &no);
-	if (info == NULL) {
-		/* Is xinerama actually off, instead of a malloc failure? */
-		if (sc->xinerama == NULL)
-			HasXinerama = 0;
-		return;
-	}
+	if (XineramaIsActive(X_Dpy))
+		info = XineramaQueryScreens(X_Dpy, &no);
 
 	if (sc->xinerama != NULL)
 		XFree(sc->xinerama);
@@ -99,6 +95,9 @@ screen_find_xinerama(struct screen_ctx *sc, int x, int y)
 	XineramaScreenInfo	*info;
 	int			 i;
 
+	if (sc->xinerama == NULL)
+		return (NULL);
+
 	for (i = 0; i < sc->xinerama_no; i++) {
 		info = &sc->xinerama[i];
 		if (x >= info->x_org && x < info->x_org + info->width &&
@@ -109,25 +108,20 @@ screen_find_xinerama(struct screen_ctx *sc, int x, int y)
 }
 
 void
-screen_update_geometry(struct screen_ctx *sc, int width, int height)
+screen_update_geometry(struct screen_ctx *sc)
 {
-	long	 geom[2], workareas[CALMWM_NGROUPS][4];
-	int	 i;
-
-	sc->xmax = geom[0] = width;
-	sc->ymax = geom[1] = height;
-	XChangeProperty(X_Dpy, sc->rootwin, _NET_DESKTOP_GEOMETRY,
-	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)geom , 2);
-
-	/* x, y, width, height. */
-	for (i = 0; i < CALMWM_NGROUPS; i++) {
-		workareas[i][0] = sc->gap.left;
-		workareas[i][1] = sc->gap.top;
-		workareas[i][2] = width - (sc->gap.left + sc->gap.right);
-		workareas[i][3] = height - (sc->gap.top + sc->gap.bottom);
-	}
+	sc->view.x = 0;
+	sc->view.y = 0;
+	sc->view.w = DisplayWidth(X_Dpy, sc->which);
+	sc->view.h = DisplayHeight(X_Dpy, sc->which);
+
+	sc->work.x = sc->view.x + sc->gap.left;
+	sc->work.y = sc->view.y + sc->gap.top;
+	sc->work.w = sc->view.w - (sc->gap.left + sc->gap.right);
+	sc->work.h = sc->view.h - (sc->gap.top + sc->gap.bottom);
+
+	screen_init_xinerama(sc);
 
-	XChangeProperty(X_Dpy, sc->rootwin, _NET_WORKAREA,
-	    XA_CARDINAL, 32, PropModeReplace,
-	    (unsigned char *)workareas, CALMWM_NGROUPS * 4);
+	xu_ewmh_net_desktop_geometry(sc);
+	xu_ewmh_net_workarea(sc);
 }
diff --git a/xevents.c b/xevents.c
index 9cd03f9..0d91067 100644
--- a/xevents.c
+++ b/xevents.c
@@ -142,9 +142,9 @@ xev_handle_configurerequest(XEvent *ee)
 		sc = cc->sc;
 
 		if (e->value_mask & CWWidth)
-			cc->geom.width = e->width;
+			cc->geom.w = e->width;
 		if (e->value_mask & CWHeight)
-			cc->geom.height = e->height;
+			cc->geom.h = e->height;
 		if (e->value_mask & CWX)
 			cc->geom.x = e->x;
 		if (e->value_mask & CWY)
@@ -152,16 +152,16 @@ xev_handle_configurerequest(XEvent *ee)
 		if (e->value_mask & CWBorderWidth)
 			wc.border_width = e->border_width;
 
-		if (cc->geom.x == 0 && cc->geom.width >= sc->xmax)
+		if (cc->geom.x == 0 && cc->geom.w >= sc->view.w)
 			cc->geom.x -= cc->bwidth;
 
-		if (cc->geom.y == 0 && cc->geom.height >= sc->ymax)
+		if (cc->geom.y == 0 && cc->geom.h >= sc->view.h)
 			cc->geom.y -= cc->bwidth;
 
 		wc.x = cc->geom.x;
 		wc.y = cc->geom.y;
-		wc.width = cc->geom.width;
-		wc.height = cc->geom.height;
+		wc.width = cc->geom.w;
+		wc.height = cc->geom.h;
 		wc.border_width = cc->bwidth;
 
 		XConfigureWindow(X_Dpy, cc->win, e->value_mask, &wc);
@@ -208,7 +208,7 @@ xev_handle_propertynotify(XEvent *ee)
 				goto test;
 		return;
 test:
-		if (e->atom == _NET_DESKTOP_NAMES)
+		if (e->atom == ewmh[_NET_DESKTOP_NAMES].atom)
 			group_update_names(sc);
 	}
 }
@@ -366,8 +366,7 @@ xev_handle_randr(XEvent *ee)
 	TAILQ_FOREACH(sc, &Screenq, entry) {
 		if (sc->which == (u_int)i) {
 			XRRUpdateConfiguration(ee);
-			screen_update_geometry(sc, rev->width, rev->height);
-			screen_init_xinerama(sc);
+			screen_update_geometry(sc);
 		}
 	}
 }
diff --git a/xutil.c b/xutil.c
index 030584b..d3f7978 100644
--- a/xutil.c
+++ b/xutil.c
@@ -128,8 +128,8 @@ xu_configure(struct client_ctx *cc)
 	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.width = cc->geom.w;
+	ce.height = cc->geom.h;
 	ce.border_width = cc->bwidth;
 	ce.above = None;
 	ce.override_redirect = 0;
@@ -207,7 +207,8 @@ xu_getstate(struct client_ctx *cc, int *state)
 {
 	long	*p = NULL;
 
-	if (xu_getprop(cc->win, WM_STATE, WM_STATE, 2L, (u_char **)&p) <= 0)
+	if (xu_getprop(cc->win, cwmh[WM_STATE].atom, cwmh[WM_STATE].atom, 2L,
+	    (u_char **)&p) <= 0)
 		return (-1);
 
 	*state = (int)*p;
@@ -225,67 +226,194 @@ xu_setstate(struct client_ctx *cc, int state)
 	dat[1] = None;
 
 	cc->state = state;
-	XChangeProperty(X_Dpy, cc->win, WM_STATE, WM_STATE, 32,
+	XChangeProperty(X_Dpy, cc->win,
+	    cwmh[WM_STATE].atom, cwmh[WM_STATE].atom, 32,
 	    PropModeReplace, (unsigned char *)dat, 2);
 }
 
-Atom		cwm_atoms[CWM_NO_ATOMS];
-char 		*atoms[CWM_NO_ATOMS] = {
-	"WM_STATE",
-	"WM_DELETE_WINDOW",
-	"WM_TAKE_FOCUS",
-	"WM_PROTOCOLS",
-	"_MOTIF_WM_HINTS",
-	"UTF8_STRING",
-	"_NET_SUPPORTED",
-	"_NET_SUPPORTING_WM_CHECK",
-	"_NET_WM_NAME",
-	"_NET_ACTIVE_WINDOW",
-	"_NET_CLIENT_LIST",
-	"_NET_NUMBER_OF_DESKTOPS",
-	"_NET_CURRENT_DESKTOP",
-	"_NET_DESKTOP_VIEWPORT",
-	"_NET_DESKTOP_GEOMETRY",
-	"_NET_VIRTUAL_ROOTS",
-	"_NET_SHOWING_DESKTOP",
-	"_NET_DESKTOP_NAMES",
-	"_NET_WM_DESKTOP",
-	"_NET_WORKAREA",
+struct atom_ctx cwmh[CWMH_NITEMS] = {
+	{"WM_STATE",			None},
+	{"WM_DELETE_WINDOW",		None},
+	{"WM_TAKE_FOCUS",		None},
+	{"WM_PROTOCOLS",		None},
+	{"_MOTIF_WM_HINTS",		None},
+	{"UTF8_STRING",			None},
+};
+struct atom_ctx ewmh[EWMH_NITEMS] = {
+	{"_NET_SUPPORTED",		None},
+	{"_NET_SUPPORTING_WM_CHECK",	None},
+	{"_NET_ACTIVE_WINDOW",		None},
+	{"_NET_CLIENT_LIST",		None},
+	{"_NET_NUMBER_OF_DESKTOPS",	None},
+	{"_NET_CURRENT_DESKTOP",	None},
+	{"_NET_DESKTOP_VIEWPORT",	None},
+	{"_NET_DESKTOP_GEOMETRY",	None},
+	{"_NET_VIRTUAL_ROOTS",		None},
+	{"_NET_SHOWING_DESKTOP",	None},
+	{"_NET_DESKTOP_NAMES",		None},
+	{"_NET_WORKAREA",		None},
+	{"_NET_WM_NAME",		None},
+	{"_NET_WM_DESKTOP",		None},
 };
 
 void
 xu_getatoms(void)
 {
-	XInternAtoms(X_Dpy, atoms, CWM_NO_ATOMS, False, cwm_atoms);
+	int	 i;
+
+	for (i = 0; i < nitems(cwmh); i++)
+		cwmh[i].atom = XInternAtom(X_Dpy, cwmh[i].name, False);
+	for (i = 0; i < nitems(ewmh); i++)
+		ewmh[i].atom = XInternAtom(X_Dpy, ewmh[i].name, False);
 }
 
+/* Root Window Properties */
 void
 xu_ewmh_net_supported(struct screen_ctx *sc)
 {
-	XChangeProperty(X_Dpy, sc->rootwin, _NET_SUPPORTED, XA_ATOM, 32,
-	    PropModeReplace,  (unsigned char *)&_NET_SUPPORTED,
-	    CWM_NO_ATOMS - CWM_NETWM_START);
+	Atom	 atom[EWMH_NITEMS];
+	int	 i;
+
+	for (i = 0; i < nitems(ewmh); i++)
+		atom[i] = ewmh[i].atom;
+
+	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SUPPORTED].atom,
+	    XA_ATOM, 32, PropModeReplace, (unsigned char *)atom, EWMH_NITEMS);
 }
 
-/*
- * The netwm spec says that to prove that the hint is not stale, one
- * must provide _NET_SUPPORTING_WM_CHECK containing a window created by
- * the root window.  The property must be set on the root window and the
- * window itself.  This child window also must have _NET_WM_NAME set with
- * the window manager name.
- */
 void
 xu_ewmh_net_supported_wm_check(struct screen_ctx *sc)
 {
 	Window	 w;
 
 	w = XCreateSimpleWindow(X_Dpy, sc->rootwin, -1, -1, 1, 1, 0, 0, 0);
-	XChangeProperty(X_Dpy, sc->rootwin, _NET_SUPPORTING_WM_CHECK,
+	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SUPPORTING_WM_CHECK].atom,
 	    XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
-	XChangeProperty(X_Dpy, w, _NET_SUPPORTING_WM_CHECK,
+	XChangeProperty(X_Dpy, w, ewmh[_NET_SUPPORTING_WM_CHECK].atom,
 	    XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
-	XChangeProperty(X_Dpy, w, _NET_WM_NAME, UTF8_STRING,
-	    8, PropModeReplace, WMNAME, strlen(WMNAME));
+	XChangeProperty(X_Dpy, w, ewmh[_NET_WM_NAME].atom,
+	    XA_WM_NAME, 8, PropModeReplace, WMNAME, strlen(WMNAME));
+}
+
+void
+xu_ewmh_net_desktop_geometry(struct screen_ctx *sc)
+{
+	long	 geom[2] = { sc->view.w, sc->view.h };
+
+	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_GEOMETRY].atom,
+	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)geom , 2);
+}
+
+void
+xu_ewmh_net_workarea(struct screen_ctx *sc)
+{
+	long	 workareas[CALMWM_NGROUPS][4];
+	int	 i;
+
+	for (i = 0; i < CALMWM_NGROUPS; i++) {
+		workareas[i][0] = sc->work.x;
+		workareas[i][1] = sc->work.y;
+		workareas[i][2] = sc->work.w;
+		workareas[i][3] = sc->work.h;
+	}
+
+	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_WORKAREA].atom,
+	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)workareas,
+	    CALMWM_NGROUPS * 4);
+}
+
+void
+xu_ewmh_net_client_list(struct screen_ctx *sc)
+{
+	struct client_ctx	*cc;
+	Window			*winlist;
+	int			 i = 0, j = 0;
+
+	TAILQ_FOREACH(cc, &Clientq, entry)
+		i++;
+	if (i == 0)
+		return;
+
+	winlist = xmalloc(i * sizeof(*winlist));
+	TAILQ_FOREACH(cc, &Clientq, entry)
+		winlist[j++] = cc->win;
+	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CLIENT_LIST].atom,
+	    XA_WINDOW, 32, PropModeReplace, (unsigned char *)winlist, i);
+	xfree(winlist);
+}
+
+void
+xu_ewmh_net_active_window(struct screen_ctx *sc, Window w)
+{
+	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_ACTIVE_WINDOW].atom,
+	    XA_WINDOW, 32, PropModeReplace, (unsigned char *)&w, 1);
+}
+
+void
+xu_ewmh_net_wm_desktop_viewport(struct screen_ctx *sc)
+{
+	long	 viewports[2] = {0, 0};
+
+	/* We don't support large desktops, so this is (0, 0). */
+	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_VIEWPORT].atom,
+	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)viewports, 2);
+}
+
+void
+xu_ewmh_net_wm_number_of_desktops(struct screen_ctx *sc)
+{
+	long	 ndesks = CALMWM_NGROUPS;
+
+	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_NUMBER_OF_DESKTOPS].atom,
+	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&ndesks, 1);
+}
+
+void
+xu_ewmh_net_showing_desktop(struct screen_ctx *sc)
+{
+	long	 zero = 0;
+
+	/* We don't support `showing desktop' mode, so this is zero.
+	 * Note that when we hide all groups, or when all groups are
+	 * hidden we could technically set this later on.
+	 */
+	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_SHOWING_DESKTOP].atom,
+	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&zero, 1);
+}
+
+void
+xu_ewmh_net_virtual_roots(struct screen_ctx *sc)
+{
+	/* We don't support virtual roots, so delete if set by previous wm. */
+	XDeleteProperty(X_Dpy, sc->rootwin, ewmh[_NET_VIRTUAL_ROOTS].atom);
+}
+
+void
+xu_ewmh_net_current_desktop(struct screen_ctx *sc, long idx)
+{
+	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_CURRENT_DESKTOP].atom,
+	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&idx, 1);
+}
+
+void
+xu_ewmh_net_desktop_names(struct screen_ctx *sc, unsigned char *data, int n)
+{
+	XChangeProperty(X_Dpy, sc->rootwin, ewmh[_NET_DESKTOP_NAMES].atom,
+	    cwmh[UTF8_STRING].atom, 8, PropModeReplace, data, n);
+}
+
+/* Application Window Properties */
+void
+xu_ewmh_net_wm_desktop(struct client_ctx *cc)
+{
+	struct group_ctx	*gc = cc->group;
+	long			 no = 0xffffffff;
+
+	if (gc)
+		no = gc->shortcut - 1;
+
+	XChangeProperty(X_Dpy, cc->win, ewmh[_NET_WM_DESKTOP].atom,
+	    XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&no, 1);
 }
 
 unsigned long