Xinqi Bao's Git
   1 /* See LICENSE file for copyright and license details. 
   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. 
   9  * Calls to fetch an X event from the event queue are blocking.  Due reading 
  10  * status text from standard input, a select()-driven main loop has been 
  11  * implemented which selects for reads on the X connection and STDIN_FILENO to 
  12  * handle all data smoothly. The event handlers of dwm are organized in an 
  13  * array which is accessed whenever a new event has been fetched. This allows 
  14  * event dispatching in O(1) time. 
  16  * Each child of the root window is called a client, except windows which have 
  17  * set the override_redirect flag.  Clients are organized in a global 
  18  * doubly-linked client list, the focus history is remembered through a global 
  19  * stack list. Each client contains an array of Bools of the same size as the 
  20  * global tags array to indicate the tags of a client. 
  22  * Keys and tagging rules are organized as arrays and defined in config.h. 
  24  * To understand everything else, start reading main(). 
  33 #include <sys/select.h> 
  34 #include <sys/types.h> 
  36 #include <X11/cursorfont.h> 
  37 #include <X11/keysym.h> 
  38 #include <X11/Xatom.h> 
  40 #include <X11/Xproto.h> 
  41 #include <X11/Xutil.h> 
  44 #define BUTTONMASK              (ButtonPressMask|ButtonReleaseMask) 
  45 #define CLEANMASK(mask)         (mask & ~(numlockmask|LockMask)) 
  46 #define LENGTH(x)               (sizeof x / sizeof x[0]) 
  48 #define MOUSEMASK               (BUTTONMASK|PointerMotionMask) 
  49 #define DEFGEOM(GEONAME,BX,BY,BW,WX,WY,WW,WH,MX,MY,MW,MH,TX,TY,TW,TH,MOX,MOY,MOW,MOH) \ 
  50 void GEONAME(void) { \ 
  51         bx = (BX); by = (BY); bw = (BW); \ 
  52         wx = (WX); wy = (WY); ww = (WW); wh = (WH); \ 
  53         mx = (MX); my = (MY); mw = (MW); mh = (MH); \ 
  54         tx = (TX); ty = (TY); tw = (TW); th = (TH); \ 
  55         mox = (MOX); moy = (MOY); mow = (MOW); moh = (MOH); \ 
  59 enum { CurNormal
, CurResize
, CurMove
, CurLast 
};        /* cursor */ 
  60 enum { ColBorder
, ColFG
, ColBG
, ColLast 
};              /* color */ 
  61 enum { NetSupported
, NetWMName
, NetLast 
};              /* EWMH atoms */ 
  62 enum { WMProtocols
, WMDelete
, WMName
, WMState
, WMLast 
};/* default atoms */ 
  65 typedef struct Client Client
; 
  69         int basew
, baseh
, incw
, inch
, maxw
, maxh
, minw
, minh
; 
  70         int minax
, maxax
, minay
, maxay
; 
  72         unsigned int bw
, oldbw
; 
  73         Bool isbanned
, isfixed
, isfloating
, isurgent
; 
  83         unsigned long norm
[ColLast
]; 
  84         unsigned long sel
[ColLast
]; 
  94 } DC
; /* draw context */ 
 104         void (*func
)(const char *arg
); 
 110         void (*arrange
)(void); 
 116         const char *instance
; 
 122 /* function declarations */ 
 123 void applyrules(Client 
*c
); 
 125 void attach(Client 
*c
); 
 126 void attachstack(Client 
*c
); 
 128 void buttonpress(XEvent 
*e
); 
 129 void checkotherwm(void); 
 131 void configure(Client 
*c
); 
 132 void configurenotify(XEvent 
*e
); 
 133 void configurerequest(XEvent 
*e
); 
 134 unsigned int counttiled(void); 
 135 void destroynotify(XEvent 
*e
); 
 136 void detach(Client 
*c
); 
 137 void detachstack(Client 
*c
); 
 139 void drawsquare(Bool filled
, Bool empty
, Bool invert
, unsigned long col
[ColLast
]); 
 140 void drawtext(const char *text
, unsigned long col
[ColLast
], Bool invert
); 
 141 void *emallocz(unsigned int size
); 
 142 void enternotify(XEvent 
*e
); 
 143 void eprint(const char *errstr
, ...); 
 144 void expose(XEvent 
*e
); 
 145 void floating(void); /* default floating layout */ 
 146 void focus(Client 
*c
); 
 147 void focusin(XEvent 
*e
); 
 148 void focusnext(const char *arg
); 
 149 void focusprev(const char *arg
); 
 150 Client 
*getclient(Window w
); 
 151 unsigned long getcolor(const char *colstr
); 
 152 long getstate(Window w
); 
 153 Bool 
gettextprop(Window w
, Atom atom
, char *text
, unsigned int size
); 
 154 void grabbuttons(Client 
*c
, Bool focused
); 
 156 unsigned int idxoftag(const char *t
); 
 157 void initfont(const char *fontstr
); 
 158 Bool 
isoccupied(unsigned int t
); 
 159 Bool 
isprotodel(Client 
*c
); 
 160 Bool 
isurgent(unsigned int t
); 
 161 Bool 
isvisible(Client 
*c
); 
 162 void keypress(XEvent 
*e
); 
 163 void killclient(const char *arg
); 
 164 void manage(Window w
, XWindowAttributes 
*wa
); 
 165 void mappingnotify(XEvent 
*e
); 
 166 void maprequest(XEvent 
*e
); 
 168 void movemouse(Client 
*c
); 
 169 Client 
*nexttiled(Client 
*c
); 
 170 void propertynotify(XEvent 
*e
); 
 171 void quit(const char *arg
); 
 172 void reapply(const char *arg
); 
 173 void resize(Client 
*c
, int x
, int y
, int w
, int h
, Bool sizehints
); 
 174 void resizemouse(Client 
*c
); 
 178 void setclientstate(Client 
*c
, long state
); 
 179 void setgeom(const char *arg
); 
 180 void setlayout(const char *arg
); 
 181 void setmfact(const char *arg
); 
 183 void spawn(const char *arg
); 
 184 void tag(const char *arg
); 
 185 unsigned int textnw(const char *text
, unsigned int len
); 
 186 unsigned int textw(const char *text
); 
 188 void tilehstack(unsigned int n
); 
 189 Client 
*tilemaster(unsigned int n
); 
 190 void tileresize(Client 
*c
, int x
, int y
, int w
, int h
); 
 192 void tilevstack(unsigned int n
); 
 193 void togglefloating(const char *arg
); 
 194 void toggletag(const char *arg
); 
 195 void toggleview(const char *arg
); 
 196 void unban(Client 
*c
); 
 197 void unmanage(Client 
*c
); 
 198 void unmapnotify(XEvent 
*e
); 
 199 void updatebarpos(void); 
 200 void updatesizehints(Client 
*c
); 
 201 void updatetitle(Client 
*c
); 
 202 void updatewmhints(Client 
*c
); 
 203 void view(const char *arg
); 
 204 void viewprevtag(const char *arg
);      /* views previous selected tags */ 
 205 int xerror(Display 
*dpy
, XErrorEvent 
*ee
); 
 206 int xerrordummy(Display 
*dpy
, XErrorEvent 
*ee
); 
 207 int xerrorstart(Display 
*dpy
, XErrorEvent 
*ee
); 
 208 void zoom(const char *arg
); 
 211 char stext
[256], buf
[256]; 
 212 int screen
, sx
, sy
, sw
, sh
; 
 213 int (*xerrorxlib
)(Display 
*, XErrorEvent 
*); 
 214 int bx
, by
, bw
, bh
, blw
, bgw
, mx
, my
, mw
, mh
, mox
, moy
, mow
, moh
, tx
, ty
, tw
, th
, wx
, wy
, ww
, wh
; 
 215 unsigned int numlockmask 
