aewl
diff aewl.c @ 759:45f23169563e
renamed dwm to aewl
updated copyright notices
updated man page
author | meillo@marmaro.de |
---|---|
date | Fri, 30 May 2008 22:18:18 +0200 |
parents | dwm.c@bc512840e5a5 |
children | 014c4cb1ae4a |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/aewl.c Fri May 30 22:18:18 2008 +0200 1.3 @@ -0,0 +1,1783 @@ 1.4 +/* (C)opyright MMVI-MMVII Anselm R. Garbe <garbeam at gmail dot com> 1.5 + * (C)opyright MMVIII markus schnalke <meillo at marmaro dot de> 1.6 + * See LICENSE file for license details. 1.7 + * 1.8 + * dynamic window manager is designed like any other X client as well. It is 1.9 + * driven through handling X events. In contrast to other X clients, a window 1.10 + * manager selects for SubstructureRedirectMask on the root window, to receive 1.11 + * events about window (dis-)appearance. Only one X connection at a time is 1.12 + * allowed to select for this event mask. 1.13 + * 1.14 + * Calls to fetch an X event from the event queue are blocking. Due reading 1.15 + * status text from standard input, a select()-driven main loop has been 1.16 + * implemented which selects for reads on the X connection and STDIN_FILENO to 1.17 + * handle all data smoothly. The event handlers of dwm are organized in an 1.18 + * array which is accessed whenever a new event has been fetched. This allows 1.19 + * event dispatching in O(1) time. 1.20 + * 1.21 + * Each child of the root window is called a client, except windows which have 1.22 + * set the override_redirect flag. Clients are organized in a global 1.23 + * doubly-linked client list, the focus history is remembered through a global 1.24 + * stack list. Each client contains an array of Bools of the same size as the 1.25 + * global tags array to indicate the tags of a client. For each client dwm 1.26 + * creates a small title window, which is resized whenever the (_NET_)WM_NAME 1.27 + * properties are updated or the client is moved/resized. 1.28 + * 1.29 + * Keys and tagging rules are organized as arrays and defined in the config.h 1.30 + * file. These arrays are kept static in event.o and tag.o respectively, 1.31 + * because no other part of dwm needs access to them. The current mode is 1.32 + * represented by the arrange() function pointer, which wether points to 1.33 + * dofloat() or dotile(). 1.34 + * 1.35 + * To understand everything else, start reading main.c:main(). 1.36 + */ 1.37 + 1.38 +#include "config.h" 1.39 +#include <errno.h> 1.40 +#include <locale.h> 1.41 +#include <regex.h> 1.42 +#include <stdio.h> 1.43 +#include <stdarg.h> 1.44 +#include <stdlib.h> 1.45 +#include <string.h> 1.46 +#include <unistd.h> 1.47 +#include <sys/select.h> 1.48 +#include <sys/types.h> 1.49 +#include <sys/wait.h> 1.50 +#include <X11/cursorfont.h> 1.51 +#include <X11/keysym.h> 1.52 +#include <X11/Xatom.h> 1.53 +#include <X11/Xlib.h> 1.54 +#include <X11/Xproto.h> 1.55 +#include <X11/Xutil.h> 1.56 + 1.57 +/* mask shorthands, used in event.c and client.c */ 1.58 +#define BUTTONMASK (ButtonPressMask | ButtonReleaseMask) 1.59 + 1.60 +enum { NetSupported, NetWMName, NetLast }; /* EWMH atoms */ 1.61 +enum { WMProtocols, WMDelete, WMState, WMLast }; /* default atoms */ 1.62 +enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ 1.63 +enum { ColBorder, ColFG, ColBG, ColLast }; /* color */ 1.64 + 1.65 +typedef struct { 1.66 + int ascent; 1.67 + int descent; 1.68 + int height; 1.69 + XFontSet set; 1.70 + XFontStruct *xfont; 1.71 +} Fnt; 1.72 + 1.73 +typedef struct { 1.74 + int x, y, w, h; 1.75 + unsigned long norm[ColLast]; 1.76 + unsigned long sel[ColLast]; 1.77 + Drawable drawable; 1.78 + Fnt font; 1.79 + GC gc; 1.80 +} DC; /* draw context */ 1.81 + 1.82 +typedef struct Client Client; 1.83 +struct Client { 1.84 + char name[256]; 1.85 + int x, y, w, h; 1.86 + int rx, ry, rw, rh; /* revert geometry */ 1.87 + int basew, baseh, incw, inch, maxw, maxh, minw, minh; 1.88 + int minax, minay, maxax, maxay; 1.89 + long flags; 1.90 + unsigned int border; 1.91 + Bool isfixed, isfloat, ismax; 1.92 + Bool tag; 1.93 + Client *next; 1.94 + Client *prev; 1.95 + Client *snext; 1.96 + Window win; 1.97 +}; 1.98 + 1.99 +typedef struct { 1.100 + const char *clpattern; 1.101 + int tag; 1.102 + Bool isfloat; 1.103 +} Rule; 1.104 + 1.105 +typedef struct { 1.106 + regex_t *clregex; 1.107 +} RReg; 1.108 + 1.109 + 1.110 +typedef struct { 1.111 + unsigned long mod; 1.112 + KeySym keysym; 1.113 + void (*func)(const char* cmd); 1.114 + const char* cmd; 1.115 +} Key; 1.116 + 1.117 + 1.118 +#define CLEANMASK(mask) (mask & ~(numlockmask | LockMask)) 1.119 +#define MOUSEMASK (BUTTONMASK | PointerMotionMask) 1.120 + 1.121 + 1.122 + 1.123 +const char *tags[]; /* all tags */ 1.124 +char stext[256]; /* status text */ 1.125 +int bh, bmw; /* bar height, bar mode label width */ 1.126 +int screen, sx, sy, sw, sh; /* screen geometry */ 1.127 +int wax, way, wah, waw; /* windowarea geometry */ 1.128 +unsigned int nmaster; /* number of master clients */ 1.129 +unsigned int ntags, numlockmask; /* number of tags, dynamic lock mask */ 1.130 +void (*handler[LASTEvent])(XEvent *); /* event handler */ 1.131 +void (*arrange)(void); /* arrange function, indicates mode */ 1.132 +Atom wmatom[WMLast], netatom[NetLast]; 1.133 +Bool running, selscreen, seltag; /* seltag is array of Bool */ 1.134 +Client *clients, *sel, *stack; /* global client list and stack */ 1.135 +Cursor cursor[CurLast]; 1.136 +DC dc; /* global draw context */ 1.137 +Display *dpy; 1.138 +Window root, barwin; 1.139 + 1.140 +Bool running = True; 1.141 +Bool selscreen = True; 1.142 +Client *clients = NULL; 1.143 +Client *sel = NULL; 1.144 +Client *stack = NULL; 1.145 +DC dc = {0}; 1.146 + 1.147 +static int (*xerrorxlib)(Display *, XErrorEvent *); 1.148 +static Bool otherwm, readin; 1.149 +static RReg *rreg = NULL; 1.150 +static unsigned int len = 0; 1.151 + 1.152 + 1.153 +TAGS 1.154 +RULES 1.155 + 1.156 + 1.157 +/* client.c */ 1.158 +void configure(Client *c); /* send synthetic configure event */ 1.159 +void focus(Client *c); /* focus c, c may be NULL */ 1.160 +Client *getclient(Window w); /* return client of w */ 1.161 +Bool isprotodel(Client *c); /* returns True if c->win supports wmatom[WMDelete] */ 1.162 +void manage(Window w, XWindowAttributes *wa); /* manage new client */ 1.163 +void resize(Client *c, Bool sizehints); /* resize c*/ 1.164 +void updatesizehints(Client *c); /* update the size hint variables of c */ 1.165 +void updatetitle(Client *c); /* update the name of c */ 1.166 +void unmanage(Client *c); /* destroy c */ 1.167 + 1.168 +/* draw.c */ 1.169 +void drawstatus(void); /* draw the bar */ 1.170 +unsigned long getcolor(const char *colstr); /* return color of colstr */ 1.171 +void setfont(const char *fontstr); /* set the font for DC */ 1.172 +unsigned int textw(const char *text); /* return the width of text in px*/ 1.173 + 1.174 +/* event.c */ 1.175 +void grabkeys(void); /* grab all keys defined in config.h */ 1.176 +void procevent(void); /* process pending X events */ 1.177 + 1.178 +/* main.c */ 1.179 +void sendevent(Window w, Atom a, long value); /* send synthetic event to w */ 1.180 +int xerror(Display *dsply, XErrorEvent *ee); /* dwm's X error handler */ 1.181 + 1.182 +/* tag.c */ 1.183 +void initrregs(void); /* initialize regexps of rules defined in config.h */ 1.184 +Client *getnext(Client *c); /* returns next visible client */ 1.185 +void settags(Client *c, Client *trans); /* sets tags of c */ 1.186 + 1.187 +/* util.c */ 1.188 +void *emallocz(unsigned int size); /* allocates zero-initialized memory, exits on error */ 1.189 +void eprint(const char *errstr, ...); /* prints errstr and exits with 1 */ 1.190 + 1.191 +/* view.c */ 1.192 +void detach(Client *c); /* detaches c from global client list */ 1.193 +void dofloat(void); /* arranges all windows floating */ 1.194 +void dotile(void); /* arranges all windows tiled */ 1.195 +void domax(void); /* arranges all windows fullscreen */ 1.196 +Bool isvisible(Client *c); /* returns True if client is visible */ 1.197 +void restack(void); /* restores z layers of all clients */ 1.198 + 1.199 + 1.200 +void toggleview(); /* toggle the viewed tag */ 1.201 +void focusnext(); /* focuses next visible client */ 1.202 +void zoom(); /* zooms the focused client to master area */ 1.203 +void killclient(); /* kill c nicely */ 1.204 +void quit(); /* quit dwm nicely */ 1.205 +void togglemode(); /* toggles global arrange function (dotile/dofloat) */ 1.206 +void togglefloat(); /* toggles focusesd client between floating/non-floating state */ 1.207 +void incnmaster(); /* increments nmaster */ 1.208 +void decnmaster(); /* decrements nmaster */ 1.209 +void toggletag(); /* toggles c tag */ 1.210 +void spawn(const char* cmd); /* forks a new subprocess with cmd */ 1.211 + 1.212 + 1.213 + 1.214 + 1.215 + 1.216 + 1.217 + 1.218 + 1.219 + 1.220 + 1.221 +/* from view.c */ 1.222 +/* static */ 1.223 + 1.224 +static Client * 1.225 +nexttiled(Client *c) { 1.226 + for(c = getnext(c); c && c->isfloat; c = getnext(c->next)); 1.227 + return c; 1.228 +} 1.229 + 1.230 +static void 1.231 +togglemax(Client *c) { 1.232 + XEvent ev; 1.233 + 1.234 + if(c->isfixed) 1.235 + return; 1.236 + 1.237 + if((c->ismax = !c->ismax)) { 1.238 + c->rx = c->x; c->x = wax; 1.239 + c->ry = c->y; c->y = way; 1.240 + c->rw = c->w; c->w = waw - 2 * BORDERPX; 1.241 + c->rh = c->h; c->h = wah - 2 * BORDERPX; 1.242 + } 1.243 + else { 1.244 + c->x = c->rx; 1.245 + c->y = c->ry; 1.246 + c->w = c->rw; 1.247 + c->h = c->rh; 1.248 + } 1.249 + resize(c, True); 1.250 + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); 1.251 +} 1.252 + 1.253 + 1.254 + 1.255 +void (*arrange)(void) = DEFMODE; 1.256 + 1.257 +void 1.258 +detach(Client *c) { 1.259 + if(c->prev) 1.260 + c->prev->next = c->next; 1.261 + if(c->next) 1.262 + c->next->prev = c->prev; 1.263 + if(c == clients) 1.264 + clients = c->next; 1.265 + c->next = c->prev = NULL; 1.266 +} 1.267 + 1.268 +void 1.269 +dofloat(void) { 1.270 + Client *c; 1.271 + 1.272 + for(c = clients; c; c = c->next) { 1.273 + if(isvisible(c)) { 1.274 + resize(c, True); 1.275 + } 1.276 + else 1.277 + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); 1.278 + } 1.279 + if(!sel || !isvisible(sel)) { 1.280 + for(c = stack; c && !isvisible(c); c = c->snext); 1.281 + focus(c); 1.282 + } 1.283 + restack(); 1.284 +} 1.285 + 1.286 +void 1.287 +dotile(void) { 1.288 + unsigned int i, n, mw, mh, tw, th; 1.289 + Client *c; 1.290 + 1.291 + for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) 1.292 + n++; 1.293 + /* window geoms */ 1.294 + mh = (n > nmaster) ? wah / nmaster : wah / (n > 0 ? n : 1); 1.295 + mw = (n > nmaster) ? waw / 2 : waw; 1.296 + th = (n > nmaster) ? wah / (n - nmaster) : 0; 1.297 + tw = waw - mw; 1.298 + 1.299 + for(i = 0, c = clients; c; c = c->next) 1.300 + if(isvisible(c)) { 1.301 + if(c->isfloat) { 1.302 + resize(c, True); 1.303 + continue; 1.304 + } 1.305 + c->ismax = False; 1.306 + c->x = wax; 1.307 + c->y = way; 1.308 + if(i < nmaster) { 1.309 + c->y += i * mh; 1.310 + c->w = mw - 2 * BORDERPX; 1.311 + c->h = mh - 2 * BORDERPX; 1.312 + } 1.313 + else { /* tile window */ 1.314 + c->x += mw; 1.315 + c->w = tw - 2 * BORDERPX; 1.316 + if(th > 2 * BORDERPX) { 1.317 + c->y += (i - nmaster) * th; 1.318 + c->h = th - 2 * BORDERPX; 1.319 + } 1.320 + else /* fallback if th <= 2 * BORDERPX */ 1.321 + c->h = wah - 2 * BORDERPX; 1.322 + } 1.323 + resize(c, False); 1.324 + i++; 1.325 + } 1.326 + else 1.327 + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); 1.328 + if(!sel || !isvisible(sel)) { 1.329 + for(c = stack; c && !isvisible(c); c = c->snext); 1.330 + focus(c); 1.331 + } 1.332 + restack(); 1.333 +} 1.334 + 1.335 +/* begin code by mitch */ 1.336 +void 1.337 +arrangemax(Client *c) { 1.338 + if(c == sel) { 1.339 + c->ismax = True; 1.340 + c->x = sx; 1.341 + c->y = bh; 1.342 + c->w = sw - 2 * BORDERPX; 1.343 + c->h = sh - bh - 2 * BORDERPX; 1.344 + XRaiseWindow(dpy, c->win); 1.345 + } else { 1.346 + c->ismax = False; 1.347 + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); 1.348 + XLowerWindow(dpy, c->win); 1.349 + } 1.350 +} 1.351 + 1.352 +void 1.353 +domax(void) { 1.354 + Client *c; 1.355 + 1.356 + for(c = clients; c; c = c->next) { 1.357 + if(isvisible(c)) { 1.358 + if(c->isfloat) { 1.359 + resize(c, True); 1.360 + continue; 1.361 + } 1.362 + arrangemax(c); 1.363 + resize(c, False); 1.364 + } else { 1.365 + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); 1.366 + } 1.367 + 1.368 + } 1.369 + if(!sel || !isvisible(sel)) { 1.370 + for(c = stack; c && !isvisible(c); c = c->snext); 1.371 + focus(c); 1.372 + } 1.373 + restack(); 1.374 +} 1.375 +/* end code by mitch */ 1.376 + 1.377 +void 1.378 +focusnext() { 1.379 + Client *c; 1.380 + 1.381 + if(!sel) 1.382 + return; 1.383 + if(!(c = getnext(sel->next))) 1.384 + c = getnext(clients); 1.385 + if(c) { 1.386 + focus(c); 1.387 + restack(); 1.388 + } 1.389 +} 1.390 + 1.391 +void 1.392 +incnmaster() { 1.393 + if((arrange == dofloat) || (wah / (nmaster + 1) <= 2 * BORDERPX)) 1.394 + return; 1.395 + nmaster++; 1.396 + if(sel) 1.397 + arrange(); 1.398 + else 1.399 + drawstatus(); 1.400 +} 1.401 + 1.402 +void 1.403 +decnmaster() { 1.404 + if((arrange == dofloat) || (nmaster <= 1)) 1.405 + return; 1.406 + nmaster--; 1.407 + if(sel) 1.408 + arrange(); 1.409 + else 1.410 + drawstatus(); 1.411 +} 1.412 + 1.413 +Bool 1.414 +isvisible(Client *c) { 1.415 + if((c->tag && seltag) || (!c->tag && !seltag)) { 1.416 + return True; 1.417 + } 1.418 + return False; 1.419 +} 1.420 + 1.421 +void 1.422 +restack(void) { 1.423 + Client *c; 1.424 + XEvent ev; 1.425 + 1.426 + drawstatus(); 1.427 + if(!sel) 1.428 + return; 1.429 + if(sel->isfloat || arrange == dofloat) 1.430 + XRaiseWindow(dpy, sel->win); 1.431 + 1.432 + /* begin code by mitch */ 1.433 + if(arrange == domax) { 1.434 + for(c = nexttiled(clients); c; c = nexttiled(c->next)) { 1.435 + arrangemax(c); 1.436 + resize(c, False); 1.437 + } 1.438 + 1.439 + } else if (arrange == dotile) { 1.440 + /* end code by mitch */ 1.441 + 1.442 + if(!sel->isfloat) 1.443 + XLowerWindow(dpy, sel->win); 1.444 + for(c = nexttiled(clients); c; c = nexttiled(c->next)) { 1.445 + if(c == sel) 1.446 + continue; 1.447 + XLowerWindow(dpy, c->win); 1.448 + } 1.449 + } 1.450 + XSync(dpy, False); 1.451 + while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); 1.452 +} 1.453 + 1.454 +void 1.455 +togglefloat() { 1.456 + if (!sel || arrange == dofloat) 1.457 + return; 1.458 + sel->isfloat = !sel->isfloat; 1.459 + arrange(); 1.460 +} 1.461 + 1.462 +void 1.463 +togglemode() { 1.464 + /* only toggle between tile and max - float is just available through togglefloat */ 1.465 + arrange = (arrange == dotile) ? domax : dotile; 1.466 + if(sel) 1.467 + arrange(); 1.468 + else 1.469 + drawstatus(); 1.470 +} 1.471 + 1.472 +void 1.473 +toggleview() { 1.474 + seltag = !seltag; 1.475 + arrange(); 1.476 +} 1.477 + 1.478 +void 1.479 +zoom() { 1.480 + unsigned int n; 1.481 + Client *c; 1.482 + 1.483 + if(!sel) 1.484 + return; 1.485 + if(sel->isfloat || (arrange == dofloat)) { 1.486 + togglemax(sel); 1.487 + return; 1.488 + } 1.489 + for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) 1.490 + n++; 1.491 + 1.492 + if((c = sel) == nexttiled(clients)) 1.493 + if(!(c = nexttiled(c->next))) 1.494 + return; 1.495 + detach(c); 1.496 + if(clients) 1.497 + clients->prev = c; 1.498 + c->next = clients; 1.499 + clients = c; 1.500 + focus(c); 1.501 + arrange(); 1.502 +} 1.503 + 1.504 + 1.505 + 1.506 + 1.507 + 1.508 + 1.509 + 1.510 + 1.511 + 1.512 + 1.513 + 1.514 + 1.515 + 1.516 + 1.517 + 1.518 + 1.519 +/* from util.c */ 1.520 + 1.521 + 1.522 +void * 1.523 +emallocz(unsigned int size) { 1.524 + void *res = calloc(1, size); 1.525 + 1.526 + if(!res) 1.527 + eprint("fatal: could not malloc() %u bytes\n", size); 1.528 + return res; 1.529 +} 1.530 + 1.531 +void 1.532 +eprint(const char *errstr, ...) { 1.533 + va_list ap; 1.534 + 1.535 + va_start(ap, errstr); 1.536 + vfprintf(stderr, errstr, ap); 1.537 + va_end(ap); 1.538 + exit(EXIT_FAILURE); 1.539 +} 1.540 + 1.541 +void 1.542 +spawn(const char* cmd) { 1.543 + static char *shell = NULL; 1.544 + 1.545 + if(!shell && !(shell = getenv("SHELL"))) 1.546 + shell = "/bin/sh"; 1.547 + if(!cmd) 1.548 + return; 1.549 + /* The double-fork construct avoids zombie processes and keeps the code 1.550 + * clean from stupid signal handlers. */ 1.551 + if(fork() == 0) { 1.552 + if(fork() == 0) { 1.553 + if(dpy) 1.554 + close(ConnectionNumber(dpy)); 1.555 + setsid(); 1.556 + execl(shell, shell, "-c", cmd, (char *)NULL); 1.557 + fprintf(stderr, "dwm: execl '%s -c %s'", shell, cmd); 1.558 + perror(" failed"); 1.559 + } 1.560 + exit(0); 1.561 + } 1.562 + wait(0); 1.563 +} 1.564 + 1.565 + 1.566 + 1.567 + 1.568 + 1.569 + 1.570 + 1.571 + 1.572 + 1.573 + 1.574 + 1.575 + 1.576 + 1.577 +/* from tag.c */ 1.578 + 1.579 +/* static */ 1.580 + 1.581 +Client * 1.582 +getnext(Client *c) { 1.583 + for(; c && !isvisible(c); c = c->next); 1.584 + return c; 1.585 +} 1.586 + 1.587 +void 1.588 +initrregs(void) { 1.589 + unsigned int i; 1.590 + regex_t *reg; 1.591 + 1.592 + if(rreg) 1.593 + return; 1.594 + len = sizeof rule / sizeof rule[0]; 1.595 + rreg = emallocz(len * sizeof(RReg)); 1.596 + for(i = 0; i < len; i++) { 1.597 + if(rule[i].clpattern) { 1.598 + reg = emallocz(sizeof(regex_t)); 1.599 + if(regcomp(reg, rule[i].clpattern, REG_EXTENDED)) 1.600 + free(reg); 1.601 + else 1.602 + rreg[i].clregex = reg; 1.603 + } 1.604 + } 1.605 +} 1.606 + 1.607 +void 1.608 +settags(Client *c, Client *trans) { 1.609 + char prop[512]; 1.610 + unsigned int i; 1.611 + regmatch_t tmp; 1.612 + Bool matched = trans != NULL; 1.613 + XClassHint ch = { 0 }; 1.614 + 1.615 + if(matched) { 1.616 + c->tag = trans->tag; 1.617 + } else { 1.618 + XGetClassHint(dpy, c->win, &ch); 1.619 + snprintf(prop, sizeof prop, "%s:%s:%s", 1.620 + ch.res_class ? ch.res_class : "", 1.621 + ch.res_name ? ch.res_name : "", c->name); 1.622 + for(i = 0; i < len; i++) 1.623 + if(rreg[i].clregex && !regexec(rreg[i].clregex, prop, 1, &tmp, 0)) { 1.624 + c->isfloat = rule[i].isfloat; 1.625 + if (rule[i].tag < 0) { 1.626 + c->tag = seltag; 1.627 + } else if (rule[i].tag == 0) { 1.628 + c->tag = True; 1.629 + } else { 1.630 + c->tag = False; 1.631 + } 1.632 + matched = True; 1.633 + break; /* perform only the first rule matching */ 1.634 + } 1.635 + if(ch.res_class) 1.636 + XFree(ch.res_class); 1.637 + if(ch.res_name) 1.638 + XFree(ch.res_name); 1.639 + } 1.640 + if(!matched) { 1.641 + c->tag = seltag; 1.642 + } 1.643 +} 1.644 + 1.645 +void 1.646 +toggletag() { 1.647 + if(!sel) 1.648 + return; 1.649 + sel->tag = !sel->tag; 1.650 + toggleview(); 1.651 +} 1.652 + 1.653 + 1.654 + 1.655 + 1.656 + 1.657 + 1.658 + 1.659 + 1.660 + 1.661 + 1.662 + 1.663 + 1.664 + 1.665 + 1.666 + 1.667 + 1.668 + 1.669 +/* from event.c */ 1.670 +/* static */ 1.671 + 1.672 +KEYS 1.673 + 1.674 + 1.675 + 1.676 +static void 1.677 +movemouse(Client *c) { 1.678 + int x1, y1, ocx, ocy, di; 1.679 + unsigned int dui; 1.680 + Window dummy; 1.681 + XEvent ev; 1.682 + 1.683 + ocx = c->x; 1.684 + ocy = c->y; 1.685 + if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, 1.686 + None, cursor[CurMove], CurrentTime) != GrabSuccess) 1.687 + return; 1.688 + c->ismax = False; 1.689 + XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui); 1.690 + for(;;) { 1.691 + XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev); 1.692 + switch (ev.type) { 1.693 + case ButtonRelease: 1.694 + resize(c, True); 1.695 + XUngrabPointer(dpy, CurrentTime); 1.696 + return; 1.697 + case ConfigureRequest: 1.698 + case Expose: 1.699 + case MapRequest: 1.700 + handler[ev.type](&ev); 1.701 + break; 1.702 + case MotionNotify: 1.703 + XSync(dpy, False); 1.704 + c->x = ocx + (ev.xmotion.x - x1); 1.705 + c->y = ocy + (ev.xmotion.y - y1); 1.706 + if(abs(wax + c->x) < SNAP) 1.707 + c->x = wax; 1.708 + else if(abs((wax + waw) - (c->x + c->w + 2 * c->border)) < SNAP) 1.709 + c->x = wax + waw - c->w - 2 * c->border; 1.710 + if(abs(way - c->y) < SNAP) 1.711 + c->y = way; 1.712 + else if(abs((way + wah) - (c->y + c->h + 2 * c->border)) < SNAP) 1.713 + c->y = way + wah - c->h - 2 * c->border; 1.714 + resize(c, False); 1.715 + break; 1.716 + } 1.717 + } 1.718 +} 1.719 + 1.720 +static void 1.721 +resizemouse(Client *c) { 1.722 + int ocx, ocy; 1.723 + int nw, nh; 1.724 + XEvent ev; 1.725 + 1.726 + ocx = c->x; 1.727 + ocy = c->y; 1.728 + if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, 1.729 + None, cursor[CurResize], CurrentTime) != GrabSuccess) 1.730 + return; 1.731 + c->ismax = False; 1.732 + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1); 1.733 + for(;;) { 1.734 + XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask , &ev); 1.735 + switch(ev.type) { 1.736 + case ButtonRelease: 1.737 + resize(c, True); 1.738 + XUngrabPointer(dpy, CurrentTime); 1.739 + return; 1.740 + case ConfigureRequest: 1.741 + case Expose: 1.742 + case MapRequest: 1.743 + handler[ev.type](&ev); 1.744 + break; 1.745 + case MotionNotify: 1.746 + XSync(dpy, False); 1.747 + nw = ev.xmotion.x - ocx - 2 * c->border + 1; 1.748 + c->w = nw > 0 ? nw : 1; 1.749 + nh = ev.xmotion.y - ocy - 2 * c->border + 1; 1.750 + c->h = nh > 0 ? nh : 1; 1.751 + resize(c, True); 1.752 + break; 1.753 + } 1.754 + } 1.755 +} 1.756 + 1.757 +static void 1.758 +buttonpress(XEvent *e) { 1.759 + int x; 1.760 + int i; 1.761 + Client *c; 1.762 + XButtonPressedEvent *ev = &e->xbutton; 1.763 + 1.764 + if(barwin == ev->window) { 1.765 + if(ev->button == Button1) { 1.766 + toggleview(); 1.767 + } 1.768 + return; 1.769 + } else if((c = getclient(ev->window))) { 1.770 + focus(c); 1.771 + if(CLEANMASK(ev->state) != MODKEY) 1.772 + return; 1.773 + if(ev->button == Button1 && (arrange == dofloat || c->isfloat)) { 1.774 + restack(); 1.775 + movemouse(c); 1.776 + } else if(ev->button == Button3 && (arrange == dofloat || c->isfloat) && !c->isfixed) { 1.777 + restack(); 1.778 + resizemouse(c); 1.779 + } 1.780 + } 1.781 +} 1.782 + 1.783 +static void 1.784 +configurerequest(XEvent *e) { 1.785 + unsigned long newmask; 1.786 + Client *c; 1.787 + XConfigureRequestEvent *ev = &e->xconfigurerequest; 1.788 + XWindowChanges wc; 1.789 + 1.790 + if((c = getclient(ev->window))) { 1.791 + c->ismax = False; 1.792 + if(ev->value_mask & CWX) 1.793 + c->x = ev->x; 1.794 + if(ev->value_mask & CWY) 1.795 + c->y = ev->y; 1.796 + if(ev->value_mask & CWWidth) 1.797 + c->w = ev->width; 1.798 + if(ev->value_mask & CWHeight) 1.799 + c->h = ev->height; 1.800 + if(ev->value_mask & CWBorderWidth) 1.801 + c->border = ev->border_width; 1.802 + wc.x = c->x; 1.803 + wc.y = c->y; 1.804 + wc.width = c->w; 1.805 + wc.height = c->h; 1.806 + newmask = ev->value_mask & (~(CWSibling | CWStackMode | CWBorderWidth)); 1.807 + if(newmask) 1.808 + XConfigureWindow(dpy, c->win, newmask, &wc); 1.809 + else 1.810 + configure(c); 1.811 + XSync(dpy, False); 1.812 + if(c->isfloat) { 1.813 + resize(c, False); 1.814 + if(!isvisible(c)) 1.815 + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); 1.816 + } 1.817 + else 1.818 + arrange(); 1.819 + } else { 1.820 + wc.x = ev->x; 1.821 + wc.y = ev->y; 1.822 + wc.width = ev->width; 1.823 + wc.height = ev->height; 1.824 + wc.border_width = ev->border_width; 1.825 + wc.sibling = ev->above; 1.826 + wc.stack_mode = ev->detail; 1.827 + XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); 1.828 + XSync(dpy, False); 1.829 + } 1.830 +} 1.831 + 1.832 +static void 1.833 +destroynotify(XEvent *e) { 1.834 + Client *c; 1.835 + XDestroyWindowEvent *ev = &e->xdestroywindow; 1.836 + 1.837 + if((c = getclient(ev->window))) 1.838 + unmanage(c); 1.839 +} 1.840 + 1.841 +static void 1.842 +enternotify(XEvent *e) { 1.843 + Client *c; 1.844 + XCrossingEvent *ev = &e->xcrossing; 1.845 + 1.846 + if(ev->mode != NotifyNormal || ev->detail == NotifyInferior) 1.847 + return; 1.848 + if((c = getclient(ev->window)) && isvisible(c)) 1.849 + focus(c); 1.850 + else if(ev->window == root) { 1.851 + selscreen = True; 1.852 + for(c = stack; c && !isvisible(c); c = c->snext); 1.853 + focus(c); 1.854 + } 1.855 +} 1.856 + 1.857 +static void 1.858 +expose(XEvent *e) { 1.859 + XExposeEvent *ev = &e->xexpose; 1.860 + 1.861 + if(ev->count == 0) { 1.862 + if(barwin == ev->window) 1.863 + drawstatus(); 1.864 + } 1.865 +} 1.866 + 1.867 +static void 1.868 +keypress(XEvent *e) { 1.869 + static unsigned int len = sizeof key / sizeof key[0]; 1.870 + unsigned int i; 1.871 + KeySym keysym; 1.872 + XKeyEvent *ev = &e->xkey; 1.873 + 1.874 + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); 1.875 + for(i = 0; i < len; i++) { 1.876 + if(keysym == key[i].keysym && CLEANMASK(key[i].mod) == CLEANMASK(ev->state)) { 1.877 + if(key[i].func) 1.878 + key[i].func(key[i].cmd); 1.879 + } 1.880 + } 1.881 +} 1.882 + 1.883 +static void 1.884 +leavenotify(XEvent *e) { 1.885 + XCrossingEvent *ev = &e->xcrossing; 1.886 + 1.887 + if((ev->window == root) && !ev->same_screen) { 1.888 + selscreen = False; 1.889 + focus(NULL); 1.890 + } 1.891 +} 1.892 + 1.893 +static void 1.894 +mappingnotify(XEvent *e) { 1.895 + XMappingEvent *ev = &e->xmapping; 1.896 + 1.897 + XRefreshKeyboardMapping(ev); 1.898 + if(ev->request == MappingKeyboard) 1.899 + grabkeys(); 1.900 +} 1.901 + 1.902 +static void 1.903 +maprequest(XEvent *e) { 1.904 + static XWindowAttributes wa; 1.905 + XMapRequestEvent *ev = &e->xmaprequest; 1.906 + 1.907 + if(!XGetWindowAttributes(dpy, ev->window, &wa)) 1.908 + return; 1.909 + if(wa.override_redirect) { 1.910 + XSelectInput(dpy, ev->window, 1.911 + (StructureNotifyMask | PropertyChangeMask)); 1.912 + return; 1.913 + } 1.914 + if(!getclient(ev->window)) 1.915 + manage(ev->window, &wa); 1.916 +} 1.917 + 1.918 +static void 1.919 +propertynotify(XEvent *e) { 1.920 + Client *c; 1.921 + Window trans; 1.922 + XPropertyEvent *ev = &e->xproperty; 1.923 + 1.924 + if(ev->state == PropertyDelete) 1.925 + return; /* ignore */ 1.926 + if((c = getclient(ev->window))) { 1.927 + switch (ev->atom) { 1.928 + default: break; 1.929 + case XA_WM_TRANSIENT_FOR: 1.930 + XGetTransientForHint(dpy, c->win, &trans); 1.931 + if(!c->isfloat && (c->isfloat = (trans != 0))) 1.932 + arrange(); 1.933 + break; 1.934 + case XA_WM_NORMAL_HINTS: 1.935 + updatesizehints(c); 1.936 + break; 1.937 + } 1.938 + if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { 1.939 + updatetitle(c); 1.940 + if(c == sel) 1.941 + drawstatus(); 1.942 + } 1.943 + } 1.944 +} 1.945 + 1.946 +static void 1.947 +unmapnotify(XEvent *e) { 1.948 + Client *c; 1.949 + XUnmapEvent *ev = &e->xunmap; 1.950 + 1.951 + if((c = getclient(ev->window))) 1.952 + unmanage(c); 1.953 +} 1.954 + 1.955 + 1.956 + 1.957 +void (*handler[LASTEvent]) (XEvent *) = { 1.958 + [ButtonPress] = buttonpress, 1.959 + [ConfigureRequest] = configurerequest, 1.960 + [DestroyNotify] = destroynotify, 1.961 + [EnterNotify] = enternotify, 1.962 + [LeaveNotify] = leavenotify, 1.963 + [Expose] = expose, 1.964 + [KeyPress] = keypress, 1.965 + [MappingNotify] = mappingnotify, 1.966 + [MapRequest] = maprequest, 1.967 + [PropertyNotify] = propertynotify, 1.968 + [UnmapNotify] = unmapnotify 1.969 +}; 1.970 + 1.971 +void 1.972 +grabkeys(void) { 1.973 + static unsigned int len = sizeof key / sizeof key[0]; 1.974 + unsigned int i; 1.975 + KeyCode code; 1.976 + 1.977 + XUngrabKey(dpy, AnyKey, AnyModifier, root); 1.978 + for(i = 0; i < len; i++) { 1.979 + code = XKeysymToKeycode(dpy, key[i].keysym); 1.980 + XGrabKey(dpy, code, key[i].mod, root, True, 1.981 + GrabModeAsync, GrabModeAsync); 1.982 + XGrabKey(dpy, code, key[i].mod | LockMask, root, True, 1.983 + GrabModeAsync, GrabModeAsync); 1.984 + XGrabKey(dpy, code, key[i].mod | numlockmask, root, True, 1.985 + GrabModeAsync, GrabModeAsync); 1.986 + XGrabKey(dpy, code, key[i].mod | numlockmask | LockMask, root, True, 1.987 + GrabModeAsync, GrabModeAsync); 1.988 + } 1.989 +} 1.990 + 1.991 +void 1.992 +procevent(void) { 1.993 + XEvent ev; 1.994 + 1.995 + while(XPending(dpy)) { 1.996 + XNextEvent(dpy, &ev); 1.997 + if(handler[ev.type]) 1.998 + (handler[ev.type])(&ev); /* call handler */ 1.999 + } 1.1000 +} 1.1001 + 1.1002 + 1.1003 + 1.1004 + 1.1005 + 1.1006 + 1.1007 + 1.1008 + 1.1009 + 1.1010 + 1.1011 + 1.1012 + 1.1013 + 1.1014 + 1.1015 + 1.1016 +/* from draw.c */ 1.1017 +/* static */ 1.1018 + 1.1019 +static unsigned int 1.1020 +textnw(const char *text, unsigned int len) { 1.1021 + XRectangle r; 1.1022 + 1.1023 + if(dc.font.set) { 1.1024 + XmbTextExtents(dc.font.set, text, len, NULL, &r); 1.1025 + return r.width; 1.1026 + } 1.1027 + return XTextWidth(dc.font.xfont, text, len); 1.1028 +} 1.1029 + 1.1030 +static void 1.1031 +drawtext(const char *text, unsigned long col[ColLast]) { 1.1032 + int x, y, w, h; 1.1033 + static char buf[256]; 1.1034 + unsigned int len, olen; 1.1035 + XGCValues gcv; 1.1036 + XRectangle r = { dc.x, dc.y, dc.w, dc.h }; 1.1037 + 1.1038 + XSetForeground(dpy, dc.gc, col[ColBG]); 1.1039 + XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); 1.1040 + if(!text) 1.1041 + return; 1.1042 + w = 0; 1.1043 + olen = len = strlen(text); 1.1044 + if(len >= sizeof buf) 1.1045 + len = sizeof buf - 1; 1.1046 + memcpy(buf, text, len); 1.1047 + buf[len] = 0; 1.1048 + h = dc.font.ascent + dc.font.descent; 1.1049 + y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent; 1.1050 + x = dc.x + (h / 2); 1.1051 + /* shorten text if necessary */ 1.1052 + while(len && (w = textnw(buf, len)) > dc.w - h) 1.1053 + buf[--len] = 0; 1.1054 + if(len < olen) { 1.1055 + if(len > 1) 1.1056 + buf[len - 1] = '.'; 1.1057 + if(len > 2) 1.1058 + buf[len - 2] = '.'; 1.1059 + if(len > 3) 1.1060 + buf[len - 3] = '.'; 1.1061 + } 1.1062 + if(w > dc.w) 1.1063 + return; /* too long */ 1.1064 + gcv.foreground = col[ColFG]; 1.1065 + if(dc.font.set) { 1.1066 + XChangeGC(dpy, dc.gc, GCForeground, &gcv); 1.1067 + XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); 1.1068 + } else { 1.1069 + gcv.font = dc.font.xfont->fid; 1.1070 + XChangeGC(dpy, dc.gc, GCForeground | GCFont, &gcv); 1.1071 + XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); 1.1072 + } 1.1073 +} 1.1074 + 1.1075 + 1.1076 + 1.1077 +void 1.1078 +drawstatus(void) { 1.1079 + int x; 1.1080 + unsigned int i; 1.1081 + 1.1082 + dc.x = dc.y = 0; 1.1083 + for(i = 0; i < ntags; i++) { 1.1084 + dc.w = textw(tags[i]); 1.1085 + drawtext(tags[i], ( ((i == 0 && seltag) || (i == 1 && !seltag)) ? dc.sel : dc.norm)); 1.1086 + dc.x += dc.w + 1; 1.1087 + } 1.1088 + dc.w = bmw; 1.1089 + drawtext("", dc.norm); 1.1090 + x = dc.x + dc.w; 1.1091 + dc.w = textw(stext); 1.1092 + dc.x = sw - dc.w; 1.1093 + if(dc.x < x) { 1.1094 + dc.x = x; 1.1095 + dc.w = sw - x; 1.1096 + } 1.1097 + drawtext(stext, dc.norm); 1.1098 + if((dc.w = dc.x - x) > bh) { 1.1099 + dc.x = x; 1.1100 + drawtext(sel ? sel->name : NULL, dc.norm); 1.1101 + } 1.1102 + XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0); 1.1103 + XSync(dpy, False); 1.1104 +} 1.1105 + 1.1106 +unsigned long 1.1107 +getcolor(const char *colstr) { 1.1108 + Colormap cmap = DefaultColormap(dpy, screen); 1.1109 + XColor color; 1.1110 + 1.1111 + if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) 1.1112 + eprint("error, cannot allocate color '%s'\n", colstr); 1.1113 + return color.pixel; 1.1114 +} 1.1115 + 1.1116 +void 1.1117 +setfont(const char *fontstr) { 1.1118 + char *def, **missing; 1.1119 + int i, n; 1.1120 + 1.1121 + missing = NULL; 1.1122 + if(dc.font.set) 1.1123 + XFreeFontSet(dpy, dc.font.set); 1.1124 + dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def); 1.1125 + if(missing) { 1.1126 + while(n--) 1.1127 + fprintf(stderr, "missing fontset: %s\n", missing[n]); 1.1128 + XFreeStringList(missing); 1.1129 + } 1.1130 + if(dc.font.set) { 1.1131 + XFontSetExtents *font_extents; 1.1132 + XFontStruct **xfonts; 1.1133 + char **font_names; 1.1134 + dc.font.ascent = dc.font.descent = 0; 1.1135 + font_extents = XExtentsOfFontSet(dc.font.set); 1.1136 + n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); 1.1137 + for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) { 1.1138 + if(dc.font.ascent < (*xfonts)->ascent) 1.1139 + dc.font.ascent = (*xfonts)->ascent; 1.1140 + if(dc.font.descent < (*xfonts)->descent) 1.1141 + dc.font.descent = (*xfonts)->descent; 1.1142 + xfonts++; 1.1143 + } 1.1144 + } else { 1.1145 + if(dc.font.xfont) 1.1146 + XFreeFont(dpy, dc.font.xfont); 1.1147 + dc.font.xfont = NULL; 1.1148 + if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))) 1.1149 + eprint("error, cannot load font: '%s'\n", fontstr); 1.1150 + dc.font.ascent = dc.font.xfont->ascent; 1.1151 + dc.font.descent = dc.font.xfont->descent; 1.1152 + } 1.1153 + dc.font.height = dc.font.ascent + dc.font.descent; 1.1154 +} 1.1155 + 1.1156 +unsigned int 1.1157 +textw(const char *text) { 1.1158 + return textnw(text, strlen(text)) + dc.font.height; 1.1159 +} 1.1160 + 1.1161 + 1.1162 + 1.1163 + 1.1164 + 1.1165 + 1.1166 + 1.1167 + 1.1168 + 1.1169 + 1.1170 + 1.1171 +/* from client.c */ 1.1172 +/* static */ 1.1173 + 1.1174 +static void 1.1175 +detachstack(Client *c) { 1.1176 + Client **tc; 1.1177 + for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext); 1.1178 + *tc = c->snext; 1.1179 +} 1.1180 + 1.1181 +static void 1.1182 +grabbuttons(Client *c, Bool focused) { 1.1183 + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); 1.1184 + 1.1185 + if(focused) { 1.1186 + XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK, 1.1187 + GrabModeAsync, GrabModeSync, None, None); 1.1188 + XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK, 1.1189 + GrabModeAsync, GrabModeSync, None, None); 1.1190 + XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK, 1.1191 + GrabModeAsync, GrabModeSync, None, None); 1.1192 + XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, 1.1193 + GrabModeAsync, GrabModeSync, None, None); 1.1194 + 1.1195 + XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK, 1.1196 + GrabModeAsync, GrabModeSync, None, None); 1.1197 + XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK, 1.1198 + GrabModeAsync, GrabModeSync, None, None); 1.1199 + XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK, 1.1200 + GrabModeAsync, GrabModeSync, None, None); 1.1201 + XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, 1.1202 + GrabModeAsync, GrabModeSync, None, None); 1.1203 + 1.1204 + XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK, 1.1205 + GrabModeAsync, GrabModeSync, None, None); 1.1206 + XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK, 1.1207 + GrabModeAsync, GrabModeSync, None, None); 1.1208 + XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK, 1.1209 + GrabModeAsync, GrabModeSync, None, None); 1.1210 + XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, 1.1211 + GrabModeAsync, GrabModeSync, None, None); 1.1212 + } else { 1.1213 + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, 1.1214 + GrabModeAsync, GrabModeSync, None, None); 1.1215 + } 1.1216 +} 1.1217 + 1.1218 +static void 1.1219 +setclientstate(Client *c, long state) { 1.1220 + long data[] = {state, None}; 1.1221 + XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, 1.1222 + PropModeReplace, (unsigned char *)data, 2); 1.1223 +} 1.1224 + 1.1225 +static int 1.1226 +xerrordummy(Display *dsply, XErrorEvent *ee) { 1.1227 + return 0; 1.1228 +} 1.1229 + 1.1230 + 1.1231 + 1.1232 +void 1.1233 +configure(Client *c) { 1.1234 + XEvent synev; 1.1235 + 1.1236 + synev.type = ConfigureNotify; 1.1237 + synev.xconfigure.display = dpy; 1.1238 + synev.xconfigure.event = c->win; 1.1239 + synev.xconfigure.window = c->win; 1.1240 + synev.xconfigure.x = c->x; 1.1241 + synev.xconfigure.y = c->y; 1.1242 + synev.xconfigure.width = c->w; 1.1243 + synev.xconfigure.height = c->h; 1.1244 + synev.xconfigure.border_width = c->border; 1.1245 + synev.xconfigure.above = None; 1.1246 + XSendEvent(dpy, c->win, True, NoEventMask, &synev); 1.1247 +} 1.1248 + 1.1249 +void 1.1250 +focus(Client *c) { 1.1251 + if(c && !isvisible(c)) 1.1252 + return; 1.1253 + if(sel && sel != c) { 1.1254 + grabbuttons(sel, False); 1.1255 + XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]); 1.1256 + } 1.1257 + if(c) { 1.1258 + detachstack(c); 1.1259 + c->snext = stack; 1.1260 + stack = c; 1.1261 + grabbuttons(c, True); 1.1262 + } 1.1263 + sel = c; 1.1264 + drawstatus(); 1.1265 + if(!selscreen) 1.1266 + return; 1.1267 + if(c) { 1.1268 + XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]); 1.1269 + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); 1.1270 + } else { 1.1271 + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); 1.1272 + } 1.1273 +} 1.1274 + 1.1275 +Client * 1.1276 +getclient(Window w) { 1.1277 + Client *c; 1.1278 + 1.1279 + for(c = clients; c; c = c->next) { 1.1280 + if(c->win == w) { 1.1281 + return c; 1.1282 + } 1.1283 + } 1.1284 + return NULL; 1.1285 +} 1.1286 + 1.1287 +Bool 1.1288 +isprotodel(Client *c) { 1.1289 + int i, n; 1.1290 + Atom *protocols; 1.1291 + Bool ret = False; 1.1292 + 1.1293 + if(XGetWMProtocols(dpy, c->win, &protocols, &n)) { 1.1294 + for(i = 0; !ret && i < n; i++) 1.1295 + if(protocols[i] == wmatom[WMDelete]) 1.1296 + ret = True; 1.1297 + XFree(protocols); 1.1298 + } 1.1299 + return ret; 1.1300 +} 1.1301 + 1.1302 +void 1.1303 +killclient() { 1.1304 + if(!sel) 1.1305 + return; 1.1306 + if(isprotodel(sel)) 1.1307 + sendevent(sel->win, wmatom[WMProtocols], wmatom[WMDelete]); 1.1308 + else 1.1309 + XKillClient(dpy, sel->win); 1.1310 +} 1.1311 + 1.1312 +void 1.1313 +manage(Window w, XWindowAttributes *wa) { 1.1314 + Client *c; 1.1315 + Window trans; 1.1316 + 1.1317 + c = emallocz(sizeof(Client)); 1.1318 + c->tag = True; 1.1319 + c->win = w; 1.1320 + c->x = wa->x; 1.1321 + c->y = wa->y; 1.1322 + c->w = wa->width; 1.1323 + c->h = wa->height; 1.1324 + if(c->w == sw && c->h == sh) { 1.1325 + c->border = 0; 1.1326 + c->x = sx; 1.1327 + c->y = sy; 1.1328 + } else { 1.1329 + c->border = BORDERPX; 1.1330 + if(c->x + c->w + 2 * c->border > wax + waw) 1.1331 + c->x = wax + waw - c->w - 2 * c->border; 1.1332 + if(c->y + c->h + 2 * c->border > way + wah) 1.1333 + c->y = way + wah - c->h - 2 * c->border; 1.1334 + if(c->x < wax) 1.1335 + c->x = wax; 1.1336 + if(c->y < way) 1.1337 + c->y = way; 1.1338 + } 1.1339 + updatesizehints(c); 1.1340 + XSelectInput(dpy, c->win, 1.1341 + StructureNotifyMask | PropertyChangeMask | EnterWindowMask); 1.1342 + XGetTransientForHint(dpy, c->win, &trans); 1.1343 + grabbuttons(c, False); 1.1344 + XSetWindowBorder(dpy, c->win, dc.norm[ColBorder]); 1.1345 + updatetitle(c); 1.1346 + settags(c, getclient(trans)); 1.1347 + if(!c->isfloat) 1.1348 + c->isfloat = trans || c->isfixed; 1.1349 + if(clients) 1.1350 + clients->prev = c; 1.1351 + c->next = clients; 1.1352 + c->snext = stack; 1.1353 + stack = clients = c; 1.1354 + XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); 1.1355 + XMapWindow(dpy, c->win); 1.1356 + setclientstate(c, NormalState); 1.1357 + if(isvisible(c)) 1.1358 + focus(c); 1.1359 + arrange(); 1.1360 +} 1.1361 + 1.1362 +void 1.1363 +resize(Client *c, Bool sizehints) { 1.1364 + float actual, dx, dy, max, min; 1.1365 + XWindowChanges wc; 1.1366 + 1.1367 + if(c->w <= 0 || c->h <= 0) 1.1368 + return; 1.1369 + if(sizehints) { 1.1370 + if(c->minw && c->w < c->minw) 1.1371 + c->w = c->minw; 1.1372 + if(c->minh && c->h < c->minh) 1.1373 + c->h = c->minh; 1.1374 + if(c->maxw && c->w > c->maxw) 1.1375 + c->w = c->maxw; 1.1376 + if(c->maxh && c->h > c->maxh) 1.1377 + c->h = c->maxh; 1.1378 + /* inspired by algorithm from fluxbox */ 1.1379 + if(c->minay > 0 && c->maxay && (c->h - c->baseh) > 0) { 1.1380 + dx = (float)(c->w - c->basew); 1.1381 + dy = (float)(c->h - c->baseh); 1.1382 + min = (float)(c->minax) / (float)(c->minay); 1.1383 + max = (float)(c->maxax) / (float)(c->maxay); 1.1384 + actual = dx / dy; 1.1385 + if(max > 0 && min > 0 && actual > 0) { 1.1386 + if(actual < min) { 1.1387 + dy = (dx * min + dy) / (min * min + 1); 1.1388 + dx = dy * min; 1.1389 + c->w = (int)dx + c->basew; 1.1390 + c->h = (int)dy + c->baseh; 1.1391 + } 1.1392 + else if(actual > max) { 1.1393 + dy = (dx * min + dy) / (max * max + 1); 1.1394 + dx = dy * min; 1.1395 + c->w = (int)dx + c->basew; 1.1396 + c->h = (int)dy + c->baseh; 1.1397 + } 1.1398 + } 1.1399 + } 1.1400 + if(c->incw) 1.1401 + c->w -= (c->w - c->basew) % c->incw; 1.1402 + if(c->inch) 1.1403 + c->h -= (c->h - c->baseh) % c->inch; 1.1404 + } 1.1405 + if(c->w == sw && c->h == sh) 1.1406 + c->border = 0; 1.1407 + else 1.1408 + c->border = BORDERPX; 1.1409 + /* offscreen appearance fixes */ 1.1410 + if(c->x > sw) 1.1411 + c->x = sw - c->w - 2 * c->border; 1.1412 + if(c->y > sh) 1.1413 + c->y = sh - c->h - 2 * c->border; 1.1414 + if(c->x + c->w + 2 * c->border < sx) 1.1415 + c->x = sx; 1.1416 + if(c->y + c->h + 2 * c->border < sy) 1.1417 + c->y = sy; 1.1418 + wc.x = c->x; 1.1419 + wc.y = c->y; 1.1420 + wc.width = c->w; 1.1421 + wc.height = c->h; 1.1422 + wc.border_width = c->border; 1.1423 + XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc); 1.1424 + configure(c); 1.1425 + XSync(dpy, False); 1.1426 +} 1.1427 + 1.1428 +void 1.1429 +updatesizehints(Client *c) { 1.1430 + long msize; 1.1431 + XSizeHints size; 1.1432 + 1.1433 + if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) 1.1434 + size.flags = PSize; 1.1435 + c->flags = size.flags; 1.1436 + if(c->flags & PBaseSize) { 1.1437 + c->basew = size.base_width; 1.1438 + c->baseh = size.base_height; 1.1439 + } else { 1.1440 + c->basew = c->baseh = 0; 1.1441 + } 1.1442 + if(c->flags & PResizeInc) { 1.1443 + c->incw = size.width_inc; 1.1444 + c->inch = size.height_inc; 1.1445 + } else { 1.1446 + c->incw = c->inch = 0; 1.1447 + } 1.1448 + if(c->flags & PMaxSize) { 1.1449 + c->maxw = size.max_width; 1.1450 + c->maxh = size.max_height; 1.1451 + } else { 1.1452 + c->maxw = c->maxh = 0; 1.1453 + } 1.1454 + if(c->flags & PMinSize) { 1.1455 + c->minw = size.min_width; 1.1456 + c->minh = size.min_height; 1.1457 + } else { 1.1458 + c->minw = c->minh = 0; 1.1459 + } 1.1460 + if(c->flags & PAspect) { 1.1461 + c->minax = size.min_aspect.x; 1.1462 + c->minay = size.min_aspect.y; 1.1463 + c->maxax = size.max_aspect.x; 1.1464 + c->maxay = size.max_aspect.y; 1.1465 + } else { 1.1466 + c->minax = c->minay = c->maxax = c->maxay = 0; 1.1467 + } 1.1468 + c->isfixed = (c->maxw && c->minw && c->maxh && c->minh && 1.1469 + c->maxw == c->minw && c->maxh == c->minh); 1.1470 +} 1.1471 + 1.1472 +void 1.1473 +updatetitle(Client *c) { 1.1474 + char **list = NULL; 1.1475 + int n; 1.1476 + XTextProperty name; 1.1477 + 1.1478 + name.nitems = 0; 1.1479 + c->name[0] = 0; 1.1480 + XGetTextProperty(dpy, c->win, &name, netatom[NetWMName]); 1.1481 + if(!name.nitems) 1.1482 + XGetWMName(dpy, c->win, &name); 1.1483 + if(!name.nitems) 1.1484 + return; 1.1485 + if(name.encoding == XA_STRING) 1.1486 + strncpy(c->name, (char *)name.value, sizeof c->name); 1.1487 + else { 1.1488 + if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { 1.1489 + strncpy(c->name, *list, sizeof c->name); 1.1490 + XFreeStringList(list); 1.1491 + } 1.1492 + } 1.1493 + XFree(name.value); 1.1494 +} 1.1495 + 1.1496 +void 1.1497 +unmanage(Client *c) { 1.1498 + Client *nc; 1.1499 + 1.1500 + /* The server grab construct avoids race conditions. */ 1.1501 + XGrabServer(dpy); 1.1502 + XSetErrorHandler(xerrordummy); 1.1503 + detach(c); 1.1504 + detachstack(c); 1.1505 + if(sel == c) { 1.1506 + for(nc = stack; nc && !isvisible(nc); nc = nc->snext); 1.1507 + focus(nc); 1.1508 + } 1.1509 + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); 1.1510 + setclientstate(c, WithdrawnState); 1.1511 + free(c); 1.1512 + XSync(dpy, False); 1.1513 + XSetErrorHandler(xerror); 1.1514 + XUngrabServer(dpy); 1.1515 + arrange(); 1.1516 +} 1.1517 + 1.1518 + 1.1519 + 1.1520 + 1.1521 + 1.1522 + 1.1523 + 1.1524 + 1.1525 + 1.1526 + 1.1527 + 1.1528 + 1.1529 + 1.1530 + 1.1531 + 1.1532 + 1.1533 + 1.1534 + 1.1535 + 1.1536 +/* static */ 1.1537 + 1.1538 + 1.1539 +static void 1.1540 +cleanup(void) { 1.1541 + close(STDIN_FILENO); 1.1542 + while(stack) { 1.1543 + resize(stack, True); 1.1544 + unmanage(stack); 1.1545 + } 1.1546 + if(dc.font.set) 1.1547 + XFreeFontSet(dpy, dc.font.set); 1.1548 + else 1.1549 + XFreeFont(dpy, dc.font.xfont); 1.1550 + XUngrabKey(dpy, AnyKey, AnyModifier, root); 1.1551 + XFreePixmap(dpy, dc.drawable); 1.1552 + XFreeGC(dpy, dc.gc); 1.1553 + XDestroyWindow(dpy, barwin); 1.1554 + XFreeCursor(dpy, cursor[CurNormal]); 1.1555 + XFreeCursor(dpy, cursor[CurResize]); 1.1556 + XFreeCursor(dpy, cursor[CurMove]); 1.1557 + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); 1.1558 + XSync(dpy, False); 1.1559 +} 1.1560 + 1.1561 +static void 1.1562 +scan(void) { 1.1563 + unsigned int i, num; 1.1564 + Window *wins, d1, d2; 1.1565 + XWindowAttributes wa; 1.1566 + 1.1567 + wins = NULL; 1.1568 + if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { 1.1569 + for(i = 0; i < num; i++) { 1.1570 + if(!XGetWindowAttributes(dpy, wins[i], &wa)) 1.1571 + continue; 1.1572 + if(wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) 1.1573 + continue; 1.1574 + if(wa.map_state == IsViewable) 1.1575 + manage(wins[i], &wa); 1.1576 + } 1.1577 + } 1.1578 + if(wins) 1.1579 + XFree(wins); 1.1580 +} 1.1581 + 1.1582 +static void 1.1583 +setup(void) { 1.1584 + int i, j; 1.1585 + unsigned int mask; 1.1586 + Window w; 1.1587 + XModifierKeymap *modmap; 1.1588 + XSetWindowAttributes wa; 1.1589 + 1.1590 + /* init atoms */ 1.1591 + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); 1.1592 + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); 1.1593 + wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); 1.1594 + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); 1.1595 + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); 1.1596 + XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, 1.1597 + PropModeReplace, (unsigned char *) netatom, NetLast); 1.1598 + /* init cursors */ 1.1599 + cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr); 1.1600 + cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing); 1.1601 + cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur); 1.1602 + /* init modifier map */ 1.1603 + numlockmask = 0; 1.1604 + modmap = XGetModifierMapping(dpy); 1.1605 + for (i = 0; i < 8; i++) { 1.1606 + for (j = 0; j < modmap->max_keypermod; j++) { 1.1607 + if(modmap->modifiermap[i * modmap->max_keypermod + j] == XKeysymToKeycode(dpy, XK_Num_Lock)) 1.1608 + numlockmask = (1 << i); 1.1609 + } 1.1610 + } 1.1611 + XFreeModifiermap(modmap); 1.1612 + /* select for events */ 1.1613 + wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask 1.1614 + | EnterWindowMask | LeaveWindowMask; 1.1615 + wa.cursor = cursor[CurNormal]; 1.1616 + XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); 1.1617 + grabkeys(); 1.1618 + initrregs(); 1.1619 + ntags = 2; 1.1620 + seltag = True; 1.1621 + /* style */ 1.1622 + dc.norm[ColBorder] = getcolor(NORMBORDERCOLOR); 1.1623 + dc.norm[ColBG] = getcolor(NORMBGCOLOR); 1.1624 + dc.norm[ColFG] = getcolor(NORMFGCOLOR); 1.1625 + dc.sel[ColBorder] = getcolor(SELBORDERCOLOR); 1.1626 + dc.sel[ColBG] = getcolor(SELBGCOLOR); 1.1627 + dc.sel[ColFG] = getcolor(SELFGCOLOR); 1.1628 + setfont(FONT); 1.1629 + /* geometry */ 1.1630 + sx = sy = 0; 1.1631 + sw = DisplayWidth(dpy, screen); 1.1632 + sh = DisplayHeight(dpy, screen); 1.1633 + nmaster = NMASTER; 1.1634 + bmw = 1; 1.1635 + /* bar */ 1.1636 + dc.h = bh = dc.font.height + 2; 1.1637 + wa.override_redirect = 1; 1.1638 + wa.background_pixmap = ParentRelative; 1.1639 + wa.event_mask = ButtonPressMask | ExposureMask; 1.1640 + barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0, 1.1641 + DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), 1.1642 + CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); 1.1643 + XDefineCursor(dpy, barwin, cursor[CurNormal]); 1.1644 + XMapRaised(dpy, barwin); 1.1645 + strcpy(stext, "dwm-"VERSION); 1.1646 + /* windowarea */ 1.1647 + wax = sx; 1.1648 + way = sy + bh; 1.1649 + wah = sh - bh; 1.1650 + waw = sw; 1.1651 + /* pixmap for everything */ 1.1652 + dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); 1.1653 + dc.gc = XCreateGC(dpy, root, 0, 0); 1.1654 + XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); 1.1655 + /* multihead support */ 1.1656 + selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); 1.1657 +} 1.1658 + 1.1659 +/* 1.1660 + * Startup Error handler to check if another window manager 1.1661 + * is already running. 1.1662 + */ 1.1663 +static int 1.1664 +xerrorstart(Display *dsply, XErrorEvent *ee) { 1.1665 + otherwm = True; 1.1666 + return -1; 1.1667 +} 1.1668 + 1.1669 + 1.1670 + 1.1671 +void 1.1672 +sendevent(Window w, Atom a, long value) { 1.1673 + XEvent e; 1.1674 + 1.1675 + e.type = ClientMessage; 1.1676 + e.xclient.window = w; 1.1677 + e.xclient.message_type = a; 1.1678 + e.xclient.format = 32; 1.1679 + e.xclient.data.l[0] = value; 1.1680 + e.xclient.data.l[1] = CurrentTime; 1.1681 + XSendEvent(dpy, w, False, NoEventMask, &e); 1.1682 + XSync(dpy, False); 1.1683 +} 1.1684 + 1.1685 +void 1.1686 +quit() { 1.1687 + readin = running = False; 1.1688 +} 1.1689 + 1.1690 +/* There's no way to check accesses to destroyed windows, thus those cases are 1.1691 + * ignored (especially on UnmapNotify's). Other types of errors call Xlibs 1.1692 + * default error handler, which may call exit. 1.1693 + */ 1.1694 +int 1.1695 +xerror(Display *dpy, XErrorEvent *ee) { 1.1696 + if(ee->error_code == BadWindow 1.1697 + || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) 1.1698 + || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) 1.1699 + || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) 1.1700 + || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) 1.1701 + || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) 1.1702 + || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) 1.1703 + || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) 1.1704 + return 0; 1.1705 + fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", 1.1706 + ee->request_code, ee->error_code); 1.1707 + return xerrorxlib(dpy, ee); /* may call exit */ 1.1708 +} 1.1709 + 1.1710 +int 1.1711 +main(int argc, char *argv[]) { 1.1712 + char *p; 1.1713 + int r, xfd; 1.1714 + fd_set rd; 1.1715 + 1.1716 + if(argc == 2 && !strncmp("-v", argv[1], 3)) { 1.1717 + fputs("dwm-"VERSION", (C)opyright MMVI-MMVII Anselm R. Garbe\n", stdout); 1.1718 + exit(EXIT_SUCCESS); 1.1719 + } else if(argc != 1) { 1.1720 + eprint("usage: dwm [-v]\n"); 1.1721 + } 1.1722 + setlocale(LC_CTYPE, ""); 1.1723 + dpy = XOpenDisplay(0); 1.1724 + if(!dpy) { 1.1725 + eprint("dwm: cannot open display\n"); 1.1726 + } 1.1727 + xfd = ConnectionNumber(dpy); 1.1728 + screen = DefaultScreen(dpy); 1.1729 + root = RootWindow(dpy, screen); 1.1730 + otherwm = False; 1.1731 + XSetErrorHandler(xerrorstart); 1.1732 + /* this causes an error if some other window manager is running */ 1.1733 + XSelectInput(dpy, root, SubstructureRedirectMask); 1.1734 + XSync(dpy, False); 1.1735 + if(otherwm) { 1.1736 + eprint("dwm: another window manager is already running\n"); 1.1737 + } 1.1738 + 1.1739 + XSync(dpy, False); 1.1740 + XSetErrorHandler(NULL); 1.1741 + xerrorxlib = XSetErrorHandler(xerror); 1.1742 + XSync(dpy, False); 1.1743 + setup(); 1.1744 + drawstatus(); 1.1745 + scan(); 1.1746 + 1.1747 + /* main event loop, also reads status text from stdin */ 1.1748 + XSync(dpy, False); 1.1749 + procevent(); 1.1750 + readin = True; 1.1751 + while(running) { 1.1752 + FD_ZERO(&rd); 1.1753 + if(readin) 1.1754 + FD_SET(STDIN_FILENO, &rd); 1.1755 + FD_SET(xfd, &rd); 1.1756 + if(select(xfd + 1, &rd, NULL, NULL, NULL) == -1) { 1.1757 + if(errno == EINTR) 1.1758 + continue; 1.1759 + eprint("select failed\n"); 1.1760 + } 1.1761 + if(FD_ISSET(STDIN_FILENO, &rd)) { 1.1762 + switch(r = read(STDIN_FILENO, stext, sizeof stext - 1)) { 1.1763 + case -1: 1.1764 + strncpy(stext, strerror(errno), sizeof stext - 1); 1.1765 + stext[sizeof stext - 1] = '\0'; 1.1766 + readin = False; 1.1767 + break; 1.1768 + case 0: 1.1769 + strncpy(stext, "EOF", 4); 1.1770 + readin = False; 1.1771 + break; 1.1772 + default: 1.1773 + for(stext[r] = '\0', p = stext + strlen(stext) - 1; p >= stext && *p == '\n'; *p-- = '\0'); 1.1774 + for(; p >= stext && *p != '\n'; --p); 1.1775 + if(p > stext) 1.1776 + strncpy(stext, p + 1, sizeof stext); 1.1777 + } 1.1778 + drawstatus(); 1.1779 + } 1.1780 + if(FD_ISSET(xfd, &rd)) 1.1781 + procevent(); 1.1782 + } 1.1783 + cleanup(); 1.1784 + XCloseDisplay(dpy); 1.1785 + return 0; 1.1786 +}