summary refs log tree commit diff
diff options
context:
space:
mode:
authoroga <oga>2008-09-29 23:16:46 +0000
committeroga <oga>2008-09-29 23:16:46 +0000
commita0739c6cd4df1890751e2527c899756d7a49b6c5 (patch)
treed0c2dd86406ecbf00616857c3b351da224e31345
parente2610449d11d3b5100aad319e8fa70c370b734bf (diff)
downloadcwm-a0739c6cd4df1890751e2527c899756d7a49b6c5.tar.gz
cwm-a0739c6cd4df1890751e2527c899756d7a49b6c5.tar.xz
cwm-a0739c6cd4df1890751e2527c899756d7a49b6c5.zip
Xinerama and XRandR dual head support for cwm(1). Now we detect the xrandr
reconfiguration events and change our sizes depending on that. We also detect
the xinerama screens for maximize, vertmaximize and initial window placement.

This could be improved by automatically resizing maximized windows when
the screen resolution changes and by moving windows that would be hidden
into visible space. Said changes will come shortly.

Tested by many. matthieu@ didn't oppose.
-rw-r--r--Makefile2
-rw-r--r--calmwm.c18
-rw-r--r--calmwm.h7
-rw-r--r--client.c131
-rw-r--r--headers.h2
-rw-r--r--screen.c25
-rw-r--r--xevents.c18
7 files changed, 174 insertions, 29 deletions
diff --git a/Makefile b/Makefile
index 2f2ee1c..8bbf13e 100644
--- a/Makefile
+++ b/Makefile
@@ -11,7 +11,7 @@ SRCS=		calmwm.c screen.c xmalloc.c client.c grab.c menu.c \
 CPPFLAGS+=	-I${X11BASE}/include -I${X11BASE}/include/freetype2 -I${.CURDIR}
 
 LDADD+=		-L${X11BASE}/lib -lXft -lXrender -lX11 -lXau -lXdmcp -lXext \
-		-lfontconfig -lexpat -lfreetype -lz 
+		-lfontconfig -lexpat -lfreetype -lz -lXinerama -lXrandr
 
 MANDIR=		${X11BASE}/man/cat
 MAN=		cwm.1 cwmrc.5
diff --git a/calmwm.c b/calmwm.c
index c64035d..446ed17 100644
--- a/calmwm.c
+++ b/calmwm.c
@@ -36,6 +36,7 @@ u_int				 Nscreens;
 struct client_ctx_q		 Clientq;
 
 int				 Doshape, Shape_ev;
+int				 HasXinerama, HasRandr, Randr_ev;
 int				 Starting;
 struct conf			 Conf;
 
@@ -121,6 +122,8 @@ dpy_init(const char *dpyname)
 
 	Doshape = XShapeQueryExtension(X_Dpy, &Shape_ev, &i);
 
+	HasRandr = XRRQueryExtension(X_Dpy, &Randr_ev, &i);
+
 	TAILQ_INIT(&Screenq);
 }
 
@@ -133,7 +136,7 @@ x_setup(void)
 
 	Nscreens = ScreenCount(X_Dpy);
 	for (i = 0; i < (int)Nscreens; i++) {
-		XMALLOC(sc, struct screen_ctx);
+		XCALLOC(sc, struct screen_ctx);
 		x_setupscreen(sc, i);
 		TAILQ_INSERT_TAIL(&Screenq, sc, entry);
 	}
@@ -161,6 +164,7 @@ x_setupscreen(struct screen_ctx *sc, u_int which)
 	Window			*wins, w0, w1;
 	XWindowAttributes	 winattr;
 	XSetWindowAttributes	 rootattr;
+	int			 fake;
 	u_int			 nwins, i;
 
 	Curscreen = sc;
@@ -246,6 +250,18 @@ x_setupscreen(struct screen_ctx *sc, u_int which)
 	XChangeWindowAttributes(X_Dpy, sc->rootwin,
 	    CWEventMask, &rootattr);
 
+	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);
 
 	return;
