|
1 /* See LICENSE file for copyright and license details. */ |
|
2 #include <errno.h> |
|
3 #include <locale.h> |
|
4 #include <regex.h> |
|
5 #include <stdarg.h> |
|
6 #include <stdio.h> |
|
7 #include <stdlib.h> |
|
8 #include <string.h> |
|
9 #include <unistd.h> |
|
10 #include <sys/select.h> |
|
11 #include <sys/wait.h> |
|
12 #include <X11/cursorfont.h> |
|
13 #include <X11/keysym.h> |
|
14 #include <X11/Xatom.h> |
|
15 #include <X11/Xproto.h> |
|
16 #include <X11/Xutil.h> |
|
17 |
|
18 /* macros */ |
|
19 #define BUTTONMASK (ButtonPressMask | ButtonReleaseMask) |
|
20 #define CLEANMASK(mask) (mask & ~(numlockmask | LockMask)) |
|
21 #define MOUSEMASK (BUTTONMASK | PointerMotionMask) |
|
22 |
|
23 /* enums */ |
|
24 enum { BarTop, BarBot, BarOff }; /* bar position */ |
|
25 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ |
|
26 enum { ColBorder, ColFG, ColBG, ColLast }; /* color */ |
|
27 enum { NetSupported, NetWMName, NetLast }; /* EWMH atoms */ |
|
28 enum { WMProtocols, WMDelete, WMName, WMState, WMLast };/* default atoms */ |
|
29 |
|
30 /* typedefs */ |
|
31 typedef struct Client Client; |
|
32 struct Client { |
|
33 char name[256]; |
|
34 int x, y, w, h; |
|
35 int rx, ry, rw, rh; /* revert geometry */ |
|
36 int basew, baseh, incw, inch, maxw, maxh, minw, minh; |
|
37 int minax, maxax, minay, maxay; |
|
38 long flags; |
|
39 unsigned int border, oldborder; |
|
40 Bool isbanned, isfixed, ismax, isfloating; |
|
41 Bool *tags; |
|
42 Client *next; |
|
43 Client *prev; |
|
44 Client *snext; |
|
45 Window win; |
|
46 }; |
|
47 |
|
48 typedef struct { |
|
49 int x, y, w, h; |
|
50 unsigned long norm[ColLast]; |
|
51 unsigned long sel[ColLast]; |
|
52 Drawable drawable; |
|
53 GC gc; |
|
54 struct { |
|
55 int ascent; |
|
56 int descent; |
|
57 int height; |
|
58 XFontSet set; |
|
59 XFontStruct *xfont; |
|
60 } font; |
|
61 } DC; /* draw context */ |
|
62 |
|
63 typedef struct { |
|
64 unsigned long mod; |
|
65 KeySym keysym; |
|
66 void (*func)(const char *arg); |
|
67 const char *arg; |
|
68 } Key; |
|
69 |
|
70 typedef struct { |
|
71 const char *symbol; |
|
72 void (*arrange)(void); |
|
73 } Layout; |
|
74 |
|
75 typedef struct { |
|
76 const char *prop; |
|
77 const char *tags; |
|
78 Bool isfloating; |
|
79 } Rule; |
|
80 |
|
81 typedef struct { |
|
82 regex_t *propregex; |
|
83 regex_t *tagregex; |
|
84 } Regs; |
|
85 |
|
86 /* functions */ |
|
87 static void eprint(const char *errstr, ...); |
|
88 static void *emallocz(unsigned int size); |
|
89 static void spawn(const char *arg); |
|
90 static void drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]); |
|
91 static unsigned long initcolor(const char *colstr); |
|
92 static void initfont(const char *fontstr); |
|
93 static Bool isoccupied(unsigned int t); |
|
94 static unsigned int textnw(const char *text, unsigned int len); |
|
95 static void drawtext(const char *text, unsigned long col[ColLast]); |
|
96 static void drawbar(void); |
|
97 static void initstyle(void); |
|
98 static void initbar(void); |
|
99 static unsigned int textw(const char *text); |
|
100 static void togglebar(const char *arg); |
|
101 static void updatebarpos(void); |
|
102 static void attachstack(Client *c); |
|
103 static void detachstack(Client *c); |
|
104 static void grabbuttons(Client *c, Bool focused); |
|
105 static Bool isprotodel(Client *c); |
|
106 static void setclientstate(Client *c, long state); |
|
107 static int xerrordummy(Display *dsply, XErrorEvent *ee); |
|
108 static void ban(Client *c); |
|
109 static void configure(Client *c); |
|
110 static void killclient(const char *arg); |
|
111 static void manage(Window w, XWindowAttributes *wa); |
|
112 static void resize(Client *c, int x, int y, int w, int h, Bool sizehints); |
|
113 static void unban(Client *c); |
|
114 static void unmanage(Client *c); |
|
115 static void updatesizehints(Client *c); |
|
116 static void updatetitle(Client *c); |
|
117 static Client *getclient(Window w); |
|
118 static void movemouse(Client *c); |
|
119 static void resizemouse(Client *c); |
|
120 static void buttonpress(XEvent *e); |
|
121 static void configurerequest(XEvent *e); |
|
122 static void configurenotify(XEvent *e); |
|
123 static void destroynotify(XEvent *e); |
|
124 static void enternotify(XEvent *e); |
|
125 static void expose(XEvent *e); |
|
126 static void keypress(XEvent *e); |
|
127 static void leavenotify(XEvent *e); |
|
128 static void mappingnotify(XEvent *e); |
|
129 static void maprequest(XEvent *e); |
|
130 static void propertynotify(XEvent *e); |
|
131 static void unmapnotify(XEvent *e); |
|
132 static void grabkeys(void); |
|
133 static unsigned int idxoftag(const char *tag); |
|
134 static void floating(void); /* default floating layout */ |
|
135 static void applyrules(Client *c); |
|
136 static void compileregs(void); |
|
137 static void focusnext(const char *arg); |
|
138 static void focusprev(const char *arg); |
|
139 static void initlayouts(void); |
|
140 static Bool isfloating(void); |
|
141 static Bool isvisible(Client *c); |
|
142 static void restack(void); |
|
143 static void setlayout(const char *arg); |
|
144 static void tag(const char *arg); |
|
145 static void togglefloating(const char *arg); |
|
146 static void togglemax(const char *arg); |
|
147 static void toggletag(const char *arg); |
|
148 static void toggleview(const char *arg); |
|
149 static void view(const char *arg); |
|
150 static void cleanup(void); |
|
151 static long getstate(Window w); |
|
152 static void scan(void); |
|
153 static void setup(void); |
|
154 static int xerrorstart(Display *dsply, XErrorEvent *ee); |
|
155 static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size); |
|
156 static void quit(const char *arg); |
|
157 static int xerror(Display *dpy, XErrorEvent *ee); |
|
158 static void arrange(void); |
|
159 static void attach(Client *c); |
|
160 static void detach(Client *c); |
|
161 static void focus(Client *c); |
|
162 static Bool isarrange(void (*func)()); |
|
163 static Client *nexttiled(Client *c); |
|
164 static void setmwfact(const char *arg); |
|
165 static void tile(void); |
|
166 static void zoom(const char *arg); |
|
167 |
|
168 #include "config.h" |
|
169 |
|
170 /* variables */ |
|
171 static char stext[256]; |
|
172 static double mwfact = MWFACT; |
|
173 static int screen, sx, sy, sw, sh, wax, way, waw, wah; |
|
174 static int (*xerrorxlib)(Display *, XErrorEvent *); |
|
175 static unsigned int bh; |
|
176 static unsigned int blw = 0; |
|
177 static unsigned int bpos = BARPOS; |
|
178 static unsigned int ltidx = 0; /* default */ |
|
179 static unsigned int nlayouts = 0; |
|
180 static unsigned int nrules = 0; |
|
181 static unsigned int ntags; |
|
182 static unsigned int numlockmask = 0; |
|
183 static void (*handler[LASTEvent]) (XEvent *) = { |
|
184 [ButtonPress] = buttonpress, |
|
185 [ConfigureRequest] = configurerequest, |
|
186 [ConfigureNotify] = configurenotify, |
|
187 [DestroyNotify] = destroynotify, |
|
188 [EnterNotify] = enternotify, |
|
189 [LeaveNotify] = leavenotify, |
|
190 [Expose] = expose, |
|
191 [KeyPress] = keypress, |
|
192 [MappingNotify] = mappingnotify, |
|
193 [MapRequest] = maprequest, |
|
194 [PropertyNotify] = propertynotify, |
|
195 [UnmapNotify] = unmapnotify |
|
196 }; |
|
197 static Atom wmatom[WMLast], netatom[NetLast]; |
|
198 static Bool otherwm, readin; |
|
199 static Bool running = True; |
|
200 static Bool *seltags; |
|
201 static Bool selscreen = True; |
|
202 static Client *clients = NULL; |
|
203 static Client *sel = NULL; |
|
204 static Client *stack = NULL; |
|
205 static Cursor cursor[CurLast]; |
|
206 static Display *dpy; |
|
207 static DC dc = {0}; |
|
208 static Window barwin, root; |
|
209 static Regs *regs = NULL; |
|
210 |
|
211 static void |
|
212 eprint(const char *errstr, ...) { |
|
213 va_list ap; |
|
214 |
|
215 va_start(ap, errstr); |
|
216 vfprintf(stderr, errstr, ap); |
|
217 va_end(ap); |
|
218 exit(EXIT_FAILURE); |
|
219 } |
|
220 |
|
221 static void * |
|
222 emallocz(unsigned int size) { |
|
223 void *res = calloc(1, size); |
|
224 |
|
225 if(!res) |
|
226 eprint("fatal: could not malloc() %u bytes\n", size); |
|
227 return res; |
|
228 } |
|
229 |
|
230 static void |
|
231 spawn(const char *arg) { |
|
232 static char *shell = NULL; |
|
233 |
|
234 if(!shell && !(shell = getenv("SHELL"))) |
|
235 shell = "/bin/sh"; |
|
236 if(!arg) |
|
237 return; |
|
238 /* The double-fork construct avoids zombie processes and keeps the code |
|
239 * clean from stupid signal handlers. */ |
|
240 if(fork() == 0) { |
|
241 if(fork() == 0) { |
|
242 if(dpy) |
|
243 close(ConnectionNumber(dpy)); |
|
244 setsid(); |
|
245 execl(shell, shell, "-c", arg, (char *)NULL); |
|
246 fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg); |
|
247 perror(" failed"); |
|
248 } |
|
249 exit(0); |
|
250 } |
|
251 wait(0); |
|
252 } |
|
253 |
|
254 static void |
|
255 drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]) { |
|
256 int x; |
|
257 XGCValues gcv; |
|
258 XRectangle r = { dc.x, dc.y, dc.w, dc.h }; |
|
259 |
|
260 gcv.foreground = col[ColFG]; |
|
261 XChangeGC(dpy, dc.gc, GCForeground, &gcv); |
|
262 x = (dc.font.ascent + dc.font.descent + 2) / 4; |
|
263 r.x = dc.x + 1; |
|
264 r.y = dc.y + 1; |
|
265 if(filled) { |
|
266 r.width = r.height = x + 1; |
|
267 XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); |
|
268 } |
|
269 else if(empty) { |
|
270 r.width = r.height = x; |
|
271 XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1); |
|
272 } |
|
273 } |
|
274 |
|
275 static unsigned long |
|
276 initcolor(const char *colstr) { |
|
277 Colormap cmap = DefaultColormap(dpy, screen); |
|
278 XColor color; |
|
279 |
|
280 if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) |
|
281 eprint("error, cannot allocate color '%s'\n", colstr); |
|
282 return color.pixel; |
|
283 } |
|
284 |
|
285 static void |
|
286 initfont(const char *fontstr) { |
|
287 char *def, **missing; |
|
288 int i, n; |
|
289 |
|
290 missing = NULL; |
|
291 if(dc.font.set) |
|
292 XFreeFontSet(dpy, dc.font.set); |
|
293 dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def); |
|
294 if(missing) { |
|
295 while(n--) |
|
296 fprintf(stderr, "dwm: missing fontset: %s\n", missing[n]); |
|
297 XFreeStringList(missing); |
|
298 } |
|
299 if(dc.font.set) { |
|
300 XFontSetExtents *font_extents; |
|
301 XFontStruct **xfonts; |
|
302 char **font_names; |
|
303 dc.font.ascent = dc.font.descent = 0; |
|
304 font_extents = XExtentsOfFontSet(dc.font.set); |
|
305 n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); |
|
306 for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) { |
|
307 if(dc.font.ascent < (*xfonts)->ascent) |
|
308 dc.font.ascent = (*xfonts)->ascent; |
|
309 if(dc.font.descent < (*xfonts)->descent) |
|
310 dc.font.descent = (*xfonts)->descent; |
|
311 xfonts++; |
|
312 } |
|
313 } |
|
314 else { |
|
315 if(dc.font.xfont) |
|
316 XFreeFont(dpy, dc.font.xfont); |
|
317 dc.font.xfont = NULL; |
|
318 if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)) |
|
319 || !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) |
|
320 eprint("error, cannot load font: '%s'\n", fontstr); |
|
321 dc.font.ascent = dc.font.xfont->ascent; |
|
322 dc.font.descent = dc.font.xfont->descent; |
|
323 } |
|
324 dc.font.height = dc.font.ascent + dc.font.descent; |
|
325 } |
|
326 |
|
327 static Bool |
|
328 isoccupied(unsigned int t) { |
|
329 Client *c; |
|
330 |
|
331 for(c = clients; c; c = c->next) |
|
332 if(c->tags[t]) |
|
333 return True; |
|
334 return False; |
|
335 } |
|
336 |
|
337 static unsigned int |
|
338 textnw(const char *text, unsigned int len) { |
|
339 XRectangle r; |
|
340 |
|
341 if(dc.font.set) { |
|
342 XmbTextExtents(dc.font.set, text, len, NULL, &r); |
|
343 return r.width; |
|
344 } |
|
345 return XTextWidth(dc.font.xfont, text, len); |
|
346 } |
|
347 |
|
348 static void |
|
349 drawtext(const char *text, unsigned long col[ColLast]) { |
|
350 int x, y, w, h; |
|
351 static char buf[256]; |
|
352 unsigned int len, olen; |
|
353 XRectangle r = { dc.x, dc.y, dc.w, dc.h }; |
|
354 |
|
355 XSetForeground(dpy, dc.gc, col[ColBG]); |
|
356 XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1); |
|
357 if(!text) |
|
358 return; |
|
359 w = 0; |
|
360 olen = len = strlen(text); |
|
361 if(len >= sizeof buf) |
|
362 len = sizeof buf - 1; |
|
363 memcpy(buf, text, len); |
|
364 buf[len] = 0; |
|
365 h = dc.font.ascent + dc.font.descent; |
|
366 y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent; |
|
367 x = dc.x + (h / 2); |
|
368 /* shorten text if necessary */ |
|
369 while(len && (w = textnw(buf, len)) > dc.w - h) |
|
370 buf[--len] = 0; |
|
371 if(len < olen) { |
|
372 if(len > 1) |
|
373 buf[len - 1] = '.'; |
|
374 if(len > 2) |
|
375 buf[len - 2] = '.'; |
|
376 if(len > 3) |
|
377 buf[len - 3] = '.'; |
|
378 } |
|
379 if(w > dc.w) |
|
380 return; /* too long */ |
|
381 XSetForeground(dpy, dc.gc, col[ColFG]); |
|
382 if(dc.font.set) |
|
383 XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); |
|
384 else |
|
385 XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); |
|
386 } |
|
387 |
|
388 static void |
|
389 drawbar(void) { |
|
390 int i, x; |
|
391 |
|
392 dc.x = dc.y = 0; |
|
393 for(i = 0; i < ntags; i++) { |
|
394 dc.w = textw(tags[i]); |
|
395 if(seltags[i]) { |
|
396 drawtext(tags[i], dc.sel); |
|
397 drawsquare(sel && sel->tags[i], isoccupied(i), dc.sel); |
|
398 } |
|
399 else { |
|
400 drawtext(tags[i], dc.norm); |
|
401 drawsquare(sel && sel->tags[i], isoccupied(i), dc.norm); |
|
402 } |
|
403 dc.x += dc.w; |
|
404 } |
|
405 dc.w = blw; |
|
406 drawtext(layouts[ltidx].symbol, dc.norm); |
|
407 x = dc.x + dc.w; |
|
408 dc.w = textw(stext); |
|
409 dc.x = sw - dc.w; |
|
410 if(dc.x < x) { |
|
411 dc.x = x; |
|
412 dc.w = sw - x; |
|
413 } |
|
414 drawtext(stext, dc.norm); |
|
415 if((dc.w = dc.x - x) > bh) { |
|
416 dc.x = x; |
|
417 if(sel) { |
|
418 drawtext(sel->name, dc.sel); |
|
419 drawsquare(sel->ismax, sel->isfloating, dc.sel); |
|
420 } |
|
421 else |
|
422 drawtext(NULL, dc.norm); |
|
423 } |
|
424 XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0); |
|
425 XSync(dpy, False); |
|
426 } |
|
427 |
|
428 static void |
|
429 initstyle(void) { |
|
430 dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR); |
|
431 dc.norm[ColBG] = initcolor(NORMBGCOLOR); |
|
432 dc.norm[ColFG] = initcolor(NORMFGCOLOR); |
|
433 dc.sel[ColBorder] = initcolor(SELBORDERCOLOR); |
|
434 dc.sel[ColBG] = initcolor(SELBGCOLOR); |
|
435 dc.sel[ColFG] = initcolor(SELFGCOLOR); |
|
436 initfont(FONT); |
|
437 dc.h = bh = dc.font.height + 2; |
|
438 } |
|
439 |
|
440 static void |
|
441 initbar(void) { |
|
442 XSetWindowAttributes wa; |
|
443 |
|
444 wa.override_redirect = 1; |
|
445 wa.background_pixmap = ParentRelative; |
|
446 wa.event_mask = ButtonPressMask | ExposureMask; |
|
447 barwin = XCreateWindow(dpy, root, sx, sy, sw, bh, 0, |
|
448 DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), |
|
449 CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); |
|
450 XDefineCursor(dpy, barwin, cursor[CurNormal]); |
|
451 updatebarpos(); |
|
452 XMapRaised(dpy, barwin); |
|
453 strcpy(stext, "dwm-"VERSION); |
|
454 dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); |
|
455 dc.gc = XCreateGC(dpy, root, 0, 0); |
|
456 XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); |
|
457 if(!dc.font.set) |
|
458 XSetFont(dpy, dc.gc, dc.font.xfont->fid); |
|
459 } |
|
460 |
|
461 static unsigned int |
|
462 textw(const char *text) { |
|
463 return textnw(text, strlen(text)) + dc.font.height; |
|
464 } |
|
465 |
|
466 static void |
|
467 togglebar(const char *arg) { |
|
468 if(bpos == BarOff) |
|
469 bpos = (BARPOS == BarOff) ? BarTop : BARPOS; |
|
470 else |
|
471 bpos = BarOff; |
|
472 updatebarpos(); |
|
473 arrange(); |
|
474 } |
|
475 |
|
476 static void |
|
477 updatebarpos(void) { |
|
478 XEvent ev; |
|
479 |
|
480 wax = sx; |
|
481 way = sy; |
|
482 wah = sh; |
|
483 waw = sw; |
|
484 switch(bpos) { |
|
485 default: |
|
486 wah -= bh; |
|
487 way += bh; |
|
488 XMoveWindow(dpy, barwin, sx, sy); |
|
489 break; |
|
490 case BarBot: |
|
491 wah -= bh; |
|
492 XMoveWindow(dpy, barwin, sx, sy + wah); |
|
493 break; |
|
494 case BarOff: |
|
495 XMoveWindow(dpy, barwin, sx, sy - bh); |
|
496 break; |
|
497 } |
|
498 XSync(dpy, False); |
|
499 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
|
500 } |
|
501 |
|
502 static void |
|
503 attachstack(Client *c) { |
|
504 c->snext = stack; |
|
505 stack = c; |
|
506 } |
|
507 |
|
508 static void |
|
509 detachstack(Client *c) { |
|
510 Client **tc; |
|
511 |
|
512 for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext); |
|
513 *tc = c->snext; |
|
514 } |
|
515 |
|
516 static void |
|
517 grabbuttons(Client *c, Bool focused) { |
|
518 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); |
|
519 |
|
520 if(focused) { |
|
521 XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK, |
|
522 GrabModeAsync, GrabModeSync, None, None); |
|
523 XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK, |
|
524 GrabModeAsync, GrabModeSync, None, None); |
|
525 XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK, |
|
526 GrabModeAsync, GrabModeSync, None, None); |
|
527 XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, |
|
528 GrabModeAsync, GrabModeSync, None, None); |
|
529 |
|
530 XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK, |
|
531 GrabModeAsync, GrabModeSync, None, None); |
|
532 XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK, |
|
533 GrabModeAsync, GrabModeSync, None, None); |
|
534 XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK, |
|
535 GrabModeAsync, GrabModeSync, None, None); |
|
536 XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, |
|
537 GrabModeAsync, GrabModeSync, None, None); |
|
538 |
|
539 XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK, |
|
540 GrabModeAsync, GrabModeSync, None, None); |
|
541 XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK, |
|
542 GrabModeAsync, GrabModeSync, None, None); |
|
543 XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK, |
|
544 GrabModeAsync, GrabModeSync, None, None); |
|
545 XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, |
|
546 GrabModeAsync, GrabModeSync, None, None); |
|
547 } |
|
548 else |
|
549 XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, |
|
550 GrabModeAsync, GrabModeSync, None, None); |
|
551 } |
|
552 |
|
553 static Bool |
|
554 isprotodel(Client *c) { |
|
555 int i, n; |
|
556 Atom *protocols; |
|
557 Bool ret = False; |
|
558 |
|
559 if(XGetWMProtocols(dpy, c->win, &protocols, &n)) { |
|
560 for(i = 0; !ret && i < n; i++) |
|
561 if(protocols[i] == wmatom[WMDelete]) |
|
562 ret = True; |
|
563 XFree(protocols); |
|
564 } |
|
565 return ret; |
|
566 } |
|
567 |
|
568 static void |
|
569 setclientstate(Client *c, long state) { |
|
570 long data[] = {state, None}; |
|
571 |
|
572 XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, |
|
573 PropModeReplace, (unsigned char *)data, 2); |
|
574 } |
|
575 |
|
576 static int |
|
577 xerrordummy(Display *dsply, XErrorEvent *ee) { |
|
578 return 0; |
|
579 } |
|
580 |
|
581 static void |
|
582 ban(Client *c) { |
|
583 if(c->isbanned) |
|
584 return; |
|
585 XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); |
|
586 c->isbanned = True; |
|
587 } |
|
588 |
|
589 static void |
|
590 configure(Client *c) { |
|
591 XConfigureEvent ce; |
|
592 |
|
593 ce.type = ConfigureNotify; |
|
594 ce.display = dpy; |
|
595 ce.event = c->win; |
|
596 ce.window = c->win; |
|
597 ce.x = c->x; |
|
598 ce.y = c->y; |
|
599 ce.width = c->w; |
|
600 ce.height = c->h; |
|
601 ce.border_width = c->border; |
|
602 ce.above = None; |
|
603 ce.override_redirect = False; |
|
604 XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); |
|
605 } |
|
606 |
|
607 static void |
|
608 killclient(const char *arg) { |
|
609 XEvent ev; |
|
610 |
|
611 if(!sel) |
|
612 return; |
|
613 if(isprotodel(sel)) { |
|
614 ev.type = ClientMessage; |
|
615 ev.xclient.window = sel->win; |
|
616 ev.xclient.message_type = wmatom[WMProtocols]; |
|
617 ev.xclient.format = 32; |
|
618 ev.xclient.data.l[0] = wmatom[WMDelete]; |
|
619 ev.xclient.data.l[1] = CurrentTime; |
|
620 XSendEvent(dpy, sel->win, False, NoEventMask, &ev); |
|
621 } |
|
622 else |
|
623 XKillClient(dpy, sel->win); |
|
624 } |
|
625 |
|
626 static void |
|
627 manage(Window w, XWindowAttributes *wa) { |
|
628 unsigned int i; |
|
629 Client *c, *t = NULL; |
|
630 Window trans; |
|
631 Status rettrans; |
|
632 XWindowChanges wc; |
|
633 |
|
634 c = emallocz(sizeof(Client)); |
|
635 c->tags = emallocz(ntags * sizeof(Bool)); |
|
636 c->win = w; |
|
637 c->x = wa->x; |
|
638 c->y = wa->y; |
|
639 c->w = wa->width; |
|
640 c->h = wa->height; |
|
641 c->oldborder = wa->border_width; |
|
642 if(c->w == sw && c->h == sh) { |
|
643 c->x = sx; |
|
644 c->y = sy; |
|
645 c->border = wa->border_width; |
|
646 } |
|
647 else { |
|
648 if(c->x + c->w + 2 * c->border > wax + waw) |
|
649 c->x = wax + waw - c->w - 2 * c->border; |
|
650 if(c->y + c->h + 2 * c->border > way + wah) |
|
651 c->y = way + wah - c->h - 2 * c->border; |
|
652 if(c->x < wax) |
|
653 c->x = wax; |
|
654 if(c->y < way) |
|
655 c->y = way; |
|
656 c->border = BORDERPX; |
|
657 } |
|
658 wc.border_width = c->border; |
|
659 XConfigureWindow(dpy, w, CWBorderWidth, &wc); |
|
660 XSetWindowBorder(dpy, w, dc.norm[ColBorder]); |
|
661 configure(c); /* propagates border_width, if size doesn't change */ |
|
662 updatesizehints(c); |
|
663 XSelectInput(dpy, w, |
|
664 StructureNotifyMask | PropertyChangeMask | EnterWindowMask); |
|
665 grabbuttons(c, False); |
|
666 updatetitle(c); |
|
667 if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success)) |
|
668 for(t = clients; t && t->win != trans; t = t->next); |
|
669 if(t) |
|
670 for(i = 0; i < ntags; i++) |
|
671 c->tags[i] = t->tags[i]; |
|
672 applyrules(c); |
|
673 if(!c->isfloating) |
|
674 c->isfloating = (rettrans == Success) || c->isfixed; |
|
675 attach(c); |
|
676 attachstack(c); |
|
677 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); /* some windows require this */ |
|
678 ban(c); |
|
679 XMapWindow(dpy, c->win); |
|
680 setclientstate(c, NormalState); |
|
681 arrange(); |
|
682 } |
|
683 |
|
684 static void |
|
685 resize(Client *c, int x, int y, int w, int h, Bool sizehints) { |
|
686 double dx, dy, max, min, ratio; |
|
687 XWindowChanges wc; |
|
688 |
|
689 if(sizehints) { |
|
690 if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0 && (w - c->basew) > 0) { |
|
691 dx = (double)(w - c->basew); |
|
692 dy = (double)(h - c->baseh); |
|
693 min = (double)(c->minax) / (double)(c->minay); |
|
694 max = (double)(c->maxax) / (double)(c->maxay); |
|
695 ratio = dx / dy; |
|
696 if(max > 0 && min > 0 && ratio > 0) { |
|
697 if(ratio < min) { |
|
698 dy = (dx * min + dy) / (min * min + 1); |
|
699 dx = dy * min; |
|
700 w = (int)dx + c->basew; |
|
701 h = (int)dy + c->baseh; |
|
702 } |
|
703 else if(ratio > max) { |
|
704 dy = (dx * min + dy) / (max * max + 1); |
|
705 dx = dy * min; |
|
706 w = (int)dx + c->basew; |
|
707 h = (int)dy + c->baseh; |
|
708 } |
|
709 } |
|
710 } |
|
711 if(c->minw && w < c->minw) |
|
712 w = c->minw; |
|
713 if(c->minh && h < c->minh) |
|
714 h = c->minh; |
|
715 if(c->maxw && w > c->maxw) |
|
716 w = c->maxw; |
|
717 if(c->maxh && h > c->maxh) |
|
718 h = c->maxh; |
|
719 if(c->incw) |
|
720 w -= (w - c->basew) % c->incw; |
|
721 if(c->inch) |
|
722 h -= (h - c->baseh) % c->inch; |
|
723 } |
|
724 if(w <= 0 || h <= 0) |
|
725 return; |
|
726 /* offscreen appearance fixes */ |
|
727 if(x > sw) |
|
728 x = sw - w - 2 * c->border; |
|
729 if(y > sh) |
|
730 y = sh - h - 2 * c->border; |
|
731 if(x + w + 2 * c->border < sx) |
|
732 x = sx; |
|
733 if(y + h + 2 * c->border < sy) |
|
734 y = sy; |
|
735 if(c->x != x || c->y != y || c->w != w || c->h != h) { |
|
736 c->x = wc.x = x; |
|
737 c->y = wc.y = y; |
|
738 c->w = wc.width = w; |
|
739 c->h = wc.height = h; |
|
740 wc.border_width = c->border; |
|
741 XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc); |
|
742 configure(c); |
|
743 XSync(dpy, False); |
|
744 } |
|
745 } |
|
746 |
|
747 static void |
|
748 unban(Client *c) { |
|
749 if(!c->isbanned) |
|
750 return; |
|
751 XMoveWindow(dpy, c->win, c->x, c->y); |
|
752 c->isbanned = False; |
|
753 } |
|
754 |
|
755 static void |
|
756 unmanage(Client *c) { |
|
757 XWindowChanges wc; |
|
758 |
|
759 wc.border_width = c->oldborder; |
|
760 /* The server grab construct avoids race conditions. */ |
|
761 XGrabServer(dpy); |
|
762 XSetErrorHandler(xerrordummy); |
|
763 XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ |
|
764 detach(c); |
|
765 detachstack(c); |
|
766 if(sel == c) |
|
767 focus(NULL); |
|
768 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); |
|
769 setclientstate(c, WithdrawnState); |
|
770 free(c->tags); |
|
771 free(c); |
|
772 XSync(dpy, False); |
|
773 XSetErrorHandler(xerror); |
|
774 XUngrabServer(dpy); |
|
775 arrange(); |
|
776 } |
|
777 |
|
778 static void |
|
779 updatesizehints(Client *c) { |
|
780 long msize; |
|
781 XSizeHints size; |
|
782 |
|
783 if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) |
|
784 size.flags = PSize; |
|
785 c->flags = size.flags; |
|
786 if(c->flags & PBaseSize) { |
|
787 c->basew = size.base_width; |
|
788 c->baseh = size.base_height; |
|
789 } |
|
790 else if(c->flags & PMinSize) { |
|
791 c->basew = size.min_width; |
|
792 c->baseh = size.min_height; |
|
793 } |
|
794 else |
|
795 c->basew = c->baseh = 0; |
|
796 if(c->flags & PResizeInc) { |
|
797 c->incw = size.width_inc; |
|
798 c->inch = size.height_inc; |
|
799 } |
|
800 else |
|
801 c->incw = c->inch = 0; |
|
802 if(c->flags & PMaxSize) { |
|
803 c->maxw = size.max_width; |
|
804 c->maxh = size.max_height; |
|
805 } |
|
806 else |
|
807 c->maxw = c->maxh = 0; |
|
808 if(c->flags & PMinSize) { |
|
809 c->minw = size.min_width; |
|
810 c->minh = size.min_height; |
|
811 } |
|
812 else if(c->flags & PBaseSize) { |
|
813 c->minw = size.base_width; |
|
814 c->minh = size.base_height; |
|
815 } |
|
816 else |
|
817 c->minw = c->minh = 0; |
|
818 if(c->flags & PAspect) { |
|
819 c->minax = size.min_aspect.x; |
|
820 c->maxax = size.max_aspect.x; |
|
821 c->minay = size.min_aspect.y; |
|
822 c->maxay = size.max_aspect.y; |
|
823 } |
|
824 else |
|
825 c->minax = c->maxax = c->minay = c->maxay = 0; |
|
826 c->isfixed = (c->maxw && c->minw && c->maxh && c->minh |
|
827 && c->maxw == c->minw && c->maxh == c->minh); |
|
828 } |
|
829 |
|
830 static void |
|
831 updatetitle(Client *c) { |
|
832 if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) |
|
833 gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name); |
|
834 } |
|
835 |
|
836 static Client * |
|
837 getclient(Window w) { |
|
838 Client *c; |
|
839 |
|
840 for(c = clients; c && c->win != w; c = c->next); |
|
841 return c; |
|
842 } |
|
843 |
|
844 static void |
|
845 movemouse(Client *c) { |
|
846 int x1, y1, ocx, ocy, di, nx, ny; |
|
847 unsigned int dui; |
|
848 Window dummy; |
|
849 XEvent ev; |
|
850 |
|
851 ocx = nx = c->x; |
|
852 ocy = ny = c->y; |
|
853 if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, |
|
854 None, cursor[CurMove], CurrentTime) != GrabSuccess) |
|
855 return; |
|
856 c->ismax = False; |
|
857 XQueryPointer(dpy, root, &dummy, &dummy, &x1, &y1, &di, &di, &dui); |
|
858 for(;;) { |
|
859 XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev); |
|
860 switch (ev.type) { |
|
861 case ButtonRelease: |
|
862 XUngrabPointer(dpy, CurrentTime); |
|
863 return; |
|
864 case ConfigureRequest: |
|
865 case Expose: |
|
866 case MapRequest: |
|
867 handler[ev.type](&ev); |
|
868 break; |
|
869 case MotionNotify: |
|
870 XSync(dpy, False); |
|
871 nx = ocx + (ev.xmotion.x - x1); |
|
872 ny = ocy + (ev.xmotion.y - y1); |
|
873 if(abs(wax + nx) < SNAP) |
|
874 nx = wax; |
|
875 else if(abs((wax + waw) - (nx + c->w + 2 * c->border)) < SNAP) |
|
876 nx = wax + waw - c->w - 2 * c->border; |
|
877 if(abs(way - ny) < SNAP) |
|
878 ny = way; |
|
879 else if(abs((way + wah) - (ny + c->h + 2 * c->border)) < SNAP) |
|
880 ny = way + wah - c->h - 2 * c->border; |
|
881 resize(c, nx, ny, c->w, c->h, False); |
|
882 break; |
|
883 } |
|
884 } |
|
885 } |
|
886 |
|
887 static void |
|
888 resizemouse(Client *c) { |
|
889 int ocx, ocy; |
|
890 int nw, nh; |
|
891 XEvent ev; |
|
892 |
|
893 ocx = c->x; |
|
894 ocy = c->y; |
|
895 if(XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, |
|
896 None, cursor[CurResize], CurrentTime) != GrabSuccess) |
|
897 return; |
|
898 c->ismax = False; |
|
899 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->border - 1, c->h + c->border - 1); |
|
900 for(;;) { |
|
901 XMaskEvent(dpy, MOUSEMASK | ExposureMask | SubstructureRedirectMask , &ev); |
|
902 switch(ev.type) { |
|
903 case ButtonRelease: |
|
904 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, |
|
905 c->w + c->border - 1, c->h + c->border - 1); |
|
906 XUngrabPointer(dpy, CurrentTime); |
|
907 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
|
908 return; |
|
909 case ConfigureRequest: |
|
910 case Expose: |
|
911 case MapRequest: |
|
912 handler[ev.type](&ev); |
|
913 break; |
|
914 case MotionNotify: |
|
915 XSync(dpy, False); |
|
916 if((nw = ev.xmotion.x - ocx - 2 * c->border + 1) <= 0) |
|
917 nw = 1; |
|
918 if((nh = ev.xmotion.y - ocy - 2 * c->border + 1) <= 0) |
|
919 nh = 1; |
|
920 resize(c, c->x, c->y, nw, nh, True); |
|
921 break; |
|
922 } |
|
923 } |
|
924 } |
|
925 |
|
926 static void |
|
927 buttonpress(XEvent *e) { |
|
928 unsigned int i, x; |
|
929 Client *c; |
|
930 XButtonPressedEvent *ev = &e->xbutton; |
|
931 |
|
932 if(barwin == ev->window) { |
|
933 x = 0; |
|
934 for(i = 0; i < ntags; i++) { |
|
935 x += textw(tags[i]); |
|
936 if(ev->x < x) { |
|
937 if(ev->button == Button1) { |
|
938 if(ev->state & MODKEY) |
|
939 tag(tags[i]); |
|
940 else |
|
941 view(tags[i]); |
|
942 } |
|
943 else if(ev->button == Button3) { |
|
944 if(ev->state & MODKEY) |
|
945 toggletag(tags[i]); |
|
946 else |
|
947 toggleview(tags[i]); |
|
948 } |
|
949 return; |
|
950 } |
|
951 } |
|
952 if((ev->x < x + blw) && ev->button == Button1) |
|
953 setlayout(NULL); |
|
954 } |
|
955 else if((c = getclient(ev->window))) { |
|
956 focus(c); |
|
957 if(CLEANMASK(ev->state) != MODKEY) |
|
958 return; |
|
959 if(ev->button == Button1 && (isfloating() || c->isfloating)) { |
|
960 restack(); |
|
961 movemouse(c); |
|
962 } |
|
963 else if(ev->button == Button2) |
|
964 zoom(NULL); |
|
965 else if(ev->button == Button3 |
|
966 && (isfloating() || c->isfloating) && !c->isfixed) |
|
967 { |
|
968 restack(); |
|
969 resizemouse(c); |
|
970 } |
|
971 } |
|
972 } |
|
973 |
|
974 static void |
|
975 configurerequest(XEvent *e) { |
|
976 Client *c; |
|
977 XConfigureRequestEvent *ev = &e->xconfigurerequest; |
|
978 XWindowChanges wc; |
|
979 |
|
980 if((c = getclient(ev->window))) { |
|
981 c->ismax = False; |
|
982 if(ev->value_mask & CWBorderWidth) |
|
983 c->border = ev->border_width; |
|
984 if(c->isfixed || c->isfloating || isfloating()) { |
|
985 if(ev->value_mask & CWX) |
|
986 c->x = ev->x; |
|
987 if(ev->value_mask & CWY) |
|
988 c->y = ev->y; |
|
989 if(ev->value_mask & CWWidth) |
|
990 c->w = ev->width; |
|
991 if(ev->value_mask & CWHeight) |
|
992 c->h = ev->height; |
|
993 if((c->x + c->w) > sw && c->isfloating) |
|
994 c->x = sw / 2 - c->w / 2; /* center in x direction */ |
|
995 if((c->y + c->h) > sh && c->isfloating) |
|
996 c->y = sh / 2 - c->h / 2; /* center in y direction */ |
|
997 if((ev->value_mask & (CWX | CWY)) |
|
998 && !(ev->value_mask & (CWWidth | CWHeight))) |
|
999 configure(c); |
|
1000 if(isvisible(c)) |
|
1001 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); |
|
1002 } |
|
1003 else |
|
1004 configure(c); |
|
1005 } |
|
1006 else { |
|
1007 wc.x = ev->x; |
|
1008 wc.y = ev->y; |
|
1009 wc.width = ev->width; |
|
1010 wc.height = ev->height; |
|
1011 wc.border_width = ev->border_width; |
|
1012 wc.sibling = ev->above; |
|
1013 wc.stack_mode = ev->detail; |
|
1014 XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); |
|
1015 } |
|
1016 XSync(dpy, False); |
|
1017 } |
|
1018 |
|
1019 static void |
|
1020 configurenotify(XEvent *e) { |
|
1021 XConfigureEvent *ev = &e->xconfigure; |
|
1022 |
|
1023 if (ev->window == root && (ev->width != sw || ev->height != sh)) { |
|
1024 sw = ev->width; |
|
1025 sh = ev->height; |
|
1026 XFreePixmap(dpy, dc.drawable); |
|
1027 dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); |
|
1028 XResizeWindow(dpy, barwin, sw, bh); |
|
1029 updatebarpos(); |
|
1030 arrange(); |
|
1031 } |
|
1032 } |
|
1033 |
|
1034 static void |
|
1035 destroynotify(XEvent *e) { |
|
1036 Client *c; |
|
1037 XDestroyWindowEvent *ev = &e->xdestroywindow; |
|
1038 |
|
1039 if((c = getclient(ev->window))) |
|
1040 unmanage(c); |
|
1041 } |
|
1042 |
|
1043 static void |
|
1044 enternotify(XEvent *e) { |
|
1045 Client *c; |
|
1046 XCrossingEvent *ev = &e->xcrossing; |
|
1047 |
|
1048 if(ev->mode != NotifyNormal || ev->detail == NotifyInferior) |
|
1049 return; |
|
1050 if((c = getclient(ev->window))) |
|
1051 focus(c); |
|
1052 else if(ev->window == root) { |
|
1053 selscreen = True; |
|
1054 focus(NULL); |
|
1055 } |
|
1056 } |
|
1057 |
|
1058 static void |
|
1059 expose(XEvent *e) { |
|
1060 XExposeEvent *ev = &e->xexpose; |
|
1061 |
|
1062 if(ev->count == 0) { |
|
1063 if(barwin == ev->window) |
|
1064 drawbar(); |
|
1065 } |
|
1066 } |
|
1067 |
|
1068 static void |
|
1069 keypress(XEvent *e) { |
|
1070 KEYS |
|
1071 unsigned int len = sizeof keys / sizeof keys[0]; |
|
1072 unsigned int i; |
|
1073 KeySym keysym; |
|
1074 XKeyEvent *ev = &e->xkey; |
|
1075 |
|
1076 keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); |
|
1077 for(i = 0; i < len; i++) |
|
1078 if(keysym == keys[i].keysym |
|
1079 && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)) |
|
1080 { |
|
1081 if(keys[i].func) |
|
1082 keys[i].func(keys[i].arg); |
|
1083 } |
|
1084 } |
|
1085 |
|
1086 static void |
|
1087 leavenotify(XEvent *e) { |
|
1088 XCrossingEvent *ev = &e->xcrossing; |
|
1089 |
|
1090 if((ev->window == root) && !ev->same_screen) { |
|
1091 selscreen = False; |
|
1092 focus(NULL); |
|
1093 } |
|
1094 } |
|
1095 |
|
1096 static void |
|
1097 mappingnotify(XEvent *e) { |
|
1098 XMappingEvent *ev = &e->xmapping; |
|
1099 |
|
1100 XRefreshKeyboardMapping(ev); |
|
1101 if(ev->request == MappingKeyboard) |
|
1102 grabkeys(); |
|
1103 } |
|
1104 |
|
1105 static void |
|
1106 maprequest(XEvent *e) { |
|
1107 static XWindowAttributes wa; |
|
1108 XMapRequestEvent *ev = &e->xmaprequest; |
|
1109 |
|
1110 if(!XGetWindowAttributes(dpy, ev->window, &wa)) |
|
1111 return; |
|
1112 if(wa.override_redirect) |
|
1113 return; |
|
1114 if(!getclient(ev->window)) |
|
1115 manage(ev->window, &wa); |
|
1116 } |
|
1117 |
|
1118 static void |
|
1119 propertynotify(XEvent *e) { |
|
1120 Client *c; |
|
1121 Window trans; |
|
1122 XPropertyEvent *ev = &e->xproperty; |
|
1123 |
|
1124 if(ev->state == PropertyDelete) |
|
1125 return; /* ignore */ |
|
1126 if((c = getclient(ev->window))) { |
|
1127 switch (ev->atom) { |
|
1128 default: break; |
|
1129 case XA_WM_TRANSIENT_FOR: |
|
1130 XGetTransientForHint(dpy, c->win, &trans); |
|
1131 if(!c->isfloating && (c->isfloating = (getclient(trans) != NULL))) |
|
1132 arrange(); |
|
1133 break; |
|
1134 case XA_WM_NORMAL_HINTS: |
|
1135 updatesizehints(c); |
|
1136 break; |
|
1137 } |
|
1138 if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { |
|
1139 updatetitle(c); |
|
1140 if(c == sel) |
|
1141 drawbar(); |
|
1142 } |
|
1143 } |
|
1144 } |
|
1145 |
|
1146 static void |
|
1147 unmapnotify(XEvent *e) { |
|
1148 Client *c; |
|
1149 XUnmapEvent *ev = &e->xunmap; |
|
1150 |
|
1151 if((c = getclient(ev->window))) |
|
1152 unmanage(c); |
|
1153 } |
|
1154 |
|
1155 static void |
|
1156 grabkeys(void) { |
|
1157 KEYS |
|
1158 unsigned int len = sizeof keys / sizeof keys[0]; |
|
1159 unsigned int i; |
|
1160 KeyCode code; |
|
1161 |
|
1162 XUngrabKey(dpy, AnyKey, AnyModifier, root); |
|
1163 for(i = 0; i < len; i++) { |
|
1164 code = XKeysymToKeycode(dpy, keys[i].keysym); |
|
1165 XGrabKey(dpy, code, keys[i].mod, root, True, |
|
1166 GrabModeAsync, GrabModeAsync); |
|
1167 XGrabKey(dpy, code, keys[i].mod | LockMask, root, True, |
|
1168 GrabModeAsync, GrabModeAsync); |
|
1169 XGrabKey(dpy, code, keys[i].mod | numlockmask, root, True, |
|
1170 GrabModeAsync, GrabModeAsync); |
|
1171 XGrabKey(dpy, code, keys[i].mod | numlockmask | LockMask, root, True, |
|
1172 GrabModeAsync, GrabModeAsync); |
|
1173 } |
|
1174 } |
|
1175 |
|
1176 static unsigned int |
|
1177 idxoftag(const char *tag) { |
|
1178 unsigned int i; |
|
1179 |
|
1180 for(i = 0; i < ntags; i++) |
|
1181 if(tags[i] == tag) |
|
1182 return i; |
|
1183 return 0; |
|
1184 } |
|
1185 |
|
1186 static void |
|
1187 floating(void) { /* default floating layout */ |
|
1188 Client *c; |
|
1189 |
|
1190 for(c = clients; c; c = c->next) |
|
1191 if(isvisible(c)) |
|
1192 resize(c, c->x, c->y, c->w, c->h, True); |
|
1193 } |
|
1194 |
|
1195 static void |
|
1196 applyrules(Client *c) { |
|
1197 static char buf[512]; |
|
1198 unsigned int i, j; |
|
1199 regmatch_t tmp; |
|
1200 Bool matched = False; |
|
1201 XClassHint ch = { 0 }; |
|
1202 |
|
1203 /* rule matching */ |
|
1204 XGetClassHint(dpy, c->win, &ch); |
|
1205 snprintf(buf, sizeof buf, "%s:%s:%s", |
|
1206 ch.res_class ? ch.res_class : "", |
|
1207 ch.res_name ? ch.res_name : "", c->name); |
|
1208 for(i = 0; i < nrules; i++) |
|
1209 if(regs[i].propregex && !regexec(regs[i].propregex, buf, 1, &tmp, 0)) { |
|
1210 c->isfloating = rules[i].isfloating; |
|
1211 for(j = 0; regs[i].tagregex && j < ntags; j++) { |
|
1212 if(!regexec(regs[i].tagregex, tags[j], 1, &tmp, 0)) { |
|
1213 matched = True; |
|
1214 c->tags[j] = True; |
|
1215 } |
|
1216 } |
|
1217 } |
|
1218 if(ch.res_class) |
|
1219 XFree(ch.res_class); |
|
1220 if(ch.res_name) |
|
1221 XFree(ch.res_name); |
|
1222 if(!matched) |
|
1223 for(i = 0; i < ntags; i++) |
|
1224 c->tags[i] = seltags[i]; |
|
1225 } |
|
1226 |
|
1227 static void |
|
1228 compileregs(void) { |
|
1229 unsigned int i; |
|
1230 regex_t *reg; |
|
1231 |
|
1232 if(regs) |
|
1233 return; |
|
1234 nrules = sizeof rules / sizeof rules[0]; |
|
1235 regs = emallocz(nrules * sizeof(Regs)); |
|
1236 for(i = 0; i < nrules; i++) { |
|
1237 if(rules[i].prop) { |
|
1238 reg = emallocz(sizeof(regex_t)); |
|
1239 if(regcomp(reg, rules[i].prop, REG_EXTENDED)) |
|
1240 free(reg); |
|
1241 else |
|
1242 regs[i].propregex = reg; |
|
1243 } |
|
1244 if(rules[i].tags) { |
|
1245 reg = emallocz(sizeof(regex_t)); |
|
1246 if(regcomp(reg, rules[i].tags, REG_EXTENDED)) |
|
1247 free(reg); |
|
1248 else |
|
1249 regs[i].tagregex = reg; |
|
1250 } |
|
1251 } |
|
1252 } |
|
1253 |
|
1254 static void |
|
1255 focusnext(const char *arg) { |
|
1256 Client *c; |
|
1257 |
|
1258 if(!sel) |
|
1259 return; |
|
1260 for(c = sel->next; c && !isvisible(c); c = c->next); |
|
1261 if(!c) |
|
1262 for(c = clients; c && !isvisible(c); c = c->next); |
|
1263 if(c) { |
|
1264 focus(c); |
|
1265 restack(); |
|
1266 } |
|
1267 } |
|
1268 |
|
1269 static void |
|
1270 focusprev(const char *arg) { |
|
1271 Client *c; |
|
1272 |
|
1273 if(!sel) |
|
1274 return; |
|
1275 for(c = sel->prev; c && !isvisible(c); c = c->prev); |
|
1276 if(!c) { |
|
1277 for(c = clients; c && c->next; c = c->next); |
|
1278 for(; c && !isvisible(c); c = c->prev); |
|
1279 } |
|
1280 if(c) { |
|
1281 focus(c); |
|
1282 restack(); |
|
1283 } |
|
1284 } |
|
1285 |
|
1286 static void |
|
1287 initlayouts(void) { |
|
1288 unsigned int i, w; |
|
1289 |
|
1290 nlayouts = sizeof layouts / sizeof layouts[0]; |
|
1291 for(blw = i = 0; i < nlayouts; i++) { |
|
1292 w = textw(layouts[i].symbol); |
|
1293 if(w > blw) |
|
1294 blw = w; |
|
1295 } |
|
1296 } |
|
1297 |
|
1298 static Bool |
|
1299 isfloating(void) { |
|
1300 return layouts[ltidx].arrange == floating; |
|
1301 } |
|
1302 |
|
1303 static Bool |
|
1304 isvisible(Client *c) { |
|
1305 unsigned int i; |
|
1306 |
|
1307 for(i = 0; i < ntags; i++) |
|
1308 if(c->tags[i] && seltags[i]) |
|
1309 return True; |
|
1310 return False; |
|
1311 } |
|
1312 |
|
1313 static void |
|
1314 restack(void) { |
|
1315 Client *c; |
|
1316 XEvent ev; |
|
1317 XWindowChanges wc; |
|
1318 |
|
1319 drawbar(); |
|
1320 if(!sel) |
|
1321 return; |
|
1322 if(sel->isfloating || isfloating()) |
|
1323 XRaiseWindow(dpy, sel->win); |
|
1324 if(!isfloating()) { |
|
1325 wc.stack_mode = Below; |
|
1326 wc.sibling = barwin; |
|
1327 if(!sel->isfloating) { |
|
1328 XConfigureWindow(dpy, sel->win, CWSibling | CWStackMode, &wc); |
|
1329 wc.sibling = sel->win; |
|
1330 } |
|
1331 for(c = nexttiled(clients); c; c = nexttiled(c->next)) { |
|
1332 if(c == sel) |
|
1333 continue; |
|
1334 XConfigureWindow(dpy, c->win, CWSibling | CWStackMode, &wc); |
|
1335 wc.sibling = c->win; |
|
1336 } |
|
1337 } |
|
1338 XSync(dpy, False); |
|
1339 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
|
1340 } |
|
1341 |
|
1342 static void |
|
1343 setlayout(const char *arg) { |
|
1344 unsigned int i; |
|
1345 |
|
1346 if(!arg) { |
|
1347 if(++ltidx == nlayouts) |
|
1348 ltidx = 0;; |
|
1349 } |
|
1350 else { |
|
1351 for(i = 0; i < nlayouts; i++) |
|
1352 if(!strcmp(arg, layouts[i].symbol)) |
|
1353 break; |
|
1354 if(i == nlayouts) |
|
1355 return; |
|
1356 ltidx = i; |
|
1357 } |
|
1358 if(sel) |
|
1359 arrange(); |
|
1360 else |
|
1361 drawbar(); |
|
1362 } |
|
1363 |
|
1364 static void |
|
1365 tag(const char *arg) { |
|
1366 unsigned int i; |
|
1367 |
|
1368 if(!sel) |
|
1369 return; |
|
1370 for(i = 0; i < ntags; i++) |
|
1371 sel->tags[i] = arg == NULL; |
|
1372 i = idxoftag(arg); |
|
1373 if(i >= 0 && i < ntags) |
|
1374 sel->tags[i] = True; |
|
1375 arrange(); |
|
1376 } |
|
1377 |
|
1378 static void |
|
1379 togglefloating(const char *arg) { |
|
1380 if(!sel) |
|
1381 return; |
|
1382 sel->isfloating = !sel->isfloating; |
|
1383 if(sel->isfloating) |
|
1384 resize(sel, sel->x, sel->y, sel->w, sel->h, True); |
|
1385 arrange(); |
|
1386 } |
|
1387 |
|
1388 static void |
|
1389 togglemax(const char *arg) { |
|
1390 XEvent ev; |
|
1391 |
|
1392 if(!sel || (!isfloating() && !sel->isfloating) || sel->isfixed) |
|
1393 return; |
|
1394 if((sel->ismax = !sel->ismax)) { |
|
1395 sel->rx = sel->x; |
|
1396 sel->ry = sel->y; |
|
1397 sel->rw = sel->w; |
|
1398 sel->rh = sel->h; |
|
1399 resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->border, True); |
|
1400 } |
|
1401 else |
|
1402 resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True); |
|
1403 drawbar(); |
|
1404 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
|
1405 } |
|
1406 |
|
1407 static void |
|
1408 toggletag(const char *arg) { |
|
1409 unsigned int i, j; |
|
1410 |
|
1411 if(!sel) |
|
1412 return; |
|
1413 i = idxoftag(arg); |
|
1414 sel->tags[i] = !sel->tags[i]; |
|
1415 for(j = 0; j < ntags && !sel->tags[j]; j++); |
|
1416 if(j == ntags) |
|
1417 sel->tags[i] = True; |
|
1418 arrange(); |
|
1419 } |
|
1420 |
|
1421 static void |
|
1422 toggleview(const char *arg) { |
|
1423 unsigned int i, j; |
|
1424 |
|
1425 i = idxoftag(arg); |
|
1426 seltags[i] = !seltags[i]; |
|
1427 for(j = 0; j < ntags && !seltags[j]; j++); |
|
1428 if(j == ntags) |
|
1429 seltags[i] = True; /* cannot toggle last view */ |
|
1430 arrange(); |
|
1431 } |
|
1432 |
|
1433 static void |
|
1434 view(const char *arg) { |
|
1435 unsigned int i; |
|
1436 |
|
1437 for(i = 0; i < ntags; i++) |
|
1438 seltags[i] = arg == NULL; |
|
1439 i = idxoftag(arg); |
|
1440 if(i >= 0 && i < ntags) |
|
1441 seltags[i] = True; |
|
1442 arrange(); |
|
1443 } |
|
1444 |
|
1445 static void |
|
1446 cleanup(void) { |
|
1447 close(STDIN_FILENO); |
|
1448 while(stack) { |
|
1449 unban(stack); |
|
1450 unmanage(stack); |
|
1451 } |
|
1452 if(dc.font.set) |
|
1453 XFreeFontSet(dpy, dc.font.set); |
|
1454 else |
|
1455 XFreeFont(dpy, dc.font.xfont); |
|
1456 XUngrabKey(dpy, AnyKey, AnyModifier, root); |
|
1457 XFreePixmap(dpy, dc.drawable); |
|
1458 XFreeGC(dpy, dc.gc); |
|
1459 XDestroyWindow(dpy, barwin); |
|
1460 XFreeCursor(dpy, cursor[CurNormal]); |
|
1461 XFreeCursor(dpy, cursor[CurResize]); |
|
1462 XFreeCursor(dpy, cursor[CurMove]); |
|
1463 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); |
|
1464 XSync(dpy, False); |
|
1465 free(seltags); |
|
1466 } |
|
1467 |
|
1468 static long |
|
1469 getstate(Window w) { |
|
1470 int format, status; |
|
1471 long result = -1; |
|
1472 unsigned char *p = NULL; |
|
1473 unsigned long n, extra; |
|
1474 Atom real; |
|
1475 |
|
1476 status = XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], |
|
1477 &real, &format, &n, &extra, (unsigned char **)&p); |
|
1478 if(status != Success) |
|
1479 return -1; |
|
1480 if(n != 0) |
|
1481 result = *p; |
|
1482 XFree(p); |
|
1483 return result; |
|
1484 } |
|
1485 |
|
1486 static void |
|
1487 scan(void) { |
|
1488 unsigned int i, num; |
|
1489 Window *wins, d1, d2; |
|
1490 XWindowAttributes wa; |
|
1491 |
|
1492 wins = NULL; |
|
1493 if(XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { |
|
1494 for(i = 0; i < num; i++) { |
|
1495 if(!XGetWindowAttributes(dpy, wins[i], &wa) |
|
1496 || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) |
|
1497 continue; |
|
1498 if(wa.map_state == IsViewable || getstate(wins[i]) == IconicState) |
|
1499 manage(wins[i], &wa); |
|
1500 } |
|
1501 for(i = 0; i < num; i++) { /* now the transients */ |
|
1502 if(!XGetWindowAttributes(dpy, wins[i], &wa)) |
|
1503 continue; |
|
1504 if(XGetTransientForHint(dpy, wins[i], &d1) |
|
1505 && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) |
|
1506 manage(wins[i], &wa); |
|
1507 } |
|
1508 } |
|
1509 if(wins) |
|
1510 XFree(wins); |
|
1511 } |
|
1512 |
|
1513 static void |
|
1514 setup(void) { |
|
1515 int i, j; |
|
1516 unsigned int mask; |
|
1517 Window w; |
|
1518 XModifierKeymap *modmap; |
|
1519 XSetWindowAttributes wa; |
|
1520 |
|
1521 /* init atoms */ |
|
1522 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); |
|
1523 wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); |
|
1524 wmatom[WMName] = XInternAtom(dpy, "WM_NAME", False); |
|
1525 wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); |
|
1526 netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); |
|
1527 netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); |
|
1528 XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, |
|
1529 PropModeReplace, (unsigned char *) netatom, NetLast); |
|
1530 /* init cursors */ |
|
1531 cursor[CurNormal] = XCreateFontCursor(dpy, XC_left_ptr); |
|
1532 cursor[CurResize] = XCreateFontCursor(dpy, XC_sizing); |
|
1533 cursor[CurMove] = XCreateFontCursor(dpy, XC_fleur); |
|
1534 /* init modifier map */ |
|
1535 modmap = XGetModifierMapping(dpy); |
|
1536 for (i = 0; i < 8; i++) |
|
1537 for (j = 0; j < modmap->max_keypermod; j++) { |
|
1538 if(modmap->modifiermap[i * modmap->max_keypermod + j] |
|
1539 == XKeysymToKeycode(dpy, XK_Num_Lock)) |
|
1540 numlockmask = (1 << i); |
|
1541 } |
|
1542 XFreeModifiermap(modmap); |
|
1543 /* select for events */ |
|
1544 wa.event_mask = SubstructureRedirectMask | SubstructureNotifyMask |
|
1545 | EnterWindowMask | LeaveWindowMask | StructureNotifyMask; |
|
1546 wa.cursor = cursor[CurNormal]; |
|
1547 XChangeWindowAttributes(dpy, root, CWEventMask | CWCursor, &wa); |
|
1548 XSelectInput(dpy, root, wa.event_mask); |
|
1549 grabkeys(); |
|
1550 compileregs(); |
|
1551 for(ntags = 0; tags[ntags]; ntags++); |
|
1552 seltags = emallocz(sizeof(Bool) * ntags); |
|
1553 seltags[0] = True; |
|
1554 /* geometry */ |
|
1555 sx = sy = 0; |
|
1556 sw = DisplayWidth(dpy, screen); |
|
1557 sh = DisplayHeight(dpy, screen); |
|
1558 initstyle(); |
|
1559 initlayouts(); |
|
1560 initbar(); |
|
1561 /* multihead support */ |
|
1562 selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); |
|
1563 } |
|
1564 |
|
1565 /* |
|
1566 * Startup Error handler to check if another window manager |
|
1567 * is already running. |
|
1568 */ |
|
1569 static int |
|
1570 xerrorstart(Display *dsply, XErrorEvent *ee) { |
|
1571 otherwm = True; |
|
1572 return -1; |
|
1573 } |
|
1574 |
|
1575 static Bool |
|
1576 gettextprop(Window w, Atom atom, char *text, unsigned int size) { |
|
1577 char **list = NULL; |
|
1578 int n; |
|
1579 XTextProperty name; |
|
1580 |
|
1581 if(!text || size == 0) |
|
1582 return False; |
|
1583 text[0] = '\0'; |
|
1584 XGetTextProperty(dpy, w, &name, atom); |
|
1585 if(!name.nitems) |
|
1586 return False; |
|
1587 if(name.encoding == XA_STRING) |
|
1588 strncpy(text, (char *)name.value, size - 1); |
|
1589 else { |
|
1590 if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success |
|
1591 && n > 0 && *list) |
|
1592 { |
|
1593 strncpy(text, *list, size - 1); |
|
1594 XFreeStringList(list); |
|
1595 } |
|
1596 } |
|
1597 text[size - 1] = '\0'; |
|
1598 XFree(name.value); |
|
1599 return True; |
|
1600 } |
|
1601 |
|
1602 static void |
|
1603 quit(const char *arg) { |
|
1604 readin = running = False; |
|
1605 } |
|
1606 |
|
1607 /* There's no way to check accesses to destroyed windows, thus those cases are |
|
1608 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs |
|
1609 * default error handler, which may call exit. |
|
1610 */ |
|
1611 static int |
|
1612 xerror(Display *dpy, XErrorEvent *ee) { |
|
1613 if(ee->error_code == BadWindow |
|
1614 || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) |
|
1615 || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) |
|
1616 || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) |
|
1617 || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) |
|
1618 || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) |
|
1619 || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) |
|
1620 || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) |
|
1621 return 0; |
|
1622 fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", |
|
1623 ee->request_code, ee->error_code); |
|
1624 return xerrorxlib(dpy, ee); /* may call exit */ |
|
1625 } |
|
1626 |
|
1627 static void |
|
1628 arrange(void) { |
|
1629 Client *c; |
|
1630 |
|
1631 for(c = clients; c; c = c->next) |
|
1632 if(isvisible(c)) |
|
1633 unban(c); |
|
1634 else |
|
1635 ban(c); |
|
1636 layouts[ltidx].arrange(); |
|
1637 focus(NULL); |
|
1638 restack(); |
|
1639 } |
|
1640 |
|
1641 static void |
|
1642 attach(Client *c) { |
|
1643 if(clients) |
|
1644 clients->prev = c; |
|
1645 c->next = clients; |
|
1646 clients = c; |
|
1647 } |
|
1648 |
|
1649 static void |
|
1650 detach(Client *c) { |
|
1651 if(c->prev) |
|
1652 c->prev->next = c->next; |
|
1653 if(c->next) |
|
1654 c->next->prev = c->prev; |
|
1655 if(c == clients) |
|
1656 clients = c->next; |
|
1657 c->next = c->prev = NULL; |
|
1658 } |
|
1659 |
|
1660 static void |
|
1661 focus(Client *c) { |
|
1662 if((!c && selscreen) || (c && !isvisible(c))) |
|
1663 for(c = stack; c && !isvisible(c); c = c->snext); |
|
1664 if(sel && sel != c) { |
|
1665 grabbuttons(sel, False); |
|
1666 XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]); |
|
1667 } |
|
1668 if(c) { |
|
1669 detachstack(c); |
|
1670 attachstack(c); |
|
1671 grabbuttons(c, True); |
|
1672 } |
|
1673 sel = c; |
|
1674 drawbar(); |
|
1675 if(!selscreen) |
|
1676 return; |
|
1677 if(c) { |
|
1678 XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]); |
|
1679 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); |
|
1680 } |
|
1681 else |
|
1682 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); |
|
1683 } |
|
1684 |
|
1685 static Bool |
|
1686 isarrange(void (*func)()) |
|
1687 { |
|
1688 return func == layouts[ltidx].arrange; |
|
1689 } |
|
1690 |
|
1691 static Client * |
|
1692 nexttiled(Client *c) { |
|
1693 for(; c && (c->isfloating || !isvisible(c)); c = c->next); |
|
1694 return c; |
|
1695 } |
|
1696 |
|
1697 static void |
|
1698 setmwfact(const char *arg) { |
|
1699 double delta; |
|
1700 |
|
1701 if(!isarrange(tile)) |
|
1702 return; |
|
1703 /* arg handling, manipulate mwfact */ |
|
1704 if(arg == NULL) |
|
1705 mwfact = MWFACT; |
|
1706 else if(1 == sscanf(arg, "%lf", &delta)) { |
|
1707 if(arg[0] != '+' && arg[0] != '-') |
|
1708 mwfact = delta; |
|
1709 else |
|
1710 mwfact += delta; |
|
1711 if(mwfact < 0.1) |
|
1712 mwfact = 0.1; |
|
1713 else if(mwfact > 0.9) |
|
1714 mwfact = 0.9; |
|
1715 } |
|
1716 arrange(); |
|
1717 } |
|
1718 |
|
1719 static void |
|
1720 tile(void) { |
|
1721 unsigned int i, n, nx, ny, nw, nh, mw, th; |
|
1722 Client *c; |
|
1723 |
|
1724 for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) |
|
1725 n++; |
|
1726 |
|
1727 /* window geoms */ |
|
1728 mw = (n == 1) ? waw : mwfact * waw; |
|
1729 th = (n > 1) ? wah / (n - 1) : 0; |
|
1730 if(n > 1 && th < bh) |
|
1731 th = wah; |
|
1732 |
|
1733 nx = wax; |
|
1734 ny = way; |
|
1735 for(i = 0, c = nexttiled(clients); c; c = nexttiled(c->next), i++) { |
|
1736 c->ismax = False; |
|
1737 if(i == 0) { /* master */ |
|
1738 nw = mw - 2 * c->border; |
|
1739 nh = wah - 2 * c->border; |
|
1740 } |
|
1741 else { /* tile window */ |
|
1742 if(i == 1) { |
|
1743 ny = way; |
|
1744 nx += mw; |
|
1745 } |
|
1746 nw = waw - mw - 2 * c->border; |
|
1747 if(i + 1 == n) /* remainder */ |
|
1748 nh = (way + wah) - ny - 2 * c->border; |
|
1749 else |
|
1750 nh = th - 2 * c->border; |
|
1751 } |
|
1752 resize(c, nx, ny, nw, nh, RESIZEHINTS); |
|
1753 if(n > 1 && th != wah) |
|
1754 ny += nh + 2 * c->border; |
|
1755 } |
|
1756 } |
|
1757 |
|
1758 static void |
|
1759 zoom(const char *arg) { |
|
1760 Client *c; |
|
1761 |
|
1762 if(!sel || !isarrange(tile) || sel->isfloating) |
|
1763 return; |
|
1764 if((c = sel) == nexttiled(clients)) |
|
1765 if(!(c = nexttiled(c->next))) |
|
1766 return; |
|
1767 detach(c); |
|
1768 attach(c); |
|
1769 focus(c); |
|
1770 arrange(); |
|
1771 } |
|
1772 |
|
1773 int |
|
1774 main(int argc, char *argv[]) { |
|
1775 char *p; |
|
1776 int r, xfd; |
|
1777 fd_set rd; |
|
1778 XEvent ev; |
|
1779 |
|
1780 if(argc == 2 && !strcmp("-v", argv[1])) |
|
1781 eprint("dwm-"VERSION", © 2006-2007 A. R. Garbe, S. van Dijk, J. Salmi, P. Hruby, S. Nagy\n"); |
|
1782 else if(argc != 1) |
|
1783 eprint("usage: dwm [-v]\n"); |
|
1784 setlocale(LC_CTYPE, ""); |
|
1785 if(!(dpy = XOpenDisplay(0))) |
|
1786 eprint("dwm: cannot open display\n"); |
|
1787 xfd = ConnectionNumber(dpy); |
|
1788 screen = DefaultScreen(dpy); |
|
1789 root = RootWindow(dpy, screen); |
|
1790 otherwm = False; |
|
1791 XSetErrorHandler(xerrorstart); |
|
1792 /* this causes an error if some other window manager is running */ |
|
1793 XSelectInput(dpy, root, SubstructureRedirectMask); |
|
1794 XSync(dpy, False); |
|
1795 if(otherwm) |
|
1796 eprint("dwm: another window manager is already running\n"); |
|
1797 |
|
1798 XSync(dpy, False); |
|
1799 XSetErrorHandler(NULL); |
|
1800 xerrorxlib = XSetErrorHandler(xerror); |
|
1801 XSync(dpy, False); |
|
1802 setup(); |
|
1803 drawbar(); |
|
1804 scan(); |
|
1805 |
|
1806 /* main event loop, also reads status text from stdin */ |
|
1807 XSync(dpy, False); |
|
1808 readin = True; |
|
1809 while(running) { |
|
1810 FD_ZERO(&rd); |
|
1811 if(readin) |
|
1812 FD_SET(STDIN_FILENO, &rd); |
|
1813 FD_SET(xfd, &rd); |
|
1814 if(select(xfd + 1, &rd, NULL, NULL, NULL) == -1) { |
|
1815 if(errno == EINTR) |
|
1816 continue; |
|
1817 eprint("select failed\n"); |
|
1818 } |
|
1819 if(FD_ISSET(STDIN_FILENO, &rd)) { |
|
1820 switch(r = read(STDIN_FILENO, stext, sizeof stext - 1)) { |
|
1821 case -1: |
|
1822 strncpy(stext, strerror(errno), sizeof stext - 1); |
|
1823 stext[sizeof stext - 1] = '\0'; |
|
1824 readin = False; |
|
1825 break; |
|
1826 case 0: |
|
1827 strncpy(stext, "EOF", 4); |
|
1828 readin = False; |
|
1829 break; |
|
1830 default: |
|
1831 for(stext[r] = '\0', p = stext + strlen(stext) - 1; p >= stext && *p == '\n'; *p-- = '\0'); |
|
1832 for(; p >= stext && *p != '\n'; --p); |
|
1833 if(p > stext) |
|
1834 strncpy(stext, p + 1, sizeof stext); |
|
1835 } |
|
1836 drawbar(); |
|
1837 } |
|
1838 while(XPending(dpy)) { |
|
1839 XNextEvent(dpy, &ev); |
|
1840 if(handler[ev.type]) |
|
1841 (handler[ev.type])(&ev); /* call handler */ |
|
1842 } |
|
1843 } |
|
1844 cleanup(); |
|
1845 XCloseDisplay(dpy); |
|
1846 return 0; |
|
1847 } |