231 static Display *dpy; |
231 static Display *dpy; |
232 static DC dc = {0}; |
232 static DC dc = {0}; |
233 static Window barwin, root; |
233 static Window barwin, root; |
234 static Regs *regs = NULL; |
234 static Regs *regs = NULL; |
235 |
235 |
236 /* configuration, allows nested code to work on above variables */ |
236 /* configuration, allows nested code to access above variables */ |
237 #include "config.h" |
237 #include "config.h" |
238 |
238 |
239 static void |
239 /* implementation */ |
240 eprint(const char *errstr, ...) { |
240 static void |
241 va_list ap; |
241 applyrules(Client *c) { |
242 |
242 static char buf[512]; |
243 va_start(ap, errstr); |
243 unsigned int i, j; |
244 vfprintf(stderr, errstr, ap); |
244 regmatch_t tmp; |
245 va_end(ap); |
245 Bool matched = False; |
246 exit(EXIT_FAILURE); |
246 XClassHint ch = { 0 }; |
247 } |
247 |
248 |
248 /* rule matching */ |
249 static void * |
249 XGetClassHint(dpy, c->win, &ch); |
250 emallocz(unsigned int size) { |
250 snprintf(buf, sizeof buf, "%s:%s:%s", |
251 void *res = calloc(1, size); |
251 ch.res_class ? ch.res_class : "", |
252 |
252 ch.res_name ? ch.res_name : "", c->name); |
253 if(!res) |
253 for(i = 0; i < nrules; i++) |
254 eprint("fatal: could not malloc() %u bytes\n", size); |
254 if(regs[i].propregex && !regexec(regs[i].propregex, buf, 1, &tmp, 0)) { |
255 return res; |
255 c->isfloating = rules[i].isfloating; |
256 } |
256 for(j = 0; regs[i].tagregex && j < ntags; j++) { |
257 |
257 if(!regexec(regs[i].tagregex, tags[j], 1, &tmp, 0)) { |
258 static void |
258 matched = True; |
259 spawn(const char *arg) { |
259 c->tags[j] = True; |
260 static char *shell = NULL; |
260 } |
261 |
261 } |
262 if(!shell && !(shell = getenv("SHELL"))) |
262 } |
263 shell = "/bin/sh"; |
263 if(ch.res_class) |
264 if(!arg) |
264 XFree(ch.res_class); |
265 return; |
265 if(ch.res_name) |
266 /* The double-fork construct avoids zombie processes and keeps the code |
266 XFree(ch.res_name); |
267 * clean from stupid signal handlers. */ |
267 if(!matched) |
268 if(fork() == 0) { |
268 for(i = 0; i < ntags; i++) |
269 if(fork() == 0) { |
269 c->tags[i] = seltags[i]; |
270 if(dpy) |
270 } |
271 close(ConnectionNumber(dpy)); |
271 |
272 setsid(); |
272 static void |
273 execl(shell, shell, "-c", arg, (char *)NULL); |
273 arrange(void) { |
274 fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg); |
274 Client *c; |
275 perror(" failed"); |
275 |
276 } |
276 for(c = clients; c; c = c->next) |
277 exit(0); |
277 if(isvisible(c)) |
278 } |
278 unban(c); |
279 wait(0); |
279 else |
|
280 ban(c); |
|
281 layouts[ltidx].arrange(); |
|
282 focus(NULL); |
|
283 restack(); |
|
284 } |
|
285 |
|
286 static void |
|
287 attach(Client *c) { |
|
288 if(clients) |
|
289 clients->prev = c; |
|
290 c->next = clients; |
|
291 clients = c; |
|
292 } |
|
293 |
|
294 static void |
|
295 attachstack(Client *c) { |
|
296 c->snext = stack; |
|
297 stack = c; |
|
298 } |
|
299 |
|
300 static void |
|
301 ban(Client *c) { |
|
302 if(c->isbanned) |
|
303 return; |
|
304 XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); |
|
305 c->isbanned = True; |
|
306 } |
|
307 |
|
308 static void |
|
309 buttonpress(XEvent *e) { |
|
310 unsigned int i, x; |
|
311 Client *c; |
|
312 XButtonPressedEvent *ev = &e->xbutton; |
|
313 |
|
314 if(barwin == ev->window) { |
|
315 x = 0; |
|
316 for(i = 0; i < ntags; i++) { |
|
317 x += textw(tags[i]); |
|
318 if(ev->x < x) { |
|
319 if(ev->button == Button1) { |
|
320 if(ev->state & MODKEY) |
|
321 tag(tags[i]); |
|
322 else |
|
323 view(tags[i]); |
|
324 } |
|
325 else if(ev->button == Button3) { |
|
326 if(ev->state & MODKEY) |
|
327 toggletag(tags[i]); |
|
328 else |
|
329 toggleview(tags[i]); |
|
330 } |
|
331 return; |
|
332 } |
|
333 } |
|
334 if((ev->x < x + blw) && ev->button == Button1) |
|
335 setlayout(NULL); |
|
336 } |
|
337 else if((c = getclient(ev->window))) { |
|
338 focus(c); |
|
339 if(CLEANMASK(ev->state) != MODKEY) |
|
340 return; |
|
341 if(ev->button == Button1 && (isfloating() || c->isfloating)) { |
|
342 restack(); |
|
343 movemouse(c); |
|
344 } |
|
345 else if(ev->button == Button2) |
|
346 zoom(NULL); |
|
347 else if(ev->button == Button3 |
|
348 && (isfloating() || c->isfloating) && !c->isfixed) |
|
349 { |
|
350 restack(); |
|
351 resizemouse(c); |
|
352 } |
|
353 } |
|
354 } |
|
355 |
|
356 static void |
|
357 cleanup(void) { |
|
358 close(STDIN_FILENO); |
|
359 while(stack) { |
|
360 unban(stack); |
|
361 unmanage(stack); |
|
362 } |
|
363 if(dc.font.set) |
|
364 XFreeFontSet(dpy, dc.font.set); |
|
365 else |
|
366 XFreeFont(dpy, dc.font.xfont); |
|
367 XUngrabKey(dpy, AnyKey, AnyModifier, root); |
|
368 XFreePixmap(dpy, dc.drawable); |
|
369 XFreeGC(dpy, dc.gc); |
|
370 XDestroyWindow(dpy, barwin); |
|
371 XFreeCursor(dpy, cursor[CurNormal]); |
|
372 XFreeCursor(dpy, cursor[CurResize]); |
|
373 XFreeCursor(dpy, cursor[CurMove]); |
|
374 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); |
|
375 XSync(dpy, False); |
|
376 free(seltags); |
|
377 } |
|
378 |
|
379 static void |
|
380 compileregs(void) { |
|
381 unsigned int i; |
|
382 regex_t *reg; |
|
383 |
|
384 if(regs) |
|
385 return; |
|
386 nrules = sizeof rules / sizeof rules[0]; |
|
387 regs = emallocz(nrules * sizeof(Regs)); |
|
388 for(i = 0; i < nrules; i++) { |
|
389 if(rules[i].prop) { |
|
390 reg = emallocz(sizeof(regex_t)); |
|
391 if(regcomp(reg, rules[i].prop, REG_EXTENDED)) |
|
392 free(reg); |
|
393 else |
|
394 regs[i].propregex = reg; |
|
395 } |
|
396 if(rules[i].tags) { |
|
397 reg = emallocz(sizeof(regex_t)); |
|
398 if(regcomp(reg, rules[i].tags, REG_EXTENDED)) |
|
399 free(reg); |
|
400 else |
|
401 regs[i].tagregex = reg; |
|
402 } |
|
403 } |
|
404 } |
|
405 |
|
406 static void |
|
407 configure(Client *c) { |
|
408 XConfigureEvent ce; |
|
409 |
|
410 ce.type = ConfigureNotify; |
|
411 ce.display = dpy; |
|
412 ce.event = c->win; |
|
413 ce.window = c->win; |
|
414 ce.x = c->x; |
|
415 ce.y = c->y; |
|
416 ce.width = c->w; |
|
417 ce.height = c->h; |
|
418 ce.border_width = c->border; |
|
419 ce.above = None; |
|
420 ce.override_redirect = False; |
|
421 XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); |
|
422 } |
|
423 |
|
424 static void |
|
425 configurenotify(XEvent *e) { |
|
426 XConfigureEvent *ev = &e->xconfigure; |
|
427 |
|
428 if (ev->window == root && (ev->width != sw || ev->height != sh)) { |
|
429 sw = ev->width; |
|
430 sh = ev->height; |
|
431 XFreePixmap(dpy, dc.drawable); |
|
432 dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); |
|
433 XResizeWindow(dpy, barwin, sw, bh); |
|
434 updatebarpos(); |
|
435 arrange(); |
|
436 } |
|
437 } |
|
438 |
|
439 static void |
|
440 configurerequest(XEvent *e) { |
|
441 Client *c; |
|
442 XConfigureRequestEvent *ev = &e->xconfigurerequest; |
|
443 XWindowChanges wc; |
|
444 |
|
445 if((c = getclient(ev->window))) { |
|
446 c->ismax = False; |
|
447 if(ev->value_mask & CWBorderWidth) |
|
448 c->border = ev->border_width; |
|
449 if(c->isfixed || c->isfloating || isfloating()) { |
|
450 if(ev->value_mask & CWX) |
|
451 c->x = ev->x; |
|
452 if(ev->value_mask & CWY) |
|
453 c->y = ev->y; |
|
454 if(ev->value_mask & CWWidth) |
|
455 c->w = ev->width; |
|
456 if(ev->value_mask & CWHeight) |
|
457 c->h = ev->height; |
|
458 if((c->x + c->w) > sw && c->isfloating) |
|
459 c->x = sw / 2 - c->w / 2; /* center in x direction */ |
|
460 if((c->y + c->h) > sh && c->isfloating) |
|
461 c->y = sh / 2 - c->h / 2; /* center in y direction */ |
|
462 if((ev->value_mask & (CWX | CWY)) |
|
463 && !(ev->value_mask & (CWWidth | CWHeight))) |
|
464 configure(c); |
|
465 if(isvisible(c)) |
|
466 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); |
|
467 } |
|
468 else |
|
469 configure(c); |
|
470 } |
|
471 else { |
|
472 wc.x = ev->x; |
|
473 wc.y = ev->y; |
|
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); |
|
480 } |
|
481 XSync(dpy, False); |
|
482 } |
|
483 |
|
484 static void |
|
485 destroynotify(XEvent *e) { |
|
486 Client *c; |
|
487 XDestroyWindowEvent *ev = &e->xdestroywindow; |
|
488 |
|
489 if((c = getclient(ev->window))) |
|
490 unmanage(c); |
|
491 } |
|
492 |
|
493 static void |
|
494 detach(Client *c) { |
|
495 if(c->prev) |
|
496 c->prev->next = c->next; |
|
497 if(c->next) |
|
498 c->next->prev = c->prev; |
|
499 if(c == clients) |
|
500 clients = c->next; |
|
501 c->next = c->prev = NULL; |
|
502 } |
|
503 |
|
504 static void |
|
505 detachstack(Client *c) { |
|
506 Client **tc; |
|
507 |
|
508 for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext); |
|
509 *tc = c->snext; |
|
510 } |
|
511 |
|
512 static void |
|
513 drawbar(void) { |
|
514 int i, x; |
|
515 |
|
516 dc.x = dc.y = 0; |
|
517 for(i = 0; i < ntags; i++) { |
|
518 dc.w = textw(tags[i]); |
|
519 if(seltags[i]) { |
|
520 drawtext(tags[i], dc.sel); |
|
521 drawsquare(sel && sel->tags[i], isoccupied(i), dc.sel); |
|
522 } |
|
523 else { |
|
524 drawtext(tags[i], dc.norm); |
|
525 drawsquare(sel && sel->tags[i], isoccupied(i), dc.norm); |
|
526 } |
|
527 dc.x += dc.w; |
|
528 } |
|
529 dc.w = blw; |
|
530 drawtext(layouts[ltidx].symbol, dc.norm); |
|
531 x = dc.x + dc.w; |
|
532 dc.w = textw(stext); |
|
533 dc.x = sw - dc.w; |
|
534 if(dc.x < x) { |
|
535 dc.x = x; |
|
536 dc.w = sw - x; |
|
537 } |
|
538 drawtext(stext, dc.norm); |
|
539 if((dc.w = dc.x - x) > bh) { |
|
540 dc.x = x; |
|
541 if(sel) { |
|
542 drawtext(sel->name, dc.sel); |
|
543 drawsquare(sel->ismax, sel->isfloating, dc.sel); |
|
544 } |
|
545 else |
|
546 drawtext(NULL, dc.norm); |
|
547 } |
|
548 XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0); |
|
549 XSync(dpy, False); |
280 } |
550 } |
281 |
551 |
282 static void |
552 static void |
283 drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]) { |
553 drawsquare(Bool filled, Bool empty, unsigned long col[ColLast]) { |
284 int x; |
554 int x; |
296 } |
566 } |
297 else if(empty) { |
567 else if(empty) { |
298 r.width = r.height = x; |
568 r.width = r.height = x; |
299 XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1); |
569 XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1); |
300 } |
570 } |
301 } |
|
302 |
|
303 static unsigned long |
|
304 initcolor(const char *colstr) { |
|
305 Colormap cmap = DefaultColormap(dpy, screen); |
|
306 XColor color; |
|
307 |
|
308 if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) |
|
309 eprint("error, cannot allocate color '%s'\n", colstr); |
|
310 return color.pixel; |
|
311 } |
|
312 |
|
313 static void |
|
314 initfont(const char *fontstr) { |
|
315 char *def, **missing; |
|
316 int i, n; |
|
317 |
|
318 missing = NULL; |
|
319 if(dc.font.set) |
|
320 XFreeFontSet(dpy, dc.font.set); |
|
321 dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def); |
|
322 if(missing) { |
|
323 while(n--) |
|
324 fprintf(stderr, "dwm: missing fontset: %s\n", missing[n]); |
|
325 XFreeStringList(missing); |
|
326 } |
|
327 if(dc.font.set) { |
|
328 XFontSetExtents *font_extents; |
|
329 XFontStruct **xfonts; |
|
330 char **font_names; |
|
331 dc.font.ascent = dc.font.descent = 0; |
|
332 font_extents = XExtentsOfFontSet(dc.font.set); |
|
333 n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); |
|
334 for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) { |
|
335 if(dc.font.ascent < (*xfonts)->ascent) |
|
336 dc.font.ascent = (*xfonts)->ascent; |
|
337 if(dc.font.descent < (*xfonts)->descent) |
|
338 dc.font.descent = (*xfonts)->descent; |
|
339 xfonts++; |
|
340 } |
|
341 } |
|
342 else { |
|
343 if(dc.font.xfont) |
|
344 XFreeFont(dpy, dc.font.xfont); |
|
345 dc.font.xfont = NULL; |
|
346 if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)) |
|
347 || !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) |
|
348 eprint("error, cannot load font: '%s'\n", fontstr); |
|
349 dc.font.ascent = dc.font.xfont->ascent; |
|
350 dc.font.descent = dc.font.xfont->descent; |
|
351 } |
|
352 dc.font.height = dc.font.ascent + dc.font.descent; |
|
353 } |
|
354 |
|
355 static Bool |
|
356 isoccupied(unsigned int t) { |
|
357 Client *c; |
|
358 |
|
359 for(c = clients; c; c = c->next) |
|
360 if(c->tags[t]) |
|
361 return True; |
|
362 return False; |
|
363 } |
|
364 |
|
365 static unsigned int |
|
366 textnw(const char *text, unsigned int len) { |
|
367 XRectangle r; |
|
368 |
|
369 if(dc.font.set) { |
|
370 XmbTextExtents(dc.font.set, text, len, NULL, &r); |
|
371 return r.width; |
|
372 } |
|
373 return XTextWidth(dc.font.xfont, text, len); |
|
374 } |
571 } |
375 |
572 |
376 static void |
573 static void |
377 drawtext(const char *text, unsigned long col[ColLast]) { |
574 drawtext(const char *text, unsigned long col[ColLast]) { |
378 int x, y, w, h; |
575 int x, y, w, h; |
411 XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); |
608 XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, buf, len); |
412 else |
609 else |
413 XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); |
610 XDrawString(dpy, dc.drawable, dc.gc, x, y, buf, len); |
414 } |
611 } |
415 |
612 |
416 static void |
613 static void * |
417 drawbar(void) { |
614 emallocz(unsigned int size) { |
418 int i, x; |
615 void *res = calloc(1, size); |
419 |
616 |
420 dc.x = dc.y = 0; |
617 if(!res) |
421 for(i = 0; i < ntags; i++) { |
618 eprint("fatal: could not malloc() %u bytes\n", size); |
422 dc.w = textw(tags[i]); |
619 return res; |
423 if(seltags[i]) { |
620 } |
424 drawtext(tags[i], dc.sel); |
621 |
425 drawsquare(sel && sel->tags[i], isoccupied(i), dc.sel); |
622 static void |
426 } |
623 enternotify(XEvent *e) { |
427 else { |
624 Client *c; |
428 drawtext(tags[i], dc.norm); |
625 XCrossingEvent *ev = &e->xcrossing; |
429 drawsquare(sel && sel->tags[i], isoccupied(i), dc.norm); |
626 |
430 } |
627 if(ev->mode != NotifyNormal || ev->detail == NotifyInferior) |
431 dc.x += dc.w; |
628 return; |
432 } |
629 if((c = getclient(ev->window))) |
433 dc.w = blw; |
630 focus(c); |
434 drawtext(layouts[ltidx].symbol, dc.norm); |
631 else if(ev->window == root) { |
435 x = dc.x + dc.w; |
632 selscreen = True; |
436 dc.w = textw(stext); |
633 focus(NULL); |
437 dc.x = sw - dc.w; |
634 } |
438 if(dc.x < x) { |
635 } |
439 dc.x = x; |
636 |
440 dc.w = sw - x; |
637 static void |
441 } |
638 eprint(const char *errstr, ...) { |
442 drawtext(stext, dc.norm); |
639 va_list ap; |
443 if((dc.w = dc.x - x) > bh) { |
640 |
444 dc.x = x; |
641 va_start(ap, errstr); |
445 if(sel) { |
642 vfprintf(stderr, errstr, ap); |
446 drawtext(sel->name, dc.sel); |
643 va_end(ap); |
447 drawsquare(sel->ismax, sel->isfloating, dc.sel); |
644 exit(EXIT_FAILURE); |
448 } |
645 } |
449 else |
646 |
450 drawtext(NULL, dc.norm); |
647 static void |
451 } |
648 expose(XEvent *e) { |
452 XCopyArea(dpy, dc.drawable, barwin, dc.gc, 0, 0, sw, bh, 0, 0); |
649 XExposeEvent *ev = &e->xexpose; |
453 XSync(dpy, False); |
650 |
454 } |
651 if(ev->count == 0) { |
455 |
652 if(barwin == ev->window) |
456 static void |
653 drawbar(); |
457 initstyle(void) { |
654 } |
458 dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR); |
655 } |
459 dc.norm[ColBG] = initcolor(NORMBGCOLOR); |
656 |
460 dc.norm[ColFG] = initcolor(NORMFGCOLOR); |
657 static void |
461 dc.sel[ColBorder] = initcolor(SELBORDERCOLOR); |
658 floating(void) { /* default floating layout */ |
462 dc.sel[ColBG] = initcolor(SELBGCOLOR); |
659 Client *c; |
463 dc.sel[ColFG] = initcolor(SELFGCOLOR); |
660 |
464 initfont(FONT); |
661 for(c = clients; c; c = c->next) |
465 dc.h = bh = dc.font.height + 2; |
662 if(isvisible(c)) |
|
663 resize(c, c->x, c->y, c->w, c->h, True); |
|
664 } |
|
665 |
|
666 static void |
|
667 focus(Client *c) { |
|
668 if((!c && selscreen) || (c && !isvisible(c))) |
|
669 for(c = stack; c && !isvisible(c); c = c->snext); |
|
670 if(sel && sel != c) { |
|
671 grabbuttons(sel, False); |
|
672 XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]); |
|
673 } |
|
674 if(c) { |
|
675 detachstack(c); |
|
676 attachstack(c); |
|
677 grabbuttons(c, True); |
|
678 } |
|
679 sel = c; |
|
680 drawbar(); |
|
681 if(!selscreen) |
|
682 return; |
|
683 if(c) { |
|
684 XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]); |
|
685 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); |
|
686 } |
|
687 else |
|
688 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); |
|
689 } |
|
690 |
|
691 static void |
|
692 focusnext(const char *arg) { |
|
693 Client *c; |
|
694 |
|
695 if(!sel) |
|
696 return; |
|
697 for(c = sel->next; c && !isvisible(c); c = c->next); |
|
698 if(!c) |
|
699 for(c = clients; c && !isvisible(c); c = c->next); |
|
700 if(c) { |
|
701 focus(c); |
|
702 restack(); |
|
703 } |
|
704 } |
|
705 |
|
706 static void |
|
707 focusprev(const char *arg) { |
|
708 Client *c; |
|
709 |
|
710 if(!sel) |
|
711 return; |
|
712 for(c = sel->prev; c && !isvisible(c); c = c->prev); |
|
713 if(!c) { |
|
714 for(c = clients; c && c->next; c = c->next); |
|
715 for(; c && !isvisible(c); c = c->prev); |
|
716 } |
|
717 if(c) { |
|
718 focus(c); |
|
719 restack(); |
|
720 } |
|
721 } |
|
722 |
|
723 static Client * |
|
724 getclient(Window w) { |
|
725 Client *c; |
|
726 |
|
727 for(c = clients; c && c->win != w; c = c->next); |
|
728 return c; |
|
729 } |
|
730 |
|
731 static long |
|
732 getstate(Window w) { |
|
733 int format, status; |
|
734 long result = -1; |
|
735 unsigned char *p = NULL; |
|
736 unsigned long n, extra; |
|
737 Atom real; |
|
738 |
|
739 status = XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], |
|
740 &real, &format, &n, &extra, (unsigned char **)&p); |
|
741 if(status != Success) |
|
742 return -1; |
|
743 if(n != 0) |
|
744 result = *p; |
|
745 XFree(p); |
|
746 return result; |
|
747 } |
|
748 |
|
749 static Bool |
|
750 gettextprop(Window w, Atom atom, char *text, unsigned int size) { |
|
751 char **list = NULL; |
|
752 int n; |
|
753 XTextProperty name; |
|
754 |
|
755 if(!text || size == 0) |
|
756 return False; |
|
757 text[0] = '\0'; |
|
758 XGetTextProperty(dpy, w, &name, atom); |
|
759 if(!name.nitems) |
|
760 return False; |
|
761 if(name.encoding == XA_STRING) |
|
762 strncpy(text, (char *)name.value, size - 1); |
|
763 else { |
|
764 if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success |
|
765 && n > 0 && *list) |
|
766 { |
|
767 strncpy(text, *list, size - 1); |
|
768 XFreeStringList(list); |
|
769 } |
|
770 } |
|
771 text[size - 1] = '\0'; |
|
772 XFree(name.value); |
|
773 return True; |
|
774 } |
|
775 |
|
776 static void |
|
777 grabbuttons(Client *c, Bool focused) { |
|
778 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); |
|
779 |
|
780 if(focused) { |
|
781 XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK, |
|
782 GrabModeAsync, GrabModeSync, None, None); |
|
783 XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK, |
|
784 GrabModeAsync, GrabModeSync, None, None); |
|
785 XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK, |
|
786 GrabModeAsync, GrabModeSync, None, None); |
|
787 XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, |
|
788 GrabModeAsync, GrabModeSync, None, None); |
|
789 |
|
790 XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK, |
|
791 GrabModeAsync, GrabModeSync, None, None); |
|
792 XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK, |
|
793 GrabModeAsync, GrabModeSync, None, None); |
|
794 XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK, |
|
795 GrabModeAsync, GrabModeSync, None, None); |
|
796 XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, |
|
797 GrabModeAsync, GrabModeSync, None, None); |
|
798 |
|
799 XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK, |
|
800 GrabModeAsync, GrabModeSync, None, None); |
|
801 XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK, |
|
802 GrabModeAsync, GrabModeSync, None, None); |
|
803 XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK, |
|
804 GrabModeAsync, GrabModeSync, None, None); |
|
805 XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, |
|
806 GrabModeAsync, GrabModeSync, None, None); |
|
807 } |
|
808 else |
|
809 XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, |
|
810 GrabModeAsync, GrabModeSync, None, None); |
|
811 } |
|
812 |
|
813 static unsigned int |
|
814 idxoftag(const char *tag) { |
|
815 unsigned int i; |
|
816 |
|
817 for(i = 0; i < ntags; i++) |
|
818 if(tags[i] == tag) |
|
819 return i; |
|
820 return 0; |
466 } |
821 } |
467 |
822 |
468 static void |
823 static void |
469 initbar(void) { |
824 initbar(void) { |
470 XSetWindowAttributes wa; |
825 XSetWindowAttributes wa; |
484 XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); |
839 XSetLineAttributes(dpy, dc.gc, 1, LineSolid, CapButt, JoinMiter); |
485 if(!dc.font.set) |
840 if(!dc.font.set) |
486 XSetFont(dpy, dc.gc, dc.font.xfont->fid); |
841 XSetFont(dpy, dc.gc, dc.font.xfont->fid); |
487 } |
842 } |
488 |
843 |
489 static unsigned int |
844 static unsigned long |
490 textw(const char *text) { |
845 initcolor(const char *colstr) { |
491 return textnw(text, strlen(text)) + dc.font.height; |
846 Colormap cmap = DefaultColormap(dpy, screen); |
492 } |
847 XColor color; |
493 |
848 |
494 static void |
849 if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color)) |
495 togglebar(const char *arg) { |
850 eprint("error, cannot allocate color '%s'\n", colstr); |
496 if(bpos == BarOff) |
851 return color.pixel; |
497 bpos = (BARPOS == BarOff) ? BarTop : BARPOS; |
852 } |
498 else |
853 |
499 bpos = BarOff; |
854 static void |
500 updatebarpos(); |
855 initfont(const char *fontstr) { |
501 arrange(); |
856 char *def, **missing; |
502 } |
857 int i, n; |
503 |
858 |
504 static void |
859 missing = NULL; |
505 updatebarpos(void) { |
860 if(dc.font.set) |
506 XEvent ev; |
861 XFreeFontSet(dpy, dc.font.set); |
507 |
862 dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def); |
508 wax = sx; |
863 if(missing) { |
509 way = sy; |
864 while(n--) |
510 wah = sh; |
865 fprintf(stderr, "dwm: missing fontset: %s\n", missing[n]); |
511 waw = sw; |
866 XFreeStringList(missing); |
512 switch(bpos) { |
867 } |
513 default: |
868 if(dc.font.set) { |
514 wah -= bh; |
869 XFontSetExtents *font_extents; |
515 way += bh; |
870 XFontStruct **xfonts; |
516 XMoveWindow(dpy, barwin, sx, sy); |
871 char **font_names; |
517 break; |
872 dc.font.ascent = dc.font.descent = 0; |
518 case BarBot: |
873 font_extents = XExtentsOfFontSet(dc.font.set); |
519 wah -= bh; |
874 n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names); |
520 XMoveWindow(dpy, barwin, sx, sy + wah); |
875 for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) { |
521 break; |
876 if(dc.font.ascent < (*xfonts)->ascent) |
522 case BarOff: |
877 dc.font.ascent = (*xfonts)->ascent; |
523 XMoveWindow(dpy, barwin, sx, sy - bh); |
878 if(dc.font.descent < (*xfonts)->descent) |
524 break; |
879 dc.font.descent = (*xfonts)->descent; |
525 } |
880 xfonts++; |
526 XSync(dpy, False); |
881 } |
527 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
882 } |
528 } |
883 else { |
529 |
884 if(dc.font.xfont) |
530 static void |
885 XFreeFont(dpy, dc.font.xfont); |
531 attachstack(Client *c) { |
886 dc.font.xfont = NULL; |
532 c->snext = stack; |
887 if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr)) |
533 stack = c; |
888 || !(dc.font.xfont = XLoadQueryFont(dpy, "fixed"))) |
534 } |
889 eprint("error, cannot load font: '%s'\n", fontstr); |
535 |
890 dc.font.ascent = dc.font.xfont->ascent; |
536 static void |
891 dc.font.descent = dc.font.xfont->descent; |
537 detachstack(Client *c) { |
892 } |
538 Client **tc; |
893 dc.font.height = dc.font.ascent + dc.font.descent; |
539 |
894 } |
540 for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext); |
895 |
541 *tc = c->snext; |
896 static void |
542 } |
897 initlayouts(void) { |
543 |
898 unsigned int i, w; |
544 static void |
899 |
545 grabbuttons(Client *c, Bool focused) { |
900 nlayouts = sizeof layouts / sizeof layouts[0]; |
546 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); |
901 for(blw = i = 0; i < nlayouts; i++) { |
547 |
902 w = textw(layouts[i].symbol); |
548 if(focused) { |
903 if(w > blw) |
549 XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK, |
904 blw = w; |
550 GrabModeAsync, GrabModeSync, None, None); |
905 } |
551 XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK, |
906 } |
552 GrabModeAsync, GrabModeSync, None, None); |
907 |
553 XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK, |
908 static void |
554 GrabModeAsync, GrabModeSync, None, None); |
909 initstyle(void) { |
555 XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, |
910 dc.norm[ColBorder] = initcolor(NORMBORDERCOLOR); |
556 GrabModeAsync, GrabModeSync, None, None); |
911 dc.norm[ColBG] = initcolor(NORMBGCOLOR); |
557 |
912 dc.norm[ColFG] = initcolor(NORMFGCOLOR); |
558 XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK, |
913 dc.sel[ColBorder] = initcolor(SELBORDERCOLOR); |
559 GrabModeAsync, GrabModeSync, None, None); |
914 dc.sel[ColBG] = initcolor(SELBGCOLOR); |
560 XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK, |
915 dc.sel[ColFG] = initcolor(SELFGCOLOR); |
561 GrabModeAsync, GrabModeSync, None, None); |
916 initfont(FONT); |
562 XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK, |
917 dc.h = bh = dc.font.height + 2; |
563 GrabModeAsync, GrabModeSync, None, None); |
918 } |
564 XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, |
919 |
565 GrabModeAsync, GrabModeSync, None, None); |
920 static Bool |
566 |
921 isarrange(void (*func)()) |
567 XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK, |
922 { |
568 GrabModeAsync, GrabModeSync, None, None); |
923 return func == layouts[ltidx].arrange; |
569 XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK, |
924 } |
570 GrabModeAsync, GrabModeSync, None, None); |
925 |
571 XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK, |
926 static Bool |
572 GrabModeAsync, GrabModeSync, None, None); |
927 isfloating(void) { |
573 XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, |
928 return layouts[ltidx].arrange == floating; |
574 GrabModeAsync, GrabModeSync, None, None); |
929 } |
575 } |
930 |
576 else |
931 static Bool |
577 XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, |
932 isoccupied(unsigned int t) { |
578 GrabModeAsync, GrabModeSync, None, None); |
933 Client *c; |
|
934 |
|
935 for(c = clients; c; c = c->next) |
|
936 if(c->tags[t]) |
|
937 return True; |
|
938 return False; |
579 } |
939 } |
580 |
940 |
581 static Bool |
941 static Bool |
582 isprotodel(Client *c) { |
942 isprotodel(Client *c) { |
583 int i, n; |
943 int i, n; |
591 XFree(protocols); |
951 XFree(protocols); |
592 } |
952 } |
593 return ret; |
953 return ret; |
594 } |
954 } |
595 |
955 |
596 static void |
956 static Bool |
597 setclientstate(Client *c, long state) { |
957 isvisible(Client *c) { |
598 long data[] = {state, None}; |
958 unsigned int i; |
599 |
959 |
600 XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, |
960 for(i = 0; i < ntags; i++) |
601 PropModeReplace, (unsigned char *)data, 2); |
961 if(c->tags[i] && seltags[i]) |
602 } |
962 return True; |
603 |
963 return False; |
604 static int |
964 } |
605 xerrordummy(Display *dsply, XErrorEvent *ee) { |
965 |
606 return 0; |
966 static void |
607 } |
967 keypress(XEvent *e) { |
608 |
968 KEYS |
609 static void |
969 unsigned int len = sizeof keys / sizeof keys[0]; |
610 ban(Client *c) { |
970 unsigned int i; |
611 if(c->isbanned) |
971 KeyCode code; |
612 return; |
972 KeySym keysym; |
613 XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); |
973 XKeyEvent *ev; |
614 c->isbanned = True; |
974 |
615 } |
975 if(!e) { /* grabkeys */ |
616 |
976 XUngrabKey(dpy, AnyKey, AnyModifier, root); |
617 static void |
977 for(i = 0; i < len; i++) { |
618 configure(Client *c) { |
978 code = XKeysymToKeycode(dpy, keys[i].keysym); |
619 XConfigureEvent ce; |
979 XGrabKey(dpy, code, keys[i].mod, root, True, |
620 |
980 GrabModeAsync, GrabModeAsync); |
621 ce.type = ConfigureNotify; |
981 XGrabKey(dpy, code, keys[i].mod | LockMask, root, True, |
622 ce.display = dpy; |
982 GrabModeAsync, GrabModeAsync); |
623 ce.event = c->win; |
983 XGrabKey(dpy, code, keys[i].mod | numlockmask, root, True, |
624 ce.window = c->win; |
984 GrabModeAsync, GrabModeAsync); |
625 ce.x = c->x; |
985 XGrabKey(dpy, code, keys[i].mod | numlockmask | LockMask, root, True, |
626 ce.y = c->y; |
986 GrabModeAsync, GrabModeAsync); |
627 ce.width = c->w; |
987 } |
628 ce.height = c->h; |
988 return; |
629 ce.border_width = c->border; |
989 } |
630 ce.above = None; |
990 ev = &e->xkey; |
631 ce.override_redirect = False; |
991 keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); |
632 XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); |
992 for(i = 0; i < len; i++) |
|
993 if(keysym == keys[i].keysym |
|
994 && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)) |
|
995 { |
|
996 if(keys[i].func) |
|
997 keys[i].func(keys[i].arg); |
|
998 } |
633 } |
999 } |
634 |
1000 |
635 static void |
1001 static void |
636 killclient(const char *arg) { |
1002 killclient(const char *arg) { |
637 XEvent ev; |
1003 XEvent ev; |
708 setclientstate(c, NormalState); |
1084 setclientstate(c, NormalState); |
709 arrange(); |
1085 arrange(); |
710 } |
1086 } |
711 |
1087 |
712 static void |
1088 static void |
713 resize(Client *c, int x, int y, int w, int h, Bool sizehints) { |
1089 mappingnotify(XEvent *e) { |
714 double dx, dy, max, min, ratio; |
1090 XMappingEvent *ev = &e->xmapping; |
715 XWindowChanges wc; |
1091 |
716 |
1092 XRefreshKeyboardMapping(ev); |
717 if(sizehints) { |
1093 if(ev->request == MappingKeyboard) |
718 if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0 && (w - c->basew) > 0) { |
1094 keypress(NULL); |
719 dx = (double)(w - c->basew); |
1095 } |
720 dy = (double)(h - c->baseh); |
1096 |
721 min = (double)(c->minax) / (double)(c->minay); |
1097 static void |
722 max = (double)(c->maxax) / (double)(c->maxay); |
1098 maprequest(XEvent *e) { |
723 ratio = dx / dy; |
1099 static XWindowAttributes wa; |
724 if(max > 0 && min > 0 && ratio > 0) { |
1100 XMapRequestEvent *ev = &e->xmaprequest; |
725 if(ratio < min) { |
1101 |
726 dy = (dx * min + dy) / (min * min + 1); |
1102 if(!XGetWindowAttributes(dpy, ev->window, &wa)) |
727 dx = dy * min; |
1103 return; |
728 w = (int)dx + c->basew; |
1104 if(wa.override_redirect) |
729 h = (int)dy + c->baseh; |
1105 return; |
730 } |
1106 if(!getclient(ev->window)) |
731 else if(ratio > max) { |
1107 manage(ev->window, &wa); |
732 dy = (dx * min + dy) / (max * max + 1); |
|
733 dx = dy * min; |
|
734 w = (int)dx + c->basew; |
|
735 h = (int)dy + c->baseh; |
|
736 } |
|
737 } |
|
738 } |
|
739 if(c->minw && w < c->minw) |
|
740 w = c->minw; |
|
741 if(c->minh && h < c->minh) |
|
742 h = c->minh; |
|
743 if(c->maxw && w > c->maxw) |
|
744 w = c->maxw; |
|
745 if(c->maxh && h > c->maxh) |
|
746 h = c->maxh; |
|
747 if(c->incw) |
|
748 w -= (w - c->basew) % c->incw; |
|
749 if(c->inch) |
|
750 h -= (h - c->baseh) % c->inch; |
|
751 } |
|
752 if(w <= 0 || h <= 0) |
|
753 return; |
|
754 /* offscreen appearance fixes */ |
|
755 if(x > sw) |
|
756 x = sw - w - 2 * c->border; |
|
757 if(y > sh) |
|
758 y = sh - h - 2 * c->border; |
|
759 if(x + w + 2 * c->border < sx) |
|
760 x = sx; |
|
761 if(y + h + 2 * c->border < sy) |
|
762 y = sy; |
|
763 if(c->x != x || c->y != y || c->w != w || c->h != h) { |
|
764 c->x = wc.x = x; |
|
765 c->y = wc.y = y; |
|
766 c->w = wc.width = w; |
|
767 c->h = wc.height = h; |
|
768 wc.border_width = c->border; |
|
769 XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc); |
|
770 configure(c); |
|
771 XSync(dpy, False); |
|
772 } |
|
773 } |
|
774 |
|
775 static void |
|
776 unban(Client *c) { |
|
777 if(!c->isbanned) |
|
778 return; |
|
779 XMoveWindow(dpy, c->win, c->x, c->y); |
|
780 c->isbanned = False; |
|
781 } |
|
782 |
|
783 static void |
|
784 unmanage(Client *c) { |
|
785 XWindowChanges wc; |
|
786 |
|
787 wc.border_width = c->oldborder; |
|
788 /* The server grab construct avoids race conditions. */ |
|
789 XGrabServer(dpy); |
|
790 XSetErrorHandler(xerrordummy); |
|
791 XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ |
|
792 detach(c); |
|
793 detachstack(c); |
|
794 if(sel == c) |
|
795 focus(NULL); |
|
796 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); |
|
797 setclientstate(c, WithdrawnState); |
|
798 free(c->tags); |
|
799 free(c); |
|
800 XSync(dpy, False); |
|
801 XSetErrorHandler(xerror); |
|
802 XUngrabServer(dpy); |
|
803 arrange(); |
|
804 } |
|
805 |
|
806 static void |
|
807 updatesizehints(Client *c) { |
|
808 long msize; |
|
809 XSizeHints size; |
|
810 |
|
811 if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) |
|
812 size.flags = PSize; |
|
813 c->flags = size.flags; |
|
814 if(c->flags & PBaseSize) { |
|
815 c->basew = size.base_width; |
|
816 c->baseh = size.base_height; |
|
817 } |
|
818 else if(c->flags & PMinSize) { |
|
819 c->basew = size.min_width; |
|
820 c->baseh = size.min_height; |
|
821 } |
|
822 else |
|
823 c->basew = c->baseh = 0; |
|
824 if(c->flags & PResizeInc) { |
|
825 c->incw = size.width_inc; |
|
826 c->inch = size.height_inc; |
|
827 } |
|
828 else |
|
829 c->incw = c->inch = 0; |
|
830 if(c->flags & PMaxSize) { |
|
831 c->maxw = size.max_width; |
|
832 c->maxh = size.max_height; |
|
833 } |
|
834 else |
|
835 c->maxw = c->maxh = 0; |
|
836 if(c->flags & PMinSize) { |
|
837 c->minw = size.min_width; |
|
838 c->minh = size.min_height; |
|
839 } |
|
840 else if(c->flags & PBaseSize) { |
|
841 c->minw = size.base_width; |
|
842 c->minh = size.base_height; |
|
843 } |
|
844 else |
|
845 c->minw = c->minh = 0; |
|
846 if(c->flags & PAspect) { |
|
847 c->minax = size.min_aspect.x; |
|
848 c->maxax = size.max_aspect.x; |
|
849 c->minay = size.min_aspect.y; |
|
850 c->maxay = size.max_aspect.y; |
|
851 } |
|
852 else |
|
853 c->minax = c->maxax = c->minay = c->maxay = 0; |
|
854 c->isfixed = (c->maxw && c->minw && c->maxh && c->minh |
|
855 && c->maxw == c->minw && c->maxh == c->minh); |
|
856 } |
|
857 |
|
858 static void |
|
859 updatetitle(Client *c) { |
|
860 if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) |
|
861 gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name); |
|
862 } |
|
863 |
|
864 static Client * |
|
865 getclient(Window w) { |
|
866 Client *c; |
|
867 |
|
868 for(c = clients; c && c->win != w; c = c->next); |
|
869 return c; |
|
870 } |
1108 } |
871 |
1109 |
872 static void |
1110 static void |
873 movemouse(Client *c) { |
1111 movemouse(Client *c) { |
874 int x1, y1, ocx, ocy, di, nx, ny; |
1112 int x1, y1, ocx, ocy, di, nx, ny; |
910 break; |
1148 break; |
911 } |
1149 } |
912 } |
1150 } |
913 } |
1151 } |
914 |
1152 |
|
1153 static Client * |
|
1154 nexttiled(Client *c) { |
|
1155 for(; c && (c->isfloating || !isvisible(c)); c = c->next); |
|
1156 return c; |
|
1157 } |
|
1158 |
|
1159 static void |
|
1160 propertynotify(XEvent *e) { |
|
1161 Client *c; |
|
1162 Window trans; |
|
1163 XPropertyEvent *ev = &e->xproperty; |
|
1164 |
|
1165 if(ev->state == PropertyDelete) |
|
1166 return; /* ignore */ |
|
1167 if((c = getclient(ev->window))) { |
|
1168 switch (ev->atom) { |
|
1169 default: break; |
|
1170 case XA_WM_TRANSIENT_FOR: |
|
1171 XGetTransientForHint(dpy, c->win, &trans); |
|
1172 if(!c->isfloating && (c->isfloating = (getclient(trans) != NULL))) |
|
1173 arrange(); |
|
1174 break; |
|
1175 case XA_WM_NORMAL_HINTS: |
|
1176 updatesizehints(c); |
|
1177 break; |
|
1178 } |
|
1179 if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { |
|
1180 updatetitle(c); |
|
1181 if(c == sel) |
|
1182 drawbar(); |
|
1183 } |
|
1184 } |
|
1185 } |
|
1186 |
|
1187 static void |
|
1188 quit(const char *arg) { |
|
1189 readin = running = False; |
|
1190 } |
|
1191 |
|
1192 static void |
|
1193 resize(Client *c, int x, int y, int w, int h, Bool sizehints) { |
|
1194 double dx, dy, max, min, ratio; |
|
1195 XWindowChanges wc; |
|
1196 |
|
1197 if(sizehints) { |
|
1198 if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0 && (w - c->basew) > 0) { |
|
1199 dx = (double)(w - c->basew); |
|
1200 dy = (double)(h - c->baseh); |
|
1201 min = (double)(c->minax) / (double)(c->minay); |
|
1202 max = (double)(c->maxax) / (double)(c->maxay); |
|
1203 ratio = dx / dy; |
|
1204 if(max > 0 && min > 0 && ratio > 0) { |
|
1205 if(ratio < min) { |
|
1206 dy = (dx * min + dy) / (min * min + 1); |
|
1207 dx = dy * min; |
|
1208 w = (int)dx + c->basew; |
|
1209 h = (int)dy + c->baseh; |
|
1210 } |
|
1211 else if(ratio > max) { |
|
1212 dy = (dx * min + dy) / (max * max + 1); |
|
1213 dx = dy * min; |
|
1214 w = (int)dx + c->basew; |
|
1215 h = (int)dy + c->baseh; |
|
1216 } |
|
1217 } |
|
1218 } |
|
1219 if(c->minw && w < c->minw) |
|
1220 w = c->minw; |
|
1221 if(c->minh && h < c->minh) |
|
1222 h = c->minh; |
|
1223 if(c->maxw && w > c->maxw) |
|
1224 w = c->maxw; |
|
1225 if(c->maxh && h > c->maxh) |
|
1226 h = c->maxh; |
|
1227 if(c->incw) |
|
1228 w -= (w - c->basew) % c->incw; |
|
1229 if(c->inch) |
|
1230 h -= (h - c->baseh) % c->inch; |
|
1231 } |
|
1232 if(w <= 0 || h <= 0) |
|
1233 return; |
|
1234 /* offscreen appearance fixes */ |
|
1235 if(x > sw) |
|
1236 x = sw - w - 2 * c->border; |
|
1237 if(y > sh) |
|
1238 y = sh - h - 2 * c->border; |
|
1239 if(x + w + 2 * c->border < sx) |
|
1240 x = sx; |
|
1241 if(y + h + 2 * c->border < sy) |
|
1242 y = sy; |
|
1243 if(c->x != x || c->y != y || c->w != w || c->h != h) { |
|
1244 c->x = wc.x = x; |
|
1245 c->y = wc.y = y; |
|
1246 c->w = wc.width = w; |
|
1247 c->h = wc.height = h; |
|
1248 wc.border_width = c->border; |
|
1249 XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc); |
|
1250 configure(c); |
|
1251 XSync(dpy, False); |
|
1252 } |
|
1253 } |
|
1254 |
915 static void |
1255 static void |
916 resizemouse(Client *c) { |
1256 resizemouse(Client *c) { |
917 int ocx, ocy; |
1257 int ocx, ocy; |
918 int nw, nh; |
1258 int nw, nh; |
919 XEvent ev; |
1259 XEvent ev; |
950 } |
1290 } |
951 } |
1291 } |
952 } |
1292 } |
953 |
1293 |
954 static void |
1294 static void |
955 buttonpress(XEvent *e) { |
|
956 unsigned int i, x; |
|
957 Client *c; |
|
958 XButtonPressedEvent *ev = &e->xbutton; |
|
959 |
|
960 if(barwin == ev->window) { |
|
961 x = 0; |
|
962 for(i = 0; i < ntags; i++) { |
|
963 x += textw(tags[i]); |
|
964 if(ev->x < x) { |
|
965 if(ev->button == Button1) { |
|
966 if(ev->state & MODKEY) |
|
967 tag(tags[i]); |
|
968 else |
|
969 view(tags[i]); |
|
970 } |
|
971 else if(ev->button == Button3) { |
|
972 if(ev->state & MODKEY) |
|
973 toggletag(tags[i]); |
|
974 else |
|
975 toggleview(tags[i]); |
|
976 } |
|
977 return; |
|
978 } |
|
979 } |
|
980 if((ev->x < x + blw) && ev->button == Button1) |
|
981 setlayout(NULL); |
|
982 } |
|
983 else if((c = getclient(ev->window))) { |
|
984 focus(c); |
|
985 if(CLEANMASK(ev->state) != MODKEY) |
|
986 return; |
|
987 if(ev->button == Button1 && (isfloating() || c->isfloating)) { |
|
988 restack(); |
|
989 movemouse(c); |
|
990 } |
|
991 else if(ev->button == Button2) |
|
992 zoom(NULL); |
|
993 else if(ev->button == Button3 |
|
994 && (isfloating() || c->isfloating) && !c->isfixed) |
|
995 { |
|
996 restack(); |
|
997 resizemouse(c); |
|
998 } |
|
999 } |
|
1000 } |
|
1001 |
|
1002 static void |
|
1003 configurerequest(XEvent *e) { |
|
1004 Client *c; |
|
1005 XConfigureRequestEvent *ev = &e->xconfigurerequest; |
|
1006 XWindowChanges wc; |
|
1007 |
|
1008 if((c = getclient(ev->window))) { |
|
1009 c->ismax = False; |
|
1010 if(ev->value_mask & CWBorderWidth) |
|
1011 c->border = ev->border_width; |
|
1012 if(c->isfixed || c->isfloating || isfloating()) { |
|
1013 if(ev->value_mask & CWX) |
|
1014 c->x = ev->x; |
|
1015 if(ev->value_mask & CWY) |
|
1016 c->y = ev->y; |
|
1017 if(ev->value_mask & CWWidth) |
|
1018 c->w = ev->width; |
|
1019 if(ev->value_mask & CWHeight) |
|
1020 c->h = ev->height; |
|
1021 if((c->x + c->w) > sw && c->isfloating) |
|
1022 c->x = sw / 2 - c->w / 2; /* center in x direction */ |
|
1023 if((c->y + c->h) > sh && c->isfloating) |
|
1024 c->y = sh / 2 - c->h / 2; /* center in y direction */ |
|
1025 if((ev->value_mask & (CWX | CWY)) |
|
1026 && !(ev->value_mask & (CWWidth | CWHeight))) |
|
1027 configure(c); |
|
1028 if(isvisible(c)) |
|
1029 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); |
|
1030 } |
|
1031 else |
|
1032 configure(c); |
|
1033 } |
|
1034 else { |
|
1035 wc.x = ev->x; |
|
1036 wc.y = ev->y; |
|
1037 wc.width = ev->width; |
|
1038 wc.height = ev->height; |
|
1039 wc.border_width = ev->border_width; |
|
1040 wc.sibling = ev->above; |
|
1041 wc.stack_mode = ev->detail; |
|
1042 XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); |
|
1043 } |
|
1044 XSync(dpy, False); |
|
1045 } |
|
1046 |
|
1047 static void |
|
1048 configurenotify(XEvent *e) { |
|
1049 XConfigureEvent *ev = &e->xconfigure; |
|
1050 |
|
1051 if (ev->window == root && (ev->width != sw || ev->height != sh)) { |
|
1052 sw = ev->width; |
|
1053 sh = ev->height; |
|
1054 XFreePixmap(dpy, dc.drawable); |
|
1055 dc.drawable = XCreatePixmap(dpy, root, sw, bh, DefaultDepth(dpy, screen)); |
|
1056 XResizeWindow(dpy, barwin, sw, bh); |
|
1057 updatebarpos(); |
|
1058 arrange(); |
|
1059 } |
|
1060 } |
|
1061 |
|
1062 static void |
|
1063 destroynotify(XEvent *e) { |
|
1064 Client *c; |
|
1065 XDestroyWindowEvent *ev = &e->xdestroywindow; |
|
1066 |
|
1067 if((c = getclient(ev->window))) |
|
1068 unmanage(c); |
|
1069 } |
|
1070 |
|
1071 static void |
|
1072 enternotify(XEvent *e) { |
|
1073 Client *c; |
|
1074 XCrossingEvent *ev = &e->xcrossing; |
|
1075 |
|
1076 if(ev->mode != NotifyNormal || ev->detail == NotifyInferior) |
|
1077 return; |
|
1078 if((c = getclient(ev->window))) |
|
1079 focus(c); |
|
1080 else if(ev->window == root) { |
|
1081 selscreen = True; |
|
1082 focus(NULL); |
|
1083 } |
|
1084 } |
|
1085 |
|
1086 static void |
|
1087 expose(XEvent *e) { |
|
1088 XExposeEvent *ev = &e->xexpose; |
|
1089 |
|
1090 if(ev->count == 0) { |
|
1091 if(barwin == ev->window) |
|
1092 drawbar(); |
|
1093 } |
|
1094 } |
|
1095 |
|
1096 static void |
|
1097 keypress(XEvent *e) { |
|
1098 KEYS |
|
1099 unsigned int len = sizeof keys / sizeof keys[0]; |
|
1100 unsigned int i; |
|
1101 KeyCode code; |
|
1102 KeySym keysym; |
|
1103 XKeyEvent *ev; |
|
1104 |
|
1105 if(!e) { /* grabkeys */ |
|
1106 XUngrabKey(dpy, AnyKey, AnyModifier, root); |
|
1107 for(i = 0; i < len; i++) { |
|
1108 code = XKeysymToKeycode(dpy, keys[i].keysym); |
|
1109 XGrabKey(dpy, code, keys[i].mod, root, True, |
|
1110 GrabModeAsync, GrabModeAsync); |
|
1111 XGrabKey(dpy, code, keys[i].mod | LockMask, root, True, |
|
1112 GrabModeAsync, GrabModeAsync); |
|
1113 XGrabKey(dpy, code, keys[i].mod | numlockmask, root, True, |
|
1114 GrabModeAsync, GrabModeAsync); |
|
1115 XGrabKey(dpy, code, keys[i].mod | numlockmask | LockMask, root, True, |
|
1116 GrabModeAsync, GrabModeAsync); |
|
1117 } |
|
1118 return; |
|
1119 } |
|
1120 ev = &e->xkey; |
|
1121 keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); |
|
1122 for(i = 0; i < len; i++) |
|
1123 if(keysym == keys[i].keysym |
|
1124 && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state)) |
|
1125 { |
|
1126 if(keys[i].func) |
|
1127 keys[i].func(keys[i].arg); |
|
1128 } |
|
1129 } |
|
1130 |
|
1131 static void |
|
1132 leavenotify(XEvent *e) { |
|
1133 XCrossingEvent *ev = &e->xcrossing; |
|
1134 |
|
1135 if((ev->window == root) && !ev->same_screen) { |
|
1136 selscreen = False; |
|
1137 focus(NULL); |
|
1138 } |
|
1139 } |
|
1140 |
|
1141 static void |
|
1142 mappingnotify(XEvent *e) { |
|
1143 XMappingEvent *ev = &e->xmapping; |
|
1144 |
|
1145 XRefreshKeyboardMapping(ev); |
|
1146 if(ev->request == MappingKeyboard) |
|
1147 keypress(NULL); |
|
1148 } |
|
1149 |
|
1150 static void |
|
1151 maprequest(XEvent *e) { |
|
1152 static XWindowAttributes wa; |
|
1153 XMapRequestEvent *ev = &e->xmaprequest; |
|
1154 |
|
1155 if(!XGetWindowAttributes(dpy, ev->window, &wa)) |
|
1156 return; |
|
1157 if(wa.override_redirect) |
|
1158 return; |
|
1159 if(!getclient(ev->window)) |
|
1160 manage(ev->window, &wa); |
|
1161 } |
|
1162 |
|
1163 static void |
|
1164 propertynotify(XEvent *e) { |
|
1165 Client *c; |
|
1166 Window trans; |
|
1167 XPropertyEvent *ev = &e->xproperty; |
|
1168 |
|
1169 if(ev->state == PropertyDelete) |
|
1170 return; /* ignore */ |
|
1171 if((c = getclient(ev->window))) { |
|
1172 switch (ev->atom) { |
|
1173 default: break; |
|
1174 case XA_WM_TRANSIENT_FOR: |
|
1175 XGetTransientForHint(dpy, c->win, &trans); |
|
1176 if(!c->isfloating && (c->isfloating = (getclient(trans) != NULL))) |
|
1177 arrange(); |
|
1178 break; |
|
1179 case XA_WM_NORMAL_HINTS: |
|
1180 updatesizehints(c); |
|
1181 break; |
|
1182 } |
|
1183 if(ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { |
|
1184 updatetitle(c); |
|
1185 if(c == sel) |
|
1186 drawbar(); |
|
1187 } |
|
1188 } |
|
1189 } |
|
1190 |
|
1191 static void |
|
1192 unmapnotify(XEvent *e) { |
|
1193 Client *c; |
|
1194 XUnmapEvent *ev = &e->xunmap; |
|
1195 |
|
1196 if((c = getclient(ev->window))) |
|
1197 unmanage(c); |
|
1198 } |
|
1199 |
|
1200 static unsigned int |
|
1201 idxoftag(const char *tag) { |
|
1202 unsigned int i; |
|
1203 |
|
1204 for(i = 0; i < ntags; i++) |
|
1205 if(tags[i] == tag) |
|
1206 return i; |
|
1207 return 0; |
|
1208 } |
|
1209 |
|
1210 static void |
|
1211 floating(void) { /* default floating layout */ |
|
1212 Client *c; |
|
1213 |
|
1214 for(c = clients; c; c = c->next) |
|
1215 if(isvisible(c)) |
|
1216 resize(c, c->x, c->y, c->w, c->h, True); |
|
1217 } |
|
1218 |
|
1219 static void |
|
1220 applyrules(Client *c) { |
|
1221 static char buf[512]; |
|
1222 unsigned int i, j; |
|
1223 regmatch_t tmp; |
|
1224 Bool matched = False; |
|
1225 XClassHint ch = { 0 }; |
|
1226 |
|
1227 /* rule matching */ |
|
1228 XGetClassHint(dpy, c->win, &ch); |
|
1229 snprintf(buf, sizeof buf, "%s:%s:%s", |
|
1230 ch.res_class ? ch.res_class : "", |
|
1231 ch.res_name ? ch.res_name : "", c->name); |
|
1232 for(i = 0; i < nrules; i++) |
|
1233 if(regs[i].propregex && !regexec(regs[i].propregex, buf, 1, &tmp, 0)) { |
|
1234 c->isfloating = rules[i].isfloating; |
|
1235 for(j = 0; regs[i].tagregex && j < ntags; j++) { |
|
1236 if(!regexec(regs[i].tagregex, tags[j], 1, &tmp, 0)) { |
|
1237 matched = True; |
|
1238 c->tags[j] = True; |
|
1239 } |
|
1240 } |
|
1241 } |
|
1242 if(ch.res_class) |
|
1243 XFree(ch.res_class); |
|
1244 if(ch.res_name) |
|
1245 XFree(ch.res_name); |
|
1246 if(!matched) |
|
1247 for(i = 0; i < ntags; i++) |
|
1248 c->tags[i] = seltags[i]; |
|
1249 } |
|
1250 |
|
1251 static void |
|
1252 compileregs(void) { |
|
1253 unsigned int i; |
|
1254 regex_t *reg; |
|
1255 |
|
1256 if(regs) |
|
1257 return; |
|
1258 nrules = sizeof rules / sizeof rules[0]; |
|
1259 regs = emallocz(nrules * sizeof(Regs)); |
|
1260 for(i = 0; i < nrules; i++) { |
|
1261 if(rules[i].prop) { |
|
1262 reg = emallocz(sizeof(regex_t)); |
|
1263 if(regcomp(reg, rules[i].prop, REG_EXTENDED)) |
|
1264 free(reg); |
|
1265 else |
|
1266 regs[i].propregex = reg; |
|
1267 } |
|
1268 if(rules[i].tags) { |
|
1269 reg = emallocz(sizeof(regex_t)); |
|
1270 if(regcomp(reg, rules[i].tags, REG_EXTENDED)) |
|
1271 free(reg); |
|
1272 else |
|
1273 regs[i].tagregex = reg; |
|
1274 } |
|
1275 } |
|
1276 } |
|
1277 |
|
1278 static void |
|
1279 focusnext(const char *arg) { |
|
1280 Client *c; |
|
1281 |
|
1282 if(!sel) |
|
1283 return; |
|
1284 for(c = sel->next; c && !isvisible(c); c = c->next); |
|
1285 if(!c) |
|
1286 for(c = clients; c && !isvisible(c); c = c->next); |
|
1287 if(c) { |
|
1288 focus(c); |
|
1289 restack(); |
|
1290 } |
|
1291 } |
|
1292 |
|
1293 static void |
|
1294 focusprev(const char *arg) { |
|
1295 Client *c; |
|
1296 |
|
1297 if(!sel) |
|
1298 return; |
|
1299 for(c = sel->prev; c && !isvisible(c); c = c->prev); |
|
1300 if(!c) { |
|
1301 for(c = clients; c && c->next; c = c->next); |
|
1302 for(; c && !isvisible(c); c = c->prev); |
|
1303 } |
|
1304 if(c) { |
|
1305 focus(c); |
|
1306 restack(); |
|
1307 } |
|
1308 } |
|
1309 |
|
1310 static void |
|
1311 initlayouts(void) { |
|
1312 unsigned int i, w; |
|
1313 |
|
1314 nlayouts = sizeof layouts / sizeof layouts[0]; |
|
1315 for(blw = i = 0; i < nlayouts; i++) { |
|
1316 w = textw(layouts[i].symbol); |
|
1317 if(w > blw) |
|
1318 blw = w; |
|
1319 } |
|
1320 } |
|
1321 |
|
1322 static Bool |
|
1323 isfloating(void) { |
|
1324 return layouts[ltidx].arrange == floating; |
|
1325 } |
|
1326 |
|
1327 static Bool |
|
1328 isvisible(Client *c) { |
|
1329 unsigned int i; |
|
1330 |
|
1331 for(i = 0; i < ntags; i++) |
|
1332 if(c->tags[i] && seltags[i]) |
|
1333 return True; |
|
1334 return False; |
|
1335 } |
|
1336 |
|
1337 static void |
|
1338 restack(void) { |
1295 restack(void) { |
1339 Client *c; |
1296 Client *c; |
1340 XEvent ev; |
1297 XEvent ev; |
1341 XWindowChanges wc; |
1298 XWindowChanges wc; |
1342 |
1299 |
1362 XSync(dpy, False); |
1319 XSync(dpy, False); |
1363 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
1320 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
1364 } |
1321 } |
1365 |
1322 |
1366 static void |
1323 static void |
1367 setlayout(const char *arg) { |
|
1368 unsigned int i; |
|
1369 |
|
1370 if(!arg) { |
|
1371 if(++ltidx == nlayouts) |
|
1372 ltidx = 0;; |
|
1373 } |
|
1374 else { |
|
1375 for(i = 0; i < nlayouts; i++) |
|
1376 if(!strcmp(arg, layouts[i].symbol)) |
|
1377 break; |
|
1378 if(i == nlayouts) |
|
1379 return; |
|
1380 ltidx = i; |
|
1381 } |
|
1382 if(sel) |
|
1383 arrange(); |
|
1384 else |
|
1385 drawbar(); |
|
1386 } |
|
1387 |
|
1388 static void |
|
1389 tag(const char *arg) { |
|
1390 unsigned int i; |
|
1391 |
|
1392 if(!sel) |
|
1393 return; |
|
1394 for(i = 0; i < ntags; i++) |
|
1395 sel->tags[i] = arg == NULL; |
|
1396 i = idxoftag(arg); |
|
1397 if(i >= 0 && i < ntags) |
|
1398 sel->tags[i] = True; |
|
1399 arrange(); |
|
1400 } |
|
1401 |
|
1402 static void |
|
1403 togglefloating(const char *arg) { |
|
1404 if(!sel) |
|
1405 return; |
|
1406 sel->isfloating = !sel->isfloating; |
|
1407 if(sel->isfloating) |
|
1408 resize(sel, sel->x, sel->y, sel->w, sel->h, True); |
|
1409 arrange(); |
|
1410 } |
|
1411 |
|
1412 static void |
|
1413 togglemax(const char *arg) { |
|
1414 XEvent ev; |
|
1415 |
|
1416 if(!sel || (!isfloating() && !sel->isfloating) || sel->isfixed) |
|
1417 return; |
|
1418 if((sel->ismax = !sel->ismax)) { |
|
1419 sel->rx = sel->x; |
|
1420 sel->ry = sel->y; |
|
1421 sel->rw = sel->w; |
|
1422 sel->rh = sel->h; |
|
1423 resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->border, True); |
|
1424 } |
|
1425 else |
|
1426 resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True); |
|
1427 drawbar(); |
|
1428 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
|
1429 } |
|
1430 |
|
1431 static void |
|
1432 toggletag(const char *arg) { |
|
1433 unsigned int i, j; |
|
1434 |
|
1435 if(!sel) |
|
1436 return; |
|
1437 i = idxoftag(arg); |
|
1438 sel->tags[i] = !sel->tags[i]; |
|
1439 for(j = 0; j < ntags && !sel->tags[j]; j++); |
|
1440 if(j == ntags) |
|
1441 sel->tags[i] = True; |
|
1442 arrange(); |
|
1443 } |
|
1444 |
|
1445 static void |
|
1446 toggleview(const char *arg) { |
|
1447 unsigned int i, j; |
|
1448 |
|
1449 i = idxoftag(arg); |
|
1450 seltags[i] = !seltags[i]; |
|
1451 for(j = 0; j < ntags && !seltags[j]; j++); |
|
1452 if(j == ntags) |
|
1453 seltags[i] = True; /* cannot toggle last view */ |
|
1454 arrange(); |
|
1455 } |
|
1456 |
|
1457 static void |
|
1458 view(const char *arg) { |
|
1459 unsigned int i; |
|
1460 |
|
1461 for(i = 0; i < ntags; i++) |
|
1462 seltags[i] = arg == NULL; |
|
1463 i = idxoftag(arg); |
|
1464 if(i >= 0 && i < ntags) |
|
1465 seltags[i] = True; |
|
1466 arrange(); |
|
1467 } |
|
1468 |
|
1469 static void |
|
1470 cleanup(void) { |
|
1471 close(STDIN_FILENO); |
|
1472 while(stack) { |
|
1473 unban(stack); |
|
1474 unmanage(stack); |
|
1475 } |
|
1476 if(dc.font.set) |
|
1477 XFreeFontSet(dpy, dc.font.set); |
|
1478 else |
|
1479 XFreeFont(dpy, dc.font.xfont); |
|
1480 XUngrabKey(dpy, AnyKey, AnyModifier, root); |
|
1481 XFreePixmap(dpy, dc.drawable); |
|
1482 XFreeGC(dpy, dc.gc); |
|
1483 XDestroyWindow(dpy, barwin); |
|
1484 XFreeCursor(dpy, cursor[CurNormal]); |
|
1485 XFreeCursor(dpy, cursor[CurResize]); |
|
1486 XFreeCursor(dpy, cursor[CurMove]); |
|
1487 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); |
|
1488 XSync(dpy, False); |
|
1489 free(seltags); |
|
1490 } |
|
1491 |
|
1492 static long |
|
1493 getstate(Window w) { |
|
1494 int format, status; |
|
1495 long result = -1; |
|
1496 unsigned char *p = NULL; |
|
1497 unsigned long n, extra; |
|
1498 Atom real; |
|
1499 |
|
1500 status = XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], |
|
1501 &real, &format, &n, &extra, (unsigned char **)&p); |
|
1502 if(status != Success) |
|
1503 return -1; |
|
1504 if(n != 0) |
|
1505 result = *p; |
|
1506 XFree(p); |
|
1507 return result; |
|
1508 } |
|
1509 |
|
1510 static void |
|
1511 scan(void) { |
1324 scan(void) { |
1512 unsigned int i, num; |
1325 unsigned int i, num; |
1513 Window *wins, d1, d2; |
1326 Window *wins, d1, d2; |
1514 XWindowAttributes wa; |
1327 XWindowAttributes wa; |
1515 |
1328 |
1584 initbar(); |
1449 initbar(); |
1585 /* multihead support */ |
1450 /* multihead support */ |
1586 selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); |
1451 selscreen = XQueryPointer(dpy, root, &w, &w, &i, &i, &i, &i, &mask); |
1587 } |
1452 } |
1588 |
1453 |
1589 /* |
1454 static void |
1590 * Startup Error handler to check if another window manager |
1455 spawn(const char *arg) { |
1591 * is already running. |
1456 static char *shell = NULL; |
1592 */ |
1457 |
1593 static int |
1458 if(!shell && !(shell = getenv("SHELL"))) |
1594 xerrorstart(Display *dsply, XErrorEvent *ee) { |
1459 shell = "/bin/sh"; |
1595 otherwm = True; |
1460 if(!arg) |
1596 return -1; |
1461 return; |
1597 } |
1462 /* The double-fork construct avoids zombie processes and keeps the code |
1598 |
1463 * clean from stupid signal handlers. */ |
1599 static Bool |
1464 if(fork() == 0) { |
1600 gettextprop(Window w, Atom atom, char *text, unsigned int size) { |
1465 if(fork() == 0) { |
1601 char **list = NULL; |
1466 if(dpy) |
1602 int n; |
1467 close(ConnectionNumber(dpy)); |
1603 XTextProperty name; |
1468 setsid(); |
1604 |
1469 execl(shell, shell, "-c", arg, (char *)NULL); |
1605 if(!text || size == 0) |
1470 fprintf(stderr, "dwm: execl '%s -c %s'", shell, arg); |
1606 return False; |
1471 perror(" failed"); |
1607 text[0] = '\0'; |
1472 } |
1608 XGetTextProperty(dpy, w, &name, atom); |
1473 exit(0); |
1609 if(!name.nitems) |
1474 } |
1610 return False; |
1475 wait(0); |
1611 if(name.encoding == XA_STRING) |
1476 } |
1612 strncpy(text, (char *)name.value, size - 1); |
1477 |
1613 else { |
1478 static void |
1614 if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success |
1479 tag(const char *arg) { |
1615 && n > 0 && *list) |
1480 unsigned int i; |
1616 { |
1481 |
1617 strncpy(text, *list, size - 1); |
1482 if(!sel) |
1618 XFreeStringList(list); |
1483 return; |
1619 } |
1484 for(i = 0; i < ntags; i++) |
1620 } |
1485 sel->tags[i] = arg == NULL; |
1621 text[size - 1] = '\0'; |
1486 i = idxoftag(arg); |
1622 XFree(name.value); |
1487 if(i >= 0 && i < ntags) |
1623 return True; |
1488 sel->tags[i] = True; |
1624 } |
1489 arrange(); |
1625 |
1490 } |
1626 static void |
1491 |
1627 quit(const char *arg) { |
1492 static unsigned int |
1628 readin = running = False; |
1493 textnw(const char *text, unsigned int len) { |
|
1494 XRectangle r; |
|
1495 |
|
1496 if(dc.font.set) { |
|
1497 XmbTextExtents(dc.font.set, text, len, NULL, &r); |
|
1498 return r.width; |
|
1499 } |
|
1500 return XTextWidth(dc.font.xfont, text, len); |
|
1501 } |
|
1502 |
|
1503 static unsigned int |
|
1504 textw(const char *text) { |
|
1505 return textnw(text, strlen(text)) + dc.font.height; |
|
1506 } |
|
1507 |
|
1508 static void |
|
1509 tile(void) { |
|
1510 unsigned int i, n, nx, ny, nw, nh, mw, th; |
|
1511 Client *c; |
|
1512 |
|
1513 for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) |
|
1514 n++; |
|
1515 |
|
1516 /* window geoms */ |
|
1517 mw = (n == 1) ? waw : mwfact * waw; |
|
1518 th = (n > 1) ? wah / (n - 1) : 0; |
|
1519 if(n > 1 && th < bh) |
|
1520 th = wah; |
|
1521 |
|
1522 nx = wax; |
|
1523 ny = way; |
|
1524 for(i = 0, c = nexttiled(clients); c; c = nexttiled(c->next), i++) { |
|
1525 c->ismax = False; |
|
1526 if(i == 0) { /* master */ |
|
1527 nw = mw - 2 * c->border; |
|
1528 nh = wah - 2 * c->border; |
|
1529 } |
|
1530 else { /* tile window */ |
|
1531 if(i == 1) { |
|
1532 ny = way; |
|
1533 nx += mw; |
|
1534 } |
|
1535 nw = waw - mw - 2 * c->border; |
|
1536 if(i + 1 == n) /* remainder */ |
|
1537 nh = (way + wah) - ny - 2 * c->border; |
|
1538 else |
|
1539 nh = th - 2 * c->border; |
|
1540 } |
|
1541 resize(c, nx, ny, nw, nh, RESIZEHINTS); |
|
1542 if(n > 1 && th != wah) |
|
1543 ny += nh + 2 * c->border; |
|
1544 } |
|
1545 } |
|
1546 |
|
1547 static void |
|
1548 togglebar(const char *arg) { |
|
1549 if(bpos == BarOff) |
|
1550 bpos = (BARPOS == BarOff) ? BarTop : BARPOS; |
|
1551 else |
|
1552 bpos = BarOff; |
|
1553 updatebarpos(); |
|
1554 arrange(); |
|
1555 } |
|
1556 |
|
1557 static void |
|
1558 togglefloating(const char *arg) { |
|
1559 if(!sel) |
|
1560 return; |
|
1561 sel->isfloating = !sel->isfloating; |
|
1562 if(sel->isfloating) |
|
1563 resize(sel, sel->x, sel->y, sel->w, sel->h, True); |
|
1564 arrange(); |
|
1565 } |
|
1566 |
|
1567 static void |
|
1568 togglemax(const char *arg) { |
|
1569 XEvent ev; |
|
1570 |
|
1571 if(!sel || (!isfloating() && !sel->isfloating) || sel->isfixed) |
|
1572 return; |
|
1573 if((sel->ismax = !sel->ismax)) { |
|
1574 sel->rx = sel->x; |
|
1575 sel->ry = sel->y; |
|
1576 sel->rw = sel->w; |
|
1577 sel->rh = sel->h; |
|
1578 resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->border, True); |
|
1579 } |
|
1580 else |
|
1581 resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True); |
|
1582 drawbar(); |
|
1583 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
|
1584 } |
|
1585 |
|
1586 static void |
|
1587 toggletag(const char *arg) { |
|
1588 unsigned int i, j; |
|
1589 |
|
1590 if(!sel) |
|
1591 return; |
|
1592 i = idxoftag(arg); |
|
1593 sel->tags[i] = !sel->tags[i]; |
|
1594 for(j = 0; j < ntags && !sel->tags[j]; j++); |
|
1595 if(j == ntags) |
|
1596 sel->tags[i] = True; |
|
1597 arrange(); |
|
1598 } |
|
1599 |
|
1600 static void |
|
1601 toggleview(const char *arg) { |
|
1602 unsigned int i, j; |
|
1603 |
|
1604 i = idxoftag(arg); |
|
1605 seltags[i] = !seltags[i]; |
|
1606 for(j = 0; j < ntags && !seltags[j]; j++); |
|
1607 if(j == ntags) |
|
1608 seltags[i] = True; /* cannot toggle last view */ |
|
1609 arrange(); |
|
1610 } |
|
1611 |
|
1612 static void |
|
1613 unban(Client *c) { |
|
1614 if(!c->isbanned) |
|
1615 return; |
|
1616 XMoveWindow(dpy, c->win, c->x, c->y); |
|
1617 c->isbanned = False; |
|
1618 } |
|
1619 |
|
1620 static void |
|
1621 unmanage(Client *c) { |
|
1622 XWindowChanges wc; |
|
1623 |
|
1624 wc.border_width = c->oldborder; |
|
1625 /* The server grab construct avoids race conditions. */ |
|
1626 XGrabServer(dpy); |
|
1627 XSetErrorHandler(xerrordummy); |
|
1628 XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ |
|
1629 detach(c); |
|
1630 detachstack(c); |
|
1631 if(sel == c) |
|
1632 focus(NULL); |
|
1633 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); |
|
1634 setclientstate(c, WithdrawnState); |
|
1635 free(c->tags); |
|
1636 free(c); |
|
1637 XSync(dpy, False); |
|
1638 XSetErrorHandler(xerror); |
|
1639 XUngrabServer(dpy); |
|
1640 arrange(); |
|
1641 } |
|
1642 |
|
1643 static void |
|
1644 unmapnotify(XEvent *e) { |
|
1645 Client *c; |
|
1646 XUnmapEvent *ev = &e->xunmap; |
|
1647 |
|
1648 if((c = getclient(ev->window))) |
|
1649 unmanage(c); |
|
1650 } |
|
1651 |
|
1652 static void |
|
1653 updatebarpos(void) { |
|
1654 XEvent ev; |
|
1655 |
|
1656 wax = sx; |
|
1657 way = sy; |
|
1658 wah = sh; |
|
1659 waw = sw; |
|
1660 switch(bpos) { |
|
1661 default: |
|
1662 wah -= bh; |
|
1663 way += bh; |
|
1664 XMoveWindow(dpy, barwin, sx, sy); |
|
1665 break; |
|
1666 case BarBot: |
|
1667 wah -= bh; |
|
1668 XMoveWindow(dpy, barwin, sx, sy + wah); |
|
1669 break; |
|
1670 case BarOff: |
|
1671 XMoveWindow(dpy, barwin, sx, sy - bh); |
|
1672 break; |
|
1673 } |
|
1674 XSync(dpy, False); |
|
1675 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
|
1676 } |
|
1677 |
|
1678 static void |
|
1679 updatesizehints(Client *c) { |
|
1680 long msize; |
|
1681 XSizeHints size; |
|
1682 |
|
1683 if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) |
|
1684 size.flags = PSize; |
|
1685 c->flags = size.flags; |
|
1686 if(c->flags & PBaseSize) { |
|
1687 c->basew = size.base_width; |
|
1688 c->baseh = size.base_height; |
|
1689 } |
|
1690 else if(c->flags & PMinSize) { |
|
1691 c->basew = size.min_width; |
|
1692 c->baseh = size.min_height; |
|
1693 } |
|
1694 else |
|
1695 c->basew = c->baseh = 0; |
|
1696 if(c->flags & PResizeInc) { |
|
1697 c->incw = size.width_inc; |
|
1698 c->inch = size.height_inc; |
|
1699 } |
|
1700 else |
|
1701 c->incw = c->inch = 0; |
|
1702 if(c->flags & PMaxSize) { |
|
1703 c->maxw = size.max_width; |
|
1704 c->maxh = size.max_height; |
|
1705 } |
|
1706 else |
|
1707 c->maxw = c->maxh = 0; |
|
1708 if(c->flags & PMinSize) { |
|
1709 c->minw = size.min_width; |
|
1710 c->minh = size.min_height; |
|
1711 } |
|
1712 else if(c->flags & PBaseSize) { |
|
1713 c->minw = size.base_width; |
|
1714 c->minh = size.base_height; |
|
1715 } |
|
1716 else |
|
1717 c->minw = c->minh = 0; |
|
1718 if(c->flags & PAspect) { |
|
1719 c->minax = size.min_aspect.x; |
|
1720 c->maxax = size.max_aspect.x; |
|
1721 c->minay = size.min_aspect.y; |
|
1722 c->maxay = size.max_aspect.y; |
|
1723 } |
|
1724 else |
|
1725 c->minax = c->maxax = c->minay = c->maxay = 0; |
|
1726 c->isfixed = (c->maxw && c->minw && c->maxh && c->minh |
|
1727 && c->maxw == c->minw && c->maxh == c->minh); |
|
1728 } |
|
1729 |
|
1730 static void |
|
1731 updatetitle(Client *c) { |
|
1732 if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) |
|
1733 gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name); |
1629 } |
1734 } |
1630 |
1735 |
1631 /* There's no way to check accesses to destroyed windows, thus those cases are |
1736 /* There's no way to check accesses to destroyed windows, thus those cases are |
1632 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs |
1737 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs |
1633 * default error handler, which may call exit. |
1738 * default error handler, which may call exit. */ |
1634 */ |
|
1635 static int |
1739 static int |
1636 xerror(Display *dpy, XErrorEvent *ee) { |
1740 xerror(Display *dpy, XErrorEvent *ee) { |
1637 if(ee->error_code == BadWindow |
1741 if(ee->error_code == BadWindow |
1638 || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) |
1742 || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) |
1639 || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) |
1743 || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) |
1646 fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", |
1750 fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", |
1647 ee->request_code, ee->error_code); |
1751 ee->request_code, ee->error_code); |
1648 return xerrorxlib(dpy, ee); /* may call exit */ |
1752 return xerrorxlib(dpy, ee); /* may call exit */ |
1649 } |
1753 } |
1650 |
1754 |
1651 static void |
1755 static int |
1652 arrange(void) { |
1756 xerrordummy(Display *dsply, XErrorEvent *ee) { |
1653 Client *c; |
1757 return 0; |
1654 |
1758 } |
1655 for(c = clients; c; c = c->next) |
1759 |
1656 if(isvisible(c)) |
1760 /* Startup Error handler to check if another window manager |
1657 unban(c); |
1761 * is already running. */ |
1658 else |
1762 static int |
1659 ban(c); |
1763 xerrorstart(Display *dsply, XErrorEvent *ee) { |
1660 layouts[ltidx].arrange(); |
1764 otherwm = True; |
1661 focus(NULL); |
1765 return -1; |
1662 restack(); |
1766 } |
1663 } |
1767 |
1664 |
1768 static void |
1665 static void |
1769 view(const char *arg) { |
1666 attach(Client *c) { |
1770 unsigned int i; |
1667 if(clients) |
1771 |
1668 clients->prev = c; |
1772 for(i = 0; i < ntags; i++) |
1669 c->next = clients; |
1773 seltags[i] = arg == NULL; |
1670 clients = c; |
1774 i = idxoftag(arg); |
1671 } |
1775 if(i >= 0 && i < ntags) |
1672 |
1776 seltags[i] = True; |
1673 static void |
|
1674 detach(Client *c) { |
|
1675 if(c->prev) |
|
1676 c->prev->next = c->next; |
|
1677 if(c->next) |
|
1678 c->next->prev = c->prev; |
|
1679 if(c == clients) |
|
1680 clients = c->next; |
|
1681 c->next = c->prev = NULL; |
|
1682 } |
|
1683 |
|
1684 static void |
|
1685 focus(Client *c) { |
|
1686 if((!c && selscreen) || (c && !isvisible(c))) |
|
1687 for(c = stack; c && !isvisible(c); c = c->snext); |
|
1688 if(sel && sel != c) { |
|
1689 grabbuttons(sel, False); |
|
1690 XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]); |
|
1691 } |
|
1692 if(c) { |
|
1693 detachstack(c); |
|
1694 attachstack(c); |
|
1695 grabbuttons(c, True); |
|
1696 } |
|
1697 sel = c; |
|
1698 drawbar(); |
|
1699 if(!selscreen) |
|
1700 return; |
|
1701 if(c) { |
|
1702 XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]); |
|
1703 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); |
|
1704 } |
|
1705 else |
|
1706 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); |
|
1707 } |
|
1708 |
|
1709 static Bool |
|
1710 isarrange(void (*func)()) |
|
1711 { |
|
1712 return func == layouts[ltidx].arrange; |
|
1713 } |
|
1714 |
|
1715 static Client * |
|
1716 nexttiled(Client *c) { |
|
1717 for(; c && (c->isfloating || !isvisible(c)); c = c->next); |
|
1718 return c; |
|
1719 } |
|
1720 |
|
1721 static void |
|
1722 setmwfact(const char *arg) { |
|
1723 double delta; |
|
1724 |
|
1725 if(!isarrange(tile)) |
|
1726 return; |
|
1727 /* arg handling, manipulate mwfact */ |
|
1728 if(arg == NULL) |
|
1729 mwfact = MWFACT; |
|
1730 else if(1 == sscanf(arg, "%lf", &delta)) { |
|
1731 if(arg[0] != '+' && arg[0] != '-') |
|
1732 mwfact = delta; |
|
1733 else |
|
1734 mwfact += delta; |
|
1735 if(mwfact < 0.1) |
|
1736 mwfact = 0.1; |
|
1737 else if(mwfact > 0.9) |
|
1738 mwfact = 0.9; |
|
1739 } |
|
1740 arrange(); |
1777 arrange(); |
1741 } |
|
1742 |
|
1743 static void |
|
1744 tile(void) { |
|
1745 unsigned int i, n, nx, ny, nw, nh, mw, th; |
|
1746 Client *c; |
|
1747 |
|
1748 for(n = 0, c = nexttiled(clients); c; c = nexttiled(c->next)) |
|
1749 n++; |
|
1750 |
|
1751 /* window geoms */ |
|
1752 mw = (n == 1) ? waw : mwfact * waw; |
|
1753 th = (n > 1) ? wah / (n - 1) : 0; |
|
1754 if(n > 1 && th < bh) |
|
1755 th = wah; |
|
1756 |
|
1757 nx = wax; |
|
1758 ny = way; |
|
1759 for(i = 0, c = nexttiled(clients); c; c = nexttiled(c->next), i++) { |
|
1760 c->ismax = False; |
|
1761 if(i == 0) { /* master */ |
|
1762 nw = mw - 2 * c->border; |
|
1763 nh = wah - 2 * c->border; |
|
1764 } |
|
1765 else { /* tile window */ |
|
1766 if(i == 1) { |
|
1767 ny = way; |
|
1768 nx += mw; |
|
1769 } |
|
1770 nw = waw - mw - 2 * c->border; |
|
1771 if(i + 1 == n) /* remainder */ |
|
1772 nh = (way + wah) - ny - 2 * c->border; |
|
1773 else |
|
1774 nh = th - 2 * c->border; |
|
1775 } |
|
1776 resize(c, nx, ny, nw, nh, RESIZEHINTS); |
|
1777 if(n > 1 && th != wah) |
|
1778 ny += nh + 2 * c->border; |
|
1779 } |
|
1780 } |
1778 } |
1781 |
1779 |
1782 static void |
1780 static void |
1783 zoom(const char *arg) { |
1781 zoom(const char *arg) { |
1784 Client *c; |
1782 Client *c; |