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);
pgpOvv1YZTejS.pgp
Description: PGP signature