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