dwm

suckless dynamic window manager
git clone https://git.beauhilton.com/dwm.git
Log | Files | Refs | README | LICENSE

dwm.c (66134B)


      1 /* See LICENSE file for copyright and license details.
      2  *
      3  * dynamic window manager is designed like any other X client as well. It is
      4  * driven through handling X events. In contrast to other X clients, a window
      5  * manager selects for SubstructureRedirectMask on the root window, to receive
      6  * events about window (dis-)appearance. Only one X connection at a time is
      7  * allowed to select for this event mask.
      8  *
      9  * The event handlers of dwm are organized in an array which is accessed
     10  * whenever a new event has been fetched. This allows event dispatching
     11  * in O(1) time.
     12  *
     13  * Each child of the root window is called a client, except windows which have
     14  * set the override_redirect flag. Clients are organized in a linked client
     15  * list on each monitor, the focus history is remembered through a stack list
     16  * on each monitor. Each client contains a bit array to indicate the tags of a
     17  * client.
     18  *
     19  * Keys and tagging rules are organized as arrays and defined in config.h.
     20  *
     21  * To understand everything else, start reading main().
     22  */
     23 #include <X11/Xatom.h>
     24 #include <X11/Xlib.h>
     25 #include <X11/Xproto.h>
     26 #include <X11/Xresource.h>
     27 #include <X11/Xutil.h>
     28 #include <X11/cursorfont.h>
     29 #include <X11/keysym.h>
     30 #include <errno.h>
     31 #include <locale.h>
     32 #include <signal.h>
     33 #include <stdarg.h>
     34 #include <stdio.h>
     35 #include <stdlib.h>
     36 #include <string.h>
     37 #include <sys/types.h>
     38 #include <sys/wait.h>
     39 #include <unistd.h>
     40 #ifdef XINERAMA
     41 #include <X11/extensions/Xinerama.h>
     42 #endif /* XINERAMA */
     43 #include <X11/Xft/Xft.h>
     44 #include <X11/Xlib-xcb.h>
     45 #include <xcb/res.h>
     46 
     47 #include "drw.h"
     48 #include "util.h"
     49 
     50 /* macros */
     51 #define BUTTONMASK (ButtonPressMask | ButtonReleaseMask)
     52 #define CLEANMASK(mask)                                                        \
     53   (mask & ~(numlockmask | LockMask) &                                          \
     54    (ShiftMask | ControlMask | Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask |      \
     55     Mod5Mask))
     56 #define GETINC(X) ((X)-2000)
     57 #define INC(X) ((X) + 2000)
     58 #define INTERSECT(x, y, w, h, m)                                               \
     59   (MAX(0, MIN((x) + (w), (m)->wx + (m)->ww) - MAX((x), (m)->wx)) *             \
     60    MAX(0, MIN((y) + (h), (m)->wy + (m)->wh) - MAX((y), (m)->wy)))
     61 #define ISINC(X) ((X) > 1000 && (X) < 3000)
     62 #define ISVISIBLE(C)                                                           \
     63   ((C->tags & C->mon->tagset[C->mon->seltags]) || C->issticky)
     64 #define PREVSEL 3000
     65 #define LENGTH(X) (sizeof X / sizeof X[0])
     66 #define MOUSEMASK (BUTTONMASK | PointerMotionMask)
     67 #define MOD(N, M) ((N) % (M) < 0 ? (N) % (M) + (M) : (N) % (M))
     68 #define WIDTH(X) ((X)->w + 2 * (X)->bw)
     69 #define HEIGHT(X) ((X)->h + 2 * (X)->bw)
     70 #define TAGMASK ((1 << LENGTH(tags)) - 1)
     71 #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
     72 #define TRUNC(X, A, B) (MAX((A), MIN((X), (B))))
     73 #define XRDB_LOAD_COLOR(R, V)                                                  \
     74   if (XrmGetResource(xrdb, R, NULL, &type, &value) == True) {                  \
     75     if (value.addr != NULL && strnlen(value.addr, 8) == 7 &&                   \
     76         value.addr[0] == '#') {                                                \
     77       int i = 1;                                                               \
     78       for (; i <= 6; i++) {                                                    \
     79         if (value.addr[i] < 48)                                                \
     80           break;                                                               \
     81         if (value.addr[i] > 57 && value.addr[i] < 65)                          \
     82           break;                                                               \
     83         if (value.addr[i] > 70 && value.addr[i] < 97)                          \
     84           break;                                                               \
     85         if (value.addr[i] > 102)                                               \
     86           break;                                                               \
     87       }                                                                        \
     88       if (i == 7) {                                                            \
     89         strncpy(V, value.addr, 7);                                             \
     90         V[7] = '\0';                                                           \
     91       }                                                                        \
     92     }                                                                          \
     93   }
     94 
     95 /* enums */
     96 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
     97 enum { SchemeNorm, SchemeSel };                  /* color schemes */
     98 enum {
     99   NetSupported,
    100   NetWMName,
    101   NetWMState,
    102   NetWMCheck,
    103   NetWMFullscreen,
    104   NetActiveWindow,
    105   NetWMWindowType,
    106   NetWMWindowTypeDialog,
    107   NetClientList,
    108   NetLast
    109 }; /* EWMH atoms */
    110 enum {
    111   WMProtocols,
    112   WMDelete,
    113   WMState,
    114   WMTakeFocus,
    115   WMLast
    116 }; /* default atoms */
    117 enum {
    118   ClkTagBar,
    119   ClkLtSymbol,
    120   ClkStatusText,
    121   ClkWinTitle,
    122   ClkClientWin,
    123   ClkRootWin,
    124   ClkLast
    125 }; /* clicks */
    126 
    127 typedef union {
    128   int i;
    129   unsigned int ui;
    130   float f;
    131   const void *v;
    132 } Arg;
    133 
    134 typedef struct {
    135   unsigned int click;
    136   unsigned int mask;
    137   unsigned int button;
    138   void (*func)(const Arg *arg);
    139   const Arg arg;
    140 } Button;
    141 
    142 typedef struct Monitor Monitor;
    143 typedef struct Client Client;
    144 struct Client {
    145   char name[256];
    146   float mina, maxa;
    147   int x, y, w, h;
    148   int oldx, oldy, oldw, oldh;
    149   int basew, baseh, incw, inch, maxw, maxh, minw, minh;
    150   int bw, oldbw;
    151   unsigned int tags;
    152   int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen,
    153       isterminal, noswallow, issticky;
    154   pid_t pid;
    155   Client *next;
    156   Client *snext;
    157   Client *swallowing;
    158   Monitor *mon;
    159   Window win;
    160 };
    161 
    162 typedef struct {
    163   unsigned int mod;
    164   KeySym keysym;
    165   void (*func)(const Arg *);
    166   const Arg arg;
    167 } Key;
    168 
    169 typedef struct {
    170   const char *symbol;
    171   void (*arrange)(Monitor *);
    172 } Layout;
    173 
    174 struct Monitor {
    175   char ltsymbol[16];
    176   float mfact;
    177   int nmaster;
    178   int num;
    179   int by;             /* bar geometry */
    180   int mx, my, mw, mh; /* screen size */
    181   int wx, wy, ww, wh; /* window area  */
    182   int gappih;         /* horizontal gap between windows */
    183   int gappiv;         /* vertical gap between windows */
    184   int gappoh;         /* horizontal outer gaps */
    185   int gappov;         /* vertical outer gaps */
    186   unsigned int seltags;
    187   unsigned int sellt;
    188   unsigned int tagset[2];
    189   int showbar;
    190   int topbar;
    191   Client *clients;
    192   Client *sel;
    193   Client *stack;
    194   Monitor *next;
    195   Window barwin;
    196   const Layout *lt[2];
    197 };
    198 
    199 typedef struct {
    200   const char *class;
    201   const char *instance;
    202   const char *title;
    203   unsigned int tags;
    204   int isfloating;
    205   int isterminal;
    206   int noswallow;
    207   int monitor;
    208 } Rule;
    209 
    210 /* function declarations */
    211 static void applyrules(Client *c);
    212 static int applysizehints(Client *c, int *x, int *y, int *w, int *h,
    213                           int interact);
    214 static void arrange(Monitor *m);
    215 static void arrangemon(Monitor *m);
    216 static void attach(Client *c);
    217 static void attachstack(Client *c);
    218 static void buttonpress(XEvent *e);
    219 static void checkotherwm(void);
    220 static void cleanup(void);
    221 static void cleanupmon(Monitor *mon);
    222 static void clientmessage(XEvent *e);
    223 static void configure(Client *c);
    224 static void configurenotify(XEvent *e);
    225 static void configurerequest(XEvent *e);
    226 static void copyvalidchars(char *text, char *rawtext);
    227 static Monitor *createmon(void);
    228 static void destroynotify(XEvent *e);
    229 static void detach(Client *c);
    230 static void detachstack(Client *c);
    231 static Monitor *dirtomon(int dir);
    232 static void drawbar(Monitor *m);
    233 static void drawbars(void);
    234 static void enternotify(XEvent *e);
    235 static void expose(XEvent *e);
    236 static void focus(Client *c);
    237 static void focusin(XEvent *e);
    238 static void focusmon(const Arg *arg);
    239 static void focusstack(const Arg *arg);
    240 static int getrootptr(int *x, int *y);
    241 static long getstate(Window w);
    242 static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
    243 static void grabbuttons(Client *c, int focused);
    244 static void grabkeys(void);
    245 static void incnmaster(const Arg *arg);
    246 static void keypress(XEvent *e);
    247 static void killclient(const Arg *arg);
    248 static void loadxrdb(void);
    249 static void manage(Window w, XWindowAttributes *wa);
    250 static void mappingnotify(XEvent *e);
    251 static void maprequest(XEvent *e);
    252 static void monocle(Monitor *m);
    253 static void motionnotify(XEvent *e);
    254 static void movemouse(const Arg *arg);
    255 static Client *nexttiled(Client *c);
    256 static void pop(Client *);
    257 static void propertynotify(XEvent *e);
    258 static void pushstack(const Arg *arg);
    259 static void quit(const Arg *arg);
    260 static Monitor *recttomon(int x, int y, int w, int h);
    261 static void resize(Client *c, int x, int y, int w, int h, int interact);
    262 static void resizeclient(Client *c, int x, int y, int w, int h);
    263 static void resizemouse(const Arg *arg);
    264 static void restack(Monitor *m);
    265 static void run(void);
    266 static void runAutostart(void);
    267 static void scan(void);
    268 static int sendevent(Client *c, Atom proto);
    269 static void sendmon(Client *c, Monitor *m);
    270 static void setclientstate(Client *c, long state);
    271 static void setfocus(Client *c);
    272 static void setfullscreen(Client *c, int fullscreen);
    273 static void setlayout(const Arg *arg);
    274 static void setmfact(const Arg *arg);
    275 static void setup(void);
    276 static void seturgent(Client *c, int urg);
    277 static void showhide(Client *c);
    278 static void sigchld(int unused);
    279 static void sighup(int unused);
    280 static void sigterm(int unused);
    281 static void spawn(const Arg *arg);
    282 static int stackpos(const Arg *arg);
    283 static void tag(const Arg *arg);
    284 static void tagmon(const Arg *arg);
    285 static void togglebar(const Arg *arg);
    286 static void togglefloating(const Arg *arg);
    287 static void togglesticky(const Arg *arg);
    288 static void togglefullscr(const Arg *arg);
    289 static void togglescratch(const Arg *arg);
    290 static void toggletag(const Arg *arg);
    291 static void toggleview(const Arg *arg);
    292 static void unfocus(Client *c, int setfocus);
    293 static void unmanage(Client *c, int destroyed);
    294 static void unmapnotify(XEvent *e);
    295 static void updatebarpos(Monitor *m);
    296 static void updatebars(void);
    297 static void updateclientlist(void);
    298 static int updategeom(void);
    299 static void updatenumlockmask(void);
    300 static void updatesizehints(Client *c);
    301 static void updatestatus(void);
    302 static void updatetitle(Client *c);
    303 static void updatewindowtype(Client *c);
    304 static void updatewmhints(Client *c);
    305 static void view(const Arg *arg);
    306 static Client *wintoclient(Window w);
    307 static Monitor *wintomon(Window w);
    308 static int xerror(Display *dpy, XErrorEvent *ee);
    309 static int xerrordummy(Display *dpy, XErrorEvent *ee);
    310 static int xerrorstart(Display *dpy, XErrorEvent *ee);
    311 static void xrdb(const Arg *arg);
    312 static void zoom(const Arg *arg);
    313 
    314 static pid_t getparentprocess(pid_t p);
    315 static int isdescprocess(pid_t p, pid_t c);
    316 static Client *swallowingclient(Window w);
    317 static Client *termforwin(const Client *c);
    318 static pid_t winpid(Window w);
    319 
    320 /* variables */
    321 static const char broken[] = "broken";
    322 static char stext[256];
    323 static char rawstext[256];
    324 static int screen;
    325 static int sw, sh;      /* X display screen geometry width, height */
    326 static int bh, blw = 0; /* bar geometry */
    327 static int lrpad;       /* sum of left and right padding for text */
    328 static int (*xerrorxlib)(Display *, XErrorEvent *);
    329 static unsigned int numlockmask = 0;
    330 static void (*handler[LASTEvent])(XEvent *) = {
    331     [ButtonPress] = buttonpress,
    332     [ClientMessage] = clientmessage,
    333     [ConfigureRequest] = configurerequest,
    334     [ConfigureNotify] = configurenotify,
    335     [DestroyNotify] = destroynotify,
    336     [EnterNotify] = enternotify,
    337     [Expose] = expose,
    338     [FocusIn] = focusin,
    339     [KeyPress] = keypress,
    340     [MappingNotify] = mappingnotify,
    341     [MapRequest] = maprequest,
    342     [MotionNotify] = motionnotify,
    343     [PropertyNotify] = propertynotify,
    344     [UnmapNotify] = unmapnotify};
    345 static Atom wmatom[WMLast], netatom[NetLast];
    346 static int restart = 0;
    347 static int running = 1;
    348 static Cur *cursor[CurLast];
    349 static Clr **scheme;
    350 static Display *dpy;
    351 static Drw *drw;
    352 static Monitor *mons, *selmon;
    353 static Window root, wmcheckwin;
    354 
    355 static xcb_connection_t *xcon;
    356 
    357 /* configuration, allows nested code to access above variables */
    358 #include "config.h"
    359 
    360 static unsigned int scratchtag = 1 << LENGTH(tags);
    361 
    362 /* compile-time check if all tags fit into an unsigned int bit array. */
    363 struct NumTags {
    364   char limitexceeded[LENGTH(tags) > 31 ? -1 : 1];
    365 };
    366 
    367 /* function implementations */
    368 void applyrules(Client *c) {
    369   const char *class, *instance;
    370   unsigned int i;
    371   const Rule *r;
    372   Monitor *m;
    373   XClassHint ch = {NULL, NULL};
    374 
    375   /* rule matching */
    376   c->isfloating = 0;
    377   c->tags = 0;
    378   XGetClassHint(dpy, c->win, &ch);
    379   class = ch.res_class ? ch.res_class : broken;
    380   instance = ch.res_name ? ch.res_name : broken;
    381 
    382   for (i = 0; i < LENGTH(rules); i++) {
    383     r = &rules[i];
    384     if ((!r->title || strstr(c->name, r->title)) &&
    385         (!r->class || strstr(class, r->class)) &&
    386         (!r->instance || strstr(instance, r->instance))) {
    387       c->isterminal = r->isterminal;
    388       c->isfloating = r->isfloating;
    389       c->noswallow = r->noswallow;
    390       c->tags |= r->tags;
    391       for (m = mons; m && m->num != r->monitor; m = m->next)
    392         ;
    393       if (m)
    394         c->mon = m;
    395     }
    396   }
    397   if (ch.res_class)
    398     XFree(ch.res_class);
    399   if (ch.res_name)
    400     XFree(ch.res_name);
    401   c->tags =
    402       c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags];
    403 }
    404 
    405 int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) {
    406   int baseismin;
    407   Monitor *m = c->mon;
    408 
    409   /* set minimum possible */
    410   *w = MAX(1, *w);
    411   *h = MAX(1, *h);
    412   if (interact) {
    413     if (*x > sw)
    414       *x = sw - WIDTH(c);
    415     if (*y > sh)
    416       *y = sh - HEIGHT(c);
    417     if (*x + *w + 2 * c->bw < 0)
    418       *x = 0;
    419     if (*y + *h + 2 * c->bw < 0)
    420       *y = 0;
    421   } else {
    422     if (*x >= m->wx + m->ww)
    423       *x = m->wx + m->ww - WIDTH(c);
    424     if (*y >= m->wy + m->wh)
    425       *y = m->wy + m->wh - HEIGHT(c);
    426     if (*x + *w + 2 * c->bw <= m->wx)
    427       *x = m->wx;
    428     if (*y + *h + 2 * c->bw <= m->wy)
    429       *y = m->wy;
    430   }
    431   if (*h < bh)
    432     *h = bh;
    433   if (*w < bh)
    434     *w = bh;
    435   if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
    436     /* see last two sentences in ICCCM 4.1.2.3 */
    437     baseismin = c->basew == c->minw && c->baseh == c->minh;
    438     if (!baseismin) { /* temporarily remove base dimensions */
    439       *w -= c->basew;
    440       *h -= c->baseh;
    441     }
    442     /* adjust for aspect limits */
    443     if (c->mina > 0 && c->maxa > 0) {
    444       if (c->maxa < (float)*w / *h)
    445         *w = *h * c->maxa + 0.5;
    446       else if (c->mina < (float)*h / *w)
    447         *h = *w * c->mina + 0.5;
    448     }
    449     if (baseismin) { /* increment calculation requires this */
    450       *w -= c->basew;
    451       *h -= c->baseh;
    452     }
    453     /* adjust for increment value */
    454     if (c->incw)
    455       *w -= *w % c->incw;
    456     if (c->inch)
    457       *h -= *h % c->inch;
    458     /* restore base dimensions */
    459     *w = MAX(*w + c->basew, c->minw);
    460     *h = MAX(*h + c->baseh, c->minh);
    461     if (c->maxw)
    462       *w = MIN(*w, c->maxw);
    463     if (c->maxh)
    464       *h = MIN(*h, c->maxh);
    465   }
    466   return *x != c->x || *y != c->y || *w != c->w || *h != c->h;
    467 }
    468 
    469 void arrange(Monitor *m) {
    470   if (m)
    471     showhide(m->stack);
    472   else
    473     for (m = mons; m; m = m->next)
    474       showhide(m->stack);
    475   if (m) {
    476     arrangemon(m);
    477     restack(m);
    478   } else
    479     for (m = mons; m; m = m->next)
    480       arrangemon(m);
    481 }
    482 
    483 void arrangemon(Monitor *m) {
    484   strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);
    485   if (m->lt[m->sellt]->arrange)
    486     m->lt[m->sellt]->arrange(m);
    487 }
    488 
    489 void attach(Client *c) {
    490   c->next = c->mon->clients;
    491   c->mon->clients = c;
    492 }
    493 
    494 void attachstack(Client *c) {
    495   c->snext = c->mon->stack;
    496   c->mon->stack = c;
    497 }
    498 
    499 void swallow(Client *p, Client *c) {
    500   if (c->noswallow || c->isterminal)
    501     return;
    502 
    503   detach(c);
    504   detachstack(c);
    505 
    506   setclientstate(c, WithdrawnState);
    507   XUnmapWindow(dpy, p->win);
    508 
    509   p->swallowing = c;
    510   c->mon = p->mon;
    511 
    512   Window w = p->win;
    513   p->win = c->win;
    514   c->win = w;
    515   updatetitle(p);
    516   arrange(p->mon);
    517   XMoveResizeWindow(dpy, p->win, p->x, p->y, p->w, p->h);
    518   configure(p);
    519   updateclientlist();
    520 }
    521 
    522 void unswallow(Client *c) {
    523   c->win = c->swallowing->win;
    524 
    525   free(c->swallowing);
    526   c->swallowing = NULL;
    527 
    528   updatetitle(c);
    529   arrange(c->mon);
    530   XMapWindow(dpy, c->win);
    531   XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
    532   configure(c);
    533   setclientstate(c, NormalState);
    534 }
    535 
    536 void buttonpress(XEvent *e) {
    537   unsigned int i, x, click, occ = 0;
    538   Arg arg = {0};
    539   Client *c;
    540   Monitor *m;
    541   XButtonPressedEvent *ev = &e->xbutton;
    542 
    543   click = ClkRootWin;
    544   /* focus monitor if necessary */
    545   if ((m = wintomon(ev->window)) && m != selmon) {
    546     unfocus(selmon->sel, 1);
    547     selmon = m;
    548     focus(NULL);
    549   }
    550   if (ev->window == selmon->barwin) {
    551     i = x = 0;
    552     for (c = m->clients; c; c = c->next)
    553       occ |= c->tags == 255 ? 0 : c->tags;
    554     do {
    555       /* do not reserve space for vacant tags */
    556       if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i))
    557         continue;
    558       x += TEXTW(tags[i]);
    559     } while (ev->x >= x && ++i < LENGTH(tags));
    560     if (i < LENGTH(tags)) {
    561       click = ClkTagBar;
    562       arg.ui = 1 << i;
    563     } else if (ev->x < x + blw)
    564       click = ClkLtSymbol;
    565     else if (ev->x > (x = selmon->ww - TEXTW(stext) + lrpad)) {
    566       click = ClkStatusText;
    567 
    568       char *text = rawstext;
    569       int i = -1;
    570       char ch;
    571       while (text[++i]) {
    572         if ((unsigned char)text[i] < ' ') {
    573           ch = text[i];
    574           text[i] = '\0';
    575           x += TEXTW(text) - lrpad;
    576           text[i] = ch;
    577           text += i + 1;
    578           i = -1;
    579           if (x >= ev->x)
    580             break;
    581         }
    582       }
    583     } else
    584       click = ClkWinTitle;
    585   } else if ((c = wintoclient(ev->window))) {
    586     focus(c);
    587     restack(selmon);
    588     XAllowEvents(dpy, ReplayPointer, CurrentTime);
    589     click = ClkClientWin;
    590   }
    591   for (i = 0; i < LENGTH(buttons); i++)
    592     if (click == buttons[i].click && buttons[i].func &&
    593         buttons[i].button == ev->button &&
    594         CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))
    595       buttons[i].func(
    596           click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);
    597 }
    598 
    599 void checkotherwm(void) {
    600   xerrorxlib = XSetErrorHandler(xerrorstart);
    601   /* this causes an error if some other window manager is running */
    602   XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask);
    603   XSync(dpy, False);
    604   XSetErrorHandler(xerror);
    605   XSync(dpy, False);
    606 }
    607 
    608 void cleanup(void) {
    609   Arg a = {.ui = ~0};
    610   Layout foo = {"", NULL};
    611   Monitor *m;
    612   size_t i;
    613 
    614   view(&a);
    615   selmon->lt[selmon->sellt] = &foo;
    616   for (m = mons; m; m = m->next)
    617     while (m->stack)
    618       unmanage(m->stack, 0);
    619   XUngrabKey(dpy, AnyKey, AnyModifier, root);
    620   while (mons)
    621     cleanupmon(mons);
    622   for (i = 0; i < CurLast; i++)
    623     drw_cur_free(drw, cursor[i]);
    624   for (i = 0; i < LENGTH(colors); i++)
    625     free(scheme[i]);
    626   XDestroyWindow(dpy, wmcheckwin);
    627   drw_free(drw);
    628   XSync(dpy, False);
    629   XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
    630   XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
    631 }
    632 
    633 void cleanupmon(Monitor *mon) {
    634   Monitor *m;
    635 
    636   if (mon == mons)
    637     mons = mons->next;
    638   else {
    639     for (m = mons; m && m->next != mon; m = m->next)
    640       ;
    641     m->next = mon->next;
    642   }
    643   XUnmapWindow(dpy, mon->barwin);
    644   XDestroyWindow(dpy, mon->barwin);
    645   free(mon);
    646 }
    647 
    648 void clientmessage(XEvent *e) {
    649   XClientMessageEvent *cme = &e->xclient;
    650   Client *c = wintoclient(cme->window);
    651 
    652   if (!c)
    653     return;
    654   if (cme->message_type == netatom[NetWMState]) {
    655     if (cme->data.l[1] == netatom[NetWMFullscreen] ||
    656         cme->data.l[2] == netatom[NetWMFullscreen])
    657       setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD    */
    658                         || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ &&
    659                             !c->isfullscreen)));
    660   } else if (cme->message_type == netatom[NetActiveWindow]) {
    661     if (c != selmon->sel && !c->isurgent)
    662       seturgent(c, 1);
    663   }
    664 }
    665 
    666 void configure(Client *c) {
    667   XConfigureEvent ce;
    668 
    669   ce.type = ConfigureNotify;
    670   ce.display = dpy;
    671   ce.event = c->win;
    672   ce.window = c->win;
    673   ce.x = c->x;
    674   ce.y = c->y;
    675   ce.width = c->w;
    676   ce.height = c->h;
    677   ce.border_width = c->bw;
    678   ce.above = None;
    679   ce.override_redirect = False;
    680   XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
    681 }
    682 
    683 void configurenotify(XEvent *e) {
    684   Monitor *m;
    685   Client *c;
    686   XConfigureEvent *ev = &e->xconfigure;
    687   int dirty;
    688 
    689   /* TODO: updategeom handling sucks, needs to be simplified */
    690   if (ev->window == root) {
    691     dirty = (sw != ev->width || sh != ev->height);
    692     sw = ev->width;
    693     sh = ev->height;
    694     if (updategeom() || dirty) {
    695       drw_resize(drw, sw, bh);
    696       updatebars();
    697       for (m = mons; m; m = m->next) {
    698         for (c = m->clients; c; c = c->next)
    699           if (c->isfullscreen)
    700             resizeclient(c, m->mx, m->my, m->mw, m->mh);
    701         XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
    702       }
    703       focus(NULL);
    704       arrange(NULL);
    705     }
    706   }
    707 }
    708 
    709 void configurerequest(XEvent *e) {
    710   Client *c;
    711   Monitor *m;
    712   XConfigureRequestEvent *ev = &e->xconfigurerequest;
    713   XWindowChanges wc;
    714 
    715   if ((c = wintoclient(ev->window))) {
    716     if (ev->value_mask & CWBorderWidth)
    717       c->bw = ev->border_width;
    718     else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) {
    719       m = c->mon;
    720       if (ev->value_mask & CWX) {
    721         c->oldx = c->x;
    722         c->x = m->mx + ev->x;
    723       }
    724       if (ev->value_mask & CWY) {
    725         c->oldy = c->y;
    726         c->y = m->my + ev->y;
    727       }
    728       if (ev->value_mask & CWWidth) {
    729         c->oldw = c->w;
    730         c->w = ev->width;
    731       }
    732       if (ev->value_mask & CWHeight) {
    733         c->oldh = c->h;
    734         c->h = ev->height;
    735       }
    736       if ((c->x + c->w) > m->mx + m->mw && c->isfloating)
    737         c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */
    738       if ((c->y + c->h) > m->my + m->mh && c->isfloating)
    739         c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */
    740       if ((ev->value_mask & (CWX | CWY)) &&
    741           !(ev->value_mask & (CWWidth | CWHeight)))
    742         configure(c);
    743       if (ISVISIBLE(c))
    744         XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
    745     } else
    746       configure(c);
    747   } else {
    748     wc.x = ev->x;
    749     wc.y = ev->y;
    750     wc.width = ev->width;
    751     wc.height = ev->height;
    752     wc.border_width = ev->border_width;
    753     wc.sibling = ev->above;
    754     wc.stack_mode = ev->detail;
    755     XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);
    756   }
    757   XSync(dpy, False);
    758 }
    759 
    760 void copyvalidchars(char *text, char *rawtext) {
    761   int i = -1, j = 0;
    762 
    763   while (rawtext[++i]) {
    764     if ((unsigned char)rawtext[i] >= ' ') {
    765       text[j++] = rawtext[i];
    766     }
    767   }
    768   text[j] = '\0';
    769 }
    770 
    771 Monitor *createmon(void) {
    772   Monitor *m;
    773 
    774   m = ecalloc(1, sizeof(Monitor));
    775   m->tagset[0] = m->tagset[1] = 1;
    776   m->mfact = mfact;
    777   m->nmaster = nmaster;
    778   m->showbar = showbar;
    779   m->topbar = topbar;
    780   m->gappih = gappih;
    781   m->gappiv = gappiv;
    782   m->gappoh = gappoh;
    783   m->gappov = gappov;
    784   m->lt[0] = &layouts[0];
    785   m->lt[1] = &layouts[1 % LENGTH(layouts)];
    786   strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
    787   return m;
    788 }
    789 
    790 void destroynotify(XEvent *e) {
    791   Client *c;
    792   XDestroyWindowEvent *ev = &e->xdestroywindow;
    793 
    794   if ((c = wintoclient(ev->window)))
    795     unmanage(c, 1);
    796 
    797   else if ((c = swallowingclient(ev->window)))
    798     unmanage(c->swallowing, 1);
    799 }
    800 
    801 void detach(Client *c) {
    802   Client **tc;
    803 
    804   for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next)
    805     ;
    806   *tc = c->next;
    807 }
    808 
    809 void detachstack(Client *c) {
    810   Client **tc, *t;
    811 
    812   for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext)
    813     ;
    814   *tc = c->snext;
    815 
    816   if (c == c->mon->sel) {
    817     for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext)
    818       ;
    819     c->mon->sel = t;
    820   }
    821 }
    822 
    823 Monitor *dirtomon(int dir) {
    824   Monitor *m = NULL;
    825 
    826   if (dir > 0) {
    827     if (!(m = selmon->next))
    828       m = mons;
    829   } else if (selmon == mons)
    830     for (m = mons; m->next; m = m->next)
    831       ;
    832   else
    833     for (m = mons; m->next != selmon; m = m->next)
    834       ;
    835   return m;
    836 }
    837 
    838 void drawbar(Monitor *m) {
    839   int x, w, sw = 0;
    840   int boxs = drw->fonts->h / 9;
    841   int boxw = drw->fonts->h / 6 + 2;
    842   unsigned int i, occ = 0, urg = 0;
    843   Client *c;
    844 
    845   /* draw status first so it can be overdrawn by tags later */
    846   if (m == selmon) { /* status is only drawn on selected monitor */
    847     drw_setscheme(drw, scheme[SchemeNorm]);
    848     sw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
    849     drw_text(drw, m->ww - sw, 0, sw, bh, 0, stext, 0);
    850   }
    851 
    852   for (c = m->clients; c; c = c->next) {
    853     occ |= c->tags == 255 ? 0 : c->tags;
    854     if (c->isurgent)
    855       urg |= c->tags;
    856   }
    857   x = 0;
    858   for (i = 0; i < LENGTH(tags); i++) {
    859     /* do not draw vacant tags */
    860     if (!(occ & 1 << i || m->tagset[m->seltags] & 1 << i))
    861       continue;
    862 
    863     w = TEXTW(tags[i]);
    864     drw_setscheme(
    865         drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]);
    866     drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i);
    867     x += w;
    868   }
    869   w = blw = TEXTW(m->ltsymbol);
    870   drw_setscheme(drw, scheme[SchemeNorm]);
    871   x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
    872 
    873   if ((w = m->ww - sw - x) > bh) {
    874     if (m->sel) {
    875       drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
    876       drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
    877       if (m->sel->isfloating)
    878         drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0);
    879     } else {
    880       drw_setscheme(drw, scheme[SchemeNorm]);
    881       drw_rect(drw, x, 0, w, bh, 1, 1);
    882     }
    883   }
    884   drw_map(drw, m->barwin, 0, 0, m->ww, bh);
    885 }
    886 
    887 void drawbars(void) {
    888   Monitor *m;
    889 
    890   for (m = mons; m; m = m->next)
    891     drawbar(m);
    892 }
    893 
    894 void enternotify(XEvent *e) {
    895   Client *c;
    896   Monitor *m;
    897   XCrossingEvent *ev = &e->xcrossing;
    898 
    899   if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) &&
    900       ev->window != root)
    901     return;
    902   c = wintoclient(ev->window);
    903   m = c ? c->mon : wintomon(ev->window);
    904   if (m != selmon) {
    905     unfocus(selmon->sel, 1);
    906     selmon = m;
    907   } else if (!c || c == selmon->sel)
    908     return;
    909   focus(c);
    910 }
    911 
    912 void expose(XEvent *e) {
    913   Monitor *m;
    914   XExposeEvent *ev = &e->xexpose;
    915 
    916   if (ev->count == 0 && (m = wintomon(ev->window)))
    917     drawbar(m);
    918 }
    919 
    920 void focus(Client *c) {
    921   if (!c || !ISVISIBLE(c))
    922     for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext)
    923       ;
    924   if (selmon->sel && selmon->sel != c)
    925     unfocus(selmon->sel, 0);
    926   if (c) {
    927     if (c->mon != selmon)
    928       selmon = c->mon;
    929     if (c->isurgent)
    930       seturgent(c, 0);
    931     detachstack(c);
    932     attachstack(c);
    933     grabbuttons(c, 1);
    934     XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
    935     setfocus(c);
    936   } else {
    937     XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
    938     XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
    939   }
    940   selmon->sel = c;
    941   drawbars();
    942 }
    943 
    944 /* there are some broken focus acquiring clients needing extra handling */
    945 void focusin(XEvent *e) {
    946   XFocusChangeEvent *ev = &e->xfocus;
    947 
    948   if (selmon->sel && ev->window != selmon->sel->win)
    949     setfocus(selmon->sel);
    950 }
    951 
    952 void focusmon(const Arg *arg) {
    953   Monitor *m;
    954 
    955   if (!mons->next)
    956     return;
    957   if ((m = dirtomon(arg->i)) == selmon)
    958     return;
    959   unfocus(selmon->sel, 0);
    960   selmon = m;
    961   focus(NULL);
    962 }
    963 
    964 void focusstack(const Arg *arg) {
    965   int i = stackpos(arg);
    966   Client *c, *p;
    967 
    968   if (i < 0 || selmon->sel->isfullscreen)
    969     return;
    970 
    971   for (p = NULL, c = selmon->clients; c && (i || !ISVISIBLE(c));
    972        i -= ISVISIBLE(c) ? 1 : 0, p = c, c = c->next)
    973     ;
    974   focus(c ? c : p);
    975   restack(selmon);
    976 }
    977 
    978 Atom getatomprop(Client *c, Atom prop) {
    979   int di;
    980   unsigned long dl;
    981   unsigned char *p = NULL;
    982   Atom da, atom = None;
    983 
    984   if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
    985                          &da, &di, &dl, &dl, &p) == Success &&
    986       p) {
    987     atom = *(Atom *)p;
    988     XFree(p);
    989   }
    990   return atom;
    991 }
    992 
    993 int getrootptr(int *x, int *y) {
    994   int di;
    995   unsigned int dui;
    996   Window dummy;
    997 
    998   return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui);
    999 }
   1000 
   1001 long getstate(Window w) {
   1002   int format;
   1003   long result = -1;
   1004   unsigned char *p = NULL;
   1005   unsigned long n, extra;
   1006   Atom real;
   1007 
   1008   if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False,
   1009                          wmatom[WMState], &real, &format, &n, &extra,
   1010                          (unsigned char **)&p) != Success)
   1011     return -1;
   1012   if (n != 0)
   1013     result = *p;
   1014   XFree(p);
   1015   return result;
   1016 }
   1017 
   1018 int gettextprop(Window w, Atom atom, char *text, unsigned int size) {
   1019   char **list = NULL;
   1020   int n;
   1021   XTextProperty name;
   1022 
   1023   if (!text || size == 0)
   1024     return 0;
   1025   text[0] = '\0';
   1026   if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems)
   1027     return 0;
   1028   if (name.encoding == XA_STRING)
   1029     strncpy(text, (char *)name.value, size - 1);
   1030   else {
   1031     if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 &&
   1032         *list) {
   1033       strncpy(text, *list, size - 1);
   1034       XFreeStringList(list);
   1035     }
   1036   }
   1037   text[size - 1] = '\0';
   1038   XFree(name.value);
   1039   return 1;
   1040 }
   1041 
   1042 void grabbuttons(Client *c, int focused) {
   1043   updatenumlockmask();
   1044   {
   1045     unsigned int i, j;
   1046     unsigned int modifiers[] = {0, LockMask, numlockmask,
   1047                                 numlockmask | LockMask};
   1048     XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
   1049     if (!focused)
   1050       XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK,
   1051                   GrabModeSync, GrabModeSync, None, None);
   1052     for (i = 0; i < LENGTH(buttons); i++)
   1053       if (buttons[i].click == ClkClientWin)
   1054         for (j = 0; j < LENGTH(modifiers); j++)
   1055           XGrabButton(dpy, buttons[i].button, buttons[i].mask | modifiers[j],
   1056                       c->win, False, BUTTONMASK, GrabModeAsync, GrabModeSync,
   1057                       None, None);
   1058   }
   1059 }
   1060 
   1061 void grabkeys(void) {
   1062   updatenumlockmask();
   1063   {
   1064     unsigned int i, j;
   1065     unsigned int modifiers[] = {0, LockMask, numlockmask,
   1066                                 numlockmask | LockMask};
   1067     KeyCode code;
   1068 
   1069     XUngrabKey(dpy, AnyKey, AnyModifier, root);
   1070     for (i = 0; i < LENGTH(keys); i++)
   1071       if ((code = XKeysymToKeycode(dpy, keys[i].keysym)))
   1072         for (j = 0; j < LENGTH(modifiers); j++)
   1073           XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, True,
   1074                    GrabModeAsync, GrabModeAsync);
   1075   }
   1076 }
   1077 
   1078 void incnmaster(const Arg *arg) {
   1079   selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
   1080   arrange(selmon);
   1081 }
   1082 
   1083 #ifdef XINERAMA
   1084 static int isuniquegeom(XineramaScreenInfo *unique, size_t n,
   1085                         XineramaScreenInfo *info) {
   1086   while (n--)
   1087     if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org &&
   1088         unique[n].width == info->width && unique[n].height == info->height)
   1089       return 0;
   1090   return 1;
   1091 }
   1092 #endif /* XINERAMA */
   1093 
   1094 void keypress(XEvent *e) {
   1095   unsigned int i;
   1096   KeySym keysym;
   1097   XKeyEvent *ev;
   1098 
   1099   ev = &e->xkey;
   1100   keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);
   1101   for (i = 0; i < LENGTH(keys); i++)
   1102     if (keysym == keys[i].keysym &&
   1103         CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) && keys[i].func)
   1104       keys[i].func(&(keys[i].arg));
   1105 }
   1106 
   1107 void killclient(const Arg *arg) {
   1108   if (!selmon->sel)
   1109     return;
   1110   if (!sendevent(selmon->sel, wmatom[WMDelete])) {
   1111     XGrabServer(dpy);
   1112     XSetErrorHandler(xerrordummy);
   1113     XSetCloseDownMode(dpy, DestroyAll);
   1114     XKillClient(dpy, selmon->sel->win);
   1115     XSync(dpy, False);
   1116     XSetErrorHandler(xerror);
   1117     XUngrabServer(dpy);
   1118   }
   1119 }
   1120 
   1121 void loadxrdb() {
   1122   Display *display;
   1123   char *resm;
   1124   XrmDatabase xrdb;
   1125   char *type;
   1126   XrmValue value;
   1127 
   1128   display = XOpenDisplay(NULL);
   1129 
   1130   if (display != NULL) {
   1131     resm = XResourceManagerString(display);
   1132 
   1133     if (resm != NULL) {
   1134       xrdb = XrmGetStringDatabase(resm);
   1135 
   1136       if (xrdb != NULL) {
   1137         XRDB_LOAD_COLOR("dwm.color0", normbordercolor);
   1138         XRDB_LOAD_COLOR("dwm.color8", selbordercolor);
   1139         XRDB_LOAD_COLOR("dwm.color0", normbgcolor);
   1140         XRDB_LOAD_COLOR("dwm.color6", normfgcolor);
   1141         XRDB_LOAD_COLOR("dwm.color0", selfgcolor);
   1142         XRDB_LOAD_COLOR("dwm.color14", selbgcolor);
   1143       }
   1144     }
   1145   }
   1146 
   1147   XCloseDisplay(display);
   1148 }
   1149 
   1150 void manage(Window w, XWindowAttributes *wa) {
   1151   Client *c, *t = NULL, *term = NULL;
   1152   Window trans = None;
   1153   XWindowChanges wc;
   1154 
   1155   c = ecalloc(1, sizeof(Client));
   1156   c->win = w;
   1157   c->pid = winpid(w);
   1158   /* geometry */
   1159   c->x = c->oldx = wa->x;
   1160   c->y = c->oldy = wa->y;
   1161   c->w = c->oldw = wa->width;
   1162   c->h = c->oldh = wa->height;
   1163   c->oldbw = wa->border_width;
   1164 
   1165   updatetitle(c);
   1166   if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
   1167     c->mon = t->mon;
   1168     c->tags = t->tags;
   1169   } else {
   1170     c->mon = selmon;
   1171     applyrules(c);
   1172     term = termforwin(c);
   1173   }
   1174 
   1175   if (c->x + WIDTH(c) > c->mon->mx + c->mon->mw)
   1176     c->x = c->mon->mx + c->mon->mw - WIDTH(c);
   1177   if (c->y + HEIGHT(c) > c->mon->my + c->mon->mh)
   1178     c->y = c->mon->my + c->mon->mh - HEIGHT(c);
   1179   c->x = MAX(c->x, c->mon->mx);
   1180   /* only fix client y-offset, if the client center might cover the bar */
   1181   c->y = MAX(c->y,
   1182              ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) &&
   1183               (c->x + (c->w / 2) < c->mon->wx + c->mon->ww))
   1184                  ? bh
   1185                  : c->mon->my);
   1186   c->bw = borderpx;
   1187 
   1188   selmon->tagset[selmon->seltags] &= ~scratchtag;
   1189   if (!strcmp(c->name, scratchpadname)) {
   1190     c->mon->tagset[c->mon->seltags] |= c->tags = scratchtag;
   1191     c->isfloating = True;
   1192     c->x = c->mon->wx + (c->mon->ww / 2 - WIDTH(c) / 2);
   1193     c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2);
   1194   }
   1195 
   1196   wc.border_width = c->bw;
   1197   XConfigureWindow(dpy, w, CWBorderWidth, &wc);
   1198   XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel);
   1199   configure(c); /* propagates border_width, if size doesn't change */
   1200   updatewindowtype(c);
   1201   updatesizehints(c);
   1202   updatewmhints(c);
   1203   XSelectInput(dpy, w,
   1204                EnterWindowMask | FocusChangeMask | PropertyChangeMask |
   1205                    StructureNotifyMask);
   1206   grabbuttons(c, 0);
   1207   if (!c->isfloating)
   1208     c->isfloating = c->oldstate = trans != None || c->isfixed;
   1209   if (c->isfloating)
   1210     XRaiseWindow(dpy, c->win);
   1211   attach(c);
   1212   attachstack(c);
   1213   XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32,
   1214                   PropModeAppend, (unsigned char *)&(c->win), 1);
   1215   XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w,
   1216                     c->h); /* some windows require this */
   1217   setclientstate(c, NormalState);
   1218   if (c->mon == selmon)
   1219     unfocus(selmon->sel, 0);
   1220   c->mon->sel = c;
   1221   arrange(c->mon);
   1222   XMapWindow(dpy, c->win);
   1223   if (term)
   1224     swallow(term, c);
   1225   focus(NULL);
   1226 }
   1227 
   1228 void mappingnotify(XEvent *e) {
   1229   XMappingEvent *ev = &e->xmapping;
   1230 
   1231   XRefreshKeyboardMapping(ev);
   1232   if (ev->request == MappingKeyboard)
   1233     grabkeys();
   1234 }
   1235 
   1236 void maprequest(XEvent *e) {
   1237   static XWindowAttributes wa;
   1238   XMapRequestEvent *ev = &e->xmaprequest;
   1239 
   1240   if (!XGetWindowAttributes(dpy, ev->window, &wa))
   1241     return;
   1242   if (wa.override_redirect)
   1243     return;
   1244   if (!wintoclient(ev->window))
   1245     manage(ev->window, &wa);
   1246 }
   1247 
   1248 void monocle(Monitor *m) {
   1249   unsigned int n;
   1250   int oh, ov, ih, iv;
   1251   Client *c;
   1252 
   1253   getgaps(m, &oh, &ov, &ih, &iv, &n);
   1254 
   1255   if (n > 0) /* override layout symbol */
   1256     snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);
   1257   for (c = nexttiled(m->clients); c; c = nexttiled(c->next))
   1258     resize(c, m->wx + ov, m->wy + oh, m->ww - 2 * c->bw - 2 * ov,
   1259            m->wh - 2 * c->bw - 2 * oh, 0);
   1260 }
   1261 
   1262 void motionnotify(XEvent *e) {
   1263   static Monitor *mon = NULL;
   1264   Monitor *m;
   1265   XMotionEvent *ev = &e->xmotion;
   1266 
   1267   if (ev->window != root)
   1268     return;
   1269   if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) {
   1270     unfocus(selmon->sel, 1);
   1271     selmon = m;
   1272     focus(NULL);
   1273   }
   1274   mon = m;
   1275 }
   1276 
   1277 void movemouse(const Arg *arg) {
   1278   int x, y, ocx, ocy, nx, ny;
   1279   Client *c;
   1280   Monitor *m;
   1281   XEvent ev;
   1282   Time lasttime = 0;
   1283 
   1284   if (!(c = selmon->sel))
   1285     return;
   1286   if (c->isfullscreen) /* no support moving fullscreen windows by mouse */
   1287     return;
   1288   restack(selmon);
   1289   ocx = c->x;
   1290   ocy = c->y;
   1291   if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
   1292                    None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess)
   1293     return;
   1294   if (!getrootptr(&x, &y))
   1295     return;
   1296   do {
   1297     XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev);
   1298     switch (ev.type) {
   1299     case ConfigureRequest:
   1300     case Expose:
   1301     case MapRequest:
   1302       handler[ev.type](&ev);
   1303       break;
   1304     case MotionNotify:
   1305       if ((ev.xmotion.time - lasttime) <= (1000 / 60))
   1306         continue;
   1307       lasttime = ev.xmotion.time;
   1308 
   1309       nx = ocx + (ev.xmotion.x - x);
   1310       ny = ocy + (ev.xmotion.y - y);
   1311       if (abs(selmon->wx - nx) < snap)
   1312         nx = selmon->wx;
   1313       else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap)
   1314         nx = selmon->wx + selmon->ww - WIDTH(c);
   1315       if (abs(selmon->wy - ny) < snap)
   1316         ny = selmon->wy;
   1317       else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap)
   1318         ny = selmon->wy + selmon->wh - HEIGHT(c);
   1319       if (!c->isfloating && selmon->lt[selmon->sellt]->arrange &&
   1320           (abs(nx - c->x) > snap || abs(ny - c->y) > snap))
   1321         togglefloating(NULL);
   1322       if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
   1323         resize(c, nx, ny, c->w, c->h, 1);
   1324       break;
   1325     }
   1326   } while (ev.type != ButtonRelease);
   1327   XUngrabPointer(dpy, CurrentTime);
   1328   if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
   1329     sendmon(c, m);
   1330     selmon = m;
   1331     focus(NULL);
   1332   }
   1333 }
   1334 
   1335 Client *nexttiled(Client *c) {
   1336   for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next)
   1337     ;
   1338   return c;
   1339 }
   1340 
   1341 void pop(Client *c) {
   1342   detach(c);
   1343   attach(c);
   1344   focus(c);
   1345   arrange(c->mon);
   1346 }
   1347 
   1348 void pushstack(const Arg *arg) {
   1349   int i = stackpos(arg);
   1350   Client *sel = selmon->sel, *c, *p;
   1351 
   1352   if (i < 0)
   1353     return;
   1354   else if (i == 0) {
   1355     detach(sel);
   1356     attach(sel);
   1357   } else {
   1358     for (p = NULL, c = selmon->clients; c; p = c, c = c->next)
   1359       if (!(i -= (ISVISIBLE(c) && c != sel)))
   1360         break;
   1361     c = c ? c : p;
   1362     detach(sel);
   1363     sel->next = c->next;
   1364     c->next = sel;
   1365   }
   1366   arrange(selmon);
   1367 }
   1368 
   1369 void propertynotify(XEvent *e) {
   1370   Client *c;
   1371   Window trans;
   1372   XPropertyEvent *ev = &e->xproperty;
   1373 
   1374   if ((ev->window == root) && (ev->atom == XA_WM_NAME)) {
   1375     updatestatus();
   1376   } else if (ev->state == PropertyDelete) {
   1377     return; /* ignore */
   1378   } else if ((c = wintoclient(ev->window))) {
   1379     switch (ev->atom) {
   1380     default:
   1381       break;
   1382     case XA_WM_TRANSIENT_FOR:
   1383       if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) &&
   1384           (c->isfloating = (wintoclient(trans)) != NULL))
   1385         arrange(c->mon);
   1386       break;
   1387     case XA_WM_NORMAL_HINTS:
   1388       updatesizehints(c);
   1389       break;
   1390     case XA_WM_HINTS:
   1391       updatewmhints(c);
   1392       drawbars();
   1393       break;
   1394     }
   1395     if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {
   1396       updatetitle(c);
   1397       if (c == c->mon->sel)
   1398         drawbar(c->mon);
   1399     }
   1400     if (ev->atom == netatom[NetWMWindowType])
   1401       updatewindowtype(c);
   1402   }
   1403 }
   1404 
   1405 void quit(const Arg *arg) {
   1406   if (arg->i)
   1407     restart = 1;
   1408   running = 0;
   1409 }
   1410 
   1411 Monitor *recttomon(int x, int y, int w, int h) {
   1412   Monitor *m, *r = selmon;
   1413   int a, area = 0;
   1414 
   1415   for (m = mons; m; m = m->next)
   1416     if ((a = INTERSECT(x, y, w, h, m)) > area) {
   1417       area = a;
   1418       r = m;
   1419     }
   1420   return r;
   1421 }
   1422 
   1423 void resize(Client *c, int x, int y, int w, int h, int interact) {
   1424   if (applysizehints(c, &x, &y, &w, &h, interact))
   1425     resizeclient(c, x, y, w, h);
   1426 }
   1427 
   1428 void resizeclient(Client *c, int x, int y, int w, int h) {
   1429   XWindowChanges wc;
   1430 
   1431   c->oldx = c->x;
   1432   c->x = wc.x = x;
   1433   c->oldy = c->y;
   1434   c->y = wc.y = y;
   1435   c->oldw = c->w;
   1436   c->w = wc.width = w;
   1437   c->oldh = c->h;
   1438   c->h = wc.height = h;
   1439   wc.border_width = c->bw;
   1440   XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth,
   1441                    &wc);
   1442   configure(c);
   1443   XSync(dpy, False);
   1444 }
   1445 
   1446 void resizemouse(const Arg *arg) {
   1447   int ocx, ocy, nw, nh;
   1448   Client *c;
   1449   Monitor *m;
   1450   XEvent ev;
   1451   Time lasttime = 0;
   1452 
   1453   if (!(c = selmon->sel))
   1454     return;
   1455   if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */
   1456     return;
   1457   restack(selmon);
   1458   ocx = c->x;
   1459   ocy = c->y;
   1460   if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
   1461                    None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)
   1462     return;
   1463   XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1,
   1464                c->h + c->bw - 1);
   1465   do {
   1466     XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev);
   1467     switch (ev.type) {
   1468     case ConfigureRequest:
   1469     case Expose:
   1470     case MapRequest:
   1471       handler[ev.type](&ev);
   1472       break;
   1473     case MotionNotify:
   1474       if ((ev.xmotion.time - lasttime) <= (1000 / 60))
   1475         continue;
   1476       lasttime = ev.xmotion.time;
   1477 
   1478       nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1);
   1479       nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1);
   1480       if (c->mon->wx + nw >= selmon->wx &&
   1481           c->mon->wx + nw <= selmon->wx + selmon->ww &&
   1482           c->mon->wy + nh >= selmon->wy &&
   1483           c->mon->wy + nh <= selmon->wy + selmon->wh) {
   1484         if (!c->isfloating && selmon->lt[selmon->sellt]->arrange &&
   1485             (abs(nw - c->w) > snap || abs(nh - c->h) > snap))
   1486           togglefloating(NULL);
   1487       }
   1488       if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)
   1489         resize(c, c->x, c->y, nw, nh, 1);
   1490       break;
   1491     }
   1492   } while (ev.type != ButtonRelease);
   1493   XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1,
   1494                c->h + c->bw - 1);
   1495   XUngrabPointer(dpy, CurrentTime);
   1496   while (XCheckMaskEvent(dpy, EnterWindowMask, &ev))
   1497     ;
   1498   if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {
   1499     sendmon(c, m);
   1500     selmon = m;
   1501     focus(NULL);
   1502   }
   1503 }
   1504 
   1505 void restack(Monitor *m) {
   1506   Client *c;
   1507   XEvent ev;
   1508   XWindowChanges wc;
   1509 
   1510   drawbar(m);
   1511   if (!m->sel)
   1512     return;
   1513   if (m->sel->isfloating || !m->lt[m->sellt]->arrange)
   1514     XRaiseWindow(dpy, m->sel->win);
   1515   if (m->lt[m->sellt]->arrange) {
   1516     wc.stack_mode = Below;
   1517     wc.sibling = m->barwin;
   1518     for (c = m->stack; c; c = c->snext)
   1519       if (!c->isfloating && ISVISIBLE(c)) {
   1520         XConfigureWindow(dpy, c->win, CWSibling | CWStackMode, &wc);
   1521         wc.sibling = c->win;
   1522       }
   1523   }
   1524   XSync(dpy, False);
   1525   while (XCheckMaskEvent(dpy, EnterWindowMask, &ev))
   1526     ;
   1527 }
   1528 
   1529 void run(void) {
   1530   XEvent ev;
   1531   /* main event loop */
   1532   XSync(dpy, False);
   1533   while (running && !XNextEvent(dpy, &ev))
   1534     if (handler[ev.type])
   1535       handler[ev.type](&ev); /* call handler */
   1536 }
   1537 
   1538 void runAutostart(void) { system("killall slstatus 2>/dev/null ; slstatus &"); }
   1539 
   1540 void scan(void) {
   1541   unsigned int i, num;
   1542   Window d1, d2, *wins = NULL;
   1543   XWindowAttributes wa;
   1544 
   1545   if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {
   1546     for (i = 0; i < num; i++) {
   1547       if (!XGetWindowAttributes(dpy, wins[i], &wa) || wa.override_redirect ||
   1548           XGetTransientForHint(dpy, wins[i], &d1))
   1549         continue;
   1550       if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)
   1551         manage(wins[i], &wa);
   1552     }
   1553     for (i = 0; i < num; i++) { /* now the transients */
   1554       if (!XGetWindowAttributes(dpy, wins[i], &wa))
   1555         continue;
   1556       if (XGetTransientForHint(dpy, wins[i], &d1) &&
   1557           (wa.map_state == IsViewable || getstate(wins[i]) == IconicState))
   1558         manage(wins[i], &wa);
   1559     }
   1560     if (wins)
   1561       XFree(wins);
   1562   }
   1563 }
   1564 
   1565 void sendmon(Client *c, Monitor *m) {
   1566   if (c->mon == m)
   1567     return;
   1568   unfocus(c, 1);
   1569   detach(c);
   1570   detachstack(c);
   1571   c->mon = m;
   1572   c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
   1573   attach(c);
   1574   attachstack(c);
   1575   focus(NULL);
   1576   arrange(NULL);
   1577 }
   1578 
   1579 void setclientstate(Client *c, long state) {
   1580   long data[] = {state, None};
   1581 
   1582   XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,
   1583                   PropModeReplace, (unsigned char *)data, 2);
   1584 }
   1585 
   1586 int sendevent(Client *c, Atom proto) {
   1587   int n;
   1588   Atom *protocols;
   1589   int exists = 0;
   1590   XEvent ev;
   1591 
   1592   if (XGetWMProtocols(dpy, c->win, &protocols, &n)) {
   1593     while (!exists && n--)
   1594       exists = protocols[n] == proto;
   1595     XFree(protocols);
   1596   }
   1597   if (exists) {
   1598     ev.type = ClientMessage;
   1599     ev.xclient.window = c->win;
   1600     ev.xclient.message_type = wmatom[WMProtocols];
   1601     ev.xclient.format = 32;
   1602     ev.xclient.data.l[0] = proto;
   1603     ev.xclient.data.l[1] = CurrentTime;
   1604     XSendEvent(dpy, c->win, False, NoEventMask, &ev);
   1605   }
   1606   return exists;
   1607 }
   1608 
   1609 void setfocus(Client *c) {
   1610   if (!c->neverfocus) {
   1611     XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
   1612     XChangeProperty(dpy, root, netatom[NetActiveWindow], XA_WINDOW, 32,
   1613                     PropModeReplace, (unsigned char *)&(c->win), 1);
   1614   }
   1615   sendevent(c, wmatom[WMTakeFocus]);
   1616 }
   1617 
   1618 void setfullscreen(Client *c, int fullscreen) {
   1619   if (fullscreen && !c->isfullscreen) {
   1620     XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
   1621                     PropModeReplace, (unsigned char *)&netatom[NetWMFullscreen],
   1622                     1);
   1623     c->isfullscreen = 1;
   1624     c->oldstate = c->isfloating;
   1625     c->oldbw = c->bw;
   1626     c->bw = 0;
   1627     c->isfloating = 1;
   1628     resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh);
   1629     XRaiseWindow(dpy, c->win);
   1630   } else if (!fullscreen && c->isfullscreen) {
   1631     XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,
   1632                     PropModeReplace, (unsigned char *)0, 0);
   1633     c->isfullscreen = 0;
   1634     c->isfloating = c->oldstate;
   1635     c->bw = c->oldbw;
   1636     c->x = c->oldx;
   1637     c->y = c->oldy;
   1638     c->w = c->oldw;
   1639     c->h = c->oldh;
   1640     resizeclient(c, c->x, c->y, c->w, c->h);
   1641     arrange(c->mon);
   1642   }
   1643 }
   1644 
   1645 int stackpos(const Arg *arg) {
   1646   int n, i;
   1647   Client *c, *l;
   1648 
   1649   if (!selmon->clients)
   1650     return -1;
   1651 
   1652   if (arg->i == PREVSEL) {
   1653     for (l = selmon->stack; l && (!ISVISIBLE(l) || l == selmon->sel);
   1654          l = l->snext)
   1655       ;
   1656     if (!l)
   1657       return -1;
   1658     for (i = 0, c = selmon->clients; c != l;
   1659          i += ISVISIBLE(c) ? 1 : 0, c = c->next)
   1660       ;
   1661     return i;
   1662   } else if (ISINC(arg->i)) {
   1663     if (!selmon->sel)
   1664       return -1;
   1665     for (i = 0, c = selmon->clients; c != selmon->sel;
   1666          i += ISVISIBLE(c) ? 1 : 0, c = c->next)
   1667       ;
   1668     for (n = i; c; n += ISVISIBLE(c) ? 1 : 0, c = c->next)
   1669       ;
   1670     return MOD(i + GETINC(arg->i), n);
   1671   } else if (arg->i < 0) {
   1672     for (i = 0, c = selmon->clients; c; i += ISVISIBLE(c) ? 1 : 0, c = c->next)
   1673       ;
   1674     return MAX(i + arg->i, 0);
   1675   } else
   1676     return arg->i;
   1677 }
   1678 
   1679 void setlayout(const Arg *arg) {
   1680   if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
   1681     selmon->sellt ^= 1;
   1682   if (arg && arg->v)
   1683     selmon->lt[selmon->sellt] = (Layout *)arg->v;
   1684   strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol,
   1685           sizeof selmon->ltsymbol);
   1686   if (selmon->sel)
   1687     arrange(selmon);
   1688   else
   1689     drawbar(selmon);
   1690 }
   1691 
   1692 /* arg > 1.0 will set mfact absolutely */
   1693 void setmfact(const Arg *arg) {
   1694   float f;
   1695 
   1696   if (!arg || !selmon->lt[selmon->sellt]->arrange)
   1697     return;
   1698   f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
   1699   if (f < 0.1 || f > 0.9)
   1700     return;
   1701   selmon->mfact = f;
   1702   arrange(selmon);
   1703 }
   1704 
   1705 void setup(void) {
   1706   int i;
   1707   XSetWindowAttributes wa;
   1708   Atom utf8string;
   1709 
   1710   /* clean up any zombies immediately */
   1711   sigchld(0);
   1712 
   1713   signal(SIGHUP, sighup);
   1714   signal(SIGTERM, sigterm);
   1715 
   1716   /* init screen */
   1717   screen = DefaultScreen(dpy);
   1718   sw = DisplayWidth(dpy, screen);
   1719   sh = DisplayHeight(dpy, screen);
   1720   root = RootWindow(dpy, screen);
   1721   drw = drw_create(dpy, screen, root, sw, sh);
   1722   if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
   1723     die("no fonts could be loaded.");
   1724   lrpad = drw->fonts->h;
   1725   bh = drw->fonts->h + 2;
   1726   updategeom();
   1727   /* init atoms */
   1728   utf8string = XInternAtom(dpy, "UTF8_STRING", False);
   1729   wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
   1730   wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
   1731   wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);
   1732   wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
   1733   netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
   1734   netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
   1735   netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
   1736   netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
   1737   netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
   1738   netatom[NetWMFullscreen] =
   1739       XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);
   1740   netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
   1741   netatom[NetWMWindowTypeDialog] =
   1742       XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
   1743   netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
   1744   /* init cursors */
   1745   cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
   1746   cursor[CurResize] = drw_cur_create(drw, XC_sizing);
   1747   cursor[CurMove] = drw_cur_create(drw, XC_fleur);
   1748   /* init appearance */
   1749   scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
   1750   for (i = 0; i < LENGTH(colors); i++)
   1751     scheme[i] = drw_scm_create(drw, colors[i], 3);
   1752   /* init bars */
   1753   updatebars();
   1754   updatestatus();
   1755   /* supporting window for NetWMCheck */
   1756   wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0);
   1757   XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32,
   1758                   PropModeReplace, (unsigned char *)&wmcheckwin, 1);
   1759   XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8,
   1760                   PropModeReplace, (unsigned char *)"dwm", 3);
   1761   XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32,
   1762                   PropModeReplace, (unsigned char *)&wmcheckwin, 1);
   1763   /* EWMH support per view */
   1764   XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
   1765                   PropModeReplace, (unsigned char *)netatom, NetLast);
   1766   XDeleteProperty(dpy, root, netatom[NetClientList]);
   1767   /* select events */
   1768   wa.cursor = cursor[CurNormal]->cursor;
   1769   wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask |
   1770                   ButtonPressMask | PointerMotionMask | EnterWindowMask |
   1771                   LeaveWindowMask | StructureNotifyMask | PropertyChangeMask;
   1772   XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa);
   1773   XSelectInput(dpy, root, wa.event_mask);
   1774   grabkeys();
   1775   focus(NULL);
   1776 }
   1777 
   1778 void seturgent(Client *c, int urg) {
   1779   XWMHints *wmh;
   1780 
   1781   c->isurgent = urg;
   1782   if (!(wmh = XGetWMHints(dpy, c->win)))
   1783     return;
   1784   wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint);
   1785   XSetWMHints(dpy, c->win, wmh);
   1786   XFree(wmh);
   1787 }
   1788 
   1789 void showhide(Client *c) {
   1790   if (!c)
   1791     return;
   1792   if (ISVISIBLE(c)) {
   1793     /* show clients top down */
   1794     XMoveWindow(dpy, c->win, c->x, c->y);
   1795     if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) &&
   1796         !c->isfullscreen)
   1797       resize(c, c->x, c->y, c->w, c->h, 0);
   1798     showhide(c->snext);
   1799   } else {
   1800     /* hide clients bottom up */
   1801     showhide(c->snext);
   1802     XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y);
   1803   }
   1804 }
   1805 
   1806 void sigchld(int unused) {
   1807   if (signal(SIGCHLD, sigchld) == SIG_ERR)
   1808     die("can't install SIGCHLD handler:");
   1809   while (0 < waitpid(-1, NULL, WNOHANG))
   1810     ;
   1811 }
   1812 
   1813 void sighup(int unused) {
   1814   Arg a = {.i = 1};
   1815   quit(&a);
   1816 }
   1817 
   1818 void sigterm(int unused) {
   1819   Arg a = {.i = 0};
   1820   quit(&a);
   1821 }
   1822 
   1823 void spawn(const Arg *arg) {
   1824   if (arg->v == dmenucmd)
   1825     dmenumon[0] = '0' + selmon->num;
   1826   selmon->tagset[selmon->seltags] &= ~scratchtag;
   1827   if (fork() == 0) {
   1828     if (dpy)
   1829       close(ConnectionNumber(dpy));
   1830     setsid();
   1831     execvp(((char **)arg->v)[0], (char **)arg->v);
   1832     fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]);
   1833     perror(" failed");
   1834     exit(EXIT_SUCCESS);
   1835   }
   1836 }
   1837 
   1838 void tag(const Arg *arg) {
   1839   if (selmon->sel && arg->ui & TAGMASK) {
   1840     selmon->sel->tags = arg->ui & TAGMASK;
   1841     focus(NULL);
   1842     arrange(selmon);
   1843   }
   1844 }
   1845 
   1846 void tagmon(const Arg *arg) {
   1847   if (!selmon->sel || !mons->next)
   1848     return;
   1849   sendmon(selmon->sel, dirtomon(arg->i));
   1850 }
   1851 
   1852 void togglebar(const Arg *arg) {
   1853   selmon->showbar = !selmon->showbar;
   1854   updatebarpos(selmon);
   1855   XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww,
   1856                     bh);
   1857   arrange(selmon);
   1858 }
   1859 
   1860 void togglefloating(const Arg *arg) {
   1861   if (!selmon->sel)
   1862     return;
   1863   if (selmon->sel->isfullscreen) /* no support for fullscreen windows */
   1864     return;
   1865   selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;
   1866   if (selmon->sel->isfloating)
   1867     resize(selmon->sel, selmon->sel->x, selmon->sel->y, selmon->sel->w,
   1868            selmon->sel->h, 0);
   1869   arrange(selmon);
   1870 }
   1871 
   1872 void togglescratch(const Arg *arg) {
   1873   Client *c;
   1874   unsigned int found = 0;
   1875 
   1876   for (c = selmon->clients; c && !(found = c->tags & scratchtag); c = c->next)
   1877     ;
   1878   if (found) {
   1879     unsigned int newtagset = selmon->tagset[selmon->seltags] ^ scratchtag;
   1880     if (newtagset) {
   1881       selmon->tagset[selmon->seltags] = newtagset;
   1882       focus(NULL);
   1883       arrange(selmon);
   1884     }
   1885     if (ISVISIBLE(c)) {
   1886       focus(c);
   1887       restack(selmon);
   1888     }
   1889   } else
   1890     spawn(arg);
   1891 }
   1892 
   1893 void togglefullscr(const Arg *arg) {
   1894   if (selmon->sel)
   1895     setfullscreen(selmon->sel, !selmon->sel->isfullscreen);
   1896 }
   1897 
   1898 void togglesticky(const Arg *arg) {
   1899   if (!selmon->sel)
   1900     return;
   1901   selmon->sel->issticky = !selmon->sel->issticky;
   1902   arrange(selmon);
   1903 }
   1904 
   1905 void toggletag(const Arg *arg) {
   1906   unsigned int newtags;
   1907 
   1908   if (!selmon->sel)
   1909     return;
   1910   newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
   1911   if (newtags) {
   1912     selmon->sel->tags = newtags;
   1913     focus(NULL);
   1914     arrange(selmon);
   1915   }
   1916 }
   1917 
   1918 void toggleview(const Arg *arg) {
   1919   unsigned int newtagset =
   1920       selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);
   1921 
   1922   if (newtagset) {
   1923     selmon->tagset[selmon->seltags] = newtagset;
   1924     focus(NULL);
   1925     arrange(selmon);
   1926   }
   1927 }
   1928 
   1929 void unfocus(Client *c, int setfocus) {
   1930   if (!c)
   1931     return;
   1932   grabbuttons(c, 0);
   1933   XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel);
   1934   if (setfocus) {
   1935     XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);
   1936     XDeleteProperty(dpy, root, netatom[NetActiveWindow]);
   1937   }
   1938 }
   1939 
   1940 void unmanage(Client *c, int destroyed) {
   1941   Monitor *m = c->mon;
   1942   XWindowChanges wc;
   1943 
   1944   if (c->swallowing) {
   1945     unswallow(c);
   1946     return;
   1947   }
   1948 
   1949   Client *s = swallowingclient(c->win);
   1950   if (s) {
   1951     free(s->swallowing);
   1952     s->swallowing = NULL;
   1953     arrange(m);
   1954     focus(NULL);
   1955     return;
   1956   }
   1957 
   1958   detach(c);
   1959   detachstack(c);
   1960   if (!destroyed) {
   1961     wc.border_width = c->oldbw;
   1962     XGrabServer(dpy); /* avoid race conditions */
   1963     XSetErrorHandler(xerrordummy);
   1964     XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */
   1965     XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
   1966     setclientstate(c, WithdrawnState);
   1967     XSync(dpy, False);
   1968     XSetErrorHandler(xerror);
   1969     XUngrabServer(dpy);
   1970   }
   1971   free(c);
   1972 
   1973   if (!s) {
   1974     arrange(m);
   1975     focus(NULL);
   1976     updateclientlist();
   1977   }
   1978 }
   1979 
   1980 void unmapnotify(XEvent *e) {
   1981   Client *c;
   1982   XUnmapEvent *ev = &e->xunmap;
   1983 
   1984   if ((c = wintoclient(ev->window))) {
   1985     if (ev->send_event)
   1986       setclientstate(c, WithdrawnState);
   1987     else
   1988       unmanage(c, 0);
   1989   }
   1990 }
   1991 
   1992 void updatebars(void) {
   1993   Monitor *m;
   1994   XSetWindowAttributes wa = {.override_redirect = True,
   1995                              .background_pixmap = ParentRelative,
   1996                              .event_mask = ButtonPressMask | ExposureMask};
   1997   XClassHint ch = {"dwm", "dwm"};
   1998   for (m = mons; m; m = m->next) {
   1999     if (m->barwin)
   2000       continue;
   2001     m->barwin = XCreateWindow(
   2002         dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),
   2003         CopyFromParent, DefaultVisual(dpy, screen),
   2004         CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
   2005     XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
   2006     XMapRaised(dpy, m->barwin);
   2007     XSetClassHint(dpy, m->barwin, &ch);
   2008   }
   2009 }
   2010 
   2011 void updatebarpos(Monitor *m) {
   2012   m->wy = m->my;
   2013   m->wh = m->mh;
   2014   if (m->showbar) {
   2015     m->wh -= bh;
   2016     m->by = m->topbar ? m->wy : m->wy + m->wh;
   2017     m->wy = m->topbar ? m->wy + bh : m->wy;
   2018   } else
   2019     m->by = -bh;
   2020 }
   2021 
   2022 void updateclientlist() {
   2023   Client *c;
   2024   Monitor *m;
   2025 
   2026   XDeleteProperty(dpy, root, netatom[NetClientList]);
   2027   for (m = mons; m; m = m->next)
   2028     for (c = m->clients; c; c = c->next)
   2029       XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32,
   2030                       PropModeAppend, (unsigned char *)&(c->win), 1);
   2031 }
   2032 
   2033 int updategeom(void) {
   2034   int dirty = 0;
   2035 
   2036 #ifdef XINERAMA
   2037   if (XineramaIsActive(dpy)) {
   2038     int i, j, n, nn;
   2039     Client *c;
   2040     Monitor *m;
   2041     XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn);
   2042     XineramaScreenInfo *unique = NULL;
   2043 
   2044     for (n = 0, m = mons; m; m = m->next, n++)
   2045       ;
   2046     /* only consider unique geometries as separate screens */
   2047     unique = ecalloc(nn, sizeof(XineramaScreenInfo));
   2048     for (i = 0, j = 0; i < nn; i++)
   2049       if (isuniquegeom(unique, j, &info[i]))
   2050         memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo));
   2051     XFree(info);
   2052     nn = j;
   2053     if (n <= nn) { /* new monitors available */
   2054       for (i = 0; i < (nn - n); i++) {
   2055         for (m = mons; m && m->next; m = m->next)
   2056           ;
   2057         if (m)
   2058           m->next = createmon();
   2059         else
   2060           mons = createmon();
   2061       }
   2062       for (i = 0, m = mons; i < nn && m; m = m->next, i++)
   2063         if (i >= n || unique[i].x_org != m->mx || unique[i].y_org != m->my ||
   2064             unique[i].width != m->mw || unique[i].height != m->mh) {
   2065           dirty = 1;
   2066           m->num = i;
   2067           m->mx = m->wx = unique[i].x_org;
   2068           m->my = m->wy = unique[i].y_org;
   2069           m->mw = m->ww = unique[i].width;
   2070           m->mh = m->wh = unique[i].height;
   2071           updatebarpos(m);
   2072         }
   2073     } else { /* less monitors available nn < n */
   2074       for (i = nn; i < n; i++) {
   2075         for (m = mons; m && m->next; m = m->next)
   2076           ;
   2077         while ((c = m->clients)) {
   2078           dirty = 1;
   2079           m->clients = c->next;
   2080           detachstack(c);
   2081           c->mon = mons;
   2082           attach(c);
   2083           attachstack(c);
   2084         }
   2085         if (m == selmon)
   2086           selmon = mons;
   2087         cleanupmon(m);
   2088       }
   2089     }
   2090     free(unique);
   2091   } else
   2092 #endif /* XINERAMA */
   2093   {    /* default monitor setup */
   2094     if (!mons)
   2095       mons = createmon();
   2096     if (mons->mw != sw || mons->mh != sh) {
   2097       dirty = 1;
   2098       mons->mw = mons->ww = sw;
   2099       mons->mh = mons->wh = sh;
   2100       updatebarpos(mons);
   2101     }
   2102   }
   2103   if (dirty) {
   2104     selmon = mons;
   2105     selmon = wintomon(root);
   2106   }
   2107   return dirty;
   2108 }
   2109 
   2110 void updatenumlockmask(void) {
   2111   unsigned int i, j;
   2112   XModifierKeymap *modmap;
   2113 
   2114   numlockmask = 0;
   2115   modmap = XGetModifierMapping(dpy);
   2116   for (i = 0; i < 8; i++)
   2117     for (j = 0; j < modmap->max_keypermod; j++)
   2118       if (modmap->modifiermap[i * modmap->max_keypermod + j] ==
   2119           XKeysymToKeycode(dpy, XK_Num_Lock))
   2120         numlockmask = (1 << i);
   2121   XFreeModifiermap(modmap);
   2122 }
   2123 
   2124 void updatesizehints(Client *c) {
   2125   long msize;
   2126   XSizeHints size;
   2127 
   2128   if (!XGetWMNormalHints(dpy, c->win, &size, &msize))
   2129     /* size is uninitialized, ensure that size.flags aren't used */
   2130     size.flags = PSize;
   2131   if (size.flags & PBaseSize) {
   2132     c->basew = size.base_width;
   2133     c->baseh = size.base_height;
   2134   } else if (size.flags & PMinSize) {
   2135     c->basew = size.min_width;
   2136     c->baseh = size.min_height;
   2137   } else
   2138     c->basew = c->baseh = 0;
   2139   if (size.flags & PResizeInc) {
   2140     c->incw = size.width_inc;
   2141     c->inch = size.height_inc;
   2142   } else
   2143     c->incw = c->inch = 0;
   2144   if (size.flags & PMaxSize) {
   2145     c->maxw = size.max_width;
   2146     c->maxh = size.max_height;
   2147   } else
   2148     c->maxw = c->maxh = 0;
   2149   if (size.flags & PMinSize) {
   2150     c->minw = size.min_width;
   2151     c->minh = size.min_height;
   2152   } else if (size.flags & PBaseSize) {
   2153     c->minw = size.base_width;
   2154     c->minh = size.base_height;
   2155   } else
   2156     c->minw = c->minh = 0;
   2157   if (size.flags & PAspect) {
   2158     c->mina = (float)size.min_aspect.y / size.min_aspect.x;
   2159     c->maxa = (float)size.max_aspect.x / size.max_aspect.y;
   2160   } else
   2161     c->maxa = c->mina = 0.0;
   2162   c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh);
   2163 }
   2164 
   2165 void updatestatus(void) {
   2166   if (!gettextprop(root, XA_WM_NAME, rawstext, sizeof(rawstext)))
   2167     strcpy(stext, "dwm-" VERSION);
   2168   else
   2169     copyvalidchars(stext, rawstext);
   2170   drawbar(selmon);
   2171 }
   2172 
   2173 void updatetitle(Client *c) {
   2174   if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))
   2175     gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name);
   2176   if (c->name[0] == '\0') /* hack to mark broken clients */
   2177     strcpy(c->name, broken);
   2178 }
   2179 
   2180 void updatewindowtype(Client *c) {
   2181   Atom state = getatomprop(c, netatom[NetWMState]);
   2182   Atom wtype = getatomprop(c, netatom[NetWMWindowType]);
   2183 
   2184   if (state == netatom[NetWMFullscreen])
   2185     setfullscreen(c, 1);
   2186   if (wtype == netatom[NetWMWindowTypeDialog])
   2187     c->isfloating = 1;
   2188 }
   2189 
   2190 void updatewmhints(Client *c) {
   2191   XWMHints *wmh;
   2192 
   2193   if ((wmh = XGetWMHints(dpy, c->win))) {
   2194     if (c == selmon->sel && wmh->flags & XUrgencyHint) {
   2195       wmh->flags &= ~XUrgencyHint;
   2196       XSetWMHints(dpy, c->win, wmh);
   2197     } else
   2198       c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0;
   2199     if (wmh->flags & InputHint)
   2200       c->neverfocus = !wmh->input;
   2201     else
   2202       c->neverfocus = 0;
   2203     XFree(wmh);
   2204   }
   2205 }
   2206 
   2207 void view(const Arg *arg) {
   2208   if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
   2209     return;
   2210   selmon->seltags ^= 1; /* toggle sel tagset */
   2211   if (arg->ui & TAGMASK)
   2212     selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
   2213   focus(NULL);
   2214   arrange(selmon);
   2215 }
   2216 
   2217 pid_t winpid(Window w) {
   2218   pid_t result = 0;
   2219 
   2220   xcb_res_client_id_spec_t spec = {0};
   2221   spec.client = w;
   2222   spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID;
   2223 
   2224   xcb_generic_error_t *e = NULL;
   2225   xcb_res_query_client_ids_cookie_t c =
   2226       xcb_res_query_client_ids(xcon, 1, &spec);
   2227   xcb_res_query_client_ids_reply_t *r =
   2228       xcb_res_query_client_ids_reply(xcon, c, &e);
   2229 
   2230   if (!r)
   2231     return (pid_t)0;
   2232 
   2233   xcb_res_client_id_value_iterator_t i =
   2234       xcb_res_query_client_ids_ids_iterator(r);
   2235   for (; i.rem; xcb_res_client_id_value_next(&i)) {
   2236     spec = i.data->spec;
   2237     if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) {
   2238       uint32_t *t = xcb_res_client_id_value_value(i.data);
   2239       result = *t;
   2240       break;
   2241     }
   2242   }
   2243 
   2244   free(r);
   2245 
   2246   if (result == (pid_t)-1)
   2247     result = 0;
   2248   return result;
   2249 }
   2250 
   2251 pid_t getparentprocess(pid_t p) {
   2252   unsigned int v = 0;
   2253 
   2254 #ifdef __linux__
   2255   FILE *f;
   2256   char buf[256];
   2257   snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p);
   2258 
   2259   if (!(f = fopen(buf, "r")))
   2260     return 0;
   2261 
   2262   fscanf(f, "%*u %*s %*c %u", &v);
   2263   fclose(f);
   2264 #endif /* __linux__ */
   2265 
   2266   return (pid_t)v;
   2267 }
   2268 
   2269 int isdescprocess(pid_t p, pid_t c) {
   2270   while (p != c && c != 0)
   2271     c = getparentprocess(c);
   2272 
   2273   return (int)c;
   2274 }
   2275 
   2276 Client *termforwin(const Client *w) {
   2277   Client *c;
   2278   Monitor *m;
   2279 
   2280   if (!w->pid || w->isterminal)
   2281     return NULL;
   2282 
   2283   for (m = mons; m; m = m->next) {
   2284     for (c = m->clients; c; c = c->next) {
   2285       if (c->isterminal && !c->swallowing && c->pid &&
   2286           isdescprocess(c->pid, w->pid))
   2287         return c;
   2288     }
   2289   }
   2290 
   2291   return NULL;
   2292 }
   2293 
   2294 Client *swallowingclient(Window w) {
   2295   Client *c;
   2296   Monitor *m;
   2297 
   2298   for (m = mons; m; m = m->next) {
   2299     for (c = m->clients; c; c = c->next) {
   2300       if (c->swallowing && c->swallowing->win == w)
   2301         return c;
   2302     }
   2303   }
   2304 
   2305   return NULL;
   2306 }
   2307 
   2308 Client *wintoclient(Window w) {
   2309   Client *c;
   2310   Monitor *m;
   2311 
   2312   for (m = mons; m; m = m->next)
   2313     for (c = m->clients; c; c = c->next)
   2314       if (c->win == w)
   2315         return c;
   2316   return NULL;
   2317 }
   2318 
   2319 Monitor *wintomon(Window w) {
   2320   int x, y;
   2321   Client *c;
   2322   Monitor *m;
   2323 
   2324   if (w == root && getrootptr(&x, &y))
   2325     return recttomon(x, y, 1, 1);
   2326   for (m = mons; m; m = m->next)
   2327     if (w == m->barwin)
   2328       return m;
   2329   if ((c = wintoclient(w)))
   2330     return c->mon;
   2331   return selmon;
   2332 }
   2333 
   2334 /* There's no way to check accesses to destroyed windows, thus those cases are
   2335  * ignored (especially on UnmapNotify's). Other types of errors call Xlibs
   2336  * default error handler, which may call exit. */
   2337 int xerror(Display *dpy, XErrorEvent *ee) {
   2338   if (ee->error_code == BadWindow ||
   2339       (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) ||
   2340       (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) ||
   2341       (ee->request_code == X_PolyFillRectangle &&
   2342        ee->error_code == BadDrawable) ||
   2343       (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) ||
   2344       (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) ||
   2345       (ee->request_code == X_GrabButton && ee->error_code == BadAccess) ||
   2346       (ee->request_code == X_GrabKey && ee->error_code == BadAccess) ||
   2347       (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))
   2348     return 0;
   2349   fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",
   2350           ee->request_code, ee->error_code);
   2351   return xerrorxlib(dpy, ee); /* may call exit */
   2352 }
   2353 
   2354 int xerrordummy(Display *dpy, XErrorEvent *ee) { return 0; }
   2355 
   2356 /* Startup Error handler to check if another window manager
   2357  * is already running. */
   2358 int xerrorstart(Display *dpy, XErrorEvent *ee) {
   2359   die("dwm: another window manager is already running");
   2360   return -1;
   2361 }
   2362 
   2363 void xrdb(const Arg *arg) {
   2364   loadxrdb();
   2365   int i;
   2366   for (i = 0; i < LENGTH(colors); i++)
   2367     scheme[i] = drw_scm_create(drw, colors[i], 3);
   2368   focus(NULL);
   2369   arrange(NULL);
   2370 }
   2371 
   2372 void zoom(const Arg *arg) {
   2373   Client *c = selmon->sel;
   2374 
   2375   if (!selmon->lt[selmon->sellt]->arrange ||
   2376       (selmon->sel && selmon->sel->isfloating))
   2377     return;
   2378   if (c == nexttiled(selmon->clients))
   2379     if (!c || !(c = nexttiled(c->next)))
   2380       return;
   2381   pop(c);
   2382 }
   2383 
   2384 int main(int argc, char *argv[]) {
   2385   if (argc == 2 && !strcmp("-v", argv[1]))
   2386     die("dwm-" VERSION);
   2387   else if (argc != 1)
   2388     die("usage: dwm [-v]");
   2389   if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
   2390     fputs("warning: no locale support\n", stderr);
   2391   if (!(dpy = XOpenDisplay(NULL)))
   2392     die("dwm: cannot open display");
   2393   if (!(xcon = XGetXCBConnection(dpy)))
   2394     die("dwm: cannot get xcb connection\n");
   2395   checkotherwm();
   2396   XrmInitialize();
   2397   loadxrdb();
   2398   setup();
   2399 #ifdef __OpenBSD__
   2400   if (pledge("stdio rpath proc exec", NULL) == -1)
   2401     die("pledge");
   2402 #endif /* __OpenBSD__ */
   2403   scan();
   2404   runAutostart();
   2405   run();
   2406   if (restart)
   2407     execvp(argv[0], argv);
   2408   cleanup();
   2409   XCloseDisplay(dpy);
   2410   return EXIT_SUCCESS;
   2411 }