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