1/* See LICENSE file for copyright and license details.2 *3 * dynamic window manager is designed like any other X client as well. It is4 * driven through handling X events. In contrast to other X clients, a window5 * manager selects for SubstructureRedirectMask on the root window, to receive6 * events about window (dis-)appearance. Only one X connection at a time is7 * allowed to select for this event mask.8 *9 * The event handlers of dwm are organized in an array which is accessed10 * whenever a new event has been fetched. This allows event dispatching11 * in O(1) time.12 *13 * Each child of the root window is called a client, except windows which have14 * set the override_redirect flag. Clients are organized in a linked client15 * list on each monitor, the focus history is remembered through a stack list16 * on each monitor. Each client contains a bit array to indicate the tags of a17 * 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 <errno.h>24#include <locale.h>25#include <signal.h>26#include <stdarg.h>27#include <stdio.h>28#include <stdlib.h>29#include <string.h>30#include <unistd.h>31#include <sys/types.h>32#include <sys/wait.h>33#include <X11/cursorfont.h>34#include <X11/keysym.h>35#include <X11/XF86keysym.h>36#include <X11/Xatom.h>37#include <X11/Xlib.h>38#include <X11/Xproto.h>39#include <X11/Xutil.h>40#ifdef XINERAMA41#include <X11/extensions/Xinerama.h>42#endif /* XINERAMA */43#include <X11/Xft/Xft.h>4445#include "drw.h"46#include "util.h"4748/* macros */49#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask)50#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask))51#define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \52 * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy)))53#define ISVISIBLE(C) ((C)->tags & (C)->mon->tagset[(C)->mon->seltags])54#define LENGTH(X) (sizeof X / sizeof X[0])55#define MOUSEMASK (BUTTONMASK|PointerMotionMask)56#define WIDTH(X) ((X)->w + 2 * (X)->bw)57#define HEIGHT(X) ((X)->h + 2 * (X)->bw)58#define TAGMASK ((1 << LENGTH(tags)) - 1)59#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)60#define MAXWH(M) ((M)->wh - ((M)->wh * 0.1))61#define MAXWW(M) ((M)->ww - ((M)->ww * 0.3))6263/* enums */64enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */65enum { SchemeNorm, SchemeSel }; /* color schemes */66enum { NetSupported, NetWMName, NetWMState, NetWMCheck,67 NetWMFullscreen, NetActiveWindow, NetWMWindowType,68 NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */69enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */70enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,71 ClkClientWin, ClkRootWin, ClkLast }; /* clicks */7273typedef union {74 int i;75 unsigned int ui;76 float f;77 const void *v;78} Arg;7980typedef struct {81 unsigned int click;82 unsigned int mask;83 unsigned int button;84 void (*func)(const Arg *arg);85 const Arg arg;86} Button;8788typedef struct Monitor Monitor;89typedef struct Client Client;90struct Client {91 char name[256];92 float mina, maxa;93 int x, y, w, h;94 int oldx, oldy, oldw, oldh;95 int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid;96 int bw, oldbw;97 unsigned int tags;98 int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;99 Client *next;100 Client *snext;101 Monitor *mon;102 Window win;103};104105typedef struct {106 unsigned int mod;107 KeySym keysym;108 void (*func)(const Arg *);109 const Arg arg;110} Key;111112typedef struct {113 const char *symbol;114 void (*arrange)(Monitor *);115} Layout;116117struct Monitor {118 char ltsymbol[16];119 float mfact;120 int nmaster;121 int num;122 int by; /* bar geometry */123 int mx, my, mw, mh; /* screen size */124 int wx, wy, ww, wh; /* window area */125 unsigned int seltags;126 unsigned int sellt;127 unsigned int tagset[2];128 int showbar;129 int topbar;130 Client *clients;131 Client *sel;132 Client *stack;133 Monitor *next;134 Window barwin;135 const Layout *lt[2];136};137138typedef struct {139 const char *class;140 const char *instance;141 const char *title;142 unsigned int tags;143 int isfloating;144 int monitor;145} Rule;146147/* function declarations */148static void applyrules(Client *c);149static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);150static void arrange(Monitor *m);151static void arrangemon(Monitor *m);152static void attach(Client *c);153static void attachstack(Client *c);154static void buttonpress(XEvent *e);155static void checkotherwm(void);156static void cleanup(void);157static void cleanupmon(Monitor *mon);158static void clientmessage(XEvent *e);159static void configure(Client *c);160static void configurenotify(XEvent *e);161static void configurerequest(XEvent *e);162static Monitor *createmon(void);163static void destroynotify(XEvent *e);164static void detach(Client *c);165static void detachstack(Client *c);166static Monitor *dirtomon(int dir);167static void drawbar(Monitor *m);168static void drawbars(void);169static void enternotify(XEvent *e);170static void expose(XEvent *e);171static void focus(Client *c);172static void focusin(XEvent *e);173static void focusmon(const Arg *arg);174static void focusstack(const Arg *arg);175static Atom getatomprop(Client *c, Atom prop);176static int getrootptr(int *x, int *y);177static long getstate(Window w);178static int gettextprop(Window w, Atom atom, char *text, unsigned int size);179static void grabbuttons(Client *c, int focused);180static void grabkeys(void);181static void incnmaster(const Arg *arg);182static void keypress(XEvent *e);183static void killclient(const Arg *arg);184static void manage(Window w, XWindowAttributes *wa);185static void mappingnotify(XEvent *e);186static void maprequest(XEvent *e);187static void monocle(Monitor *m);188static void motionnotify(XEvent *e);189static void movemouse(const Arg *arg);190static void movestack(const Arg *arg);191static Client *nexttiled(Client *c);192static void pop(Client *c);193static void propertynotify(XEvent *e);194static void quit(const Arg *arg);195static Monitor *recttomon(int x, int y, int w, int h);196static void resize(Client *c, int x, int y, int w, int h, int interact);197static void resizeclient(Client *c, int x, int y, int w, int h);198static void resizemouse(const Arg *arg);199static void restack(Monitor *m);200static void run(void);201static void scan(void);202static int sendevent(Client *c, Atom proto);203static void sendmon(Client *c, Monitor *m);204static void setclientstate(Client *c, long state);205static void setfocus(Client *c);206static void setfullscreen(Client *c, int fullscreen);207static void setlayout(const Arg *arg);208static void setmfact(const Arg *arg);209static void setup(void);210static void seturgent(Client *c, int urg);211static void showhide(Client *c);212static void sigchld(int unused);213static void spawn(const Arg *arg);214static void tag(const Arg *arg);215static void tagmon(const Arg *arg);216static void tile(Monitor *m);217static void togglebar(const Arg *arg);218static void togglefloating(const Arg *arg);219static void togglefullscrn(const Arg *arg);220static void toggletag(const Arg *arg);221static void toggleview(const Arg *arg);222static void unfocus(Client *c, int setfocus);223static void unmanage(Client *c, int destroyed);224static void unmapnotify(XEvent *e);225static void updatebarpos(Monitor *m);226static void updatebars(void);227static void updateclientlist(void);228static int updategeom(void);229static void updatenumlockmask(void);230static void updatesizehints(Client *c);231static void updatestatus(void);232static void updatetitle(Client *c);233static void updatewindowtype(Client *c);234static void updatewmhints(Client *c);235static void view(const Arg *arg);236static Client *wintoclient(Window w);237static Monitor *wintomon(Window w);238static int xerror(Display *dpy, XErrorEvent *ee);239static int xerrordummy(Display *dpy, XErrorEvent *ee);240static int xerrorstart(Display *dpy, XErrorEvent *ee);241static void zoom(const Arg *arg);242243/* variables */244static const char broken[] = "broken";245static char stext[256];246static int screen;247static int sw, sh; /* X display screen geometry width, height */248static int bh; /* bar height */249static int lrpad; /* sum of left and right padding for text */250static int (*xerrorxlib)(Display *, XErrorEvent *);251static unsigned int numlockmask = 0;252static void (*handler[LASTEvent]) (XEvent *) = {253 [ButtonPress] = buttonpress,254 [ClientMessage] = clientmessage,255 [ConfigureRequest] = configurerequest,256 [ConfigureNotify] = configurenotify,257 [DestroyNotify] = destroynotify,258 [EnterNotify] = enternotify,259 [Expose] = expose,260 [FocusIn] = focusin,261 [KeyPress] = keypress,262 [MappingNotify] = mappingnotify,263 [MapRequest] = maprequest,264 [MotionNotify] = motionnotify,265 [PropertyNotify] = propertynotify,266 [UnmapNotify] = unmapnotify267};268static Atom wmatom[WMLast], netatom[NetLast];269static int running = 1;270static Cur *cursor[CurLast];271static Clr **scheme;272static Display *dpy;273static Drw *drw;274static Monitor *mons, *selmon;275static Window root, wmcheckwin;276277/* configuration, allows nested code to access above variables */278#include "config.h"279280/* compile-time check if all tags fit into an unsigned int bit array. */281struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };282283/* function implementations */284void285applyrules(Client *c)286{287 const char *class, *instance;288 unsigned int i;289 const Rule *r;290 Monitor *m;291 XClassHint ch = { NULL, NULL };292293 /* rule matching */294 c->isfloating = 0;295 c->tags = 0;296 XGetClassHint(dpy, c->win, &ch);297 class = ch.res_class ? ch.res_class : broken;298 instance = ch.res_name ? ch.res_name : broken;299300 for (i = 0; i < LENGTH(rules); i++) {301 r = &rules[i];302 if ((!r->title || strstr(c->name, r->title))303 && (!r->class || strstr(class, r->class))304 && (!r->instance || strstr(instance, r->instance)))305 {306 c->isfloating = r->isfloating;307 c->tags |= r->tags;308 for (m = mons; m && m->num != r->monitor; m = m->next);309 if (m)310 c->mon = m;311 }312 }313 if (ch.res_class)314 XFree(ch.res_class);315 if (ch.res_name)316 XFree(ch.res_name);317 c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags];318}319320int321applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact)322{323 int baseismin;324 Monitor *m = c->mon;325326 /* set minimum possible */327 *w = MAX(1, *w);328 *h = MAX(1, *h);329 if (interact) {330 if (*x > sw)331 *x = sw - WIDTH(c);332 if (*y > sh)333 *y = sh - HEIGHT(c);334 if (*x + *w + 2 * c->bw < 0)335 *x = 0;336 if (*y + *h + 2 * c->bw < 0)337 *y = 0;338 } else {339 if (*x >= m->wx + m->ww)340 *x = m->wx + m->ww - WIDTH(c);341 if (*y >= m->wy + m->wh)342 *y = m->wy + m->wh - HEIGHT(c);343 if (*x + *w + 2 * c->bw <= m->wx)344 *x = m->wx;345 if (*y + *h + 2 * c->bw <= m->wy)346 *y = m->wy;347 }348 if (*h < bh)349 *h = bh;350 if (*w < bh)351 *w = bh;352 if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {353 if (!c->hintsvalid)354 updatesizehints(c);355 /* see last two sentences in ICCCM 4.1.2.3 */356 baseismin = c->basew == c->minw && c->baseh == c->minh;357 if (!baseismin) { /* temporarily remove base dimensions */358 *w -= c->basew;359 *h -= c->baseh;360 }361 /* adjust for aspect limits */362 if (c->mina > 0 && c->maxa > 0) {363 if (c->maxa < (float)*w / *h)364 *w = *h * c->maxa + 0.5;365 else if (c->mina < (float)*h / *w)366 *h = *w * c->mina + 0.5;367 }368 if (baseismin) { /* increment calculation requires this */369 *w -= c->basew;370 *h -= c->baseh;371 }372 /* adjust for increment value */373 if (c->incw)374 *w -= *w % c->incw;375 if (c->inch)376 *h -= *h % c->inch;377 /* restore base dimensions */378 *w = MAX(*w + c->basew, c->minw);379 *h = MAX(*h + c->baseh, c->minh);380 if (c->maxw)381 *w = MIN(*w, c->maxw);382 if (c->maxh)383 *h = MIN(*h, c->maxh);384 }385 return *x != c->x || *y != c->y || *w != c->w || *h != c->h;386}387388void389arrange(Monitor *m)390{391 if (m)392 showhide(m->stack);393 else for (m = mons; m; m = m->next)394 showhide(m->stack);395 if (m) {396 arrangemon(m);397 restack(m);398 } else for (m = mons; m; m = m->next)399 arrangemon(m);400}401402void403arrangemon(Monitor *m)404{405 strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol);406 if (m->lt[m->sellt]->arrange)407 m->lt[m->sellt]->arrange(m);408}409410void411attach(Client *c)412{413 c->next = c->mon->clients;414 c->mon->clients = c;415}416417void418attachstack(Client *c)419{420 c->snext = c->mon->stack;421 c->mon->stack = c;422}423424void425buttonpress(XEvent *e)426{427 unsigned int i, x, click;428 Arg arg = {0};429 Client *c;430 Monitor *m;431 XButtonPressedEvent *ev = &e->xbutton;432433 click = ClkRootWin;434 /* focus monitor if necessary */435 if ((m = wintomon(ev->window)) && m != selmon) {436 unfocus(selmon->sel, 1);437 selmon = m;438 focus(NULL);439 }440 if (ev->window == selmon->barwin) {441 i = x = 0;442 do443 x += TEXTW(tags[i]);444 while (ev->x >= x && ++i < LENGTH(tags));445 if (i < LENGTH(tags)) {446 click = ClkTagBar;447 arg.ui = 1 << i;448 } else if (ev->x < x + TEXTW(selmon->ltsymbol))449 click = ClkLtSymbol;450 else if (ev->x > selmon->ww - (int)TEXTW(stext))451 click = ClkStatusText;452 else453 click = ClkWinTitle;454 } else if ((c = wintoclient(ev->window))) {455 focus(c);456 restack(selmon);457 XAllowEvents(dpy, ReplayPointer, CurrentTime);458 click = ClkClientWin;459 }460 for (i = 0; i < LENGTH(buttons); i++)461 if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button462 && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state))463 buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg);464}465466void467checkotherwm(void)468{469 xerrorxlib = XSetErrorHandler(xerrorstart);470 /* this causes an error if some other window manager is running */471 XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask);472 XSync(dpy, False);473 XSetErrorHandler(xerror);474 XSync(dpy, False);475}476477void478cleanup(void)479{480 Arg a = {.ui = ~0};481 Layout foo = { "", NULL };482 Monitor *m;483 size_t i;484485 view(&a);486 selmon->lt[selmon->sellt] = &foo;487 for (m = mons; m; m = m->next)488 while (m->stack)489 unmanage(m->stack, 0);490 XUngrabKey(dpy, AnyKey, AnyModifier, root);491 while (mons)492 cleanupmon(mons);493 for (i = 0; i < CurLast; i++)494 drw_cur_free(drw, cursor[i]);495 for (i = 0; i < LENGTH(colors); i++)496 free(scheme[i]);497 free(scheme);498 XDestroyWindow(dpy, wmcheckwin);499 drw_free(drw);500 XSync(dpy, False);501 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);502 XDeleteProperty(dpy, root, netatom[NetActiveWindow]);503}504505void506cleanupmon(Monitor *mon)507{508 Monitor *m;509510 if (mon == mons)511 mons = mons->next;512 else {513 for (m = mons; m && m->next != mon; m = m->next);514 m->next = mon->next;515 }516 XUnmapWindow(dpy, mon->barwin);517 XDestroyWindow(dpy, mon->barwin);518 free(mon);519}520521void522clientmessage(XEvent *e)523{524 XClientMessageEvent *cme = &e->xclient;525 Client *c = wintoclient(cme->window);526527 if (!c)528 return;529 if (cme->message_type == netatom[NetWMState]) {530 if (cme->data.l[1] == netatom[NetWMFullscreen]531 || cme->data.l[2] == netatom[NetWMFullscreen])532 setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */533 || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen)));534 } else if (cme->message_type == netatom[NetActiveWindow]) {535 if (c != selmon->sel && !c->isurgent)536 seturgent(c, 1);537 }538}539540void541configure(Client *c)542{543 XConfigureEvent ce;544545 ce.type = ConfigureNotify;546 ce.display = dpy;547 ce.event = c->win;548 ce.window = c->win;549 ce.x = c->x;550 ce.y = c->y;551 ce.width = c->w;552 ce.height = c->h;553 ce.border_width = c->bw;554 ce.above = None;555 ce.override_redirect = False;556 XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);557}558559void560configurenotify(XEvent *e)561{562 Monitor *m;563 Client *c;564 XConfigureEvent *ev = &e->xconfigure;565 int dirty;566567 /* TODO: updategeom handling sucks, needs to be simplified */568 if (ev->window == root) {569 dirty = (sw != ev->width || sh != ev->height);570 sw = ev->width;571 sh = ev->height;572 if (updategeom() || dirty) {573 drw_resize(drw, sw, bh);574 updatebars();575 for (m = mons; m; m = m->next) {576 for (c = m->clients; c; c = c->next)577 if (c->isfullscreen)578 resizeclient(c, m->mx, m->my, m->mw, m->mh);579 XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);580 }581 focus(NULL);582 arrange(NULL);583 }584 }585}586587void588configurerequest(XEvent *e)589{590 Client *c;591 Monitor *m;592 XConfigureRequestEvent *ev = &e->xconfigurerequest;593 XWindowChanges wc;594595 if ((c = wintoclient(ev->window))) {596 if (ev->value_mask & CWBorderWidth)597 c->bw = ev->border_width;598 else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) {599 m = c->mon;600 if (ev->value_mask & CWX) {601 c->oldx = c->x;602 c->x = m->mx + ev->x;603 }604 if (ev->value_mask & CWY) {605 c->oldy = c->y;606 c->y = m->my + ev->y;607 }608 if (ev->value_mask & CWWidth) {609 c->oldw = c->w;610 c->w = ev->width;611 }612 if (ev->value_mask & CWHeight) {613 c->oldh = c->h;614 c->h = ev->height;615 }616 if ((c->x + c->w) > m->mx + m->mw && c->isfloating)617 c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */618 if ((c->y + c->h) > m->my + m->mh && c->isfloating)619 c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */620 if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight)))621 configure(c);622 if (ISVISIBLE(c))623 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);624 } else625 configure(c);626 } else {627 wc.x = ev->x;628 wc.y = ev->y;629 wc.width = ev->width;630 wc.height = ev->height;631 wc.border_width = ev->border_width;632 wc.sibling = ev->above;633 wc.stack_mode = ev->detail;634 XConfigureWindow(dpy, ev->window, ev->value_mask, &wc);635 }636 XSync(dpy, False);637}638639Monitor *640createmon(void)641{642 Monitor *m;643644 m = ecalloc(1, sizeof(Monitor));645 m->tagset[0] = m->tagset[1] = 1;646 m->mfact = mfact;647 m->nmaster = nmaster;648 m->showbar = showbar;649 m->topbar = topbar;650 m->lt[0] = &layouts[0];651 m->lt[1] = &layouts[1 % LENGTH(layouts)];652 strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);653 return m;654}655656void657destroynotify(XEvent *e)658{659 Client *c;660 XDestroyWindowEvent *ev = &e->xdestroywindow;661662 if ((c = wintoclient(ev->window)))663 unmanage(c, 1);664}665666void667detach(Client *c)668{669 Client **tc;670671 for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next);672 *tc = c->next;673}674675void676detachstack(Client *c)677{678 Client **tc, *t;679680 for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext);681 *tc = c->snext;682683 if (c == c->mon->sel) {684 for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext);685 c->mon->sel = t;686 }687}688689Monitor *690dirtomon(int dir)691{692 Monitor *m = NULL;693694 if (dir > 0) {695 if (!(m = selmon->next))696 m = mons;697 } else if (selmon == mons)698 for (m = mons; m->next; m = m->next);699 else700 for (m = mons; m->next != selmon; m = m->next);701 return m;702}703704void705drawbar(Monitor *m)706{707 int x, w, tw = 0;708 int boxs = drw->fonts->h / 9;709 int boxw = drw->fonts->h / 6 + 2;710 unsigned int i, occ = 0, urg = 0;711 Client *c;712713 if (!m->showbar)714 return;715716 /* draw status first so it can be overdrawn by tags later */717 if (m == selmon) { /* status is only drawn on selected monitor */718 drw_setscheme(drw, scheme[SchemeNorm]);719 tw = TEXTW(stext) - lrpad / 2; /* no right padding so status text hugs the corner */720 drw_text(drw, m->ww - tw, 0, tw, bh, lrpad / 2 - 2, stext, 0);721 }722723 for (c = m->clients; c; c = c->next) {724 occ |= c->tags;725 if (c->isurgent)726 urg |= c->tags;727 }728 x = 0;729 for (i = 0; i < LENGTH(tags); i++) {730 w = TEXTW(tags[i]);731 drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]);732 drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i);733 if (occ & 1 << i)734 drw_rect(drw, x + boxs, boxs, boxw, boxw,735 m == selmon && selmon->sel && selmon->sel->tags & 1 << i,736 urg & 1 << i);737 x += w;738 }739 w = TEXTW(m->ltsymbol);740 drw_setscheme(drw, scheme[SchemeNorm]);741 x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);742743 if ((w = m->ww - tw - x) > bh) {744 if (m->sel) {745 drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);746 drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);747 if (m->sel->isfloating)748 drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0);749 } else {750 drw_setscheme(drw, scheme[SchemeNorm]);751 drw_rect(drw, x, 0, w, bh, 1, 1);752 }753 }754 drw_map(drw, m->barwin, 0, 0, m->ww, bh);755}756757void758drawbars(void)759{760 Monitor *m;761762 for (m = mons; m; m = m->next)763 drawbar(m);764}765766void767enternotify(XEvent *e)768{769 Client *c;770 Monitor *m;771 XCrossingEvent *ev = &e->xcrossing;772773 if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root)774 return;775 c = wintoclient(ev->window);776 m = c ? c->mon : wintomon(ev->window);777 if (m != selmon) {778 unfocus(selmon->sel, 1);779 selmon = m;780 } else if (!c || c == selmon->sel)781 return;782 focus(c);783}784785void786expose(XEvent *e)787{788 Monitor *m;789 XExposeEvent *ev = &e->xexpose;790791 if (ev->count == 0 && (m = wintomon(ev->window)))792 drawbar(m);793}794795void796focus(Client *c)797{798 if (!c || !ISVISIBLE(c))799 for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext);800 if (selmon->sel && selmon->sel != c)801 unfocus(selmon->sel, 0);802 if (c) {803 if (c->mon != selmon)804 selmon = c->mon;805 if (c->isurgent)806 seturgent(c, 0);807 detachstack(c);808 attachstack(c);809 grabbuttons(c, 1);810 XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);811 setfocus(c);812 } else {813 XSetInputFocus(dpy, selmon->barwin, RevertToPointerRoot, CurrentTime);814 XDeleteProperty(dpy, root, netatom[NetActiveWindow]);815 }816 selmon->sel = c;817 drawbars();818}819820/* there are some broken focus acquiring clients needing extra handling */821void822focusin(XEvent *e)823{824 XFocusChangeEvent *ev = &e->xfocus;825826 if (selmon->sel && ev->window != selmon->sel->win)827 setfocus(selmon->sel);828}829830void831focusmon(const Arg *arg)832{833 Monitor *m;834835 if (!mons->next)836 return;837 if ((m = dirtomon(arg->i)) == selmon)838 return;839 unfocus(selmon->sel, 0);840 selmon = m;841 focus(NULL);842}843844void845focusstack(const Arg *arg)846{847 Client *c = NULL, *i;848849 if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen))850 return;851 if (arg->i > 0) {852 for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next);853 if (!c)854 for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next);855 } else {856 for (i = selmon->clients; i != selmon->sel; i = i->next)857 if (ISVISIBLE(i))858 c = i;859 if (!c)860 for (; i; i = i->next)861 if (ISVISIBLE(i))862 c = i;863 }864 if (c) {865 focus(c);866 restack(selmon);867 }868}869870Atom871getatomprop(Client *c, Atom prop)872{873 int di;874 unsigned long dl;875 unsigned char *p = NULL;876 Atom da, atom = None;877878 if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,879 &da, &di, &dl, &dl, &p) == Success && p) {880 atom = *(Atom *)p;881 XFree(p);882 }883 return atom;884}885886int887getrootptr(int *x, int *y)888{889 int di;890 unsigned int dui;891 Window dummy;892893 return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui);894}895896long897getstate(Window w)898{899 int format;900 long result = -1;901 unsigned char *p = NULL;902 unsigned long n, extra;903 Atom real;904905 if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState],906 &real, &format, &n, &extra, (unsigned char **)&p) != Success)907 return -1;908 if (n != 0)909 result = *p;910 XFree(p);911 return result;912}913914int915gettextprop(Window w, Atom atom, char *text, unsigned int size)916{917 char **list = NULL;918 int n;919 XTextProperty name;920921 if (!text || size == 0)922 return 0;923 text[0] = '\0';924 if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems)925 return 0;926 if (name.encoding == XA_STRING) {927 strncpy(text, (char *)name.value, size - 1);928 } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) {929 strncpy(text, *list, size - 1);930 XFreeStringList(list);931 }932 text[size - 1] = '\0';933 XFree(name.value);934 return 1;935}936937void938grabbuttons(Client *c, int focused)939{940 updatenumlockmask();941 {942 unsigned int i, j;943 unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };944 XUngrabButton(dpy, AnyButton, AnyModifier, c->win);945 if (!focused)946 XGrabButton(dpy, AnyButton, AnyModifier, c->win, False,947 BUTTONMASK, GrabModeSync, GrabModeSync, None, None);948 for (i = 0; i < LENGTH(buttons); i++)949 if (buttons[i].click == ClkClientWin)950 for (j = 0; j < LENGTH(modifiers); j++)951 XGrabButton(dpy, buttons[i].button,952 buttons[i].mask | modifiers[j],953 c->win, False, BUTTONMASK,954 GrabModeAsync, GrabModeSync, None, None);955 }956}957958void959grabkeys(void)960{961 updatenumlockmask();962 {963 unsigned int i, j;964 unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };965 KeyCode code;966967 XUngrabKey(dpy, AnyKey, AnyModifier, root);968 for (i = 0; i < LENGTH(keys); i++)969 if ((code = XKeysymToKeycode(dpy, keys[i].keysym)))970 for (j = 0; j < LENGTH(modifiers); j++)971 XGrabKey(dpy, code, keys[i].mod | modifiers[j], root,972 True, GrabModeAsync, GrabModeAsync);973 }974}975976void977incnmaster(const Arg *arg)978{979 int n, nmaster;980 Client *c;981982 if ((c = selmon->sel) && c->isfullscreen)983 setfullscreen(c, False); /* focus is lost without this */984985 n = 0;986 for (c = selmon->clients; c; c = c->next)987 if (ISVISIBLE(c)) n++;988989 nmaster = MAX(selmon->nmaster + arg->i, 0);990 if (nmaster > n)991 nmaster = n;992993 selmon->nmaster = nmaster;994 arrange(selmon);995}996997#ifdef XINERAMA998static int999isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info)1000{1001 while (n--)1002 if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org1003 && unique[n].width == info->width && unique[n].height == info->height)1004 return 0;1005 return 1;1006}1007#endif /* XINERAMA */10081009void1010keypress(XEvent *e)1011{1012 unsigned int i;1013 KeySym keysym;1014 XKeyEvent *ev;10151016 ev = &e->xkey;1017 keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0);1018 for (i = 0; i < LENGTH(keys); i++)1019 if (keysym == keys[i].keysym1020 && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)1021 && keys[i].func)1022 keys[i].func(&(keys[i].arg));1023}10241025void1026killclient(const Arg *arg)1027{1028 if (!selmon->sel)1029 return;1030 if (!sendevent(selmon->sel, wmatom[WMDelete])) {1031 XGrabServer(dpy);1032 XSetErrorHandler(xerrordummy);1033 XSetCloseDownMode(dpy, DestroyAll);1034 XKillClient(dpy, selmon->sel->win);1035 XSync(dpy, False);1036 XSetErrorHandler(xerror);1037 XUngrabServer(dpy);1038 }1039}10401041void1042manage(Window w, XWindowAttributes *wa)1043{1044 Client *sc, *c, *t = NULL;1045 Window trans = None;1046 XWindowChanges wc;1047 Atom wtype;10481049 c = ecalloc(1, sizeof(Client));1050 c->win = w;10511052 wtype = getatomprop(c, netatom[NetWMWindowType]);1053 if ((sc = selmon->sel) && sc->isfullscreen)1054 setfullscreen(sc, False);10551056 /* geometry */1057 c->x = c->oldx = wa->x;1058 c->y = c->oldy = wa->y;1059 if (wtype == netatom[NetWMWindowTypeDialog]) {1060 c->w = c->oldw = MIN(wa->width, MAXWW(selmon));1061 c->h = c->oldh = MIN(wa->height, MAXWH(selmon));1062 } else {1063 c->w = c->oldw = wa->width;1064 c->h = c->oldh = wa->height;1065 }1066 c->oldbw = wa->border_width;10671068 updatetitle(c);1069 if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {1070 c->mon = t->mon;1071 c->tags = t->tags;1072 } else {1073 c->mon = selmon;1074 applyrules(c);1075 }10761077 if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww)1078 c->x = c->mon->wx + c->mon->ww - WIDTH(c);1079 if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh)1080 c->y = c->mon->wy + c->mon->wh - HEIGHT(c);1081 c->x = MAX(c->x, c->mon->wx);1082 c->y = MAX(c->y, c->mon->wy);1083 c->bw = borderpx;10841085 wc.border_width = c->bw;1086 XConfigureWindow(dpy, w, CWBorderWidth, &wc);1087 XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel);1088 configure(c); /* propagates border_width, if size doesn't change */1089 updatewindowtype(c);1090 updatesizehints(c);1091 updatewmhints(c);1092 XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);1093 grabbuttons(c, 0);1094 if (!c->isfloating)1095 c->isfloating = c->oldstate = trans != None || c->isfixed;1096 if (c->isfloating)1097 XRaiseWindow(dpy, c->win);1098 attach(c);1099 attachstack(c);1100 XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend,1101 (unsigned char *) &(c->win), 1);1102 XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */1103 setclientstate(c, NormalState);1104 if (c->mon == selmon)1105 unfocus(selmon->sel, 0);1106 c->mon->sel = c;1107 arrange(c->mon);1108 XMapWindow(dpy, c->win);1109 focus(NULL);1110}11111112void1113mappingnotify(XEvent *e)1114{1115 XMappingEvent *ev = &e->xmapping;11161117 XRefreshKeyboardMapping(ev);1118 if (ev->request == MappingKeyboard)1119 grabkeys();1120}11211122void1123maprequest(XEvent *e)1124{1125 static XWindowAttributes wa;1126 XMapRequestEvent *ev = &e->xmaprequest;11271128 if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect)1129 return;1130 if (!wintoclient(ev->window))1131 manage(ev->window, &wa);1132}11331134void1135monocle(Monitor *m)1136{1137 unsigned int n = 0;1138 Client *c;11391140 for (c = m->clients; c; c = c->next)1141 if (ISVISIBLE(c))1142 n++;1143 if (n > 0) /* override layout symbol */1144 snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n);1145 for (c = nexttiled(m->clients); c; c = nexttiled(c->next))1146 resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0);1147}11481149void1150motionnotify(XEvent *e)1151{1152 static Monitor *mon = NULL;1153 Monitor *m;1154 XMotionEvent *ev = &e->xmotion;11551156 if (ev->window != root)1157 return;1158 if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) {1159 unfocus(selmon->sel, 1);1160 selmon = m;1161 focus(NULL);1162 }1163 mon = m;1164}11651166void1167movemouse(const Arg *arg)1168{1169 int x, y, ocx, ocy, nx, ny;1170 Client *c;1171 Monitor *m;1172 XEvent ev;1173 Time lasttime = 0;11741175 if (!(c = selmon->sel) || !c->isfloating)1176 return;1177 restack(selmon);1178 ocx = c->x;1179 ocy = c->y;1180 if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,1181 None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess)1182 return;1183 if (!getrootptr(&x, &y))1184 return;1185 do {1186 XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);1187 switch(ev.type) {1188 case ConfigureRequest:1189 case Expose:1190 case MapRequest:1191 handler[ev.type](&ev);1192 break;1193 case MotionNotify:1194 if ((ev.xmotion.time - lasttime) <= (1000 / 60))1195 continue;1196 lasttime = ev.xmotion.time;11971198 nx = ocx + (ev.xmotion.x - x);1199 ny = ocy + (ev.xmotion.y - y);1200 if (abs(selmon->wx - nx) < snap)1201 nx = selmon->wx;1202 else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap)1203 nx = selmon->wx + selmon->ww - WIDTH(c);1204 if (abs(selmon->wy - ny) < snap)1205 ny = selmon->wy;1206 else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap)1207 ny = selmon->wy + selmon->wh - HEIGHT(c);1208 if (!c->isfloating && selmon->lt[selmon->sellt]->arrange1209 && (abs(nx - c->x) > snap || abs(ny - c->y) > snap))1210 togglefloating(NULL);1211 if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)1212 resize(c, nx, ny, c->w, c->h, 1);1213 break;1214 }1215 } while (ev.type != ButtonRelease);1216 XUngrabPointer(dpy, CurrentTime);1217 if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {1218 selmon = m;1219 sendmon(c, m);1220 }1221}12221223void1224movestack(const Arg *arg) {1225 Client **c, **at = NULL, **lt = NULL, **to = NULL;12261227 if(!selmon->sel || selmon->sel->isfloating)1228 return;1229 for(c = &selmon->clients; *c; c = &(*c)->next) {1230 if(*c == selmon->sel)1231 at = c;1232 else if(ISVISIBLE(*c) && !(*c)->isfloating) {1233 lt = c;1234 if(arg->i < 0 && !at)1235 to = c;1236 if(arg->i > 0 && at && !to)1237 to = &(*c)->next;1238 }1239 }1240 if(!lt)1241 /* no other client tiled */1242 return;1243 if(!to)1244 /* wrap around */1245 to = arg->i > 0 ? &selmon->clients : &(*lt)->next;1246 *at = selmon->sel->next;1247 selmon->sel->next = *to;1248 *to = selmon->sel;1249 arrange(selmon);1250}12511252Client *1253nexttiled(Client *c)1254{1255 for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next);1256 return c;1257}12581259void1260pop(Client *c)1261{1262 detach(c);1263 attach(c);1264 focus(c);1265 arrange(c->mon);1266}12671268void1269propertynotify(XEvent *e)1270{1271 Client *c;1272 Window trans;1273 XPropertyEvent *ev = &e->xproperty;12741275 if ((ev->window == root) && (ev->atom == XA_WM_NAME))1276 updatestatus();1277 else if (ev->state == PropertyDelete)1278 return; /* ignore */1279 else if ((c = wintoclient(ev->window))) {1280 switch(ev->atom) {1281 default: break;1282 case XA_WM_TRANSIENT_FOR:1283 if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) &&1284 (c->isfloating = (wintoclient(trans)) != NULL))1285 arrange(c->mon);1286 break;1287 case XA_WM_NORMAL_HINTS:1288 c->hintsvalid = 0;1289 break;1290 case XA_WM_HINTS:1291 updatewmhints(c);1292 drawbars();1293 break;1294 }1295 if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) {1296 updatetitle(c);1297 if (c == c->mon->sel)1298 drawbar(c->mon);1299 }1300 if (ev->atom == netatom[NetWMWindowType])1301 updatewindowtype(c);1302 }1303}13041305void1306quit(const Arg *arg)1307{1308 running = 0;1309}13101311Monitor *1312recttomon(int x, int y, int w, int h)1313{1314 Monitor *m, *r = selmon;1315 int a, area = 0;13161317 for (m = mons; m; m = m->next)1318 if ((a = INTERSECT(x, y, w, h, m)) > area) {1319 area = a;1320 r = m;1321 }1322 return r;1323}13241325void1326resize(Client *c, int x, int y, int w, int h, int interact)1327{1328 if (applysizehints(c, &x, &y, &w, &h, interact))1329 resizeclient(c, x, y, w, h);1330}13311332void1333resizeclient(Client *c, int x, int y, int w, int h)1334{1335 XWindowChanges wc;13361337 c->oldx = c->x; c->x = wc.x = x;1338 c->oldy = c->y; c->y = wc.y = y;1339 c->oldw = c->w; c->w = wc.width = w;1340 c->oldh = c->h; c->h = wc.height = h;1341 wc.border_width = c->bw;1342 XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc);1343 configure(c);1344 XSync(dpy, False);1345}13461347void1348resizemouse(const Arg *arg)1349{1350 int ocx, ocy, nw, nh;1351 Client *c;1352 Monitor *m;1353 XEvent ev;1354 Time lasttime = 0;13551356 if (!(c = selmon->sel))1357 return;1358 if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */1359 return;1360 restack(selmon);1361 ocx = c->x;1362 ocy = c->y;1363 if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,1364 None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)1365 return;1366 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);1367 do {1368 XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev);1369 switch(ev.type) {1370 case ConfigureRequest:1371 case Expose:1372 case MapRequest:1373 handler[ev.type](&ev);1374 break;1375 case MotionNotify:1376 if ((ev.xmotion.time - lasttime) <= (1000 / 60))1377 continue;1378 lasttime = ev.xmotion.time;13791380 nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1);1381 nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1);1382 if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww1383 && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh)1384 {1385 if (!c->isfloating && selmon->lt[selmon->sellt]->arrange1386 && (abs(nw - c->w) > snap || abs(nh - c->h) > snap))1387 togglefloating(NULL);1388 }1389 if (!selmon->lt[selmon->sellt]->arrange || c->isfloating)1390 resize(c, c->x, c->y, nw, nh, 1);1391 break;1392 }1393 } while (ev.type != ButtonRelease);1394 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1);1395 XUngrabPointer(dpy, CurrentTime);1396 while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));1397 if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) {1398 sendmon(c, m);1399 selmon = m;1400 focus(NULL);1401 }1402}14031404void1405restack(Monitor *m)1406{1407 Client *c;1408 XEvent ev;1409 XWindowChanges wc;14101411 drawbar(m);1412 if (!m->sel)1413 return;1414 if (m->sel->isfloating || !m->lt[m->sellt]->arrange)1415 XRaiseWindow(dpy, m->sel->win);1416 if (m->lt[m->sellt]->arrange) {1417 wc.stack_mode = Below;1418 wc.sibling = m->barwin;1419 for (c = m->stack; c; c = c->snext)1420 if (!c->isfloating && ISVISIBLE(c)) {1421 XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc);1422 wc.sibling = c->win;1423 }1424 }1425 XSync(dpy, False);1426 while (XCheckMaskEvent(dpy, EnterWindowMask, &ev));1427}14281429void1430run(void)1431{1432 XEvent ev;1433 /* main event loop */1434 XSync(dpy, False);1435 while (running && !XNextEvent(dpy, &ev))1436 if (handler[ev.type])1437 handler[ev.type](&ev); /* call handler */1438}14391440void1441scan(void)1442{1443 unsigned int i, num;1444 Window d1, d2, *wins = NULL;1445 XWindowAttributes wa;14461447 if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) {1448 for (i = 0; i < num; i++) {1449 if (!XGetWindowAttributes(dpy, wins[i], &wa)1450 || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1))1451 continue;1452 if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)1453 manage(wins[i], &wa);1454 }1455 for (i = 0; i < num; i++) { /* now the transients */1456 if (!XGetWindowAttributes(dpy, wins[i], &wa))1457 continue;1458 if (XGetTransientForHint(dpy, wins[i], &d1)1459 && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState))1460 manage(wins[i], &wa);1461 }1462 if (wins)1463 XFree(wins);1464 }1465}14661467void1468sendmon(Client *c, Monitor *m)1469{1470 if (c->mon == m)1471 return;1472 unfocus(c, 1);1473 detach(c);1474 detachstack(c);1475 c->mon = m;1476 c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */1477 attach(c);1478 attachstack(c);1479 focus(NULL);1480 arrange(NULL);1481}14821483void1484setclientstate(Client *c, long state)1485{1486 long data[] = { state, None };14871488 XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32,1489 PropModeReplace, (unsigned char *)data, 2);1490}14911492int1493sendevent(Client *c, Atom proto)1494{1495 int n;1496 Atom *protocols;1497 int exists = 0;1498 XEvent ev;14991500 if (XGetWMProtocols(dpy, c->win, &protocols, &n)) {1501 while (!exists && n--)1502 exists = protocols[n] == proto;1503 XFree(protocols);1504 }1505 if (exists) {1506 ev.type = ClientMessage;1507 ev.xclient.window = c->win;1508 ev.xclient.message_type = wmatom[WMProtocols];1509 ev.xclient.format = 32;1510 ev.xclient.data.l[0] = proto;1511 ev.xclient.data.l[1] = CurrentTime;1512 XSendEvent(dpy, c->win, False, NoEventMask, &ev);1513 }1514 return exists;1515}15161517void1518setfocus(Client *c)1519{1520 if (!c->neverfocus) {1521 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);1522 XChangeProperty(dpy, root, netatom[NetActiveWindow],1523 XA_WINDOW, 32, PropModeReplace,1524 (unsigned char *) &(c->win), 1);1525 }1526 sendevent(c, wmatom[WMTakeFocus]);1527}15281529void1530setfullscreen(Client *c, int fullscreen)1531{1532 if (fullscreen && !c->isfullscreen) {1533 XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,1534 PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1);1535 c->isfullscreen = 1;1536 c->oldstate = c->isfloating;1537 c->oldbw = c->bw;1538 c->bw = 0;1539 c->isfloating = 1;1540 resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh);1541 XRaiseWindow(dpy, c->win);1542 } else if (!fullscreen && c->isfullscreen){1543 XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32,1544 PropModeReplace, (unsigned char*)0, 0);1545 c->isfullscreen = 0;1546 c->isfloating = c->oldstate;1547 c->bw = c->oldbw;1548 c->x = c->oldx;1549 c->y = c->oldy;1550 c->w = c->oldw;1551 c->h = c->oldh;1552 resizeclient(c, c->x, c->y, c->w, c->h);1553 arrange(c->mon);1554 }1555}15561557void1558setlayout(const Arg *arg)1559{1560 if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])1561 selmon->sellt ^= 1;1562 if (arg && arg->v)1563 selmon->lt[selmon->sellt] = (Layout *)arg->v;1564 strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol);1565 if (selmon->sel)1566 arrange(selmon);1567 else1568 drawbar(selmon);1569}15701571/* arg > 1.0 will set mfact absolutely */1572void1573setmfact(const Arg *arg)1574{1575 float f;15761577 if (!arg || !selmon->lt[selmon->sellt]->arrange)1578 return;1579 f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;1580 if (f < 0.05 || f > 0.95)1581 return;1582 selmon->mfact = f;1583 arrange(selmon);1584}15851586void1587setup(void)1588{1589 int i;1590 XSetWindowAttributes wa;1591 Atom utf8string;15921593 /* clean up any zombies immediately */1594 sigchld(0);15951596 /* init screen */1597 screen = DefaultScreen(dpy);1598 sw = DisplayWidth(dpy, screen);1599 sh = DisplayHeight(dpy, screen);1600 root = RootWindow(dpy, screen);1601 drw = drw_create(dpy, screen, root, sw, sh);1602 if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))1603 die("no fonts could be loaded.");1604 lrpad = drw->fonts->h;1605 bh = drw->fonts->h + 2;1606 updategeom();1607 /* init atoms */1608 utf8string = XInternAtom(dpy, "UTF8_STRING", False);1609 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);1610 wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);1611 wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False);1612 wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);1613 netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);1614 netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);1615 netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);1616 netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);1617 netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);1618 netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False);1619 netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);1620 netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);1621 netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);1622 /* init cursors */1623 cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);1624 cursor[CurResize] = drw_cur_create(drw, XC_sizing);1625 cursor[CurMove] = drw_cur_create(drw, XC_fleur);1626 /* init appearance */1627 scheme = ecalloc(LENGTH(colors), sizeof(Clr *));1628 for (i = 0; i < LENGTH(colors); i++)1629 scheme[i] = drw_scm_create(drw, colors[i], 3);1630 /* init bars */1631 updatebars();1632 updatestatus();1633 /* supporting window for NetWMCheck */1634 wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0);1635 XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32,1636 PropModeReplace, (unsigned char *) &wmcheckwin, 1);1637 XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8,1638 PropModeReplace, (unsigned char *) "dwm", 3);1639 XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32,1640 PropModeReplace, (unsigned char *) &wmcheckwin, 1);1641 /* EWMH support per view */1642 XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,1643 PropModeReplace, (unsigned char *) netatom, NetLast);1644 XDeleteProperty(dpy, root, netatom[NetClientList]);1645 /* select events */1646 wa.cursor = cursor[CurNormal]->cursor;1647 wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask1648 |ButtonPressMask|PointerMotionMask|EnterWindowMask1649 |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask;1650 XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa);1651 XSelectInput(dpy, root, wa.event_mask);1652 grabkeys();1653 focus(NULL);1654}16551656void1657seturgent(Client *c, int urg)1658{1659 XWMHints *wmh;16601661 c->isurgent = urg;1662 if (!(wmh = XGetWMHints(dpy, c->win)))1663 return;1664 wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint);1665 XSetWMHints(dpy, c->win, wmh);1666 XFree(wmh);1667}16681669void1670showhide(Client *c)1671{1672 if (!c)1673 return;1674 if (ISVISIBLE(c)) {1675 /* show clients top down */1676 XMoveWindow(dpy, c->win, c->x, c->y);1677 if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen)1678 resize(c, c->x, c->y, c->w, c->h, 0);1679 showhide(c->snext);1680 } else {1681 /* hide clients bottom up */1682 showhide(c->snext);1683 XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y);1684 }1685}16861687void1688sigchld(int unused)1689{1690 if (signal(SIGCHLD, sigchld) == SIG_ERR)1691 die("can't install SIGCHLD handler:");1692 while (0 < waitpid(-1, NULL, WNOHANG));1693}16941695void1696spawn(const Arg *arg)1697{1698 if (fork() == 0) {1699 if (dpy)1700 close(ConnectionNumber(dpy));1701 setsid();1702 execvp(((char **)arg->v)[0], (char **)arg->v);1703 die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]);1704 }1705}17061707void1708tag(const Arg *arg)1709{1710 if (selmon->sel && arg->ui & TAGMASK) {1711 selmon->sel->tags = arg->ui & TAGMASK;1712 focus(NULL);1713 arrange(selmon);1714 }1715}17161717void1718tagmon(const Arg *arg)1719{1720 if (!selmon->sel || !mons->next)1721 return;1722 sendmon(selmon->sel, dirtomon(arg->i));1723}17241725void1726tile(Monitor *m)1727{1728 unsigned int i, n, h, mw, my, ty;1729 Client *c;17301731 for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);1732 if (n == 0)1733 return;17341735 if (n > m->nmaster)1736 mw = m->nmaster ? m->ww * m->mfact : 0;1737 else1738 mw = m->ww - gappx;1739 for (i = 0, my = ty = gappx, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)1740 if (i < m->nmaster) {1741 h = (m->wh - my) / (MIN(n, m->nmaster) - i) - gappx;1742 resize(c, m->wx + gappx, m->wy + my, mw - (2*c->bw) - gappx, h - (2*c->bw), 0);1743 if (my + HEIGHT(c) + gappx < m->wh)1744 my += HEIGHT(c) + gappx;1745 } else {1746 h = (m->wh - ty) / (n - i) - gappx;1747 resize(c, m->wx + mw + gappx, m->wy + ty, m->ww - mw - (2*c->bw) - 2*gappx, h - (2*c->bw), 0);1748 if (ty + HEIGHT(c) + gappx < m->wh)1749 ty += HEIGHT(c) + gappx;1750 }1751}17521753void1754togglebar(const Arg *arg)1755{1756 selmon->showbar = !selmon->showbar;1757 updatebarpos(selmon);1758 XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);1759 arrange(selmon);1760}17611762void1763togglefloating(const Arg *arg)1764{1765 if (!selmon->sel)1766 return;1767 if (selmon->sel->isfullscreen) /* no support for fullscreen windows */1768 return;1769 selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed;1770 if (selmon->sel->isfloating)1771 resize(selmon->sel, selmon->sel->x, selmon->sel->y,1772 selmon->sel->w, selmon->sel->h, 0);1773 arrange(selmon);1774}17751776void1777togglefullscrn(const Arg *arg) {1778 Client *c;17791780 if (!(c = selmon->sel))1781 return;17821783 setfullscreen(c, !c->isfullscreen);1784 if (c->isfullscreen)1785 c->isfloating = False;1786}17871788void1789toggletag(const Arg *arg)1790{1791 unsigned int newtags;17921793 if (!selmon->sel)1794 return;1795 newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);1796 if (newtags) {1797 selmon->sel->tags = newtags;1798 focus(NULL);1799 arrange(selmon);1800 }1801}18021803void1804toggleview(const Arg *arg)1805{1806 unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK);18071808 if (newtagset) {1809 selmon->tagset[selmon->seltags] = newtagset;1810 focus(NULL);1811 arrange(selmon);1812 }1813}18141815void1816unfocus(Client *c, int setfocus)1817{1818 if (!c)1819 return;1820 grabbuttons(c, 0);1821 XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel);1822 if (setfocus) {1823 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime);1824 XDeleteProperty(dpy, root, netatom[NetActiveWindow]);1825 }1826}18271828void1829unmanage(Client *c, int destroyed)1830{1831 Monitor *m = c->mon;1832 XWindowChanges wc;18331834 detach(c);1835 detachstack(c);1836 if (!destroyed) {1837 wc.border_width = c->oldbw;1838 XGrabServer(dpy); /* avoid race conditions */1839 XSetErrorHandler(xerrordummy);1840 XSelectInput(dpy, c->win, NoEventMask);1841 XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */1842 XUngrabButton(dpy, AnyButton, AnyModifier, c->win);1843 setclientstate(c, WithdrawnState);1844 XSync(dpy, False);1845 XSetErrorHandler(xerror);1846 XUngrabServer(dpy);1847 }1848 free(c);1849 focus(NULL);1850 updateclientlist();1851 arrange(m);1852}18531854void1855unmapnotify(XEvent *e)1856{1857 Client *c;1858 XUnmapEvent *ev = &e->xunmap;18591860 if ((c = wintoclient(ev->window))) {1861 if (ev->send_event)1862 setclientstate(c, WithdrawnState);1863 else1864 unmanage(c, 0);1865 }1866}18671868void1869updatebars(void)1870{1871 Monitor *m;1872 XSetWindowAttributes wa = {1873 .override_redirect = True,1874 .background_pixmap = ParentRelative,1875 .event_mask = ButtonPressMask|ExposureMask1876 };1877 XClassHint ch = {"dwm", "dwm"};1878 for (m = mons; m; m = m->next) {1879 if (m->barwin)1880 continue;1881 m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),1882 CopyFromParent, DefaultVisual(dpy, screen),1883 CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);1884 XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);1885 XMapRaised(dpy, m->barwin);1886 XSetClassHint(dpy, m->barwin, &ch);1887 }1888}18891890void1891updatebarpos(Monitor *m)1892{1893 m->wy = m->my;1894 m->wh = m->mh;1895 if (m->showbar) {1896 m->wh -= bh;1897 m->by = m->topbar ? m->wy : m->wy + m->wh;1898 m->wy = m->topbar ? m->wy + bh : m->wy;1899 } else1900 m->by = -bh;1901}19021903void1904updateclientlist()1905{1906 Client *c;1907 Monitor *m;19081909 XDeleteProperty(dpy, root, netatom[NetClientList]);1910 for (m = mons; m; m = m->next)1911 for (c = m->clients; c; c = c->next)1912 XChangeProperty(dpy, root, netatom[NetClientList],1913 XA_WINDOW, 32, PropModeAppend,1914 (unsigned char *) &(c->win), 1);1915}19161917int1918updategeom(void)1919{1920 int dirty = 0;19211922#ifdef XINERAMA1923 if (XineramaIsActive(dpy)) {1924 int i, j, n, nn;1925 Client *c;1926 Monitor *m;1927 XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn);1928 XineramaScreenInfo *unique = NULL;19291930 for (n = 0, m = mons; m; m = m->next, n++);1931 /* only consider unique geometries as separate screens */1932 unique = ecalloc(nn, sizeof(XineramaScreenInfo));1933 for (i = 0, j = 0; i < nn; i++)1934 if (isuniquegeom(unique, j, &info[i]))1935 memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo));1936 XFree(info);1937 nn = j;19381939 /* new monitors if nn > n */1940 for (i = n; i < nn; i++) {1941 for (m = mons; m && m->next; m = m->next);1942 if (m)1943 m->next = createmon();1944 else1945 mons = createmon();1946 }1947 for (i = 0, m = mons; i < nn && m; m = m->next, i++)1948 if (i >= n1949 || unique[i].x_org != m->mx || unique[i].y_org != m->my1950 || unique[i].width != m->mw || unique[i].height != m->mh)1951 {1952 dirty = 1;1953 m->num = i;1954 m->mx = m->wx = unique[i].x_org;1955 m->my = m->wy = unique[i].y_org;1956 m->mw = m->ww = unique[i].width;1957 m->mh = m->wh = unique[i].height;1958 updatebarpos(m);1959 }1960 /* removed monitors if n > nn */1961 for (i = nn; i < n; i++) {1962 for (m = mons; m && m->next; m = m->next);1963 while ((c = m->clients)) {1964 dirty = 1;1965 m->clients = c->next;1966 detachstack(c);1967 c->mon = mons;1968 attach(c);1969 attachstack(c);1970 }1971 if (m == selmon)1972 selmon = mons;1973 cleanupmon(m);1974 }1975 free(unique);1976 } else1977#endif /* XINERAMA */1978 { /* default monitor setup */1979 if (!mons)1980 mons = createmon();1981 if (mons->mw != sw || mons->mh != sh) {1982 dirty = 1;1983 mons->mw = mons->ww = sw;1984 mons->mh = mons->wh = sh;1985 updatebarpos(mons);1986 }1987 }1988 if (dirty) {1989 selmon = mons;1990 selmon = wintomon(root);1991 }1992 return dirty;1993}19941995void1996updatenumlockmask(void)1997{1998 unsigned int i, j;1999 XModifierKeymap *modmap;20002001 numlockmask = 0;2002 modmap = XGetModifierMapping(dpy);2003 for (i = 0; i < 8; i++)2004 for (j = 0; j < modmap->max_keypermod; j++)2005 if (modmap->modifiermap[i * modmap->max_keypermod + j]2006 == XKeysymToKeycode(dpy, XK_Num_Lock))2007 numlockmask = (1 << i);2008 XFreeModifiermap(modmap);2009}20102011void2012updatesizehints(Client *c)2013{2014 long msize;2015 XSizeHints size;20162017 if (!XGetWMNormalHints(dpy, c->win, &size, &msize))2018 /* size is uninitialized, ensure that size.flags aren't used */2019 size.flags = PSize;2020 if (size.flags & PBaseSize) {2021 c->basew = size.base_width;2022 c->baseh = size.base_height;2023 } else if (size.flags & PMinSize) {2024 c->basew = size.min_width;2025 c->baseh = size.min_height;2026 } else2027 c->basew = c->baseh = 0;2028 if (size.flags & PResizeInc) {2029 c->incw = size.width_inc;2030 c->inch = size.height_inc;2031 } else2032 c->incw = c->inch = 0;2033 if (size.flags & PMaxSize) {2034 c->maxw = size.max_width;2035 c->maxh = size.max_height;2036 } else2037 c->maxw = c->maxh = 0;2038 if (size.flags & PMinSize) {2039 c->minw = size.min_width;2040 c->minh = size.min_height;2041 } else if (size.flags & PBaseSize) {2042 c->minw = size.base_width;2043 c->minh = size.base_height;2044 } else2045 c->minw = c->minh = 0;2046 if (size.flags & PAspect) {2047 c->mina = (float)size.min_aspect.y / size.min_aspect.x;2048 c->maxa = (float)size.max_aspect.x / size.max_aspect.y;2049 } else2050 c->maxa = c->mina = 0.0;2051 c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh);2052 c->hintsvalid = 1;2053}20542055void2056updatestatus(void)2057{2058 if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))2059 strcpy(stext, "dwm-"VERSION);2060 drawbar(selmon);2061}20622063void2064updatetitle(Client *c)2065{2066 if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name))2067 gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name);2068 if (c->name[0] == '\0') /* hack to mark broken clients */2069 strcpy(c->name, broken);2070}20712072void2073updatewindowtype(Client *c)2074{2075 Atom state = getatomprop(c, netatom[NetWMState]);2076 Atom wtype = getatomprop(c, netatom[NetWMWindowType]);20772078 if (state == netatom[NetWMFullscreen])2079 setfullscreen(c, 1);2080 if (wtype == netatom[NetWMWindowTypeDialog])2081 c->isfloating = 1;2082}20832084void2085updatewmhints(Client *c)2086{2087 XWMHints *wmh;20882089 if ((wmh = XGetWMHints(dpy, c->win))) {2090 if (c == selmon->sel && wmh->flags & XUrgencyHint) {2091 wmh->flags &= ~XUrgencyHint;2092 XSetWMHints(dpy, c->win, wmh);2093 } else2094 c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0;2095 if (wmh->flags & InputHint)2096 c->neverfocus = !wmh->input;2097 else2098 c->neverfocus = 0;2099 XFree(wmh);2100 }2101}21022103void2104view(const Arg *arg)2105{2106 Client *c;21072108 if ((c = selmon->sel) && c->isfullscreen)2109 setfullscreen(c, False);21102111 if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])2112 return;2113 selmon->seltags ^= 1; /* toggle sel tagset */2114 if (arg->ui & TAGMASK)2115 selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;2116 focus(NULL);2117 arrange(selmon);2118}21192120Client *2121wintoclient(Window w)2122{2123 Client *c;2124 Monitor *m;21252126 for (m = mons; m; m = m->next)2127 for (c = m->clients; c; c = c->next)2128 if (c->win == w)2129 return c;2130 return NULL;2131}21322133Monitor *2134wintomon(Window w)2135{2136 int x, y;2137 Client *c;2138 Monitor *m;21392140 if (w == root && getrootptr(&x, &y))2141 return recttomon(x, y, 1, 1);2142 for (m = mons; m; m = m->next)2143 if (w == m->barwin)2144 return m;2145 if ((c = wintoclient(w)))2146 return c->mon;2147 return selmon;2148}21492150/* There's no way to check accesses to destroyed windows, thus those cases are2151 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs2152 * default error handler, which may call exit. */2153int2154xerror(Display *dpy, XErrorEvent *ee)2155{2156 if (ee->error_code == BadWindow2157 || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch)2158 || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable)2159 || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable)2160 || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable)2161 || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch)2162 || (ee->request_code == X_GrabButton && ee->error_code == BadAccess)2163 || (ee->request_code == X_GrabKey && ee->error_code == BadAccess)2164 || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable))2165 return 0;2166 fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n",2167 ee->request_code, ee->error_code);2168 return xerrorxlib(dpy, ee); /* may call exit */2169}21702171int2172xerrordummy(Display *dpy, XErrorEvent *ee)2173{2174 return 0;2175}21762177/* Startup Error handler to check if another window manager2178 * is already running. */2179int2180xerrorstart(Display *dpy, XErrorEvent *ee)2181{2182 die("dwm: another window manager is already running");2183 return -1;2184}21852186void2187zoom(const Arg *arg)2188{2189 Client *c = selmon->sel;21902191 if (c && c->isfullscreen)2192 setfullscreen(c, False);21932194 if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating)2195 return;2196 if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next)))2197 return;2198 pop(c);2199}22002201int2202main(int argc, char *argv[])2203{2204 if (argc == 2 && !strcmp("-v", argv[1]))2205 die("dwm-"VERSION);2206 else if (argc != 1)2207 die("usage: dwm [-v]");2208 if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())2209 fputs("warning: no locale support\n", stderr);2210 if (!(dpy = XOpenDisplay(NULL)))2211 die("dwm: cannot open display");2212 checkotherwm();2213 setup();2214#ifdef __OpenBSD__2215 if (pledge("stdio rpath proc exec", NULL) == -1)2216 die("pledge");2217#endif /* __OpenBSD__ */2218 scan();2219 run();2220 cleanup();2221 XCloseDisplay(dpy);2222 return EXIT_SUCCESS;2223}