diff --git a/calmwm.h b/calmwm.h
index c20e66d..be26b57 100644
--- a/calmwm.h
+++ b/calmwm.h
@@ -62,6 +62,9 @@ struct screen_ctx {
 
 	XftDraw		*xftdraw;
 	XftColor	 xftcolor;
+
+	int		 xinerama_no;
+	XineramaScreenInfo *xinerama;
 };
 
 TAILQ_HEAD(screen_ctx_q, screen_ctx);
@@ -369,6 +372,7 @@ void			 xev_handle_keyrelease(struct xevent *, XEvent *);
 void			 xev_handle_expose(struct xevent *, XEvent *);
 void			 xev_handle_clientmessage(struct xevent *, XEvent *);
 void			 xev_handle_shape(struct xevent *, XEvent *);
+void			 xev_handle_randr(struct xevent *, XEvent *);
 void			 xev_handle_mapping(struct xevent *, XEvent *);
 
 #define XEV_QUICK(a, b, c, d, e) do {		\
@@ -417,6 +421,7 @@ char			*xstrdup(const char *);
 struct screen_ctx	*screen_fromroot(Window);
 struct screen_ctx	*screen_current(void);
 void			 screen_updatestackingorder(void);
+void			 screen_init_xinerama(struct screen_ctx *);
 
 void			 conf_setup(struct conf *, const char *);
 void			 conf_client(struct client_ctx *);
@@ -512,6 +517,8 @@ extern u_int				 Nscreens;
 extern struct client_ctx_q		 Clientq;
 
 extern int				 Doshape, Shape_ev;
+extern int				 Doshape, Shape_ev;
+extern int				 HasXinerama, HasRandr, Randr_ev;
 extern struct conf			 Conf;
 
 #endif /* _CALMWM_H_ */
diff --git a/client.c b/client.c
index 72ccb1e..6e2ef24 100644
--- a/client.c
+++ b/client.c
@@ -22,6 +22,8 @@
 #include "calmwm.h"
 
 int	_inwindowbounds(struct client_ctx *, int, int);
+XineramaScreenInfo	*client_find_xinerama_screen(int , int ,
+			     struct screen_ctx *);
 
 static char		 emptystring[] = "";
 
@@ -322,16 +324,36 @@ void
 client_maximize(struct client_ctx *cc)
 {
 	struct screen_ctx	*sc = CCTOSC(cc);
+	int			 xmax = sc->xmax, ymax = sc->ymax;
+	int			 x_org = 0, y_org = 0;
 
 	if (cc->flags & CLIENT_MAXIMIZED) {
 		cc->geom = cc->savegeom;
 	} else {
 		if (!(cc->flags & CLIENT_VMAXIMIZED))
 			cc->savegeom = cc->geom;
-		cc->geom.x = Conf.gap_left;
-		cc->geom.y = Conf.gap_top;
-		cc->geom.height = sc->ymax - (Conf.gap_top + Conf.gap_bottom);
-		cc->geom.width = sc->xmax - (Conf.gap_left + Conf.gap_right);
+		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 = client_find_xinerama_screen(cc->geom.x + 
+			    cc->geom.width / 2, cc->geom.y +
+			    cc->geom.height / 2, CCTOSC(cc));
+			if (xine == NULL)
+				goto calc;
+			x_org = xine->x_org;
+			y_org = xine->y_org;
+			xmax = xine->width;
+			ymax = xine->height;
+		}
+calc:
+		cc->geom.x = x_org + Conf.gap_left;
+		cc->geom.y = y_org + Conf.gap_top;
+		cc->geom.height = ymax - (Conf.gap_top + Conf.gap_bottom);
+		cc->geom.width = xmax - (Conf.gap_left + Conf.gap_right);
 		cc->flags |= CLIENT_DOMAXIMIZE;
 	}
 