= 0; 
 216 void (*handler
[LASTEvent
]) (XEvent 
*) = { 
 217         [ButtonPress
] = buttonpress
, 
 218         [ConfigureRequest
] = configurerequest
, 
 219         [ConfigureNotify
] = configurenotify
, 
 220         [DestroyNotify
] = destroynotify
, 
 221         [EnterNotify
] = enternotify
, 
 224         [KeyPress
] = keypress
, 
 225         [MappingNotify
] = mappingnotify
, 
 226         [MapRequest
] = maprequest
, 
 227         [PropertyNotify
] = propertynotify
, 
 228         [UnmapNotify
] = unmapnotify
 
 230 Atom wmatom
[WMLast
], netatom
[NetLast
]; 
 231 Bool otherwm
, readin
; 
 235 Client 
*clients 
= NULL
; 
 237 Client 
*stack 
= NULL
; 
 238 Cursor cursor
[CurLast
]; 
 245 /* configuration, allows nested code to access above variables */ 
 247 #define TAGSZ (LENGTH(tags) * sizeof(Bool)) 
 248 static Bool tmp
[LENGTH(tags
)]; 
 250 /* function implementations */ 
 253 applyrules(Client 
*c
) { 
 255         Bool matched 
= False
; 
 257         XClassHint ch 
= { 0 }; 
 260         XGetClassHint(dpy
, c
->win
, &ch
); 
 261         for(i 
= 0; i 
< LENGTH(rules
); i
++) { 
 263                 if((r
->title 
&& strstr(c
->name
, r
->title
)) 
 264                 || (ch
.res_class 
&& r
->class && strstr(ch
.res_class
, r
->class)) 
 265                 || (ch
.res_name 
&& r
->instance 
&& strstr(ch
.res_name
, r
->instance
))) 
 267                         c
->isfloating 
= r
->isfloating
; 
 269                                 c
->tags
[idxoftag(r
->tag
)] = True
; 
 279                 memcpy(c
->tags
, seltags
, TAGSZ
); 
 286         for(c 
= clients
; c
; c 
= c
->next
) 
 306 attachstack(Client 
*c
) { 
 315         XMoveWindow(dpy
, c
->win
, c
->x 
+ 2 * sw
, c
->y
); 
 320 buttonpress(XEvent 
*e
) { 
 323         XButtonPressedEvent 
*ev 
= &e
->xbutton
; 
 325         if(ev
->window 
== barwin
) { 
 326                 if((ev
->x 
< bgw
) && ev
->button 
== Button1
) { 
 331                 for(i 
= 0; i 
< LENGTH(tags
); i
++) { 
 333                         if(ev
->x 
> bgw 
&& ev
->x 
< x
) { 
 334                                 if(ev
->button 
== Button1
) { 
 335                                         if(ev
->state 
& MODKEY
) 
 340                                 else if(ev
->button 
== Button3
) { 
 341                                         if(ev
->state 
& MODKEY
) 
 349                 if((ev
->x 
< x 
+ blw
) && ev
->button 
== Button1
)  
 352         else if((c 
= getclient(ev
->window
))) { 
 354                 if(CLEANMASK(ev
->state
) != MODKEY
) 
 356                 if(ev
->button 
== Button1
) { 
 360                 else if(ev
->button 
== Button2
) { 
 361                         if((floating 
!= lt
->arrange
) && c
->isfloating
) 
 362                                 togglefloating(NULL
); 
 366                 else if(ev
->button 
== Button3 
&& !c
->isfixed
) { 
 376         XSetErrorHandler(xerrorstart
); 
 378         /* this causes an error if some other window manager is running */ 
 379         XSelectInput(dpy
, DefaultRootWindow(dpy
), SubstructureRedirectMask
); 
 382                 eprint("dwm: another window manager is already running\n"); 
 384         XSetErrorHandler(NULL
); 
 385         xerrorxlib 
= XSetErrorHandler(xerror
); 
 397                 XFreeFontSet(dpy
, dc
.font
.set
); 
 399                 XFreeFont(dpy
, dc
.font
.xfont
); 
 400         XUngrabKey(dpy
, AnyKey
, AnyModifier
, root
); 
 401         XFreePixmap(dpy
, dc
.drawable
); 
 403         XFreeCursor(dpy
, cursor
[CurNormal
]); 
 404         XFreeCursor(dpy
, cursor
[CurResize
]); 
 405         XFreeCursor(dpy
, cursor
[CurMove
]); 
 406         XDestroyWindow(dpy
, barwin
); 
 408         XSetInputFocus(dpy
, PointerRoot
, RevertToPointerRoot
, CurrentTime
); 
 412 configure(Client 
*c
) { 
 415         ce
.type 
= ConfigureNotify
; 
 423         ce
.border_width 
= c
->bw
; 
 425         ce
.override_redirect 
= False
; 
 426         XSendEvent(dpy
, c
->win
, False
, StructureNotifyMask
, (XEvent 
*)&ce
); 
 430 configurenotify(XEvent 
*e
) { 
 431         XConfigureEvent 
*ev 
= &e
->xconfigure
; 
 433         if(ev
->window 
== root 
&& (ev
->width 
!= sw 
|| ev
->height 
!= sh
)) { 
 436                 setgeom(geom
->symbol
); 
 441 configurerequest(XEvent 
*e
) { 
 443         XConfigureRequestEvent 
*ev 
= &e
->xconfigurerequest
; 
 446         if((c 
= getclient(ev
->window
))) { 
 447                 if(ev
->value_mask 
& CWBorderWidth
) 
 448                         c
->bw 
= ev
->border_width
; 
 449                 if(c
->isfixed 
|| c
->isfloating 
|| lt
->isfloating
) { 
 450                         if(ev
->value_mask 
& CWX
) 
 452                         if(ev
->value_mask 
& CWY
) 
 454                         if(ev
->value_mask 
& CWWidth
) 
 456                         if(ev
->value_mask 
& CWHeight
) 
 458                         if((c
->x 
- sx 
+ c
->w
) > sw 
&& c
->isfloating
) 
 459                                 c
->x 
= sx 
+ (sw 
/ 2 - c
->w 
/ 2); /* center in x direction */ 
 460                         if((c
->y 
- sy 
+ c
->h
) > sh 
&& c
->isfloating
) 
 461                                 c
->y 
= sy 
+ (sh 
/ 2 - c
->h 
/ 2); /* center in y direction */ 
 462                         if((ev
->value_mask 
& (CWX
|CWY
)) 
 463                         && !(ev
->value_mask 
& (CWWidth
|CWHeight
))) 
 466                                 XMoveResizeWindow(dpy
, c
->win
, c
->x
, c
->y
, c
->w
, c
->h
); 
 474                 wc
.width 
= ev
->width
; 
 475                 wc
.height 
= ev
->height
; 
 476                 wc
.border_width 
= ev
->border_width
; 
 477                 wc
.sibling 
= ev
->above
; 
 478                 wc
.stack_mode 
= ev
->detail
; 
 479                 XConfigureWindow(dpy
, ev
->window
, ev
->value_mask
, &wc
); 
 489         for(n 
= 0, c 
= nexttiled(clients
); c
; c 
= nexttiled(c
->next
), n
++); 
 494 destroynotify(XEvent 
*e
) { 
 496         XDestroyWindowEvent 
*ev 
= &e
->xdestroywindow
; 
 498         if((c 
= getclient(ev
->window
))) 
 505                 c
->prev
->next 
= c
->next
; 
 507                 c
->next
->prev 
= c
->prev
; 
 510         c
->next 
= c
->prev 
= NULL
; 
 514 detachstack(Client 
*c
) { 
 517         for(tc
=&stack
; *tc 
&& *tc 
!= c
; tc
=&(*tc
)->snext
); 
 529                 drawtext(geom
->symbol
, dc
.norm
, False
); 
 532         for(c 
= stack
; c 
&& !isvisible(c
); c 
= c
->snext
); 
 533         for(i 
= 0; i 
< LENGTH(tags
); i
++) { 
 534                 dc
.w 
= textw(tags
[i
]); 
 536                         drawtext(tags
[i
], dc
.sel
, isurgent(i
)); 
 537                         drawsquare(c 
&& c
->tags
[i
], isoccupied(i
), isurgent(i
), dc
.sel
); 
 540                         drawtext(tags
[i
], dc
.norm
, isurgent(i
)); 
 541                         drawsquare(c 
&& c
->tags
[i
], isoccupied(i
), isurgent(i
), dc
.norm
); 
 547                 drawtext(lt
->symbol
, dc
.norm
, False
); 
 558         drawtext(stext
, dc
.norm
, False
); 
 559         if((dc
.w 
= dc
.x 
- x
) > bh
) { 
 562                         drawtext(c
->name
, dc
.sel
, False
); 
 563                         drawsquare(False
, c
->isfloating
, False
, dc
.sel
); 
 566                         drawtext(NULL
, dc
.norm
, False
); 
 568         XCopyArea(dpy
, dc
.drawable
, barwin
, dc
.gc
, 0, 0, bw
, bh
, 0, 0); 
 573 drawsquare(Bool filled
, Bool empty
, Bool invert
, unsigned long col
[ColLast
]) { 
 576         XRectangle r 
= { dc
.x
, dc
.y
, dc
.w
, dc
.h 
}; 
 578         gcv
.foreground 
= col
[invert 
? ColBG 
: ColFG
]; 
 579         XChangeGC(dpy
, dc
.gc
, GCForeground
, &gcv
); 
 580         x 
= (dc
.font
.ascent 
+ dc
.font
.descent 
+ 2) / 4; 
 584                 r
.width 
= r
.height 
= x 
+ 1; 
 585                 XFillRectangles(dpy
, dc
.drawable
, dc
.gc
, &r
, 1); 
 588                 r
.width 
= r
.height 
= x
; 
 589                 XDrawRectangles(dpy
, dc
.drawable
, dc
.gc
, &r
, 1); 
 594 drawtext(const char *text
, unsigned long col
[ColLast
], Bool invert
) { 
 596         unsigned int len
, olen
; 
 597         XRectangle r 
= { dc
.x
, dc
.y
, dc
.w
, dc
.h 
}; 
 599         XSetForeground(dpy
, dc
.gc
, col
[invert 
? ColFG 
: ColBG
]); 
 600         XFillRectangles(dpy
, dc
.drawable
, dc
.gc
, &r
, 1); 
 604         olen 
= len 
= strlen(text
); 
 605         if(len 
>= sizeof buf
) 
 606                 len 
= sizeof buf 
- 1; 
 607         memcpy(buf
, text
, len
); 
 609         h 
= dc
.font
.ascent 
+ dc
.font
.descent
; 
 610         y 
= dc
.y 
+ (dc
.h 
/ 2) - (h 
/ 2) + dc
.font
.ascent
; 
 612         /* shorten text if necessary */ 
 613         while(len 
&& (w 
= textnw(buf
, len
)) > dc
.w 
- h
) 
 624                 return; /* too long */ 
 625         XSetForeground(dpy
, dc
.gc
, col
[invert 
? ColBG 
: ColFG
]); 
 627                 XmbDrawString(dpy
, dc
.drawable
, dc
.font
.set
, dc
.gc
, x
, y
, buf
, len
); 
 629                 XDrawString(dpy
, dc
.drawable
, dc
.gc
, x
, y
, buf
, len
); 
 633 emallocz(unsigned int size
) { 
 634         void *res 
= calloc(1, size
); 
 637                 eprint("fatal: could not malloc() %u bytes\n", size
); 
 642 enternotify(XEvent 
*e
) { 
 644         XCrossingEvent 
*ev 
= &e
->xcrossing
; 
 646         if((ev
->mode 
!= NotifyNormal 
|| ev
->detail 
== NotifyInferior
) && ev
->window 
!= root
) 
 648         if((c 
= getclient(ev
->window
))) 
 655 eprint(const char *errstr
, ...) { 
 658         va_start(ap
, errstr
); 
 659         vfprintf(stderr
, errstr
, ap
); 
 666         XExposeEvent 
*ev 
= &e
->xexpose
; 
 668         if(ev
->count 
== 0 && (ev
->window 
== barwin
)) 
 673 floating(void) { /* default floating layout */ 
 676         for(c 
= clients
; c
; c 
= c
->next
) 
 678                         resize(c
, c
->x
, c
->y
, c
->w
, c
->h
, True
); 
 683         if(!c 
|| (c 
&& !isvisible(c
))) 
 684                 for(c 
= stack
; c 
&& !isvisible(c
); c 
= c
->snext
); 
 685         if(sel 
&& sel 
!= c
) { 
 686                 grabbuttons(sel
, False
); 
 687                 XSetWindowBorder(dpy
, sel
->win
, dc
.norm
[ColBorder
]); 
 692                 grabbuttons(c
, True
); 
 696                 XSetWindowBorder(dpy
, c
->win
, dc
.sel
[ColBorder
]); 
 697                 XSetInputFocus(dpy
, c
->win
, RevertToPointerRoot
, CurrentTime
); 
 700                 XSetInputFocus(dpy
, root
, RevertToPointerRoot
, CurrentTime
); 
 705 focusin(XEvent 
*e
) { /* there are some broken focus acquiring clients */ 
 706         XFocusChangeEvent 
*ev 
= &e
->xfocus
; 
 708         if(sel 
&& ev
->window 
!= sel
->win
) 
 709                 XSetInputFocus(dpy
, sel
->win
, RevertToPointerRoot
, CurrentTime
); 
 713 focusnext(const char *arg
) { 
 718         for(c 
= sel
->next
; c 
&& !isvisible(c
); c 
= c
->next
); 
 720                 for(c 
= clients
; c 
&& !isvisible(c
); c 
= c
->next
); 
 728 focusprev(const char *arg
) { 
 733         for(c 
= sel
->prev
; c 
&& !isvisible(c
); c 
= c
->prev
); 
 735                 for(c 
= clients
; c 
&& c
->next
; c 
= c
->next
); 
 736                 for(; c 
&& !isvisible(c
); c 
= c
->prev
); 
 745 getclient(Window w
) { 
 748         for(c 
= clients
; c 
&& c
->win 
!= w
; c 
= c
->next
); 
 753 getcolor(const char *colstr
) { 
 754         Colormap cmap 
= DefaultColormap(dpy
, screen
); 
 757         if(!XAllocNamedColor(dpy
, cmap
, colstr
, &color
, &color
)) 
 758                 eprint("error, cannot allocate color '%s'\n", colstr
); 
 766         unsigned char *p 
= NULL
; 
 767         unsigned long n
, extra
; 
 770         status 
= XGetWindowProperty(dpy
, w
, wmatom
[WMState
], 0L, 2L, False
, wmatom
[WMState
], 
 771                         &real
, &format
, &n
, &extra
, (unsigned char **)&p
); 
 772         if(status 
!= Success
) 
 781 gettextprop(Window w
, Atom atom
, char *text
, unsigned int size
) { 
 786         if(!text 
|| size 
== 0) 
 789         XGetTextProperty(dpy
, w
, &name
, atom
); 
 792         if(name
.encoding 
== XA_STRING
) 
 793                 strncpy(text
, (char *)name
.value
, size 
- 1); 
 795                 if(XmbTextPropertyToTextList(dpy
, &name
, &list
, &n
) >= Success
 
 797                         strncpy(text
, *list
, size 
- 1); 
 798                         XFreeStringList(list
); 
 801         text
[size 
- 1] = '\0'; 
 807 grabbuttons(Client 
*c
, Bool focused
) { 
 808         XUngrabButton(dpy
, AnyButton
, AnyModifier
, c
->win
); 
 811                 XGrabButton(dpy
, Button1
, MODKEY
, c
->win
, False
, BUTTONMASK
, 
 812                                 GrabModeAsync
, GrabModeSync
, None
, None
); 
 813                 XGrabButton(dpy
, Button1
, MODKEY
|LockMask
, c
->win
, False
, BUTTONMASK
, 
 814                                 GrabModeAsync
, GrabModeSync
, None
, None
); 
 815                 XGrabButton(dpy
, Button1
, MODKEY
|numlockmask
, c
->win
, False
, BUTTONMASK
, 
 816                                 GrabModeAsync
, GrabModeSync
, None
, None
); 
 817                 XGrabButton(dpy
, Button1
, MODKEY
|numlockmask
|LockMask
, c
->win
, False
, BUTTONMASK
, 
 818                                 GrabModeAsync
, GrabModeSync
, None
, None
); 
 820                 XGrabButton(dpy
, Button2
, MODKEY
, c
->win
, False
, BUTTONMASK
, 
 821                                 GrabModeAsync
, GrabModeSync
, None
, None
); 
 822                 XGrabButton(dpy
, Button2
, MODKEY
|LockMask
, c
->win
, False
, BUTTONMASK
, 
 823                                 GrabModeAsync
, GrabModeSync
, None
, None
); 
 824                 XGrabButton(dpy
, Button2
, MODKEY
|numlockmask
, c
->win
, False
, BUTTONMASK
, 
 825                                 GrabModeAsync
, GrabModeSync
, None
, None
); 
 826                 XGrabButton(dpy
, Button2
, MODKEY
|numlockmask
|LockMask
, c
->win
, False
, BUTTONMASK
, 
 827                                 GrabModeAsync
, GrabModeSync
, None
, None
); 
 829                 XGrabButton(dpy
, Button3
, MODKEY
, c
->win
, False
, BUTTONMASK
, 
 830                                 GrabModeAsync
, GrabModeSync
, None
, None
); 
 831                 XGrabButton(dpy
, Button3
, MODKEY
|LockMask
, c
->win
, False
, BUTTONMASK
, 
 832                                 GrabModeAsync
, GrabModeSync
, None
, None
); 
 833                 XGrabButton(dpy
, Button3
, MODKEY
|numlockmask
, c
->win
, False
, BUTTONMASK
, 
 834                                 GrabModeAsync
, GrabModeSync
, None
, None
); 
 835                 XGrabButton(dpy
, Button3
, MODKEY
|numlockmask
|LockMask
, c
->win
, False
, BUTTONMASK
, 
 836                                 GrabModeAsync
, GrabModeSync
, None
, None
); 
 839                 XGrabButton(dpy
, AnyButton
, AnyModifier
, c
->win
, False
, BUTTONMASK
, 
 840                                 GrabModeAsync
, GrabModeSync
, None
, None
); 
 847         XModifierKeymap 
*modmap
; 
 849         /* init modifier map */ 
 850         modmap 
= XGetModifierMapping(dpy
); 
 851         for(i 
= 0; i 
< 8; i
++) 
 852                 for(j 
= 0; j 
< modmap
->max_keypermod
; j
++) { 
 853                         if(modmap
->modifiermap
[i 
* modmap
->max_keypermod 
+ j
] == XKeysymToKeycode(dpy
, XK_Num_Lock
)) 
 854                                 numlockmask 
= (1 << i
); 
 856         XFreeModifiermap(modmap
); 
 858         XUngrabKey(dpy
, AnyKey
, AnyModifier
, root
); 
 859         for(i 
= 0; i 
< LENGTH(keys
); i
++) { 
 860                 code 
= XKeysymToKeycode(dpy
, keys
[i
].keysym
); 
 861                 XGrabKey(dpy
, code
, keys
[i
].mod
, root
, True
, 
 862                                 GrabModeAsync
, GrabModeAsync
); 
 863                 XGrabKey(dpy
, code
, keys
[i
].mod
|LockMask
, root
, True
, 
 864                                 GrabModeAsync
, GrabModeAsync
); 
 865                 XGrabKey(dpy
, code
, keys
[i
].mod
|numlockmask
, root
, True
, 
 866                                 GrabModeAsync
, GrabModeAsync
); 
 867                 XGrabKey(dpy
, code
, keys
[i
].mod
|numlockmask
|LockMask
, root
, True
, 
 868                                 GrabModeAsync
, GrabModeAsync
); 
 873 idxoftag(const char *t
) { 
 876         for(i 
= 0; (i 
< LENGTH(tags
)) && t 
&& strcmp(tags
[i
], t
); i
++); 
 877         return (i 
< LENGTH(tags
)) ? i 
: 0; 
 881 initfont(const char *fontstr
) { 
 882         char *def
, **missing
; 
 887                 XFreeFontSet(dpy
, dc
.font
.set
); 
 888         dc
.font
.set 
= XCreateFontSet(dpy
, fontstr
, &missing
, &n
, &def
); 
 891                         fprintf(stderr
, "dwm: missing fontset: %s\n", missing
[n
]); 
 892                 XFreeStringList(missing
); 
 895                 XFontSetExtents 
*font_extents
; 
 896                 XFontStruct 
**xfonts
; 
 898                 dc
.font
.ascent 
= dc
.font
.descent 
= 0; 
 899                 font_extents 
= XExtentsOfFontSet(dc
.font
.set
); 
 900                 n 
= XFontsOfFontSet(dc
.font
.set
, &xfonts
, &font_names
); 
 901                 for(i 
= 0, dc
.font
.ascent 
= 0, dc
.font
.descent 
= 0; i 
< n
; i
++) { 
 902                         if(dc
.font
.ascent 
< (*xfonts
)->ascent
) 
 903                                 dc
.font
.ascent 
= (*xfonts
)->ascent
; 
 904                         if(dc
.font
.descent 
< (*xfonts
)->descent
) 
 905                                 dc
.font
.descent 
= (*xfonts
)->descent
; 
 911                         XFreeFont(dpy
, dc
.font
.xfont
); 
 912                 dc
.font
.xfont 
= NULL
; 
 913                 if(!(dc
.font
.xfont 
= XLoadQueryFont(dpy
, fontstr
)) 
 914                 && !(dc
.font
.xfont 
= XLoadQueryFont(dpy
, "fixed"))) 
 915                         eprint("error, cannot load font: '%s'\n", fontstr
); 
 916                 dc
.font
.ascent 
= dc
.font
.xfont
->ascent
; 
 917                 dc
.font
.descent 
= dc
.font
.xfont
->descent
; 
 919         dc
.font
.height 
= dc
.font
.ascent 
+ dc
.font
.descent
; 
 923 isoccupied(unsigned int t
) { 
 926         for(c 
= clients
; c
; c 
= c
->next
) 
 933 isprotodel(Client 
*c
) { 
 938         if(XGetWMProtocols(dpy
, c
->win
, &protocols
, &n
)) { 
 939                 for(i 
= 0; !ret 
&& i 
< n
; i
++) 
 940                         if(protocols
[i
] == wmatom
[WMDelete
]) 
 948 isurgent(unsigned int t
) { 
 951         for(c 
= clients
; c
; c 
= c
->next
) 
 952                 if(c
->isurgent 
&& c
->tags
[t
]) 
 958 isvisible(Client 
*c
) { 
 961         for(i 
= 0; i 
< LENGTH(tags
); i
++) 
 962                 if(c
->tags
[i
] && seltags
[i
]) 
 968 keypress(XEvent 
*e
) { 
 974         keysym 
= XKeycodeToKeysym(dpy
, (KeyCode
)ev
->keycode
, 0); 
 975         for(i 
= 0; i 
< LENGTH(keys
); i
++) 
 976                 if(keysym 
== keys
[i
].keysym
 
 977                 && CLEANMASK(keys
[i
].mod
) == CLEANMASK(ev
->state
)) 
 980                                 keys
[i
].func(keys
[i
].arg
); 
 985 killclient(const char *arg
) { 
 990         if(isprotodel(sel
)) { 
 991                 ev
.type 
= ClientMessage
; 
 992                 ev
.xclient
.window 
= sel
->win
; 
 993                 ev
.xclient
.message_type 
= wmatom
[WMProtocols
]; 
 994                 ev
.xclient
.format 
= 32; 
 995                 ev
.xclient
.data
.l
[0] = wmatom
[WMDelete
]; 
 996                 ev
.xclient
.data
.l
[1] = CurrentTime
; 
 997                 XSendEvent(dpy
, sel
->win
, False
, NoEventMask
, &ev
); 
1000                 XKillClient(dpy
, sel
->win
); 
1004 manage(Window w
, XWindowAttributes 
*wa
) { 
1005         Client 
*c
, *t 
= NULL
; 
1010         c 
= emallocz(sizeof(Client
)); 
1011         c
->tags 
= emallocz(TAGSZ
); 
1019         c
->oldbw 
= wa
->border_width
; 
1020         if(c
->w 
== sw 
&& c
->h 
== sh
) { 
1023                 c
->bw 
= wa
->border_width
; 
1026                 if(c
->x 
+ c
->w 
+ 2 * c
->bw 
> wx 
+ ww
) 
1027                         c
->x 
= wx 
+ ww 
- c
->w 
- 2 * c
->bw
; 
1028                 if(c
->y 
+ c
->h 
+ 2 * c
->bw 
> wy 
+ wh
) 
1029                         c
->y 
= wy 
+ wh 
- c
->h 
- 2 * c
->bw
; 
1037         wc
.border_width 
= c
->bw
; 
1038         XConfigureWindow(dpy
, w
, CWBorderWidth
, &wc
); 
1039         XSetWindowBorder(dpy
, w
, dc
.norm
[ColBorder
]); 
1040         configure(c
); /* propagates border_width, if size doesn't change */ 
1042         XSelectInput(dpy
, w
, EnterWindowMask
|FocusChangeMask
|PropertyChangeMask
|StructureNotifyMask
); 
1043         grabbuttons(c
, False
); 
1045         if((rettrans 
= XGetTransientForHint(dpy
, w
, &trans
) == Success
)) 
1046                 for(t 
= clients
; t 
&& t
->win 
!= trans
; t 
= t
->next
); 
1048                 memcpy(c
->tags
, t
->tags
, TAGSZ
); 
1052                 c
->isfloating 
= (rettrans 
== Success
) || c
->isfixed
; 
1055         XMoveResizeWindow(dpy
, c
->win
, c
->x
, c
->y
, c
->w
, c
->h
); /* some windows require this */ 
1057         XMapWindow(dpy
, c
->win
); 
1058         setclientstate(c
, NormalState
); 
1063 mappingnotify(XEvent 
*e
) { 
1064         XMappingEvent 
*ev 
= &e
->xmapping
; 
1066         XRefreshKeyboardMapping(ev
); 
1067         if(ev
->request 
== MappingKeyboard
) 
1072 maprequest(XEvent 
*e
) { 
1073         static XWindowAttributes wa
; 
1074         XMapRequestEvent 
*ev 
= &e
->xmaprequest
; 
1076         if(!XGetWindowAttributes(dpy
, ev
->window
, &wa
)) 
1078         if(wa
.override_redirect
) 
1080         if(!getclient(ev
->window
)) 
1081                 manage(ev
->window
, &wa
); 
1088         for(c 
= clients
; c
; c 
= c
->next
) 
1090                         resize(c
, mox
, moy
, mow 
- 2 * c
->bw
, moh 
- 2 * c
->bw
, RESIZEHINTS
); 
1094 movemouse(Client 
*c
) { 
1095         int x1
, y1
, ocx
, ocy
, di
, nx
, ny
; 
1102         if(XGrabPointer(dpy
, root
, False
, MOUSEMASK
, GrabModeAsync
, GrabModeAsync
, 
1103                         None
, cursor
[CurMove
], CurrentTime
) != GrabSuccess
) 
1105         XQueryPointer(dpy
, root
, &dummy
, &dummy
, &x1
, &y1
, &di
, &di
, &dui
); 
1107                 XMaskEvent(dpy
, MOUSEMASK
|ExposureMask
|SubstructureRedirectMask
, &ev
); 
1110                         XUngrabPointer(dpy
, CurrentTime
); 
1112                 case ConfigureRequest
: 
1115                         handler
[ev
.type
](&ev
); 
1119                         nx 
= ocx 
+ (ev
.xmotion
.x 
- x1
); 
1120                         ny 
= ocy 
+ (ev
.xmotion
.y 
- y1
); 
1121                         if(abs(wx 
- nx
) < SNAP
) 
1123                         else if(abs((wx 
+ ww
) - (nx 
+ c
->w 
+ 2 * c
->bw
)) < SNAP
) 
1124                                 nx 
= wx 
+ ww 
- c
->w 
- 2 * c
->bw
; 
1125                         if(abs(wy 
- ny
) < SNAP
) 
1127                         else if(abs((wy 
+ wh
) - (ny 
+ c
->h 
+ 2 * c
->bw
)) < SNAP
) 
1128                                 ny 
= wy 
+ wh 
- c
->h 
- 2 * c
->bw
; 
1129                         if(!c
->isfloating 
&& !lt
->isfloating 
&& (abs(nx 
- c
->x
) > SNAP 
|| abs(ny 
- c
->y
) > SNAP
)) 
1130                                 togglefloating(NULL
); 
1131                         if((lt
->isfloating
) || c
->isfloating
) 
1132                                 resize(c
, nx
, ny
, c
->w
, c
->h
, False
); 
1139 nexttiled(Client 
*c
) { 
1140         for(; c 
&& (c
->isfloating 
|| !isvisible(c
)); c 
= c
->next
); 
1145 propertynotify(XEvent 
*e
) { 
1148         XPropertyEvent 
*ev 
= &e
->xproperty
; 
1150         if(ev
->state 
== PropertyDelete
) 
1151                 return; /* ignore */ 
1152         if((c 
= getclient(ev
->window
))) { 
1155                 case XA_WM_TRANSIENT_FOR
: 
1156                         XGetTransientForHint(dpy
, c
->win
, &trans
); 
1157                         if(!c
->isfloating 
&& (c
->isfloating 
= (getclient(trans
) != NULL
))) 
1160                 case XA_WM_NORMAL_HINTS
: 
1168                 if(ev
->atom 
== XA_WM_NAME 
|| ev
->atom 
== netatom
[NetWMName
]) { 
1177 quit(const char *arg
) { 
1178         readin 
= running 
= False
; 
1182 reapply(const char *arg
) { 
1183         static Bool zerotags
[LENGTH(tags
)] = { 0 }; 
1186         for(c 
= clients
; c
; c 
= c
->next
) { 
1187                 memcpy(c
->tags
, zerotags
, sizeof zerotags
); 
1194 resize(Client 
*c
, int x
, int y
, int w
, int h
, Bool sizehints
) { 
1198                 /* set minimum possible */ 
1204                 /* temporarily remove base dimensions */ 
1208                 /* adjust for aspect limits */ 
1209                 if (c
->minay 
> 0 && c
->maxay 
> 0 && c
->minax 
> 0 && c
->maxax 
> 0) { 
1210                         if (w 
* c
->maxay 
> h 
* c
->maxax
) 
1211                                 w 
= h 
* c
->maxax 
/ c
->maxay
; 
1212                         else if (w 
* c
->minay 
< h 
* c
->minax
) 
1213                                 h 
= w 
* c
->minay 
/ c
->minax
; 
1216                 /* adjust for increment value */ 
1222                 /* restore base dimensions */ 
1226                 if(c
->minw 
> 0 && w 
< c
->minw
) 
1228                 if(c
->minh 
> 0 && h 
< c
->minh
) 
1230                 if(c
->maxw 
> 0 && w 
> c
->maxw
) 
1232                 if(c
->maxh 
> 0 && h 
> c
->maxh
) 
1235         if(w 
<= 0 || h 
<= 0) 
1238                 x 
= sw 
- w 
- 2 * c
->bw
; 
1240                 y 
= sh 
- h 
- 2 * c
->bw
; 
1241         if(x 
+ w 
+ 2 * c
->bw 
< sx
) 
1243         if(y 
+ h 
+ 2 * c
->bw 
< sy
) 
1245         if(c
->x 
!= x 
|| c
->y 
!= y 
|| c
->w 
!= w 
|| c
->h 
!= h
) { 
1248                 c
->w 
= wc
.width 
= w
; 
1249                 c
->h 
= wc
.height 
= h
; 
1250                 wc
.border_width 
= c
->bw
; 
1251                 XConfigureWindow(dpy
, c
->win
, 
1252                                 CWX
|CWY
|CWWidth
|CWHeight
|CWBorderWidth
, &wc
); 
1259 resizemouse(Client 
*c
) { 
1266         if(XGrabPointer(dpy
, root
, False
, MOUSEMASK
, GrabModeAsync
, GrabModeAsync
, 
1267                         None
, cursor
[CurResize
], CurrentTime
) != GrabSuccess
) 
1269         XWarpPointer(dpy
, None
, c
->win
, 0, 0, 0, 0, c
->w 
+ c
->bw 
- 1, c
->h 
+ c
->bw 
- 1); 
1271                 XMaskEvent(dpy
, MOUSEMASK
|ExposureMask
|SubstructureRedirectMask 
, &ev
); 
1274                         XWarpPointer(dpy
, None
, c
->win
, 0, 0, 0, 0, 
1275                                         c
->w 
+ c
->bw 
- 1, c
->h 
+ c
->bw 
- 1); 
1276                         XUngrabPointer(dpy
, CurrentTime
); 
1277                         while(XCheckMaskEvent(dpy
, EnterWindowMask
, &ev
)); 
1279                 case ConfigureRequest
: 
1282                         handler
[ev
.type
](&ev
); 
1286                         if((nw 
= ev
.xmotion
.x 
- ocx 
- 2 * c
->bw 
+ 1) <= 0) 
1288                         if((nh 
= ev
.xmotion
.y 
- ocy 
- 2 * c
->bw 
+ 1) <= 0) 
1290                         if(!c
->isfloating 
&& !lt
->isfloating 
&& (abs(nw 
- c
->w
) > SNAP 
|| abs(nh 
- c
->h
) > SNAP
)) 
1291                                 togglefloating(NULL
); 
1292                         if((lt
->isfloating
) || c
->isfloating
) 
1293                                 resize(c
, c
->x
, c
->y
, nw
, nh
, True
); 
1308         if(sel
->isfloating 
|| lt
->isfloating
) 
1309                 XRaiseWindow(dpy
, sel
->win
); 
1310         if(!lt
->isfloating
) { 
1311                 wc
.stack_mode 
= Below
; 
1312                 wc
.sibling 
= barwin
; 
1313                 if(!sel
->isfloating
) { 
1314                         XConfigureWindow(dpy
, sel
->win
, CWSibling
|CWStackMode
, &wc
); 
1315                         wc
.sibling 
= sel
->win
; 
1317                 for(c 
= nexttiled(clients
); c
; c 
= nexttiled(c
->next
)) { 
1320                         XConfigureWindow(dpy
, c
->win
, CWSibling
|CWStackMode
, &wc
); 
1321                         wc
.sibling 
= c
->win
; 
1325         while(XCheckMaskEvent(dpy
, EnterWindowMask
, &ev
)); 
1331         char sbuf
[sizeof stext
]; 
1334         unsigned int len
, offset
; 
1337         /* main event loop, also reads status text from stdin */ 
1339         xfd 
= ConnectionNumber(dpy
); 
1342         len 
= sizeof stext 
- 1; 
1343         sbuf
[len
] = stext
[len
] = '\0'; /* 0-terminator is never touched */ 
1347                         FD_SET(STDIN_FILENO
, &rd
); 
1349                 if(select(xfd 
+ 1, &rd
, NULL
, NULL
, NULL
) == -1) { 
1352                         eprint("select failed\n"); 
1354                 if(FD_ISSET(STDIN_FILENO
, &rd
)) { 
1355                         switch((r 
= read(STDIN_FILENO
, sbuf 
+ offset
, len 
- offset
))) { 
1357                                 strncpy(stext
, strerror(errno
), len
); 
1361                                 strncpy(stext
, "EOF", 4); 
1365                                 for(p 
= sbuf 
+ offset
; r 
> 0; p
++, r
--, offset
++) 
1366                                         if(*p 
== '\n' || *p 
== '\0') { 
1368                                                 strncpy(stext
, sbuf
, len
); 
1369                                                 p 
+= r 
- 1; /* p is sbuf + offset + r - 1 */ 
1370                                                 for(r 
= 0; *(p 
- r
) && *(p 
- r
) != '\n'; r
++); 
1373                                                         memmove(sbuf
, p 
- r 
+ 1, r
); 
1380                 while(XPending(dpy
)) { 
1381                         XNextEvent(dpy
, &ev
); 
1382                         if(handler
[ev
.type
]) 
1383                                 (handler
[ev
.type
])(&ev
); /* call handler */ 
1390         unsigned int i
, num
; 
1391         Window 
*wins
, d1
, d2
; 
1392         XWindowAttributes wa
; 
1395         if(XQueryTree(dpy
, root
, &d1
, &d2
, &wins
, &num
)) { 
1396                 for(i 
= 0; i 
< num
; i
++) { 
1397                         if(!XGetWindowAttributes(dpy
, wins
[i
], &wa
) 
1398                                         || wa
.override_redirect 
|| XGetTransientForHint(dpy
, wins
[i
], &d1
)) 
1400                         if(wa
.map_state 
== IsViewable 
|| getstate(wins
[i
]) == IconicState
) 
1401                                 manage(wins
[i
], &wa
); 
1403                 for(i 
= 0; i 
< num
; i
++) { /* now the transients */ 
1404                         if(!XGetWindowAttributes(dpy
, wins
[i
], &wa
)) 
1406                         if(XGetTransientForHint(dpy
, wins
[i
], &d1
) 
1407                                         && (wa
.map_state 
== IsViewable 
|| getstate(wins
[i
]) == IconicState
)) 
1408                                 manage(wins
[i
], &wa
); 
1416 setclientstate(Client 
*c
, long state
) { 
1417         long data
[] = {state
, None
}; 
1419         XChangeProperty(dpy
, c
->win
, wmatom
[WMState
], wmatom
[WMState
], 32, 
1420                         PropModeReplace
, (unsigned char *)data
, 2); 
1424 setgeom(const char *arg
) { 
1428                 if(++geom 
== &geoms
[LENGTH(geoms
)]) 
1432                 for(i 
= 0; i 
< LENGTH(geoms
); i
++) 
1433                         if(!strcmp(geoms
[i
].symbol
, arg
)) 
1435                 if(i 
== LENGTH(geoms
)) 
1445 setlayout(const char *arg
) { 
1449                 if(++lt 
== &layouts
[LENGTH(layouts
)]) 
1453                 for(i 
= 0; i 
< LENGTH(layouts
); i
++) 
1454                         if(!strcmp(arg
, layouts
[i
].symbol
)) 
1456                 if(i 
== LENGTH(layouts
)) 
1467 setmfact(const char *arg
) { 
1472         delta 
= strtod(arg
, NULL
); 
1473         if(arg
[0] == '-' || arg
[0] == '+') { 
1474                 if(mfact 
+ delta 
< 0.1 || mfact 
+ delta 
> 0.9) 
1479                 if(delta 
< 0.1 || delta 
> 0.9) 
1483         setgeom(geom
->symbol
); 
1489         XSetWindowAttributes wa
; 
1492         screen 
= DefaultScreen(dpy
); 
1493         root 
= RootWindow(dpy
, screen
); 
1496         /* apply default geometry */ 
1499         sw 
= DisplayWidth(dpy
, screen
); 
1500         sh 
= DisplayHeight(dpy
, screen
); 
1501         bh 
= dc
.font
.height 
+ 2; 
1506         wmatom
[WMProtocols
] = XInternAtom(dpy
, "WM_PROTOCOLS", False
); 
1507         wmatom
[WMDelete
] = XInternAtom(dpy
, "WM_DELETE_WINDOW", False
); 
1508         wmatom
[WMName
] = XInternAtom(dpy
, "WM_NAME", False
); 
1509         wmatom
[WMState
] = XInternAtom(dpy
, "WM_STATE", False
); 
1510         netatom
[NetSupported
] = XInternAtom(dpy
, "_NET_SUPPORTED", False
); 
1511         netatom
[NetWMName
] = XInternAtom(dpy
, "_NET_WM_NAME", False
); 
1514         wa
.cursor 
= cursor
[CurNormal
] = XCreateFontCursor(dpy
, XC_left_ptr
); 
1515         cursor
[CurResize
] = XCreateFontCursor(dpy
, XC_sizing
); 
1516         cursor
[CurMove
] = XCreateFontCursor(dpy
, XC_fleur
); 
1518         /* init appearance */ 
1519         dc
.norm
[ColBorder
] = getcolor(NORMBORDERCOLOR
); 
1520         dc
.norm
[ColBG
] = getcolor(NORMBGCOLOR
); 
1521         dc
.norm
[ColFG
] = getcolor(NORMFGCOLOR
); 
1522         dc
.sel
[ColBorder
] = getcolor(SELBORDERCOLOR
); 
1523         dc
.sel
[ColBG
] = getcolor(SELBGCOLOR
); 
1524         dc
.sel
[ColFG
] = getcolor(SELFGCOLOR
); 
1527         dc
.drawable 
= XCreatePixmap(dpy
, root
, DisplayWidth(dpy
, screen
), bh
, DefaultDepth(dpy
, screen
)); 
1528         dc
.gc 
= XCreateGC(dpy
, root
, 0, 0); 
1529         XSetLineAttributes(dpy
, dc
.gc
, 1, LineSolid
, CapButt
, JoinMiter
); 
1531                 XSetFont(dpy
, dc
.gc
, dc
.font
.xfont
->fid
); 
1534         seltags 
= emallocz(TAGSZ
); 
1535         prevtags 
= emallocz(TAGSZ
); 
1536         seltags
[0] = prevtags
[0] = True
; 
1542         for(blw 
= i 
= 0; LENGTH(layouts
) > 1 && i 
< LENGTH(layouts
); i
++) { 
1543                 w 
= textw(layouts
[i
].symbol
); 
1547         for(bgw 
= i 
= 0; LENGTH(geoms
) > 1 && i 
< LENGTH(geoms
); i
++) { 
1548                 w 
= textw(geoms
[i
].symbol
); 
1553         wa
.override_redirect 
= 1; 
1554         wa
.background_pixmap 
= ParentRelative
; 
1555         wa
.event_mask 
= ButtonPressMask
|ExposureMask
; 
1557         barwin 
= XCreateWindow(dpy
, root
, bx
, by
, bw
, bh
, 0, DefaultDepth(dpy
, screen
), 
1558                                 CopyFromParent
, DefaultVisual(dpy
, screen
), 
1559                                 CWOverrideRedirect
|CWBackPixmap
|CWEventMask
, &wa
); 
1560         XDefineCursor(dpy
, barwin
, cursor
[CurNormal
]); 
1561         XMapRaised(dpy
, barwin
); 
1562         strcpy(stext
, "dwm-"VERSION
); 
1565         /* EWMH support per view */ 
1566         XChangeProperty(dpy
, root
, netatom
[NetSupported
], XA_ATOM
, 32, 
1567                         PropModeReplace
, (unsigned char *) netatom
, NetLast
); 
1569         /* select for events */ 
1570         wa
.event_mask 
= SubstructureRedirectMask
|SubstructureNotifyMask
 
1571                         |EnterWindowMask
|LeaveWindowMask
|StructureNotifyMask
; 
1572         XChangeWindowAttributes(dpy
, root
, CWEventMask
|CWCursor
, &wa
); 
1573         XSelectInput(dpy
, root
, wa
.event_mask
); 
1581 spawn(const char *arg
) { 
1582         static char *shell 
= NULL
; 
1584         if(!shell 
&& !(shell 
= getenv("SHELL"))) 
1588         /* The double-fork construct avoids zombie processes and keeps the code 
1589          * clean from stupid signal handlers. */ 
1593                                 close(ConnectionNumber(dpy
)); 
1595                         execl(shell
, shell
, "-c", arg
, (char *)NULL
); 
1596                         fprintf(stderr
, "dwm: execl '%s -c %s'", shell
, arg
); 
1605 tag(const char *arg
) { 
1610         for(i 
= 0; i 
< LENGTH(tags
); i
++) 
1611                 sel
->tags
[i
] = (NULL 
== arg
); 
1612         sel
->tags
[idxoftag(arg
)] = True
; 
1617 textnw(const char *text
, unsigned int len
) { 
1621                 XmbTextExtents(dc
.font
.set
, text
, len
, NULL
, &r
); 
1624         return XTextWidth(dc
.font
.xfont
, text
, len
); 
1628 textw(const char *text
) { 
1629         return textnw(text
, strlen(text
)) + dc
.font
.height
; 
1635         unsigned int i
, n 
= counttiled(); 
1649         for(i 
= 0, c 
= nexttiled(c
->next
); c
; c 
= nexttiled(c
->next
), i
++) { 
1650                 if(i 
+ 1 == n
) /* remainder */ 
1651                         tileresize(c
, x
, ty
, (tx 
+ tw
) - x 
- 2 * c
->bw
, th 
- 2 * c
->bw
); 
1653                         tileresize(c
, x
, ty
, w 
- 2 * c
->bw
, th 
- 2 * c
->bw
); 
1655                         x 
= c
->x 
+ c
->w 
+ 2 * c
->bw
; 
1660 tilemaster(unsigned int n
) { 
1661         Client 
*c 
= nexttiled(clients
); 
1664                 tileresize(c
, mox
, moy
, mow 
- 2 * c
->bw
, moh 
- 2 * c
->bw
); 
1666                 tileresize(c
, mx
, my
, mw 
- 2 * c
->bw
, mh 
- 2 * c
->bw
); 
1671 tileresize(Client 
*c
, int x
, int y
, int w
, int h
) { 
1672         resize(c
, x
, y
, w
, h
, RESIZEHINTS
); 
1673         if((RESIZEHINTS
) && ((c
->h 
< bh
) || (c
->h 
> h
) || (c
->w 
< bh
) || (c
->w 
> w
))) 
1674                 /* client doesn't accept size constraints */ 
1675                 resize(c
, x
, y
, w
, h
, False
); 
1681         unsigned int i
, n 
= counttiled(); 
1695         for(i 
= 0, c 
= nexttiled(c
->next
); c
; c 
= nexttiled(c
->next
), i
++) { 
1696                 if(i 
+ 1 == n
) /* remainder */ 
1697                         tileresize(c
, tx
, y
, tw 
- 2 * c
->bw
, (ty 
+ th
) - y 
- 2 * c
->bw
); 
1699                         tileresize(c
, tx
, y
, tw 
- 2 * c
->bw
, h 
- 2 * c
->bw
); 
1701                         y 
= c
->y 
+ c
->h 
+ 2 * c
->bw
; 
1706 togglefloating(const char *arg
) { 
1709         sel
->isfloating 
= !sel
->isfloating
; 
1711                 resize(sel
, sel
->x
, sel
->y
, sel
->w
, sel
->h
, True
); 
1716 toggletag(const char *arg
) { 
1722         sel
->tags
[i
] = !sel
->tags
[i
]; 
1723         for(j 
= 0; j 
< LENGTH(tags
) && !sel
->tags
[j
]; j
++); 
1724         if(j 
== LENGTH(tags
)) 
1725                 sel
->tags
[i
] = True
; /* at least one tag must be enabled */ 
1730 toggleview(const char *arg
) { 
1734         seltags
[i
] = !seltags
[i
]; 
1735         for(j 
= 0; j 
< LENGTH(tags
) && !seltags
[j
]; j
++); 
1736         if(j 
== LENGTH(tags
)) 
1737                 seltags
[i
] = True
; /* at least one tag must be viewed */ 
1745         XMoveWindow(dpy
, c
->win
, c
->x
, c
->y
); 
1746         c
->isbanned 
= False
; 
1750 unmanage(Client 
*c
) { 
1753         wc
.border_width 
= c
->oldbw
; 
1754         /* The server grab construct avoids race conditions. */ 
1756         XSetErrorHandler(xerrordummy
); 
1757         XConfigureWindow(dpy
, c
->win
, CWBorderWidth
, &wc
); /* restore border */ 
1762         XUngrabButton(dpy
, AnyButton
, AnyModifier
, c
->win
); 
1763         setclientstate(c
, WithdrawnState
); 
1767         XSetErrorHandler(xerror
); 
1773 unmapnotify(XEvent 
*e
) { 
1775         XUnmapEvent 
*ev 
= &e
->xunmap
; 
1777         if((c 
= getclient(ev
->window
))) 
1782 updatebarpos(void) { 
1784         if(dc
.drawable 
!= 0) 
1785                 XFreePixmap(dpy
, dc
.drawable
); 
1786         dc
.drawable 
= XCreatePixmap(dpy
, root
, bw
, bh
, DefaultDepth(dpy
, screen
)); 
1787         XMoveResizeWindow(dpy
, barwin
, bx
, by
, bw
, bh
); 
1791 updatesizehints(Client 
*c
) { 
1795         if(!XGetWMNormalHints(dpy
, c
->win
, &size
, &msize
) || !size
.flags
) 
1797         c
->flags 
= size
.flags
; 
1798         if(c
->flags 
& PBaseSize
) { 
1799                 c
->basew 
= size
.base_width
; 
1800                 c
->baseh 
= size
.base_height
; 
1802         else if(c
->flags 
& PMinSize
) { 
1803                 c
->basew 
= size
.min_width
; 
1804                 c
->baseh 
= size
.min_height
; 
1807                 c
->basew 
= c
->baseh 
= 0; 
1808         if(c
->flags 
& PResizeInc
) { 
1809                 c
->incw 
= size
.width_inc
; 
1810                 c
->inch 
= size
.height_inc
; 
1813                 c
->incw 
= c
->inch 
= 0; 
1814         if(c
->flags 
& PMaxSize
) { 
1815                 c
->maxw 
= size
.max_width
; 
1816                 c
->maxh 
= size
.max_height
; 
1819                 c
->maxw 
= c
->maxh 
= 0; 
1820         if(c
->flags 
& PMinSize
) { 
1821                 c
->minw 
= size
.min_width
; 
1822                 c
->minh 
= size
.min_height
; 
1824         else if(c
->flags 
& PBaseSize
) { 
1825                 c
->minw 
= size
.base_width
; 
1826                 c
->minh 
= size
.base_height
; 
1829                 c
->minw 
= c
->minh 
= 0; 
1830         if(c
->flags 
& PAspect
) { 
1831                 c
->minax 
= size
.min_aspect
.x
; 
1832                 c
->maxax 
= size
.max_aspect
.x
; 
1833                 c
->minay 
= size
.min_aspect
.y
; 
1834                 c
->maxay 
= size
.max_aspect
.y
; 
1837                 c
->minax 
= c
->maxax 
= c
->minay 
= c
->maxay 
= 0; 
1838         c
->isfixed 
= (c
->maxw 
&& c
->minw 
&& c
->maxh 
&& c
->minh
 
1839                         && c
->maxw 
== c
->minw 
&& c
->maxh 
== c
->minh
); 
1843 updatetitle(Client 
*c
) { 
1844         if(!gettextprop(c
->win
, netatom
[NetWMName
], c
->name
, sizeof c
->name
)) 
1845                 gettextprop(c
->win
, wmatom
[WMName
], c
->name
, sizeof c
->name
); 
1849 updatewmhints(Client 
*c
) { 
1852         if((wmh 
= XGetWMHints(dpy
, c
->win
))) { 
1854                         sel
->isurgent 
= False
; 
1856                         c
->isurgent 
= (wmh
->flags 
& XUrgencyHint
) ? True 
: False
; 
1863 view(const char *arg
) { 
1866         for(i 
= 0; i 
< LENGTH(tags
); i
++) 
1867                 tmp
[i
] = (NULL 
== arg
); 
1868         tmp
[idxoftag(arg
)] = True
; 
1870         if(memcmp(seltags
, tmp
, TAGSZ
) != 0) { 
1871                 memcpy(prevtags
, seltags
, TAGSZ
); 
1872                 memcpy(seltags
, tmp
, TAGSZ
); 
1878 viewprevtag(const char *arg
) { 
1880         memcpy(tmp
, seltags
, TAGSZ
); 
1881         memcpy(seltags
, prevtags
, TAGSZ
); 
1882         memcpy(prevtags
, tmp
, TAGSZ
); 
1886 /* There's no way to check accesses to destroyed windows, thus those cases are 
1887  * ignored (especially on UnmapNotify's).  Other types of errors call Xlibs 
1888  * default error handler, which may call exit.  */ 
1890 xerror(Display 
*dpy
, XErrorEvent 
*ee
) { 
1891         if(ee
->error_code 
== BadWindow
 
1892         || (ee
->request_code 
== X_SetInputFocus 
&& ee
->error_code 
== BadMatch
) 
1893         || (ee
->request_code 
== X_PolyText8 
&& ee
->error_code 
== BadDrawable
) 
1894         || (ee
->request_code 
== X_PolyFillRectangle 
&& ee
->error_code 
== BadDrawable
) 
1895         || (ee
->request_code 
== X_PolySegment 
&& ee
->error_code 
== BadDrawable
) 
1896         || (ee
->request_code 
== X_ConfigureWindow 
&& ee
->error_code 
== BadMatch
) 
1897         || (ee
->request_code 
== X_GrabKey 
&& ee
->error_code 
== BadAccess
) 
1898         || (ee
->request_code 
== X_CopyArea 
&& ee
->error_code 
== BadDrawable
)) 
1900         fprintf(stderr
, "dwm: fatal error: request code=%d, error code=%d\n", 
1901                 ee
->request_code
, ee
->error_code
); 
1902         return xerrorxlib(dpy
, ee
); /* may call exit */ 
1906 xerrordummy(Display 
*dpy
, XErrorEvent 
*ee
) { 
1910 /* Startup Error handler to check if another window manager 
1911  * is already running. */ 
1913 xerrorstart(Display 
*dpy
, XErrorEvent 
*ee
) { 
1919 zoom(const char *arg
) { 
1922         if(!sel 
|| lt
->isfloating 
|| sel
->isfloating
) 
1924         if(c 
== nexttiled(clients
)) 
1925                 if(!(c 
= nexttiled(c
->next
))) 
1934 main(int argc
, char *argv
[]) { 
1935         if(argc 
== 2 && !strcmp("-v", argv
[1])) 
1936                 eprint("dwm-"VERSION
", © 2006-2008 dwm engineers, see LICENSE for details\n"); 
1938                 eprint("usage: dwm [-v]\n"); 
1940         setlocale(LC_CTYPE
, ""); 
1941         if(!(dpy 
= XOpenDisplay(0))) 
1942                 eprint("dwm: cannot open display\n");