# HG changeset patch # User meillo@marmaro.de # Date 1212178698 -7200 # Node ID 45f23169563eade62b1ff0173060bef2e8c17e9a # Parent bc512840e5a551ef552316512f250f90210013f6 renamed dwm to aewl updated copyright notices updated man page diff -r bc512840e5a5 -r 45f23169563e LICENSE --- a/LICENSE Fri May 30 21:57:51 2008 +0200 +++ b/LICENSE Fri May 30 22:18:18 2008 +0200 @@ -2,21 +2,22 @@ (C)opyright MMVI-MMVII Anselm R. Garbe (C)opyright MMVI-MMVII Sander van Dijk +(C)opyright MMVIII markus schnalke Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff -r bc512840e5a5 -r 45f23169563e Makefile --- a/Makefile Fri May 30 21:57:51 2008 +0200 +++ b/Makefile Fri May 30 22:18:18 2008 +0200 @@ -1,53 +1,53 @@ -# dwm - dynamic window manager +# aewl - a fork of dwm, the dynamic window manager # (C)opyright MMVI-MMVII Anselm R. Garbe include config.mk -all: options dwm +all: options aewl options: - @echo dwm build options: + @echo aewl build options: @echo "CFLAGS = ${CFLAGS}" @echo "LDFLAGS = ${LDFLAGS}" @echo "CC = ${CC}" -dwm.o: dwm.c config.h config.mk +aewl.o: aewl.c config.h config.mk @echo CC $< @${CC} -c ${CFLAGS} $< -dwm: dwm.o +aewl: aewl.o @echo CC -o $@ - @${CC} -o $@ dwm.o ${LDFLAGS} + @${CC} -o $@ aewl.o ${LDFLAGS} @strip $@ clean: @echo cleaning - @rm -f dwm *.o dwm-${VERSION}.tar.gz + @rm -f aewl *.o aewl-*.tar.gz dist: clean @echo creating dist tarball - @mkdir -p dwm-${VERSION} - @cp -R LICENSE Makefile README config.*.h config.mk \ - dwm.1 dwm.h ${SRC} dwm-${VERSION} - @tar -cf dwm-${VERSION}.tar dwm-${VERSION} - @gzip dwm-${VERSION}.tar - @rm -rf dwm-${VERSION} + @mkdir -p aewl-${VERSION} + @cp -R LICENSE Makefile README config.h config.mk \ + aewl.1 aewl.h ${SRC} aewl-${VERSION} + @tar -cf aewl-${VERSION}.tar aewl-${VERSION} + @gzip aewl-${VERSION}.tar + @rm -rf aewl-${VERSION} install: all @echo installing executable file to ${DESTDIR}${PREFIX}/bin @mkdir -p ${DESTDIR}${PREFIX}/bin - @cp -f dwm ${DESTDIR}${PREFIX}/bin - @chmod 755 ${DESTDIR}${PREFIX}/bin/dwm + @cp -f aewl ${DESTDIR}${PREFIX}/bin + @chmod 755 ${DESTDIR}${PREFIX}/bin/aewl @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1 @mkdir -p ${DESTDIR}${MANPREFIX}/man1 - @sed 's/VERSION/${VERSION}/g' < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 - @chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 + @sed 's/VERSION/${VERSION}/g' < aewl.1 > ${DESTDIR}${MANPREFIX}/man1/aewl.1 + @chmod 644 ${DESTDIR}${MANPREFIX}/man1/aewl.1 uninstall: @echo removing executable file from ${DESTDIR}${PREFIX}/bin - @rm -f ${DESTDIR}${PREFIX}/bin/dwm + @rm -f ${DESTDIR}${PREFIX}/bin/aewl @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 - @rm -f ${DESTDIR}${MANPREFIX}/man1/dwm.1 + @rm -f ${DESTDIR}${MANPREFIX}/man1/aewl.1 .PHONY: all options clean dist install uninstall diff -r bc512840e5a5 -r 45f23169563e aewl.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/aewl.1 Fri May 30 22:18:18 2008 +0200 @@ -0,0 +1,127 @@ +.TH AEWL 1 aewl-VERSION + + +.SH NAME +aewl \- a fork of dwm, the dynamic window manager + + +.SH SYNOPSIS +.B aewl +.RB [ \-v ] + + +.SH DESCRIPTION +This man page is the one from dwm 4.4.1 on which aewl is based. +It is partly updated, but not totally. +.P +dwm is a dynamic window manager for X. It manages windows in tiling and +floating modes. Either mode can be applied dynamically, optimizing the +environment for the application in use and the task performed. +.P +In tiling mode windows are managed in a master and stacking area. The master +area contains the windows which currently need most attention, whereas the +stacking area contains all other windows. In floating mode windows can be +resized and moved freely. Dialog windows are always managed floating, +regardless of the mode applied. +.P +Windows are grouped by tags. Each window can be tagged with one or multiple +tags. Selecting certain tags displays all windows with these tags. +.P +dwm contains a small status bar which displays all available tags, the mode, +the title of the focused window, and the text read from standard input. The +selected tags are indicated with a different color. The tags of the focused +window are indicated with a filled square in the top left corner. The tags +which are applied to one or more windows are indicated with an empty square in +the top left corner. +.P +dwm draws a small border around windows to indicate the focus state. + + +.SH OPTIONS +.TP +.B \-v +prints version information to standard output, then exits. + + +.SH USAGE + +.SS Status bar +.TP +.B Standard input +is read and displayed in the status text area. +.TP +.B Button1 +click on the bar to display the other tag. + +.SS Keyboard commands +.TP +.B Mod1-Shift-Return +Start +.BR xterm (1). +.TP +.B Mod1-Tab +Focus next window. +.TP +.B Mod1-i +Increase the number of windows in the master area (tiling mode only). +.TP +.B Mod1-d +Decrease the number of windows in the master area (tiling mode only). +.TP +.B Mod1-F1 +View other tag. +.TP +.B Mod1-F3 +Move current window to other tag. +.TP +.B Mod1-^ +Start +.BR dmenu (1) +.TP +.B Mod1-1 +Zooms/cycles current window to/from master area (tiling mode), toggles maximization current window (floating mode). +.TP +.B Mod1-2 +Close focused window. +.TP +.B Mod1-space +Toggle between tiling and max mode (affects all windows). +.TP +.B Mod1-Shift-space +Toggle focused window between floating and non-floating state. +.TP +.B Mod1-Shift-q +Quit dwm. + +.SS Mouse commands +.TP +.B Mod1-Button1 +Move current window while dragging (floating mode only). +.TP +.B Mod1-Button3 +Resize current window while dragging (floating mode only). + + +.SH CUSTOMIZATION +dwm is customized by creating a custom config.h and (re)compiling the source +code. This keeps it fast, secure and simple. + + +.SH SEE ALSO +.BR dmenu (1) + + +.SH BUGS +The status bar may display +.BR "EOF" +when dwm has been started by an X session manager like +.BR xdm (1), +because those close standard output before executing dwm. +.P +Java applications which use the XToolkit/XAWT backend may draw grey windows +only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early +JDK 1.6 versions, because it assumes a reparenting window manager. As a workaround +you can use JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or you +can set the following environment variable (to use the older Motif +backend instead): +.BR AWT_TOOLKIT=MToolkit . diff -r bc512840e5a5 -r 45f23169563e aewl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/aewl.c Fri May 30 22:18:18 2008 +0200 @@ -0,0 +1,1783 @@ +/* (C)opyright MMVI-MMVII Anselm R. Garbe + * (C)opyright MMVIII markus schnalke + * See LICENSE file for license details. + * + * dynamic window manager is designed like any other X client as well. It is + * driven through handling X events. In contrast to other X clients, a window + * manager selects for SubstructureRedirectMask on the root window, to receive + * events about window (dis-)appearance. Only one X connection at a time is + * allowed to select for this event mask. + * + * Calls to fetch an X event from the event queue are blocking. Due reading + * status text from standard input, a select()-driven main loop has been + * implemented which selects for reads on the X connection and STDIN_FILENO to + * handle all data smoothly. The event handlers of dwm are organized in an + * array which is accessed whenever a new event has been fetched. This allows + * event dispatching in O(1) time. + * + * Each child of the root window is called a client, except windows which have + * set the override_redirect flag. Clients are organized in a global + * doubly-linked client list, the focus history is remembered through a global + * stack list. Each client contains an array of Bools of the same size as the + * global tags array to indicate the tags of a client. For each client dwm + * creates a small title window, which is resized whenever the (_NET_)WM_NAME + * properties are updated or the client is moved/resized. + * + * Keys and tagging rules are organized as arrays and defined in the config.h + * file. These arrays are kept static in event.o and tag.o respectively, + * because no other part of dwm needs access to them. The current mode is + * represented by the arrange() function pointer, which wether points to + * dofloat() or dotile(). + * + * To understand everything else, start reading main.c:main(). + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* mask shorthands, used in event.c and client.c */ +#define BUTTONMASK (ButtonPressMask | ButtonReleaseMask) + +enum { NetSupported, NetWMName, NetLast }; /* EWMH atoms */ +enum { WMProtocols, WMDelete, WMState, WMLast }; /* default atoms */ +enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +enum { ColBorder, ColFG, ColBG, ColLast }; /* color */ + +typedef struct { + int ascent; + int descent; + int height; + XFontSet set; + XFontStruct *xfont; +} Fnt; + +typedef struct { + int x, y, w, h; + unsigned long norm[ColLast]; + unsigned long sel[ColLast]; + Drawable drawable; + Fnt font; + GC gc; +} DC; /* draw context */ + +typedef struct Client Client; +struct Client { + char name[256]; + int x, y, w, h; + int rx, ry, rw, rh; /* revert geometry */ + int basew, baseh, incw, inch, maxw, maxh, minw, minh; + int minax, minay, maxax, maxay; + long flags; + unsigned int border; + Bool isfixed, isfloat, ismax; + Bool tag; + Client *next; + Client *prev; + Client *snext; + Window win; +}; + +typedef struct { + const char *clpattern; + int tag; + Bool isfloat; +} Rule; + +typedef struct { + regex_t *clregex; +} RReg; + + +typedef struct { + unsigned long mod; + KeySym keysym; + void (*func)(const char* cmd); + const char* cmd; +} Key; + + +#define CLEANMASK(mask) (mask & ~(numlockmask | LockMask)) +#define MOUSEMASK (BUTTONMASK | PointerMotionMask) + + + +const char *tags[]; /* all tags */ +char stext[256]; /* status text */ +int bh, bmw; /* bar height, bar mode label width */ +int screen, sx, sy, sw, sh; /* screen geometry */ +int wax, way, wah, waw; /* windowarea geometry */ +unsigned int nmaster; /* number of master clients */ +unsigned int ntags, numlockmask; /* number of tags, dynamic lock mask */ +void (*handler[LASTEvent])(XEvent *); /* event handler */ +void (*arrange)(void); /* arrange function, indicates mode */ +Atom wmatom[WMLast], netatom[NetLast]; +Bool running, selscreen, seltag; /* seltag is array of Bool */ +Client *clients, *sel, *stack; /* global client list and stack */ +Cursor cursor[CurLast]; +DC dc; /* global draw context */ +Display *dpy; +Window root, barwin; + +Bool running = True; +Bool selscreen = True; +Client *clients = NULL; +Client *sel = NULL; +Client *stack = NULL; +DC dc = {0}; + +static int (*xerrorxlib)(Display *, XErrorEvent *); +static Bool otherwm, readin; +static RReg *rreg = NULL; +static unsigned int len = 0; + + +TAGS +RULES + + +/* client.c */ +void configure(Client *c); /* send synthetic configure event */ +void focus(Client *c); /* focus c, c may be NULL */ +Client *getclient(Window w); /* return client of w */ +Bool isprotodel(Client *c); /* returns True if c->win supports wmatom[WMDelete] */ +void manage(Window w, XWindowAttributes *wa); /* manage new client */ +void resize(Client *c, Bool sizehints); /* resize c*/ +void updatesizehints(Client *c); /* update the size hint variables of c */ +void updatetitle(Client *c); /* update the name of c */ +void unmanage(Client *c); /* destroy c */ + +/* draw.c */ +void drawstatus(void); /* draw the bar */ +unsigned long getcolor(const char *colstr); /* return color of colstr */ +void setfont(const char *fontstr); /* set the font for DC */ +unsigned int textw(const char *text); /* return the width of text in px*/ + +/* event.c */ +void grabkeys(void); /* grab all keys defined in config.h */ +void procevent(void); /* process pending X events */ + +/* main.c */ +void sendevent(Window w, Atom a, long value); /* send synthetic event to w */ +int xerror(Display *dsply, XErrorEvent *ee); /* dwm's X error handler */ + +/* tag.c */ +void initrregs(void); /* initialize regexps of rules defined in config.h */ +Client *getnext(Client *c); /* returns next visible client */ +void settags(Client *c, Client *trans); /* sets tags of c */ + +/* util.c */ +void *emallocz(unsigned int size); /* allocates zero-initialized memory, exits on error */ +void eprint(const char *errstr, ...); /* prints errstr and exits with 1 */ + +/* view.c */ +void detach(Client *c); /* detaches c from global client list */ +void dofloat(void); /* arranges all windows floating */ +void dotile(void); /* arranges all windows tiled */ +void domax(void); /* arranges all windows fullscreen */ +Bool isvisible(Client *c); /* returns True if client is visible */ +void restack(void); /* restores z layers of all clients */ + + +void toggleview(); /* toggle the viewed tag */ +void focusnext(); /* focuses next visible client */ +void zoom(); /* zooms the focused client to master area */ +void killclient(); /* kill c nicely */ +void quit(); /* quit dwm nicely */ +void togglemode(); /* toggles global arrange function (dotile/dofloat) */ +void togglefloat(); /* toggles focusesd client between floating/non-floating state */ +void incnmaster(); /* increments nmaster */ +void decnmaster(); /* decrements nmaster */ +void toggletag(); /* toggles c tag */ +void spawn(const char* cmd); /* forks a new subprocess with cmd */ + + + + + + + + + + +/* from view.c */ +/* static */ + +static Client * +nexttiled(Client *c) { + for(c = getnext(c); c && c->isfloat; c = getnext(c->next)); + return c; +} + +static void +togglemax(Client *c) { + XEvent ev; + + if(c->isfixed) + return; + + if((c->ismax = !c->ismax)) { + c->rx = c->x; c->x = wax; + c->ry = c->y; c->y = way; + c->rw = c->w; c->w = waw - 2 * BORDERPX; + c->rh = c->h; c->h = wah - 2 * BORDERPX; + } + else { + c->x = c->rx; + c->y = c->ry; + c->w = c->rw; + c->h = c->rh; + } + resize(c, True); + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + + + +void (*arrange)(void) = DEFMODE; + +void +detach(Client *c) { + if(c->prev) + c->prev->next = c->next; + if(c->next) + c->next->prev = c->prev; + if(c == clients) + clients = c->next; + c->next = c->prev = NULL; +} + +void +dofloat(void) { + Client *c; + + for(c = clients; c; c = c->next) { + if(isvisible(c)) { + resize(c, True); + } + else + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + } + if(!sel || !isvisible(sel)) { + for(c = stack; c && !isvisible(c); c = c->snext); + focus(c); + } + restack(); +} + +void +dotile(void) { + unsigned int i, n, mw, mh, tw, th; + Client *c; + + for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) + n++; + /* window geoms */ + mh = (n > nmaster) ? wah / nmaster : wah / (n > 0 ? n : 1); + mw = (n > nmaster) ? waw / 2 : waw; + th = (n > nmaster) ? wah / (n - nmaster) : 0; + tw = waw - mw; + + for(i = 0, c = clients; c; c = c->next) + if(isvisible(c)) { + if(c->isfloat) { + resize(c, True); + continue; + } + c->ismax = False; + c->x = wax; + c->y = way; + if(i < nmaster) { + c->y += i * mh; + c->w = mw - 2 * BORDERPX; + c->h = mh - 2 * BORDERPX; + } + else { /* tile window */ + c->x += mw; + c->w = tw - 2 * BORDERPX; + if(th > 2 * BORDERPX) { + c->y += (i - nmaster) * th; + c->h = th - 2 * BORDERPX; + } + else /* fallback if th <= 2 * BORDERPX */ + c->h = wah - 2 * BORDERPX; + } + resize(c, False); + i++; + } + else + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + if(!sel || !isvisible(sel)) { + for(c = stack; c && !isvisible(c); c = c->snext); + focus(c); + } + restack(); +} + +/* begin code by mitch */ +void +arrangemax(Client *c) { + if(c == sel) { + c->ismax = True; + c->x = sx; + c->y = bh; + c->w = sw - 2 * BORDERPX; + c->h = sh - bh - 2 * BORDERPX; + XRaiseWindow(dpy, c->win); + } else { + c->ismax = False; + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + XLowerWindow(dpy, c->win); + } +} + +void +domax(void) { + Client *c; + + for(c = clients; c; c = c->next) { + if(isvisible(c)) { + if(c->isfloat) { + resize(c, True); + continue; + } + arrangemax(c); + resize(c, False); + } else { + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + } + + } + if(!sel || !isvisible(sel)) { + for(c = stack; c && !isvisible(c); c = c->snext); + focus(c); + } + restack(); +} +/* end code by mitch */ + +void +focusnext() { + Client *c; + + if(!sel) + return; + if(!(c = getnext(sel->next))) + c = getnext(clients); + if(c) { + focus(c); + restack(); + } +} + +void +incnmaster() { + if((arrange == dofloat) || (wah / (nmaster + 1) <= 2 * BORDERPX)) + return; + nmaster++; + if(sel) + arrange(); + else + drawstatus(); +} + +void +decnmaster() { + if((arrange == dofloat) || (nmaster <= 1)) + return; + nmaster--; + if(sel) + arrange(); + else + drawstatus(); +} + +Bool +isvisible(Client *c) { + if((c->tag && seltag) || (!c->tag && !seltag)) { + return True; + } + return False; +} + +void +restack(void) { + Client *c; + XEvent ev; + + drawstatus(); + if(!sel) + return; + if(sel->isfloat || arrange == dofloat) + XRaiseWindow(dpy, sel->win); + + /* begin code by mitch */ + if(arrange == domax) { + for(c = nexttiled(clients); c; c = nexttiled(c->next)) { + arrangemax(c); + resize(c, False); + } + + } else if (arrange == dotile) { + /* end code by mitch */ + + if(!sel->isfloat) + XLowerWindow(dpy, sel->win); + for(c = nexttiled(clients); c; c = nexttiled(c->next)) { + if(c == sel) + continue; + XLowerWindow(dpy, c->win); + } + } + XSync(dpy, False); + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +void +togglefloat() { + if (!sel || arrange == dofloat) + return; + sel->isfloat = !sel->isfloat; + arrange(); +} + +void +togglemode() { + /* only toggle between tile and max - float is just available through togglefloat */ + arrange = (arrange == dotile) ? domax : dotile; + if(sel) + arrange(); + else + drawstatus(); +} + +void +toggleview() { + seltag = !seltag; + arrange(); +} + +void +zoom() { + unsigned int n; + Client *c; + + if(!sel) + return; + if(sel->isfloat || (arrange == dofloat)) { + togglemax(sel); + return; + } + for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) + n++; + + if((c = sel) == nexttiled(clients)) + if(!(c = nexttiled(c->next))) + return; + detach(c); + if(clients) + clients->prev = c; + c->next = clients; + clients = c; + focus(c); + arrange(); +} + + + + + + + + + + + + + + + + +/* from util.c */ + + +void * +emallocz(unsigned int size) { + void *res = calloc(1, size); + + if(!res) + eprint("fatal: could not malloc() %u bytes\n", size); + return res; +} + +void +eprint(const char *errstr, ...) { + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(EXIT_FAILURE); +} + +void +spawn(const char* cmd) { + static char *shell = NULL; + + if(!shell && !(shell = getenv("SHELL"))) + shell = "/bin/sh"; + if(!cmd) + return; + /* The double-fork construct avoids zombie processes and keeps the code + * clean from stupid signal handlers. */ + if(fork() == 0) { + if(fork() == 0) { + if(dpy) + close(ConnectionNumber(dpy)); + setsid(); + execl(shell, shell, "-c", cmd, (char *)NULL); + fprintf(stderr, "dwm: execl '%s -c %s'", shell, cmd); + perror(" failed"); + } + exit(0); + } + wait(0); +} + + + + + + + + + + + + + +/* from tag.c */ + +/* static */ + +Client * +getnext(Client *c) { + for(; c && !isvisible(c); c = c->next); + return c; +} + +void +initrregs(void) { + unsigned int i; + regex_t *reg; + + if(rreg) + return; + len = sizeof rule / sizeof rule[0]; + rreg = emallocz(len * sizeof(RReg)); + for(i = 0; i < len; i++) { + if(rule[i].clpattern) { + reg = emallocz(sizeof(regex_t)); + if(regcomp(reg, rule[i].clpattern, REG_EXTENDED)) + free(reg); + else + rreg[i].clregex = reg; + } + } +} + +void +settags(Client *c, Client *trans) { + char prop[512]; + unsigned int i; + regmatch_t tmp; + Bool matched = trans != NULL; + XClassHint ch = { 0 }; + + if(matched) { + c->tag = trans->tag; + } else { + XGetClassHint(dpy, c->win, &ch); + snprintf(prop, sizeof prop, "%s:%s:%s", + ch.res_class ? ch.res_class : "", + ch.res_name ? ch.res_name : "", c->name); + for(i = 0; i < len; i++) + if(rreg[i].clregex && !regexec(rreg[i].clregex, prop, 1, &tmp, 0)) { + c->isfloat = rule[i].isfloat; + if (rule[i].tag < 0) { + c->tag = seltag; + } else if (rule[i].tag == 0) { + c->tag = True; + } else { + c->tag = False; + } + matched = True; + break; /* perform only the first rule matching */ + } + if(ch.res_class) + XFree(ch.res_class); + if(ch.res_name) + XFree(ch.res_name); + } + if(!matched) { + c->tag = seltag; + } +} + +void +toggletag() { + if(!sel) + return; + sel->tag = !sel->tag; + toggleview(); +} + + + + + + + + + + + + + + + + + +/* from event.c */ +/* static */ + +KEYS + + + +static void +movemouse(Client *c) { + int x1, y1, ocx, ocy, di; + unsigned int dui; + Window dummy; + XEvent ev; + + ocx = c->x; + ocy = c->y; + if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove], CurrentTime) != GrabSuccess) + return; + c->ismax = False; + XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui); + for(;;) { + XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev); + switch (ev.type) { + case ButtonRelease: + resize(c, True); + XUngrabPointer(dpy, CurrentTime); + return; + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + XSync(dpy, False); + c->x = ocx + (ev.xmotion.x - x1); + c->y = ocy + (ev.xmotion.y - y1); + if(abs(wax + c->x) < SNAP) + c->x = wax; + else if(abs((wax + waw) - (c->x + c->w + 2 * c->border)) < SNAP) + c->x = wax + waw - c->w - 2 * c->border; + if(abs(way - c->y) < SNAP) + c->y = way; + else if(abs((way + wah) - (c->y + c->h + 2 * c->border)) < SNAP) + c->y = way + wah - c->h - 2 * c->border; + resize(c, False); + break; + } + } +} + +static void +resizemouse(Client *c) { + int ocx, ocy; + int nw, nh; + XEvent ev; + + ocx = c->x; + ocy = c->y; + if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize], CurrentTime) != GrabSuccess) + return; + c->ismax = False; + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1); + for(;;) { + XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask , &ev); + switch(ev.type) { + case ButtonRelease: + resize(c, True); + XUngrabPointer(dpy, CurrentTime); + return; + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + XSync(dpy, False); + nw = ev.xmotion.x - ocx - 2 * c->border + 1; + c->w = nw > 0 ? nw : 1; + nh = ev.xmotion.y - ocy - 2 * c->border + 1; + c->h = nh > 0 ? nh : 1; + resize(c, True); + break; + } + } +} + +static void +buttonpress(XEvent *e) { + int x; + int i; + Client *c; + XButtonPressedEvent *ev = &e->xbutton; + + if(barwin == ev->window) { + if(ev->button == Button1) { + toggleview(); + } + return; + } else if((c = getclient(ev->window))) { + focus(c); + if(CLEANMASK(ev->state) != MODKEY) + return; + if(ev->button == Button1 && (arrange == dofloat || c->isfloat)) { + restack(); + movemouse(c); + } else if(ev->button == Button3 && (arrange == dofloat || c->isfloat) && !c->isfixed) { + restack(); + resizemouse(c); + } + } +} + +static void +configurerequest(XEvent *e) { + unsigned long newmask; + Client *c; + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + + if((c = getclient(ev->window))) { + c->ismax = False; + if(ev->value_mask & CWX) + c->x = ev->x; + if(ev->value_mask & CWY) + c->y = ev->y; + if(ev->value_mask & CWWidth) + c->w = ev->width; + if(ev->value_mask & CWHeight) + c->h = ev->height; + if(ev->value_mask & CWBorderWidth) + c->border = ev->border_width; + wc.x = c->x; + wc.y = c->y; + wc.width = c->w; + wc.height = c->h; + newmask = ev->value_mask & (~(CWSibling | CWStackMode | CWBorderWidth)); + if(newmask) + XConfigureWindow(dpy, c->win, newmask, &wc); + else + configure(c); + XSync(dpy, False); + if(c->isfloat) { + resize(c, False); + if(!isvisible(c)) + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + } + else + arrange(); + } else { + wc.x = ev->x; + wc.y = ev->y; + wc.width = ev->width; + wc.height = ev->height; + wc.border_width = ev->border_width; + wc.sibling = ev->above; + wc.stack_mode = ev->detail; + XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); + XSync(dpy, False); + } +} + +static void +destroynotify(XEvent *e) { + Client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if((c = getclient(ev->window))) + unmanage(c); +} + +static void +enternotify(XEvent *e) { + Client *c; + XCrossingEvent *ev = &e->xcrossing; + + if(ev->mode != NotifyNormal || ev->detail == NotifyInferior) + return; + if((c = getclient(ev->window)) && isvisible(c)) + focus(c); + else if(ev->window == root) { + selscreen = True; + for(c = stack; c && !isvisible(c); c = c->snext); + focus(c); + } +} + +static void +expose(XEvent *e) { + XExposeEvent *ev = &e->xexpose; + + if(ev->count == 0) { + if(barwin == ev->window) + drawstatus(); + } +} + +static void +keypress(XEvent *e) { + static unsigned int len = sizeof key / sizeof key[0]; + unsigned int i; + KeySym keysym; + XKeyEvent *ev = &e->xkey; + + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + for(i = 0; i < len; i++) { + if(keysym == key[i].keysym && CLEANMASK(key[i].mod) == CLEANMASK(ev->state)) { + if(key[i].func) + key[i].func(key[i].cmd); + } + } +} + +static void +leavenotify(XEvent *e) { + XCrossingEvent *ev = &e->xcrossing; + + if((ev->window == root) && !ev->same_screen) { + selscreen = False; + focus(NULL); + } +} + +static void +mappingnotify(XEvent *e) { + XMappingEvent *ev = &e->xmapping; + + XRefreshKeyboardMapping(ev); + if(ev->request == MappingKeyboard) + grabkeys(); +} + +static void +maprequest(XEvent *e) { + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + + if(!XGetWindowAttributes(dpy, ev->window, &wa)) + return; + if(wa.override_redirect) { + XSelectInput(dpy, ev->window, + (StructureNotifyMask | PropertyChangeMask)); + return; + } + if(!getclient(ev->window)) + manage(ev->window, &wa); +} + +static void +propertynotify(XEvent *e) { + Client *c; + Window trans; + XPropertyEvent *ev = &e->xproperty; + + if(ev->state == PropertyDelete) + return; /* ignore */ + if((c = getclient(ev->window))) { + switch (ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: + XGetTransientForHint(dpy, c->win, &trans); + if(!c->isfloat && (c->isfloat = (trans != 0))) + arrange(); + break; + case XA_WM_NORMAL_HINTS: + updatesizehints(c); + break; + } + if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + updatetitle(c); + if(c == sel) + drawstatus(); + } + } +} + +static void +unmapnotify(XEvent *e) { + Client *c; + XUnmapEvent *ev = &e->xunmap; + + if((c = getclient(ev->window))) + unmanage(c); +} + + + +void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ConfigureRequest] = configurerequest, + [DestroyNotify] = destroynotify, + [EnterNotify] = enternotify, + [LeaveNotify] = leavenotify, + [Expose] = expose, + [KeyPress] = keypress, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, + [PropertyNotify] = propertynotify, + [UnmapNotify] = unmapnotify +}; + +void +grabkeys(void) { + static unsigned int len = sizeof key / sizeof key[0]; + unsigned int i; + KeyCode code; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for(i = 0; i < len; i++) { + code = XKeysymToKeycode(dpy, key[i].keysym); + XGrabKey(dpy, code, key[i].mod, root, True, + GrabModeAsync, GrabModeAsync); + XGrabKey(dpy, code, key[i].mod | LockMask, root, True, + GrabModeAsync, GrabModeAsync); + XGrabKey(dpy, code, key[i].mod | numlockmask, root, True, + GrabModeAsync, GrabModeAsync); + XGrabKey(dpy, code, key[i].mod | numlockmask | LockMask, root, True, + GrabModeAsync, GrabModeAsync); + } +} + +void +procevent(void) { + XEvent ev; + + while(XPending(dpy)) { + XNextEvent(dpy, &ev); + if(handler[ev.type]) + (handler[ev.type])(&ev); /* call handler */ + } +} + + + + + + + + + + + + + + + +/* from draw.c */ +/* static */ + +static unsigned int +textnw(const char *text, unsigned int len) { + XRectangle r; + + if(dc.font.set) { + XmbTextExtents(dc.font.set, text, len, NULL, &r); + return r.width; + } + return XTextWidth(dc.font.xfont, text, len); +} + +static void +drawtext(const char *text, unsigned long col[ColLast]) { + int x, y, w, h; + static char buf[256]; + unsigned int len, olen; + XGCValues gcv; + XRectangle r = { dc.x, dc.y, dc.w, dc.h }; + + XSetForeground(dpy, dc.gc, col[ColBG]); + XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); + if(!text) + return; + w = 0; + olen = len = strlen(text); + if(len >= sizeof buf) + len = sizeof buf - 1; + memcpy(buf, text, len); + buf[len] = 0; + h = dc.font.ascent + dc.font.descent; + y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent; + x = dc.x + (h / 2); + /* shorten text if necessary */ + while(len && (w = textnw(buf, len)) > dc.w - h) + buf[--len] = 0; + if(len < olen) { + if(len > 1) + buf[len - 1] = '.'; + if(len > 2) + buf[len - 2] = '.'; + if(len > 3) + buf[len - 3] = '.'; + } + if(w > dc.w) + return; /* too long */ + gcv.foreground = col[ColFG]; + if(dc.font.set) { + XChangeGC(dpy, dc.gc, GCForeground, &gcv); + XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); + } else { + gcv.font = dc.font.xfont->fid; + XChangeGC(dpy, dc.gc, GCForeground | GCFont, &gcv); + XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); + } +} + + + +void +drawstatus(void) { + int x; + unsigned int i; + + dc.x = dc.y = 0; + for(i = 0; i < ntags; i++) { + dc.w = textw(tags[i]); + drawtext(tags[i], ( ((i == 0 && seltag) || (i == 1 && !seltag)) ? dc.sel : dc.norm)); + dc.x += dc.w + 1; + } + dc.w = bmw; + drawtext("", dc.norm); + x = dc.x + dc.w; + dc.w = textw(stext); + dc.x = sw - dc.w; + if(dc.x < x) { + dc.x = x; + dc.w = sw - x; + } + drawtext(stext, dc.norm); + if((dc.w = dc.x - x) > bh) { + dc.x = x; + drawtext(sel ? sel->name : NULL, dc.norm); + } + XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0); + XSync(dpy, False); +} + +unsigned long +getcolor(const char *colstr) { + Colormap cmap = DefaultColormap(dpy, screen); + XColor color; + + if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) + eprint("error, cannot allocate color '%s'\n", colstr); + return color.pixel; +} + +void +setfont(const char *fontstr) { + char *def, **missing; + int i, n; + + missing = NULL; + if(dc.font.set) + XFreeFontSet(dpy, dc.font.set); + dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def); + if(missing) { + while(n--) + fprintf(stderr, "missing fontset: %s\n", missing[n]); + XFreeStringList(missing); + } + if(dc.font.set) { + XFontSetExtents *font_extents; + XFontStruct **xfonts; + char **font_names; + dc.font.ascent = dc.font.descent = 0; + font_extents = XExtentsOfFontSet(dc.font.set); + n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); + for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) { + if(dc.font.ascent < (*xfonts)->ascent) + dc.font.ascent = (*xfonts)->ascent; + if(dc.font.descent < (*xfonts)->descent) + dc.font.descent = (*xfonts)->descent; + xfonts++; + } + } else { + if(dc.font.xfont) + XFreeFont(dpy, dc.font.xfont); + dc.font.xfont = NULL; + if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))) + eprint("error, cannot load font: '%s'\n", fontstr); + dc.font.ascent = dc.font.xfont->ascent; + dc.font.descent = dc.font.xfont->descent; + } + dc.font.height = dc.font.ascent + dc.font.descent; +} + +unsigned int +textw(const char *text) { + return textnw(text, strlen(text)) + dc.font.height; +} + + + + + + + + + + + +/* from client.c */ +/* static */ + +static void +detachstack(Client *c) { + Client **tc; + for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext); + *tc = c->snext; +} + +static void +grabbuttons(Client *c, Bool focused) { + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + + if(focused) { + XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + + XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + + XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + } else { + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + } +} + +static void +setclientstate(Client *c, long state) { + long data[] = {state, None}; + XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, + PropModeReplace, (unsigned char *)data, 2); +} + +static int +xerrordummy(Display *dsply, XErrorEvent *ee) { + return 0; +} + + + +void +configure(Client *c) { + XEvent synev; + + synev.type = ConfigureNotify; + synev.xconfigure.display = dpy; + synev.xconfigure.event = c->win; + synev.xconfigure.window = c->win; + synev.xconfigure.x = c->x; + synev.xconfigure.y = c->y; + synev.xconfigure.width = c->w; + synev.xconfigure.height = c->h; + synev.xconfigure.border_width = c->border; + synev.xconfigure.above = None; + XSendEvent(dpy, c->win, True, NoEventMask, &synev); +} + +void +focus(Client *c) { + if(c && !isvisible(c)) + return; + if(sel && sel != c) { + grabbuttons(sel, False); + XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]); + } + if(c) { + detachstack(c); + c->snext = stack; + stack = c; + grabbuttons(c, True); + } + sel = c; + drawstatus(); + if(!selscreen) + return; + if(c) { + XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]); + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + } else { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + } +} + +Client * +getclient(Window w) { + Client *c; + + for(c = clients; c; c = c->next) { + if(c->win == w) { + return c; + } + } + return NULL; +} + +Bool +isprotodel(Client *c) { + int i, n; + Atom *protocols; + Bool ret = False; + + if(XGetWMProtocols(dpy, c->win, &protocols, &n)) { + for(i = 0; !ret && i < n; i++) + if(protocols[i] == wmatom[WMDelete]) + ret = True; + XFree(protocols); + } + return ret; +} + +void +killclient() { + if(!sel) + return; + if(isprotodel(sel)) + sendevent(sel->win, wmatom[WMProtocols], wmatom[WMDelete]); + else + XKillClient(dpy, sel->win); +} + +void +manage(Window w, XWindowAttributes *wa) { + Client *c; + Window trans; + + c = emallocz(sizeof(Client)); + c->tag = True; + c->win = w; + c->x = wa->x; + c->y = wa->y; + c->w = wa->width; + c->h = wa->height; + if(c->w == sw && c->h == sh) { + c->border = 0; + c->x = sx; + c->y = sy; + } else { + c->border = BORDERPX; + if(c->x + c->w + 2 * c->border > wax + waw) + c->x = wax + waw - c->w - 2 * c->border; + if(c->y + c->h + 2 * c->border > way + wah) + c->y = way + wah - c->h - 2 * c->border; + if(c->x < wax) + c->x = wax; + if(c->y < way) + c->y = way; + } + updatesizehints(c); + XSelectInput(dpy, c->win, + StructureNotifyMask | PropertyChangeMask | EnterWindowMask); + XGetTransientForHint(dpy, c->win, &trans); + grabbuttons(c, False); + XSetWindowBorder(dpy, c->win, dc.norm[ColBorder]); + updatetitle(c); + settags(c, getclient(trans)); + if(!c->isfloat) + c->isfloat = trans || c->isfixed; + if(clients) + clients->prev = c; + c->next = clients; + c->snext = stack; + stack = clients = c; + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); + XMapWindow(dpy, c->win); + setclientstate(c, NormalState); + if(isvisible(c)) + focus(c); + arrange(); +} + +void +resize(Client *c, Bool sizehints) { + float actual, dx, dy, max, min; + XWindowChanges wc; + + if(c->w <= 0 || c->h <= 0) + return; + if(sizehints) { + if(c->minw && c->w < c->minw) + c->w = c->minw; + if(c->minh && c->h < c->minh) + c->h = c->minh; + if(c->maxw && c->w > c->maxw) + c->w = c->maxw; + if(c->maxh && c->h > c->maxh) + c->h = c->maxh; + /* inspired by algorithm from fluxbox */ + if(c->minay > 0 && c->maxay && (c->h - c->baseh) > 0) { + dx = (float)(c->w - c->basew); + dy = (float)(c->h - c->baseh); + min = (float)(c->minax) / (float)(c->minay); + max = (float)(c->maxax) / (float)(c->maxay); + actual = dx / dy; + if(max > 0 && min > 0 && actual > 0) { + if(actual < min) { + dy = (dx * min + dy) / (min * min + 1); + dx = dy * min; + c->w = (int)dx + c->basew; + c->h = (int)dy + c->baseh; + } + else if(actual > max) { + dy = (dx * min + dy) / (max * max + 1); + dx = dy * min; + c->w = (int)dx + c->basew; + c->h = (int)dy + c->baseh; + } + } + } + if(c->incw) + c->w -= (c->w - c->basew) % c->incw; + if(c->inch) + c->h -= (c->h - c->baseh) % c->inch; + } + if(c->w == sw && c->h == sh) + c->border = 0; + else + c->border = BORDERPX; + /* offscreen appearance fixes */ + if(c->x > sw) + c->x = sw - c->w - 2 * c->border; + if(c->y > sh) + c->y = sh - c->h - 2 * c->border; + if(c->x + c->w + 2 * c->border < sx) + c->x = sx; + if(c->y + c->h + 2 * c->border < sy) + c->y = sy; + wc.x = c->x; + wc.y = c->y; + wc.width = c->w; + wc.height = c->h; + wc.border_width = c->border; + XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); +} + +void +updatesizehints(Client *c) { + long msize; + XSizeHints size; + + if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) + size.flags = PSize; + c->flags = size.flags; + if(c->flags & PBaseSize) { + c->basew = size.base_width; + c->baseh = size.base_height; + } else { + c->basew = c->baseh = 0; + } + if(c->flags & PResizeInc) { + c->incw = size.width_inc; + c->inch = size.height_inc; + } else { + c->incw = c->inch = 0; + } + if(c->flags & PMaxSize) { + c->maxw = size.max_width; + c->maxh = size.max_height; + } else { + c->maxw = c->maxh = 0; + } + if(c->flags & PMinSize) { + c->minw = size.min_width; + c->minh = size.min_height; + } else { + c->minw = c->minh = 0; + } + if(c->flags & PAspect) { + c->minax = size.min_aspect.x; + c->minay = size.min_aspect.y; + c->maxax = size.max_aspect.x; + c->maxay = size.max_aspect.y; + } else { + c->minax = c->minay = c->maxax = c->maxay = 0; + } + c->isfixed = (c->maxw && c->minw && c->maxh && c->minh && + c->maxw == c->minw && c->maxh == c->minh); +} + +void +updatetitle(Client *c) { + char **list = NULL; + int n; + XTextProperty name; + + name.nitems = 0; + c->name[0] = 0; + XGetTextProperty(dpy, c->win, &name, netatom[NetWMName]); + if(!name.nitems) + XGetWMName(dpy, c->win, &name); + if(!name.nitems) + return; + if(name.encoding == XA_STRING) + strncpy(c->name, (char *)name.value, sizeof c->name); + else { + if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { + strncpy(c->name, *list, sizeof c->name); + XFreeStringList(list); + } + } + XFree(name.value); +} + +void +unmanage(Client *c) { + Client *nc; + + /* The server grab construct avoids race conditions. */ + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + detach(c); + detachstack(c); + if(sel == c) { + for(nc = stack; nc && !isvisible(nc); nc = nc->snext); + focus(nc); + } + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + setclientstate(c, WithdrawnState); + free(c); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + arrange(); +} + + + + + + + + + + + + + + + + + + + +/* static */ + + +static void +cleanup(void) { + close(STDIN_FILENO); + while(stack) { + resize(stack, True); + unmanage(stack); + } + if(dc.font.set) + XFreeFontSet(dpy, dc.font.set); + else + XFreeFont(dpy, dc.font.xfont); + XUngrabKey(dpy, AnyKey, AnyModifier, root); + XFreePixmap(dpy, dc.drawable); + XFreeGC(dpy, dc.gc); + XDestroyWindow(dpy, barwin); + XFreeCursor(dpy, cursor[CurNormal]); + XFreeCursor(dpy, cursor[CurResize]); + XFreeCursor(dpy, cursor[CurMove]); + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + XSync(dpy, False); +} + +static void +scan(void) { + unsigned int i, num; + Window *wins, d1, d2; + XWindowAttributes wa; + + wins = NULL; + if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for(i = 0; i < num; i++) { + if(!XGetWindowAttributes(dpy, wins[i], &wa)) + continue; + if(wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) + continue; + if(wa.map_state == IsViewable) + manage(wins[i], &wa); + } + } + if(wins) + XFree(wins); +} + +static void +setup(void) { + int i, j; + unsigned int mask; + Window w; + XModifierKeymap *modmap; + XSetWindowAttributes wa; + + /* init atoms */ + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); + XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, + PropModeReplace, (unsigned char *) netatom, NetLast); + /* init cursors */ + cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr); + cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing); + cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur); + /* init modifier map */ + numlockmask = 0; + modmap = XGetModifierMapping(dpy); + for (i = 0; i < 8; i++) { + for (j = 0; j < modmap->max_keypermod; j++) { + if(modmap->modifiermap[i * modmap->max_keypermod + j] == XKeysymToKeycode(dpy, XK_Num_Lock)) + numlockmask = (1 << i); + } + } + XFreeModifiermap(modmap); + /* select for events */ + wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask + | EnterWindowMask | LeaveWindowMask; + wa.cursor = cursor[CurNormal]; + XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); + grabkeys(); + initrregs(); + ntags = 2; + seltag = True; + /* style */ + dc.norm[ColBorder] = getcolor(NORMBORDERCOLOR); + dc.norm[ColBG] = getcolor(NORMBGCOLOR); + dc.norm[ColFG] = getcolor(NORMFGCOLOR); + dc.sel[ColBorder] = getcolor(SELBORDERCOLOR); + dc.sel[ColBG] = getcolor(SELBGCOLOR); + dc.sel[ColFG] = getcolor(SELFGCOLOR); + setfont(FONT); + /* geometry */ + sx = sy = 0; + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + nmaster = NMASTER; + bmw = 1; + /* bar */ + dc.h = bh = dc.font.height + 2; + wa.override_redirect = 1; + wa.background_pixmap = ParentRelative; + wa.event_mask = ButtonPressMask | ExposureMask; + barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0, + DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); + XDefineCursor(dpy, barwin, cursor[CurNormal]); + XMapRaised(dpy, barwin); + strcpy(stext, "dwm-"VERSION); + /* windowarea */ + wax = sx; + way = sy + bh; + wah = sh - bh; + waw = sw; + /* pixmap for everything */ + dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); + dc.gc = XCreateGC(dpy, root, 0, 0); + XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); + /* multihead support */ + selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); +} + +/* + * Startup Error handler to check if another window manager + * is already running. + */ +static int +xerrorstart(Display *dsply, XErrorEvent *ee) { + otherwm = True; + return -1; +} + + + +void +sendevent(Window w, Atom a, long value) { + XEvent e; + + e.type = ClientMessage; + e.xclient.window = w; + e.xclient.message_type = a; + e.xclient.format = 32; + e.xclient.data.l[0] = value; + e.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, w, False, NoEventMask, &e); + XSync(dpy, False); +} + +void +quit() { + readin = running = False; +} + +/* There's no way to check accesses to destroyed windows, thus those cases are + * ignored (especially on UnmapNotify's). Other types of errors call Xlibs + * default error handler, which may call exit. + */ +int +xerror(Display *dpy, XErrorEvent *ee) { + if(ee->error_code == BadWindow + || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) + || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) + || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) + || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) + || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) + || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) + || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) + return 0; + fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", + ee->request_code, ee->error_code); + return xerrorxlib(dpy, ee); /* may call exit */ +} + +int +main(int argc, char *argv[]) { + char *p; + int r, xfd; + fd_set rd; + + if(argc == 2 && !strncmp("-v", argv[1], 3)) { + fputs("dwm-"VERSION", (C)opyright MMVI-MMVII Anselm R. Garbe\n", stdout); + exit(EXIT_SUCCESS); + } else if(argc != 1) { + eprint("usage: dwm [-v]\n"); + } + setlocale(LC_CTYPE, ""); + dpy = XOpenDisplay(0); + if(!dpy) { + eprint("dwm: cannot open display\n"); + } + xfd = ConnectionNumber(dpy); + screen = DefaultScreen(dpy); + root = RootWindow(dpy, screen); + otherwm = False; + XSetErrorHandler(xerrorstart); + /* this causes an error if some other window manager is running */ + XSelectInput(dpy, root, SubstructureRedirectMask); + XSync(dpy, False); + if(otherwm) { + eprint("dwm: another window manager is already running\n"); + } + + XSync(dpy, False); + XSetErrorHandler(NULL); + xerrorxlib = XSetErrorHandler(xerror); + XSync(dpy, False); + setup(); + drawstatus(); + scan(); + + /* main event loop, also reads status text from stdin */ + XSync(dpy, False); + procevent(); + readin = True; + while(running) { + FD_ZERO(&rd); + if(readin) + FD_SET(STDIN_FILENO, &rd); + FD_SET(xfd, &rd); + if(select(xfd + 1, &rd, NULL, NULL, NULL) == -1) { + if(errno == EINTR) + continue; + eprint("select failed\n"); + } + if(FD_ISSET(STDIN_FILENO, &rd)) { + switch(r = read(STDIN_FILENO, stext, sizeof stext - 1)) { + case -1: + strncpy(stext, strerror(errno), sizeof stext - 1); + stext[sizeof stext - 1] = '\0'; + readin = False; + break; + case 0: + strncpy(stext, "EOF", 4); + readin = False; + break; + default: + for(stext[r] = '\0', p = stext + strlen(stext) - 1; p >= stext && *p == '\n'; *p-- = '\0'); + for(; p >= stext && *p != '\n'; --p); + if(p > stext) + strncpy(stext, p + 1, sizeof stext); + } + drawstatus(); + } + if(FD_ISSET(xfd, &rd)) + procevent(); + } + cleanup(); + XCloseDisplay(dpy); + return 0; +} diff -r bc512840e5a5 -r 45f23169563e config.mk --- a/config.mk Fri May 30 21:57:51 2008 +0200 +++ b/config.mk Fri May 30 22:18:18 2008 +0200 @@ -1,5 +1,5 @@ -# dwm version -VERSION = 4.4.1 +# aewl version +VERSION = 0.1 # Customize below to fit your system @@ -15,7 +15,8 @@ LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 # flags -CFLAGS = -Os ${INCS} -DVERSION=\"${VERSION}\" -W -Wall -pedantic +DEBUG = -Wall -Wpointer-arith -Wstrict-prototypes -O2 -W -pedantic +CFLAGS = ${INCS} -DVERSION=\"${VERSION}\" ${DEBUG} LDFLAGS = ${LIBS} #CFLAGS = -g -Wall -O2 ${INCS} -DVERSION=\"${VERSION}\" #LDFLAGS = -g ${LIBS} diff -r bc512840e5a5 -r 45f23169563e dwm.1 --- a/dwm.1 Fri May 30 21:57:51 2008 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,149 +0,0 @@ -.TH DWM 1 dwm-VERSION -.SH NAME -dwm \- dynamic window manager -.SH SYNOPSIS -.B dwm -.RB [ \-v ] -.SH DESCRIPTION -dwm is a dynamic window manager for X. It manages windows in tiling and -floating modes. Either mode can be applied dynamically, optimizing the -environment for the application in use and the task performed. -.P -In tiling mode windows are managed in a master and stacking area. The master -area contains the windows which currently need most attention, whereas the -stacking area contains all other windows. In floating mode windows can be -resized and moved freely. Dialog windows are always managed floating, -regardless of the mode applied. -.P -Windows are grouped by tags. Each window can be tagged with one or multiple -tags. Selecting certain tags displays all windows with these tags. -.P -dwm contains a small status bar which displays all available tags, the mode, -the title of the focused window, and the text read from standard input. The -selected tags are indicated with a different color. The tags of the focused -window are indicated with a filled square in the top left corner. The tags -which are applied to one or more windows are indicated with an empty square in -the top left corner. -.P -dwm draws a small border around windows to indicate the focus state. -.SH OPTIONS -.TP -.B \-v -prints version information to standard output, then exits. -.SH USAGE -.SS Status bar -.TP -.B Standard input -is read and displayed in the status text area. -.TP -.B Button1 -click on a tag label to display all windows with that tag, click on the mode -label toggles between tiling and floating mode. -.TP -.B Button3 -click on a tag label adds/removes all windows with that tag to/from the view. -.TP -.B Button4 -click on the mode label increases the number of windows in the master area (tiling mode only). -.TP -.B Button5 -click on the mode label decreases the number of windows in the master area (tiling mode only). -.TP -.B Mod1-Button1 -click on a tag label applies that tag to the focused window. -.TP -.B Mod1-Button3 -click on a tag label adds/removes that tag to/from the focused window. -.SS Keyboard commands -.TP -.B Mod1-Shift-Return -Start -.BR xterm (1). -.TP -.B Mod1-Tab -Focus next window. -.TP -.B Mod1-Shift-Tab -Focus previous window. -.TP -.B Mod1-Return -Zooms/cycles current window to/from master area (tiling mode), toggles maximization current window (floating mode). -.TP -.B Mod1-g -Grow master area (tiling mode only). -.TP -.B Mod1-s -Shrink master area (tiling mode only). -.TP -.B Mod1-i -Increase the number of windows in the master area (tiling mode only). -.TP -.B Mod1-d -Decrease the number of windows in the master area (tiling mode only). -.TP -.B Mod1-Shift-[1..n] -Apply -.RB nth -tag to current window. -.TP -.B Mod1-Shift-0 -Apply all tags to current window. -.TP -.B Mod1-Control-Shift-[1..n] -Add/remove -.B nth -tag to/from current window. -.TP -.B Mod1-Shift-c -Close focused window. -.TP -.B Mod1-space -Toggle between tiling and floating mode (affects all windows). -.TP -.B Mod1-Shift-space -Toggle focused window between floating and non-floating state (tiling mode only). -.TP -.B Mod1-[1..n] -View all windows with -.BR nth -tag. -.TP -.B Mod1-0 -View all windows with any tag. -.TP -.B Mod1-Control-[1..n] -Add/remove all windows with -.BR nth -tag to/from the view. -.TP -.B Mod1-Shift-q -Quit dwm. -.SS Mouse commands -.TP -.B Mod1-Button1 -Move current window while dragging (floating mode only). -.TP -.B Mod1-Button2 -Zoom current window to the master area (tiling mode only). -.TP -.B Mod1-Button3 -Resize current window while dragging (floating mode only). -.SH CUSTOMIZATION -dwm is customized by creating a custom config.h and (re)compiling the source -code. This keeps it fast, secure and simple. -.SH SEE ALSO -.BR dmenu (1) -.SH BUGS -The status bar may display -.BR "EOF" -when dwm has been started by an X session manager like -.BR xdm (1), -because those close standard output before executing dwm. -.P -Java applications which use the XToolkit/XAWT backend may draw grey windows -only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early -JDK 1.6 versions, because it assumes a reparenting window manager. As a workaround -you can use JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or you -can set the following environment variable (to use the older Motif -backend instead): -.BR AWT_TOOLKIT=MToolkit . diff -r bc512840e5a5 -r 45f23169563e dwm.c --- a/dwm.c Fri May 30 21:57:51 2008 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1782 +0,0 @@ -/* (C)opyright MMVI-MMVII Anselm R. Garbe - * See LICENSE file for license details. - * - * dynamic window manager is designed like any other X client as well. It is - * driven through handling X events. In contrast to other X clients, a window - * manager selects for SubstructureRedirectMask on the root window, to receive - * events about window (dis-)appearance. Only one X connection at a time is - * allowed to select for this event mask. - * - * Calls to fetch an X event from the event queue are blocking. Due reading - * status text from standard input, a select()-driven main loop has been - * implemented which selects for reads on the X connection and STDIN_FILENO to - * handle all data smoothly. The event handlers of dwm are organized in an - * array which is accessed whenever a new event has been fetched. This allows - * event dispatching in O(1) time. - * - * Each child of the root window is called a client, except windows which have - * set the override_redirect flag. Clients are organized in a global - * doubly-linked client list, the focus history is remembered through a global - * stack list. Each client contains an array of Bools of the same size as the - * global tags array to indicate the tags of a client. For each client dwm - * creates a small title window, which is resized whenever the (_NET_)WM_NAME - * properties are updated or the client is moved/resized. - * - * Keys and tagging rules are organized as arrays and defined in the config.h - * file. These arrays are kept static in event.o and tag.o respectively, - * because no other part of dwm needs access to them. The current mode is - * represented by the arrange() function pointer, which wether points to - * dofloat() or dotile(). - * - * To understand everything else, start reading main.c:main(). - */ - -#include "config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* mask shorthands, used in event.c and client.c */ -#define BUTTONMASK (ButtonPressMask | ButtonReleaseMask) - -enum { NetSupported, NetWMName, NetLast }; /* EWMH atoms */ -enum { WMProtocols, WMDelete, WMState, WMLast }; /* default atoms */ -enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ -enum { ColBorder, ColFG, ColBG, ColLast }; /* color */ - -typedef struct { - int ascent; - int descent; - int height; - XFontSet set; - XFontStruct *xfont; -} Fnt; - -typedef struct { - int x, y, w, h; - unsigned long norm[ColLast]; - unsigned long sel[ColLast]; - Drawable drawable; - Fnt font; - GC gc; -} DC; /* draw context */ - -typedef struct Client Client; -struct Client { - char name[256]; - int x, y, w, h; - int rx, ry, rw, rh; /* revert geometry */ - int basew, baseh, incw, inch, maxw, maxh, minw, minh; - int minax, minay, maxax, maxay; - long flags; - unsigned int border; - Bool isfixed, isfloat, ismax; - Bool tag; - Client *next; - Client *prev; - Client *snext; - Window win; -}; - -typedef struct { - const char *clpattern; - int tag; - Bool isfloat; -} Rule; - -typedef struct { - regex_t *clregex; -} RReg; - - -typedef struct { - unsigned long mod; - KeySym keysym; - void (*func)(const char* cmd); - const char* cmd; -} Key; - - -#define CLEANMASK(mask) (mask & ~(numlockmask | LockMask)) -#define MOUSEMASK (BUTTONMASK | PointerMotionMask) - - - -const char *tags[]; /* all tags */ -char stext[256]; /* status text */ -int bh, bmw; /* bar height, bar mode label width */ -int screen, sx, sy, sw, sh; /* screen geometry */ -int wax, way, wah, waw; /* windowarea geometry */ -unsigned int nmaster; /* number of master clients */ -unsigned int ntags, numlockmask; /* number of tags, dynamic lock mask */ -void (*handler[LASTEvent])(XEvent *); /* event handler */ -void (*arrange)(void); /* arrange function, indicates mode */ -Atom wmatom[WMLast], netatom[NetLast]; -Bool running, selscreen, seltag; /* seltag is array of Bool */ -Client *clients, *sel, *stack; /* global client list and stack */ -Cursor cursor[CurLast]; -DC dc; /* global draw context */ -Display *dpy; -Window root, barwin; - -Bool running = True; -Bool selscreen = True; -Client *clients = NULL; -Client *sel = NULL; -Client *stack = NULL; -DC dc = {0}; - -static int (*xerrorxlib)(Display *, XErrorEvent *); -static Bool otherwm, readin; -static RReg *rreg = NULL; -static unsigned int len = 0; - - -TAGS -RULES - - -/* client.c */ -void configure(Client *c); /* send synthetic configure event */ -void focus(Client *c); /* focus c, c may be NULL */ -Client *getclient(Window w); /* return client of w */ -Bool isprotodel(Client *c); /* returns True if c->win supports wmatom[WMDelete] */ -void manage(Window w, XWindowAttributes *wa); /* manage new client */ -void resize(Client *c, Bool sizehints); /* resize c*/ -void updatesizehints(Client *c); /* update the size hint variables of c */ -void updatetitle(Client *c); /* update the name of c */ -void unmanage(Client *c); /* destroy c */ - -/* draw.c */ -void drawstatus(void); /* draw the bar */ -unsigned long getcolor(const char *colstr); /* return color of colstr */ -void setfont(const char *fontstr); /* set the font for DC */ -unsigned int textw(const char *text); /* return the width of text in px*/ - -/* event.c */ -void grabkeys(void); /* grab all keys defined in config.h */ -void procevent(void); /* process pending X events */ - -/* main.c */ -void sendevent(Window w, Atom a, long value); /* send synthetic event to w */ -int xerror(Display *dsply, XErrorEvent *ee); /* dwm's X error handler */ - -/* tag.c */ -void initrregs(void); /* initialize regexps of rules defined in config.h */ -Client *getnext(Client *c); /* returns next visible client */ -void settags(Client *c, Client *trans); /* sets tags of c */ - -/* util.c */ -void *emallocz(unsigned int size); /* allocates zero-initialized memory, exits on error */ -void eprint(const char *errstr, ...); /* prints errstr and exits with 1 */ - -/* view.c */ -void detach(Client *c); /* detaches c from global client list */ -void dofloat(void); /* arranges all windows floating */ -void dotile(void); /* arranges all windows tiled */ -void domax(void); /* arranges all windows fullscreen */ -Bool isvisible(Client *c); /* returns True if client is visible */ -void restack(void); /* restores z layers of all clients */ - - -void toggleview(); /* toggle the viewed tag */ -void focusnext(); /* focuses next visible client */ -void zoom(); /* zooms the focused client to master area */ -void killclient(); /* kill c nicely */ -void quit(); /* quit dwm nicely */ -void togglemode(); /* toggles global arrange function (dotile/dofloat) */ -void togglefloat(); /* toggles focusesd client between floating/non-floating state */ -void incnmaster(); /* increments nmaster */ -void decnmaster(); /* decrements nmaster */ -void toggletag(); /* toggles c tag */ -void spawn(const char* cmd); /* forks a new subprocess with cmd */ - - - - - - - - - - -/* from view.c */ -/* static */ - -static Client * -nexttiled(Client *c) { - for(c = getnext(c); c && c->isfloat; c = getnext(c->next)); - return c; -} - -static void -togglemax(Client *c) { - XEvent ev; - - if(c->isfixed) - return; - - if((c->ismax = !c->ismax)) { - c->rx = c->x; c->x = wax; - c->ry = c->y; c->y = way; - c->rw = c->w; c->w = waw - 2 * BORDERPX; - c->rh = c->h; c->h = wah - 2 * BORDERPX; - } - else { - c->x = c->rx; - c->y = c->ry; - c->w = c->rw; - c->h = c->rh; - } - resize(c, True); - while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); -} - - - -void (*arrange)(void) = DEFMODE; - -void -detach(Client *c) { - if(c->prev) - c->prev->next = c->next; - if(c->next) - c->next->prev = c->prev; - if(c == clients) - clients = c->next; - c->next = c->prev = NULL; -} - -void -dofloat(void) { - Client *c; - - for(c = clients; c; c = c->next) { - if(isvisible(c)) { - resize(c, True); - } - else - XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); - } - if(!sel || !isvisible(sel)) { - for(c = stack; c && !isvisible(c); c = c->snext); - focus(c); - } - restack(); -} - -void -dotile(void) { - unsigned int i, n, mw, mh, tw, th; - Client *c; - - for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) - n++; - /* window geoms */ - mh = (n > nmaster) ? wah / nmaster : wah / (n > 0 ? n : 1); - mw = (n > nmaster) ? waw / 2 : waw; - th = (n > nmaster) ? wah / (n - nmaster) : 0; - tw = waw - mw; - - for(i = 0, c = clients; c; c = c->next) - if(isvisible(c)) { - if(c->isfloat) { - resize(c, True); - continue; - } - c->ismax = False; - c->x = wax; - c->y = way; - if(i < nmaster) { - c->y += i * mh; - c->w = mw - 2 * BORDERPX; - c->h = mh - 2 * BORDERPX; - } - else { /* tile window */ - c->x += mw; - c->w = tw - 2 * BORDERPX; - if(th > 2 * BORDERPX) { - c->y += (i - nmaster) * th; - c->h = th - 2 * BORDERPX; - } - else /* fallback if th <= 2 * BORDERPX */ - c->h = wah - 2 * BORDERPX; - } - resize(c, False); - i++; - } - else - XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); - if(!sel || !isvisible(sel)) { - for(c = stack; c && !isvisible(c); c = c->snext); - focus(c); - } - restack(); -} - -/* begin code by mitch */ -void -arrangemax(Client *c) { - if(c == sel) { - c->ismax = True; - c->x = sx; - c->y = bh; - c->w = sw - 2 * BORDERPX; - c->h = sh - bh - 2 * BORDERPX; - XRaiseWindow(dpy, c->win); - } else { - c->ismax = False; - XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); - XLowerWindow(dpy, c->win); - } -} - -void -domax(void) { - Client *c; - - for(c = clients; c; c = c->next) { - if(isvisible(c)) { - if(c->isfloat) { - resize(c, True); - continue; - } - arrangemax(c); - resize(c, False); - } else { - XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); - } - - } - if(!sel || !isvisible(sel)) { - for(c = stack; c && !isvisible(c); c = c->snext); - focus(c); - } - restack(); -} -/* end code by mitch */ - -void -focusnext() { - Client *c; - - if(!sel) - return; - if(!(c = getnext(sel->next))) - c = getnext(clients); - if(c) { - focus(c); - restack(); - } -} - -void -incnmaster() { - if((arrange == dofloat) || (wah / (nmaster + 1) <= 2 * BORDERPX)) - return; - nmaster++; - if(sel) - arrange(); - else - drawstatus(); -} - -void -decnmaster() { - if((arrange == dofloat) || (nmaster <= 1)) - return; - nmaster--; - if(sel) - arrange(); - else - drawstatus(); -} - -Bool -isvisible(Client *c) { - if((c->tag && seltag) || (!c->tag && !seltag)) { - return True; - } - return False; -} - -void -restack(void) { - Client *c; - XEvent ev; - - drawstatus(); - if(!sel) - return; - if(sel->isfloat || arrange == dofloat) - XRaiseWindow(dpy, sel->win); - - /* begin code by mitch */ - if(arrange == domax) { - for(c = nexttiled(clients); c; c = nexttiled(c->next)) { - arrangemax(c); - resize(c, False); - } - - } else if (arrange == dotile) { - /* end code by mitch */ - - if(!sel->isfloat) - XLowerWindow(dpy, sel->win); - for(c = nexttiled(clients); c; c = nexttiled(c->next)) { - if(c == sel) - continue; - XLowerWindow(dpy, c->win); - } - } - XSync(dpy, False); - while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); -} - -void -togglefloat() { - if (!sel || arrange == dofloat) - return; - sel->isfloat = !sel->isfloat; - arrange(); -} - -void -togglemode() { - /* only toggle between tile and max - float is just available through togglefloat */ - arrange = (arrange == dotile) ? domax : dotile; - if(sel) - arrange(); - else - drawstatus(); -} - -void -toggleview() { - seltag = !seltag; - arrange(); -} - -void -zoom() { - unsigned int n; - Client *c; - - if(!sel) - return; - if(sel->isfloat || (arrange == dofloat)) { - togglemax(sel); - return; - } - for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) - n++; - - if((c = sel) == nexttiled(clients)) - if(!(c = nexttiled(c->next))) - return; - detach(c); - if(clients) - clients->prev = c; - c->next = clients; - clients = c; - focus(c); - arrange(); -} - - - - - - - - - - - - - - - - -/* from util.c */ - - -void * -emallocz(unsigned int size) { - void *res = calloc(1, size); - - if(!res) - eprint("fatal: could not malloc() %u bytes\n", size); - return res; -} - -void -eprint(const char *errstr, ...) { - va_list ap; - - va_start(ap, errstr); - vfprintf(stderr, errstr, ap); - va_end(ap); - exit(EXIT_FAILURE); -} - -void -spawn(const char* cmd) { - static char *shell = NULL; - - if(!shell && !(shell = getenv("SHELL"))) - shell = "/bin/sh"; - if(!cmd) - return; - /* The double-fork construct avoids zombie processes and keeps the code - * clean from stupid signal handlers. */ - if(fork() == 0) { - if(fork() == 0) { - if(dpy) - close(ConnectionNumber(dpy)); - setsid(); - execl(shell, shell, "-c", cmd, (char *)NULL); - fprintf(stderr, "dwm: execl '%s -c %s'", shell, cmd); - perror(" failed"); - } - exit(0); - } - wait(0); -} - - - - - - - - - - - - - -/* from tag.c */ - -/* static */ - -Client * -getnext(Client *c) { - for(; c && !isvisible(c); c = c->next); - return c; -} - -void -initrregs(void) { - unsigned int i; - regex_t *reg; - - if(rreg) - return; - len = sizeof rule / sizeof rule[0]; - rreg = emallocz(len * sizeof(RReg)); - for(i = 0; i < len; i++) { - if(rule[i].clpattern) { - reg = emallocz(sizeof(regex_t)); - if(regcomp(reg, rule[i].clpattern, REG_EXTENDED)) - free(reg); - else - rreg[i].clregex = reg; - } - } -} - -void -settags(Client *c, Client *trans) { - char prop[512]; - unsigned int i; - regmatch_t tmp; - Bool matched = trans != NULL; - XClassHint ch = { 0 }; - - if(matched) { - c->tag = trans->tag; - } else { - XGetClassHint(dpy, c->win, &ch); - snprintf(prop, sizeof prop, "%s:%s:%s", - ch.res_class ? ch.res_class : "", - ch.res_name ? ch.res_name : "", c->name); - for(i = 0; i < len; i++) - if(rreg[i].clregex && !regexec(rreg[i].clregex, prop, 1, &tmp, 0)) { - c->isfloat = rule[i].isfloat; - if (rule[i].tag < 0) { - c->tag = seltag; - } else if (rule[i].tag == 0) { - c->tag = True; - } else { - c->tag = False; - } - matched = True; - break; /* perform only the first rule matching */ - } - if(ch.res_class) - XFree(ch.res_class); - if(ch.res_name) - XFree(ch.res_name); - } - if(!matched) { - c->tag = seltag; - } -} - -void -toggletag() { - if(!sel) - return; - sel->tag = !sel->tag; - toggleview(); -} - - - - - - - - - - - - - - - - - -/* from event.c */ -/* static */ - -KEYS - - - -static void -movemouse(Client *c) { - int x1, y1, ocx, ocy, di; - unsigned int dui; - Window dummy; - XEvent ev; - - ocx = c->x; - ocy = c->y; - if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, - None, cursor[CurMove], CurrentTime) != GrabSuccess) - return; - c->ismax = False; - XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui); - for(;;) { - XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev); - switch (ev.type) { - case ButtonRelease: - resize(c, True); - XUngrabPointer(dpy, CurrentTime); - return; - case ConfigureRequest: - case Expose: - case MapRequest: - handler[ev.type](&ev); - break; - case MotionNotify: - XSync(dpy, False); - c->x = ocx + (ev.xmotion.x - x1); - c->y = ocy + (ev.xmotion.y - y1); - if(abs(wax + c->x) < SNAP) - c->x = wax; - else if(abs((wax + waw) - (c->x + c->w + 2 * c->border)) < SNAP) - c->x = wax + waw - c->w - 2 * c->border; - if(abs(way - c->y) < SNAP) - c->y = way; - else if(abs((way + wah) - (c->y + c->h + 2 * c->border)) < SNAP) - c->y = way + wah - c->h - 2 * c->border; - resize(c, False); - break; - } - } -} - -static void -resizemouse(Client *c) { - int ocx, ocy; - int nw, nh; - XEvent ev; - - ocx = c->x; - ocy = c->y; - if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, - None, cursor[CurResize], CurrentTime) != GrabSuccess) - return; - c->ismax = False; - XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1); - for(;;) { - XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask , &ev); - switch(ev.type) { - case ButtonRelease: - resize(c, True); - XUngrabPointer(dpy, CurrentTime); - return; - case ConfigureRequest: - case Expose: - case MapRequest: - handler[ev.type](&ev); - break; - case MotionNotify: - XSync(dpy, False); - nw = ev.xmotion.x - ocx - 2 * c->border + 1; - c->w = nw > 0 ? nw : 1; - nh = ev.xmotion.y - ocy - 2 * c->border + 1; - c->h = nh > 0 ? nh : 1; - resize(c, True); - break; - } - } -} - -static void -buttonpress(XEvent *e) { - int x; - int i; - Client *c; - XButtonPressedEvent *ev = &e->xbutton; - - if(barwin == ev->window) { - if(ev->button == Button1) { - toggleview(); - } - return; - } else if((c = getclient(ev->window))) { - focus(c); - if(CLEANMASK(ev->state) != MODKEY) - return; - if(ev->button == Button1 && (arrange == dofloat || c->isfloat)) { - restack(); - movemouse(c); - } else if(ev->button == Button3 && (arrange == dofloat || c->isfloat) && !c->isfixed) { - restack(); - resizemouse(c); - } - } -} - -static void -configurerequest(XEvent *e) { - unsigned long newmask; - Client *c; - XConfigureRequestEvent *ev = &e->xconfigurerequest; - XWindowChanges wc; - - if((c = getclient(ev->window))) { - c->ismax = False; - if(ev->value_mask & CWX) - c->x = ev->x; - if(ev->value_mask & CWY) - c->y = ev->y; - if(ev->value_mask & CWWidth) - c->w = ev->width; - if(ev->value_mask & CWHeight) - c->h = ev->height; - if(ev->value_mask & CWBorderWidth) - c->border = ev->border_width; - wc.x = c->x; - wc.y = c->y; - wc.width = c->w; - wc.height = c->h; - newmask = ev->value_mask & (~(CWSibling | CWStackMode | CWBorderWidth)); - if(newmask) - XConfigureWindow(dpy, c->win, newmask, &wc); - else - configure(c); - XSync(dpy, False); - if(c->isfloat) { - resize(c, False); - if(!isvisible(c)) - XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); - } - else - arrange(); - } else { - wc.x = ev->x; - wc.y = ev->y; - wc.width = ev->width; - wc.height = ev->height; - wc.border_width = ev->border_width; - wc.sibling = ev->above; - wc.stack_mode = ev->detail; - XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); - XSync(dpy, False); - } -} - -static void -destroynotify(XEvent *e) { - Client *c; - XDestroyWindowEvent *ev = &e->xdestroywindow; - - if((c = getclient(ev->window))) - unmanage(c); -} - -static void -enternotify(XEvent *e) { - Client *c; - XCrossingEvent *ev = &e->xcrossing; - - if(ev->mode != NotifyNormal || ev->detail == NotifyInferior) - return; - if((c = getclient(ev->window)) && isvisible(c)) - focus(c); - else if(ev->window == root) { - selscreen = True; - for(c = stack; c && !isvisible(c); c = c->snext); - focus(c); - } -} - -static void -expose(XEvent *e) { - XExposeEvent *ev = &e->xexpose; - - if(ev->count == 0) { - if(barwin == ev->window) - drawstatus(); - } -} - -static void -keypress(XEvent *e) { - static unsigned int len = sizeof key / sizeof key[0]; - unsigned int i; - KeySym keysym; - XKeyEvent *ev = &e->xkey; - - keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); - for(i = 0; i < len; i++) { - if(keysym == key[i].keysym && CLEANMASK(key[i].mod) == CLEANMASK(ev->state)) { - if(key[i].func) - key[i].func(key[i].cmd); - } - } -} - -static void -leavenotify(XEvent *e) { - XCrossingEvent *ev = &e->xcrossing; - - if((ev->window == root) && !ev->same_screen) { - selscreen = False; - focus(NULL); - } -} - -static void -mappingnotify(XEvent *e) { - XMappingEvent *ev = &e->xmapping; - - XRefreshKeyboardMapping(ev); - if(ev->request == MappingKeyboard) - grabkeys(); -} - -static void -maprequest(XEvent *e) { - static XWindowAttributes wa; - XMapRequestEvent *ev = &e->xmaprequest; - - if(!XGetWindowAttributes(dpy, ev->window, &wa)) - return; - if(wa.override_redirect) { - XSelectInput(dpy, ev->window, - (StructureNotifyMask | PropertyChangeMask)); - return; - } - if(!getclient(ev->window)) - manage(ev->window, &wa); -} - -static void -propertynotify(XEvent *e) { - Client *c; - Window trans; - XPropertyEvent *ev = &e->xproperty; - - if(ev->state == PropertyDelete) - return; /* ignore */ - if((c = getclient(ev->window))) { - switch (ev->atom) { - default: break; - case XA_WM_TRANSIENT_FOR: - XGetTransientForHint(dpy, c->win, &trans); - if(!c->isfloat && (c->isfloat = (trans != 0))) - arrange(); - break; - case XA_WM_NORMAL_HINTS: - updatesizehints(c); - break; - } - if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { - updatetitle(c); - if(c == sel) - drawstatus(); - } - } -} - -static void -unmapnotify(XEvent *e) { - Client *c; - XUnmapEvent *ev = &e->xunmap; - - if((c = getclient(ev->window))) - unmanage(c); -} - - - -void (*handler[LASTEvent]) (XEvent *) = { - [ButtonPress] = buttonpress, - [ConfigureRequest] = configurerequest, - [DestroyNotify] = destroynotify, - [EnterNotify] = enternotify, - [LeaveNotify] = leavenotify, - [Expose] = expose, - [KeyPress] = keypress, - [MappingNotify] = mappingnotify, - [MapRequest] = maprequest, - [PropertyNotify] = propertynotify, - [UnmapNotify] = unmapnotify -}; - -void -grabkeys(void) { - static unsigned int len = sizeof key / sizeof key[0]; - unsigned int i; - KeyCode code; - - XUngrabKey(dpy, AnyKey, AnyModifier, root); - for(i = 0; i < len; i++) { - code = XKeysymToKeycode(dpy, key[i].keysym); - XGrabKey(dpy, code, key[i].mod, root, True, - GrabModeAsync, GrabModeAsync); - XGrabKey(dpy, code, key[i].mod | LockMask, root, True, - GrabModeAsync, GrabModeAsync); - XGrabKey(dpy, code, key[i].mod | numlockmask, root, True, - GrabModeAsync, GrabModeAsync); - XGrabKey(dpy, code, key[i].mod | numlockmask | LockMask, root, True, - GrabModeAsync, GrabModeAsync); - } -} - -void -procevent(void) { - XEvent ev; - - while(XPending(dpy)) { - XNextEvent(dpy, &ev); - if(handler[ev.type]) - (handler[ev.type])(&ev); /* call handler */ - } -} - - - - - - - - - - - - - - - -/* from draw.c */ -/* static */ - -static unsigned int -textnw(const char *text, unsigned int len) { - XRectangle r; - - if(dc.font.set) { - XmbTextExtents(dc.font.set, text, len, NULL, &r); - return r.width; - } - return XTextWidth(dc.font.xfont, text, len); -} - -static void -drawtext(const char *text, unsigned long col[ColLast]) { - int x, y, w, h; - static char buf[256]; - unsigned int len, olen; - XGCValues gcv; - XRectangle r = { dc.x, dc.y, dc.w, dc.h }; - - XSetForeground(dpy, dc.gc, col[ColBG]); - XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); - if(!text) - return; - w = 0; - olen = len = strlen(text); - if(len >= sizeof buf) - len = sizeof buf - 1; - memcpy(buf, text, len); - buf[len] = 0; - h = dc.font.ascent + dc.font.descent; - y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent; - x = dc.x + (h / 2); - /* shorten text if necessary */ - while(len && (w = textnw(buf, len)) > dc.w - h) - buf[--len] = 0; - if(len < olen) { - if(len > 1) - buf[len - 1] = '.'; - if(len > 2) - buf[len - 2] = '.'; - if(len > 3) - buf[len - 3] = '.'; - } - if(w > dc.w) - return; /* too long */ - gcv.foreground = col[ColFG]; - if(dc.font.set) { - XChangeGC(dpy, dc.gc, GCForeground, &gcv); - XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); - } else { - gcv.font = dc.font.xfont->fid; - XChangeGC(dpy, dc.gc, GCForeground | GCFont, &gcv); - XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); - } -} - - - -void -drawstatus(void) { - int x; - unsigned int i; - - dc.x = dc.y = 0; - for(i = 0; i < ntags; i++) { - dc.w = textw(tags[i]); - drawtext(tags[i], ( ((i == 0 && seltag) || (i == 1 && !seltag)) ? dc.sel : dc.norm)); - dc.x += dc.w + 1; - } - dc.w = bmw; - drawtext("", dc.norm); - x = dc.x + dc.w; - dc.w = textw(stext); - dc.x = sw - dc.w; - if(dc.x < x) { - dc.x = x; - dc.w = sw - x; - } - drawtext(stext, dc.norm); - if((dc.w = dc.x - x) > bh) { - dc.x = x; - drawtext(sel ? sel->name : NULL, dc.norm); - } - XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0); - XSync(dpy, False); -} - -unsigned long -getcolor(const char *colstr) { - Colormap cmap = DefaultColormap(dpy, screen); - XColor color; - - if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) - eprint("error, cannot allocate color '%s'\n", colstr); - return color.pixel; -} - -void -setfont(const char *fontstr) { - char *def, **missing; - int i, n; - - missing = NULL; - if(dc.font.set) - XFreeFontSet(dpy, dc.font.set); - dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def); - if(missing) { - while(n--) - fprintf(stderr, "missing fontset: %s\n", missing[n]); - XFreeStringList(missing); - } - if(dc.font.set) { - XFontSetExtents *font_extents; - XFontStruct **xfonts; - char **font_names; - dc.font.ascent = dc.font.descent = 0; - font_extents = XExtentsOfFontSet(dc.font.set); - n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); - for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) { - if(dc.font.ascent < (*xfonts)->ascent) - dc.font.ascent = (*xfonts)->ascent; - if(dc.font.descent < (*xfonts)->descent) - dc.font.descent = (*xfonts)->descent; - xfonts++; - } - } else { - if(dc.font.xfont) - XFreeFont(dpy, dc.font.xfont); - dc.font.xfont = NULL; - if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))) - eprint("error, cannot load font: '%s'\n", fontstr); - dc.font.ascent = dc.font.xfont->ascent; - dc.font.descent = dc.font.xfont->descent; - } - dc.font.height = dc.font.ascent + dc.font.descent; -} - -unsigned int -textw(const char *text) { - return textnw(text, strlen(text)) + dc.font.height; -} - - - - - - - - - - - -/* from client.c */ -/* static */ - -static void -detachstack(Client *c) { - Client **tc; - for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext); - *tc = c->snext; -} - -static void -grabbuttons(Client *c, Bool focused) { - XUngrabButton(dpy, AnyButton, AnyModifier, c->win); - - if(focused) { - XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - - XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - - XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - } else { - XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, - GrabModeAsync, GrabModeSync, None, None); - } -} - -static void -setclientstate(Client *c, long state) { - long data[] = {state, None}; - XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, - PropModeReplace, (unsigned char *)data, 2); -} - -static int -xerrordummy(Display *dsply, XErrorEvent *ee) { - return 0; -} - - - -void -configure(Client *c) { - XEvent synev; - - synev.type = ConfigureNotify; - synev.xconfigure.display = dpy; - synev.xconfigure.event = c->win; - synev.xconfigure.window = c->win; - synev.xconfigure.x = c->x; - synev.xconfigure.y = c->y; - synev.xconfigure.width = c->w; - synev.xconfigure.height = c->h; - synev.xconfigure.border_width = c->border; - synev.xconfigure.above = None; - XSendEvent(dpy, c->win, True, NoEventMask, &synev); -} - -void -focus(Client *c) { - if(c && !isvisible(c)) - return; - if(sel && sel != c) { - grabbuttons(sel, False); - XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]); - } - if(c) { - detachstack(c); - c->snext = stack; - stack = c; - grabbuttons(c, True); - } - sel = c; - drawstatus(); - if(!selscreen) - return; - if(c) { - XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]); - XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); - } else { - XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); - } -} - -Client * -getclient(Window w) { - Client *c; - - for(c = clients; c; c = c->next) { - if(c->win == w) { - return c; - } - } - return NULL; -} - -Bool -isprotodel(Client *c) { - int i, n; - Atom *protocols; - Bool ret = False; - - if(XGetWMProtocols(dpy, c->win, &protocols, &n)) { - for(i = 0; !ret && i < n; i++) - if(protocols[i] == wmatom[WMDelete]) - ret = True; - XFree(protocols); - } - return ret; -} - -void -killclient() { - if(!sel) - return; - if(isprotodel(sel)) - sendevent(sel->win, wmatom[WMProtocols], wmatom[WMDelete]); - else - XKillClient(dpy, sel->win); -} - -void -manage(Window w, XWindowAttributes *wa) { - Client *c; - Window trans; - - c = emallocz(sizeof(Client)); - c->tag = True; - c->win = w; - c->x = wa->x; - c->y = wa->y; - c->w = wa->width; - c->h = wa->height; - if(c->w == sw && c->h == sh) { - c->border = 0; - c->x = sx; - c->y = sy; - } else { - c->border = BORDERPX; - if(c->x + c->w + 2 * c->border > wax + waw) - c->x = wax + waw - c->w - 2 * c->border; - if(c->y + c->h + 2 * c->border > way + wah) - c->y = way + wah - c->h - 2 * c->border; - if(c->x < wax) - c->x = wax; - if(c->y < way) - c->y = way; - } - updatesizehints(c); - XSelectInput(dpy, c->win, - StructureNotifyMask | PropertyChangeMask | EnterWindowMask); - XGetTransientForHint(dpy, c->win, &trans); - grabbuttons(c, False); - XSetWindowBorder(dpy, c->win, dc.norm[ColBorder]); - updatetitle(c); - settags(c, getclient(trans)); - if(!c->isfloat) - c->isfloat = trans || c->isfixed; - if(clients) - clients->prev = c; - c->next = clients; - c->snext = stack; - stack = clients = c; - XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); - XMapWindow(dpy, c->win); - setclientstate(c, NormalState); - if(isvisible(c)) - focus(c); - arrange(); -} - -void -resize(Client *c, Bool sizehints) { - float actual, dx, dy, max, min; - XWindowChanges wc; - - if(c->w <= 0 || c->h <= 0) - return; - if(sizehints) { - if(c->minw && c->w < c->minw) - c->w = c->minw; - if(c->minh && c->h < c->minh) - c->h = c->minh; - if(c->maxw && c->w > c->maxw) - c->w = c->maxw; - if(c->maxh && c->h > c->maxh) - c->h = c->maxh; - /* inspired by algorithm from fluxbox */ - if(c->minay > 0 && c->maxay && (c->h - c->baseh) > 0) { - dx = (float)(c->w - c->basew); - dy = (float)(c->h - c->baseh); - min = (float)(c->minax) / (float)(c->minay); - max = (float)(c->maxax) / (float)(c->maxay); - actual = dx / dy; - if(max > 0 && min > 0 && actual > 0) { - if(actual < min) { - dy = (dx * min + dy) / (min * min + 1); - dx = dy * min; - c->w = (int)dx + c->basew; - c->h = (int)dy + c->baseh; - } - else if(actual > max) { - dy = (dx * min + dy) / (max * max + 1); - dx = dy * min; - c->w = (int)dx + c->basew; - c->h = (int)dy + c->baseh; - } - } - } - if(c->incw) - c->w -= (c->w - c->basew) % c->incw; - if(c->inch) - c->h -= (c->h - c->baseh) % c->inch; - } - if(c->w == sw && c->h == sh) - c->border = 0; - else - c->border = BORDERPX; - /* offscreen appearance fixes */ - if(c->x > sw) - c->x = sw - c->w - 2 * c->border; - if(c->y > sh) - c->y = sh - c->h - 2 * c->border; - if(c->x + c->w + 2 * c->border < sx) - c->x = sx; - if(c->y + c->h + 2 * c->border < sy) - c->y = sy; - wc.x = c->x; - wc.y = c->y; - wc.width = c->w; - wc.height = c->h; - wc.border_width = c->border; - XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc); - configure(c); - XSync(dpy, False); -} - -void -updatesizehints(Client *c) { - long msize; - XSizeHints size; - - if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) - size.flags = PSize; - c->flags = size.flags; - if(c->flags & PBaseSize) { - c->basew = size.base_width; - c->baseh = size.base_height; - } else { - c->basew = c->baseh = 0; - } - if(c->flags & PResizeInc) { - c->incw = size.width_inc; - c->inch = size.height_inc; - } else { - c->incw = c->inch = 0; - } - if(c->flags & PMaxSize) { - c->maxw = size.max_width; - c->maxh = size.max_height; - } else { - c->maxw = c->maxh = 0; - } - if(c->flags & PMinSize) { - c->minw = size.min_width; - c->minh = size.min_height; - } else { - c->minw = c->minh = 0; - } - if(c->flags & PAspect) { - c->minax = size.min_aspect.x; - c->minay = size.min_aspect.y; - c->maxax = size.max_aspect.x; - c->maxay = size.max_aspect.y; - } else { - c->minax = c->minay = c->maxax = c->maxay = 0; - } - c->isfixed = (c->maxw && c->minw && c->maxh && c->minh && - c->maxw == c->minw && c->maxh == c->minh); -} - -void -updatetitle(Client *c) { - char **list = NULL; - int n; - XTextProperty name; - - name.nitems = 0; - c->name[0] = 0; - XGetTextProperty(dpy, c->win, &name, netatom[NetWMName]); - if(!name.nitems) - XGetWMName(dpy, c->win, &name); - if(!name.nitems) - return; - if(name.encoding == XA_STRING) - strncpy(c->name, (char *)name.value, sizeof c->name); - else { - if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { - strncpy(c->name, *list, sizeof c->name); - XFreeStringList(list); - } - } - XFree(name.value); -} - -void -unmanage(Client *c) { - Client *nc; - - /* The server grab construct avoids race conditions. */ - XGrabServer(dpy); - XSetErrorHandler(xerrordummy); - detach(c); - detachstack(c); - if(sel == c) { - for(nc = stack; nc && !isvisible(nc); nc = nc->snext); - focus(nc); - } - XUngrabButton(dpy, AnyButton, AnyModifier, c->win); - setclientstate(c, WithdrawnState); - free(c); - XSync(dpy, False); - XSetErrorHandler(xerror); - XUngrabServer(dpy); - arrange(); -} - - - - - - - - - - - - - - - - - - - -/* static */ - - -static void -cleanup(void) { - close(STDIN_FILENO); - while(stack) { - resize(stack, True); - unmanage(stack); - } - if(dc.font.set) - XFreeFontSet(dpy, dc.font.set); - else - XFreeFont(dpy, dc.font.xfont); - XUngrabKey(dpy, AnyKey, AnyModifier, root); - XFreePixmap(dpy, dc.drawable); - XFreeGC(dpy, dc.gc); - XDestroyWindow(dpy, barwin); - XFreeCursor(dpy, cursor[CurNormal]); - XFreeCursor(dpy, cursor[CurResize]); - XFreeCursor(dpy, cursor[CurMove]); - XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); - XSync(dpy, False); -} - -static void -scan(void) { - unsigned int i, num; - Window *wins, d1, d2; - XWindowAttributes wa; - - wins = NULL; - if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { - for(i = 0; i < num; i++) { - if(!XGetWindowAttributes(dpy, wins[i], &wa)) - continue; - if(wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) - continue; - if(wa.map_state == IsViewable) - manage(wins[i], &wa); - } - } - if(wins) - XFree(wins); -} - -static void -setup(void) { - int i, j; - unsigned int mask; - Window w; - XModifierKeymap *modmap; - XSetWindowAttributes wa; - - /* init atoms */ - wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); - wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); - wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); - netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); - netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); - XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, - PropModeReplace, (unsigned char *) netatom, NetLast); - /* init cursors */ - cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr); - cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing); - cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur); - /* init modifier map */ - numlockmask = 0; - modmap = XGetModifierMapping(dpy); - for (i = 0; i < 8; i++) { - for (j = 0; j < modmap->max_keypermod; j++) { - if(modmap->modifiermap[i * modmap->max_keypermod + j] == XKeysymToKeycode(dpy, XK_Num_Lock)) - numlockmask = (1 << i); - } - } - XFreeModifiermap(modmap); - /* select for events */ - wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask - | EnterWindowMask | LeaveWindowMask; - wa.cursor = cursor[CurNormal]; - XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); - grabkeys(); - initrregs(); - ntags = 2; - seltag = True; - /* style */ - dc.norm[ColBorder] = getcolor(NORMBORDERCOLOR); - dc.norm[ColBG] = getcolor(NORMBGCOLOR); - dc.norm[ColFG] = getcolor(NORMFGCOLOR); - dc.sel[ColBorder] = getcolor(SELBORDERCOLOR); - dc.sel[ColBG] = getcolor(SELBGCOLOR); - dc.sel[ColFG] = getcolor(SELFGCOLOR); - setfont(FONT); - /* geometry */ - sx = sy = 0; - sw = DisplayWidth(dpy, screen); - sh = DisplayHeight(dpy, screen); - nmaster = NMASTER; - bmw = 1; - /* bar */ - dc.h = bh = dc.font.height + 2; - wa.override_redirect = 1; - wa.background_pixmap = ParentRelative; - wa.event_mask = ButtonPressMask | ExposureMask; - barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0, - DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), - CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); - XDefineCursor(dpy, barwin, cursor[CurNormal]); - XMapRaised(dpy, barwin); - strcpy(stext, "dwm-"VERSION); - /* windowarea */ - wax = sx; - way = sy + bh; - wah = sh - bh; - waw = sw; - /* pixmap for everything */ - dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); - dc.gc = XCreateGC(dpy, root, 0, 0); - XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); - /* multihead support */ - selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); -} - -/* - * Startup Error handler to check if another window manager - * is already running. - */ -static int -xerrorstart(Display *dsply, XErrorEvent *ee) { - otherwm = True; - return -1; -} - - - -void -sendevent(Window w, Atom a, long value) { - XEvent e; - - e.type = ClientMessage; - e.xclient.window = w; - e.xclient.message_type = a; - e.xclient.format = 32; - e.xclient.data.l[0] = value; - e.xclient.data.l[1] = CurrentTime; - XSendEvent(dpy, w, False, NoEventMask, &e); - XSync(dpy, False); -} - -void -quit() { - readin = running = False; -} - -/* There's no way to check accesses to destroyed windows, thus those cases are - * ignored (especially on UnmapNotify's). Other types of errors call Xlibs - * default error handler, which may call exit. - */ -int -xerror(Display *dpy, XErrorEvent *ee) { - if(ee->error_code == BadWindow - || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) - || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) - || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) - || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) - || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) - || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) - || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) - return 0; - fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", - ee->request_code, ee->error_code); - return xerrorxlib(dpy, ee); /* may call exit */ -} - -int -main(int argc, char *argv[]) { - char *p; - int r, xfd; - fd_set rd; - - if(argc == 2 && !strncmp("-v", argv[1], 3)) { - fputs("dwm-"VERSION", (C)opyright MMVI-MMVII Anselm R. Garbe\n", stdout); - exit(EXIT_SUCCESS); - } else if(argc != 1) { - eprint("usage: dwm [-v]\n"); - } - setlocale(LC_CTYPE, ""); - dpy = XOpenDisplay(0); - if(!dpy) { - eprint("dwm: cannot open display\n"); - } - xfd = ConnectionNumber(dpy); - screen = DefaultScreen(dpy); - root = RootWindow(dpy, screen); - otherwm = False; - XSetErrorHandler(xerrorstart); - /* this causes an error if some other window manager is running */ - XSelectInput(dpy, root, SubstructureRedirectMask); - XSync(dpy, False); - if(otherwm) { - eprint("dwm: another window manager is already running\n"); - } - - XSync(dpy, False); - XSetErrorHandler(NULL); - xerrorxlib = XSetErrorHandler(xerror); - XSync(dpy, False); - setup(); - drawstatus(); - scan(); - - /* main event loop, also reads status text from stdin */ - XSync(dpy, False); - procevent(); - readin = True; - while(running) { - FD_ZERO(&rd); - if(readin) - FD_SET(STDIN_FILENO, &rd); - FD_SET(xfd, &rd); - if(select(xfd + 1, &rd, NULL, NULL, NULL) == -1) { - if(errno == EINTR) - continue; - eprint("select failed\n"); - } - if(FD_ISSET(STDIN_FILENO, &rd)) { - switch(r = read(STDIN_FILENO, stext, sizeof stext - 1)) { - case -1: - strncpy(stext, strerror(errno), sizeof stext - 1); - stext[sizeof stext - 1] = '\0'; - readin = False; - break; - case 0: - strncpy(stext, "EOF", 4); - readin = False; - break; - default: - for(stext[r] = '\0', p = stext + strlen(stext) - 1; p >= stext && *p == '\n'; *p-- = '\0'); - for(; p >= stext && *p != '\n'; --p); - if(p > stext) - strncpy(stext, p + 1, sizeof stext); - } - drawstatus(); - } - if(FD_ISSET(xfd, &rd)) - procevent(); - } - cleanup(); - XCloseDisplay(dpy); - return 0; -}