@@ -342,15 +364,27 @@ void
 client_vertmaximize(struct client_ctx *cc)
 {
 	struct screen_ctx	*sc = CCTOSC(cc);
+	int			 y_org = 0, ymax = sc->ymax;
 
 	if (cc->flags & CLIENT_VMAXIMIZED) {
 		cc->geom = cc->savegeom;
 	} else {
 		if (!(cc->flags & CLIENT_MAXIMIZED))
 			cc->savegeom = cc->geom;
-		cc->geom.y = cc->bwidth + Conf.gap_top;
-		cc->geom.height = (sc->ymax - cc->bwidth * 2) -
-		    (Conf.gap_top + Conf.gap_bottom);
+		if (HasXinerama) {
+			XineramaScreenInfo *xine;
+			xine = client_find_xinerama_screen(cc->geom.x + 
+			    cc->geom.width / 2, cc->geom.y +
+			    cc->geom.height / 2, CCTOSC(cc));
+			if (xine == NULL)
+				goto calc;
+			y_org = xine->y_org;
+			ymax = xine->height;
+		}
+calc:
+		cc->geom.y = y_org + cc->bwidth + Conf.gap_top;
+		cc->geom.height = ymax - (cc->bwidth * 2) - (Conf.gap_top +
+		    Conf.gap_bottom);
 		cc->flags |= CLIENT_DOVMAXIMIZE;
 	}
 
@@ -667,20 +701,18 @@ void
 client_placecalc(struct client_ctx *cc)
 {
 	struct screen_ctx	*sc = CCTOSC(cc);
-	int			 yslack, xslack, xmouse, ymouse;
-
-	yslack = sc->ymax - cc->geom.height - cc->bwidth;
-	xslack = sc->xmax - cc->geom.width - cc->bwidth;
-
-	xu_ptr_getpos(sc->rootwin, &xmouse, &ymouse);
-
-	xmouse = MAX(xmouse, cc->bwidth) - cc->geom.width / 2;
-	ymouse = MAX(ymouse, cc->bwidth) - cc->geom.height / 2;
-
-	xmouse = MAX(xmouse, (int)cc->bwidth);
-	ymouse = MAX(ymouse, (int)cc->bwidth);
+	int			 yslack, xslack;
 
 	if (cc->size->flags & USPosition) {
+		/*
+		 * Ignore XINERAMA screens, just make sure it's somewhere
+		 * in the virtual desktop. else it stops people putting xterms
+		 * at startup in the screen the mouse doesn't start in *sigh*.
+		 * XRandR bits mean that {x,y}max shouldn't be outside what's
+		 * currently there.
+		 */
+		yslack = sc->ymax - cc->geom.height - cc->bwidth;
+		xslack = sc->xmax - cc->geom.width - cc->bwidth;
 		if (cc->size->x >= 0)
 			cc->geom.x = MAX(MIN(cc->size->x, xslack), cc->bwidth);
 		else
@@ -690,23 +722,50 @@ client_placecalc(struct client_ctx *cc)
 		else 
 			cc->geom.y = cc->bwidth;
 	} else {
-		if (xslack >= 0) {
+		XineramaScreenInfo	*info;
+		int			 xmouse, ymouse, xorig, yorig;
+		int			 xmax, ymax;
+
+		xu_ptr_getpos(sc->rootwin, &xmouse, &ymouse);
+		if (HasXinerama) {
+			info = client_find_xinerama_screen(xmouse, ymouse, sc);
+			if (info == NULL)
+				goto noxine;
+			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;
+		}
+		xmouse = MAX(xmouse, xorig + cc->bwidth) - cc->geom.width / 2;
+		ymouse = MAX(ymouse, yorig + cc->bwidth) - cc->geom.height / 2;
+
+		xmouse = MAX(xmouse, xorig + (int)cc->bwidth);
+		ymouse = MAX(ymouse, yorig + (int)cc->bwidth);
+
+		xslack = xmax - cc->geom.width - cc->bwidth;
+		yslack = ymax - cc->geom.height - cc->bwidth;
+		if (xslack >= xorig) {
 			cc->geom.x = MAX(MIN(xmouse, xslack),
-					 Conf.gap_left + cc->bwidth);
+			    xorig + Conf.gap_left + cc->bwidth);
 			if (cc->geom.x > (xslack - Conf.gap_right))
 				cc->geom.x -= Conf.gap_right;
 		} else {
-			cc->geom.x = cc->bwidth + Conf.gap_left;
-			cc->geom.width = sc->xmax - Conf.gap_left;
+			cc->geom.x = xorig + cc->bwidth + Conf.gap_left;
+			cc->geom.width = xmax - Conf.gap_left;
 		}
-		if (yslack >= 0) {
+		if (yslack >= yorig) {
 			cc->geom.y = MAX(MIN(ymouse, yslack),
-					 Conf.gap_top + cc->bwidth);
+			    yorig + Conf.gap_top + cc->bwidth);
 			if (cc->geom.y > (yslack - Conf.gap_bottom))
 				cc->geom.y -= Conf.gap_bottom;
 		} else {
-			cc->geom.y = cc->bwidth + Conf.gap_top;
-			cc->geom.height = sc->ymax - Conf.gap_top;
+			cc->geom.y = yorig + cc->bwidth + Conf.gap_top;
+			cc->geom.height = ymax - Conf.gap_top;
 		}
 	}
 }
@@ -793,3 +852,21 @@ _inwindowbounds(struct client_ctx *cc, int x, int y)
 	return (x < cc->geom.width && x >= 0 &&
 	    y < cc->geom.height && y >= 0);
 }
