I have been using dwm for a long time now, and there is one thing from
Compiz/OSX desktops that I miss; Exposé.  I often find myself getting a
little confused about where all my windows are, and want a nice
overview.  The only way to do this properly is with XComposite.  I would
like to point out now that this does not add support for any of the
fancy transparency/shadows etc. that Compiz supports - the windows are
automatically composited by the X server.  The only change is that the
full window contents is now available as offscreen pixmaps.

This patch adds two things to dwm:
 - XComposite & XRender support (very small amount of code)
 - Exposé style window selector (with example use in config.def.h)

Currently all the windows are scaled down and fitted to a grid on the
screen.  When a window is clicked on it is made the master window and
you are taken to its tag(s).  Pressing any key cancels.  It would be
quite easy to add keyboard support, but I only use it when sitting back
(in "mouse mode"), so have not done so.  It works best with monocle
mode.

The patch is against current mercurial tip (rev. 1396), but will
probably work on other versions.

I have tested it a bit, and it shouldn't eat your computer, but no
guarantees.

Tom
-- 
Thomas Spurden
diff -r 440dda47ae5b config.def.h
--- a/config.def.h      Fri May 29 09:29:22 2009 +0100
+++ b/config.def.h      Thu Jun 04 13:29:18 2009 +0100
@@ -78,6 +78,10 @@
        TAGKEYS(                        XK_8,                      7)
        TAGKEYS(                        XK_9,                      8)
        { MODKEY|ShiftMask,             XK_q,      quit,           {0} },
+#ifdef XCOMPOSITE
+       { MODKEY,                       XK_e,      xcompexpose,    { .ui = ~0 } 
},
+       { MODKEY|ShiftMask,             XK_e,      xcompexpose,    { .ui = 0 } 
},
+#endif
 };
 
 /* button definitions */
diff -r 440dda47ae5b config.mk
--- a/config.mk Fri May 29 09:29:22 2009 +0100
+++ b/config.mk Thu Jun 04 13:29:18 2009 +0100
@@ -14,12 +14,16 @@
 XINERAMALIBS = -L${X11LIB} -lXinerama
 XINERAMAFLAGS = -DXINERAMA
 
+# XComposite
+XCOMPOSITELIBS = -L${X11LIB} -lXcomposite -lXrender -lm
+XCOMPOSITEFLAGS = -DXCOMPOSITE
+
 # includes and libs
 INCS = -I. -I/usr/include -I${X11INC}
-LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 ${XINERAMALIBS}
+LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 ${XINERAMALIBS} ${XCOMPOSITELIBS}
 
 # flags
-CPPFLAGS = -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
+CPPFLAGS = -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} ${XCOMPOSITEFLAGS}
 CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
 LDFLAGS = -s ${LIBS}
 
diff -r 440dda47ae5b dwm.c
--- a/dwm.c     Fri May 29 09:29:22 2009 +0100
+++ b/dwm.c     Thu Jun 04 13:29:18 2009 +0100
@@ -39,6 +39,11 @@
 #ifdef XINERAMA
 #include <X11/extensions/Xinerama.h>
 #endif
+#ifdef XCOMPOSITE
+#include <math.h>
+#include <X11/extensions/Xcomposite.h>
+#include <X11/extensions/Xrender.h>
+#endif
 
 /* macros */
 #define BUTTONMASK              (ButtonPressMask|ButtonReleaseMask)
@@ -89,6 +94,12 @@
        Client *next;
        Client *snext;
        Window win;
+#ifdef XCOMPOSITE
+       struct {
+               Picture pic;
+               int x, y, w, h;
+       } composite;
+#endif
 };
 
 typedef struct {
@@ -203,6 +214,17 @@
 static int xerrorstart(Display *dpy, XErrorEvent *ee);
 static void zoom(const Arg *arg);
 
+#ifdef XCOMPOSITE
+static void xcompexpose(const Arg *arg);
+static struct {
+       Picture overlayPic;
+       Window overlayWin;
+       unsigned int tagmask;
+       void (*button)(XEvent*);
+       void (*key)(XEvent*);
+} xcomp;
+#endif
+
 /* variables */
 static char stext[256];
 static int screen;
@@ -964,6 +986,9 @@
        attach(c);
        attachstack(c);
        XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* 
some windows require this */
+#ifdef XCOMPOSITE
+       XCompositeRedirectWindow(dpy, c->win, CompositeRedirectAutomatic);
+#endif
        XMapWindow(dpy, c->win);
        setclientstate(c, NormalState);
        arrange();
@@ -1182,6 +1207,186 @@
        while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
 }
 
+#ifdef XCOMPOSITE
+Picture
+xcomppicfromwin(Window win) {
+       XWindowAttributes attr;
+       XRenderPictFormat* fmt;
+       XRenderPictureAttributes pa;
+       Picture pict;
+
+       XGetWindowAttributes(dpy, win, &attr);
+       fmt = XRenderFindVisualFormat(dpy, attr.visual);
+       pa.subwindow_mode = IncludeInferiors;
+       pict = XRenderCreatePicture(dpy, win, fmt, CPSubwindowMode, &pa);
+
+       return pict;
+}
+
+void
+xcompscaleclient(Client* c, double sf) {
+       XTransform xform = {{
+               { XDoubleToFixed(1/sf), XDoubleToFixed(0), XDoubleToFixed(0) },
+               { XDoubleToFixed(0), XDoubleToFixed(1/sf), XDoubleToFixed(0) },
+               { XDoubleToFixed(0), XDoubleToFixed(0), XDoubleToFixed(1) }
+       }};
+       XRenderSetPictureTransform(dpy, c->composite.pic, &xform);
+       XRenderSetPictureFilter(dpy, c->composite.pic, FilterBilinear, 0, 0);
+       c->composite.w = ((double)c->w * sf);
+       c->composite.h = ((double)c->h * sf);
+}
+
+void xcompfitclient(Client* c, int w, int h) {
+       double sfx = (double)w / (double)c->w;
+       double sfy = (double)h / (double)c->h;
+       xcompscaleclient(c, sfx > sfy ? sfy : sfx);
+}
+
+void
+xcompcleanup(void) {
+       XRenderFreePicture(dpy, xcomp.overlayPic);
+       XCompositeReleaseOverlayWindow(dpy, xcomp.overlayWin);
+       XUngrabPointer(dpy, CurrentTime);
+       handler[ButtonPress] = xcomp.button;
+       handler[KeyPress] = xcomp.key;
+}
+
+void
+xcompbuttonpress(XEvent* ev) {
+       XButtonEvent* bev = &ev->xbutton;
+       Client* c;
+
+       for(c = clients; c != NULL; c = c->next) {
+               if((c->tags & xcomp.tagmask) == 0) {
+                       continue;
+               }
+               if(INRECT(bev->x, bev->y, c->composite.x, c->composite.y, 
c->composite.w, c->composite.h)) {
+                       {
+                               Arg arg;
+                               arg.ui = c->tags;
+                               view(&arg);
+                       }
+                       /* From zoom */
+                       detach(c);
+                       attach(c);
+                       focus(c);
+                       arrange();
+
+                       xcompcleanup();
+                       /* From swarp */
+                       XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w / 2, 
c->h / 2);
+                       XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, 
CurrentTime);
+               }
+       }
+}
+
+void
+xcompkeypress(XEvent* ev) {
+       /* Would be nice to have keyboard control */
+       xcompcleanup();
+}
+
+Bool
+xcomparrangeclients(void) {
+       int nsqx, nsqy;
+       int sqw, sqh;
+       unsigned int n;
+       Client* c;
+
+       for(c = clients, n = 0; c != NULL; c = c->next) {
+               if((c->tags & xcomp.tagmask) == 0) {
+                       continue;
+               }
+               if(c->composite.pic != None) {
+                       XRenderFreePicture(dpy, c->composite.pic);
+               }
+               c->composite.pic = xcomppicfromwin(c->win);
+               c->composite.x = 0;
+               c->composite.y = 0;
+               c->composite.w = c->w;
+               c->composite.h = c->h;
+               n++;
+       }
+
+       if(n <= 1) {
+               return True;
+       }
+
+       nsqy = nsqx = ceil(sqrt((double)n));
+       if(n <= nsqx * (nsqy - 1)) {
+               nsqy -= 1;
+       }
+
+       sqw = sw / nsqx;
+       sqh = sh / nsqy;
+
+       for(n = 0, c = clients; c != NULL; c = c->next) {
+               if((c->tags & xcomp.tagmask) == 0) {
+                       continue;
+               }
+               xcompfitclient(c, sqw - 10, sqh - 10);
+               c->composite.x = (sqw * (n % nsqx));
+               c->composite.y = (sqh * (n / nsqx));
+
+               c->composite.x += (sqw - c->composite.w) / 2;
+               c->composite.y += (sqh - c->composite.h) / 2;
+
+               XRenderComposite(dpy, PictOpOver,
+                       c->composite.pic, None, xcomp.overlayPic,
+                       0, 0, 0, 0,
+                       c->composite.x, c->composite.y,
+                       c->composite.w, c->composite.h
+               );
+               n++;
+       }
+       return False;
+}
+
+void
+xcompexpose(const Arg *arg) {
+       XRenderColor col = { .red = 0, .green = 0, .blue = 0, .alpha = 0x4000 };
+
+       if(arg->ui & TAGMASK) {
+               xcomp.tagmask = arg->ui;
+       } else {
+               xcomp.tagmask = tagset[seltags];
+       }
+
+       if(clients == NULL) {
+               return;
+       }
+       xcomp.overlayWin = XCompositeGetOverlayWindow(dpy, root);
+       if(xcomp.overlayWin == None) {
+               fprintf(stderr, "dwm: composite: Could not get overlay 
window\n");
+               return;
+       }
+       xcomp.overlayPic = xcomppicfromwin(xcomp.overlayWin);
+
+       XRenderFillRectangle(dpy, PictOpOver, xcomp.overlayPic, &col, 0, 0, sw, 
sh);
+
+       if(xcomparrangeclients()) {
+               XRenderFreePicture(dpy, xcomp.overlayPic);
+               XCompositeReleaseOverlayWindow(dpy, xcomp.overlayWin);
+               return;
+       }
+
+       if(XGrabPointer(dpy, xcomp.overlayWin, True, ButtonPressMask, 
GrabModeAsync, GrabModeAsync, xcomp.overlayWin, None, CurrentTime) != 
GrabSuccess) {
+               fprintf(stderr, "dwm: composite: Could not grab pointer\n");
+               xcompcleanup();
+               return;
+       }
+       if(XGrabKeyboard(dpy, xcomp.overlayWin, True, GrabModeAsync, 
GrabModeAsync, CurrentTime) != GrabSuccess) {
+               fprintf(stderr, "dwm: composite: Could not grab keyboard\n");
+               xcompcleanup();
+               return;
+       }
+       xcomp.button = handler[ButtonPress];
+       xcomp.key = handler[KeyPress];
+       handler[ButtonPress] = &xcompbuttonpress;
+       handler[KeyPress] = &xcompkeypress;
+}
+#endif
+
 void
 run(void) {
        XEvent ev;
@@ -1273,6 +1478,30 @@
        lt[1] = &layouts[1 % LENGTH(layouts)];
        updategeom();
 
+#ifdef XCOMPOSITE
+       {
+               int event_base, error_base, major = 0, minor = 2;
+               if(!XRenderQueryExtension(dpy, &event_base, &error_base)) {
+                       die("Server does not support XRender");
+               }
+               major = 0;
+               minor = 10;
+               XRenderQueryVersion(dpy, &major, &minor);
+               if(minor < 10) {
+                       die("Server XRender version %i.%i incompatible", major, 
minor);
+               }
+               if(!XCompositeQueryExtension(dpy, &event_base, &error_base)) {
+                       die("Server does not support XComposite");
+               }
+               major = 0;
+               minor = 2;
+               XCompositeQueryVersion(dpy, &major, &minor);
+               if(minor < 2) {
+                       die("Server XComposite version %i.%i incompatible", 
major, minor);
+               }
+               XCompositeRedirectSubwindows(dpy, screen, 
CompositeRedirectAutomatic);
+       }
+#endif
        /* init atoms */
        wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
        wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);

Attachment: pgpOvv1YZTejS.pgp
Description: PGP signature

Reply via email to