+
+/*
+ * Find which xinerama screen the coordinates (x,y) is on.
+ */
+XineramaScreenInfo *
+client_find_xinerama_screen(int x, int y, struct screen_ctx *sc)
+{
+	XineramaScreenInfo	*info;
+	int			 i;
+
+	for (i = 0; i < sc->xinerama_no; i++) {
+		info = &sc->xinerama[i];
+		if (x > info->x_org && x < info->x_org + info->width &&
+		    y > info->y_org && y < info->y_org + info->height)
+			return (info);
+	}
+	return (NULL);
+}
diff --git a/headers.h b/headers.h
index 2d0f1cc..b973475 100644
--- a/headers.h
+++ b/headers.h
@@ -41,6 +41,8 @@
 
 #include <X11/cursorfont.h>
 #include <X11/extensions/shape.h>
+#include <X11/extensions/Xinerama.h>
+#include <X11/extensions/Xrandr.h>
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #include <X11/keysym.h>
diff --git a/screen.c b/screen.c
index c03003d..70ee0db 100644
--- a/screen.c
+++ b/screen.c
@@ -67,3 +67,28 @@ screen_updatestackingorder(void)
 
 	XFree(wins);
 }
+
+void
+screen_init_xinerama(struct screen_ctx *sc)
+{
+	XineramaScreenInfo *info;
+	int no, fake;
+
+	if (HasXinerama == 0 || XineramaIsActive(X_Dpy) == 0) {
+		HasXinerama = 0;
+		sc->xinerama_no = 0;
+	}
+		
+	info = XineramaQueryScreens(X_Dpy, &no);
+	if (info == NULL) {
+		/*is xinerama is actually off, instead of a malloc failure? */
+		if (sc->xinerama == NULL)
+			HasXinerama = NULL;
+		return;
+	}
+
+	if (sc->xinerama != NULL)
+		XFree(sc->xinerama);
+	sc->xinerama = info;
+	sc->xinerama_no = no;
+}
diff --git a/xevents.c b/xevents.c
index 274af43..dd8de77 100644
--- a/xevents.c
+++ b/xevents.c
@@ -375,6 +375,22 @@ xev_handle_shape(struct xevent *xev, XEvent *ee)
 		client_do_shape(cc);
 }
 
+void
+xev_handle_randr(struct xevent *xev, XEvent *ee)
+{
+	XRRScreenChangeNotifyEvent	*rev = (XRRScreenChangeNotifyEvent *)ee;
+	struct client_ctx		*cc;
+	struct screen_ctx		*sc;
+
+	if ((cc = client_find(rev->window)) != NULL) {
+		XRRUpdateConfiguration(ee);
+		sc = CCTOSC(cc);
+		sc->xmax = rev->width;
+		sc->ymax = rev->height;
+		screen_init_xinerama(sc);
+	}
+}
+
 /* 
  * Called when the keymap has changed.
  * Ungrab all keys, reload keymap and then regrab
@@ -525,6 +541,8 @@ xev_loop(void)
 		default:
 			if (e.type == Shape_ev)
 				xev_handle_shape(xev, &e);
+			else if (e.type == Randr_ev)
+				xev_handle_randr(xev, &e);
 			break;
 		}