|
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 |
|
|
44 #include "drw.h" |
|
|
45 #include "util.h" |
|
|
46 |
|
|
47 /* macros */ |
|
|
48 #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) |
|
|
49 #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) |
|
|
50 #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ |
|
|
51 * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) |
|
|
52 #define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) |
|
|
53 #define MOUSEMASK (BUTTONMASK|PointerMotionMask) |
|
|
54 #define WIDTH(X) ((X)->w + 2 * (X)->bw) |
|
|
55 #define HEIGHT(X) ((X)->h + 2 * (X)->bw) |
|
|
56 #define TAGMASK ((1 << LENGTH(tags)) - 1) |
|
|
57 #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) |
|
|
58 |
|
|
59 /* enums */ |
|
|
60 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ |
|
|
61 enum { SchemeNorm, SchemeSel }; /* color schemes */ |
|
|
62 enum { NetSupported, NetWMName, NetWMState, NetWMCheck, |
|
|
63 NetWMFullscreen, NetActiveWindow, NetWMWindowType, |
|
|
64 NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ |
|
|
65 enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ |
|
|
66 enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, |
|
|
67 ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ |
|
|
68 |
|
|
69 typedef union { |
|
|
70 int i; |
|
|
71 unsigned int ui; |
|
|
72 float f; |
|
|
73 const void *v; |
|
|
74 } Arg; |
|
|
75 |
|
|
76 typedef struct { |
|
|
77 unsigned int click; |
|
|
78 unsigned int mask; |
|
|
79 unsigned int button; |
|
|
80 void (*func)(const Arg *arg); |
|
|
81 const Arg arg; |
|
|
82 } Button; |
|
|
83 |
|
|
84 typedef struct Monitor Monitor; |
|
|
85 typedef struct Client Client; |
|
|
86 struct Client { |
|
|
87 char name[256]; |
|
|
88 float mina, maxa; |
|
|
89 int x, y, w, h; |
|
|
90 int oldx, oldy, oldw, oldh; |
|
|
91 int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; |
|
|
92 int bw, oldbw; |
|
|
93 unsigned int tags; |
|
|
94 int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; |
|
|
95 Client *next; |
|
|
96 Client *snext; |
|
|
97 Monitor *mon; |
|
|
98 Window win; |
|
|
99 }; |
|
|
100 |
|
|
101 typedef struct { |
|
|
102 unsigned int mod; |
|
|
103 KeySym keysym; |
|
|
104 void (*func)(const Arg *); |
|
|
105 const Arg arg; |
|
|
106 } Key; |
|
|
107 |
|
|
108 typedef struct { |
|
|
109 const char *symbol; |
|
|
110 void (*arrange)(Monitor *); |
|
|
111 } Layout; |
|
|
112 |
|
|
113 struct Monitor { |
|
|
114 char ltsymbol[16]; |
|
|
115 float mfact; |
|
|
116 int nmaster; |
|
|
117 int num; |
|
|
118 int by; /* bar geometry */ |
|
|
119 int mx, my, mw, mh; /* screen size */ |
|
|
120 int wx, wy, ww, wh; /* window area */ |
|
|
121 unsigned int seltags; |
|
|
122 unsigned int sellt; |
|
|
123 unsigned int tagset[2]; |
|
|
124 int showbar; |
|
|
125 int topbar; |
|
|
126 Client *clients; |
|
|
127 Client *sel; |
|
|
128 Client *stack; |
|
|
129 Monitor *next; |
|
|
130 Window barwin; |
|
|
131 const Layout *lt[2]; |
|
|
132 }; |
|
|
133 |
|
|
134 typedef struct { |
|
|
135 const char *class; |
|
|
136 const char *instance; |
|
|
137 const char *title; |
|
|
138 unsigned int tags; |
|
|
139 int isfloating; |
|
|
140 int monitor; |
|
|
141 } Rule; |
|
|
142 |
|
|
143 /* function declarations */ |
|
|
144 static void applyrules(Client *c); |
|
|
145 static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); |
|
|
146 static void arrange(Monitor *m); |
|
|
147 static void arrangemon(Monitor *m); |
|
|
148 static void attach(Client *c); |
|
|
149 static void attachstack(Client *c); |
|
|
150 static void buttonpress(XEvent *e); |
|
|
151 static void checkotherwm(void); |
|
|
152 static void cleanup(void); |
|
|
153 static void cleanupmon(Monitor *mon); |
|
|
154 static void clientmessage(XEvent *e); |
|
|
155 static void configure(Client *c); |
|
|
156 static void configurenotify(XEvent *e); |
|
|
157 static void configurerequest(XEvent *e); |
|
|
158 static Monitor *createmon(void); |
|
|
159 static void destroynotify(XEvent *e); |
|
|
160 static void detach(Client *c); |
|
|
161 static void detachstack(Client *c); |
|
|
162 static Monitor *dirtomon(int dir); |
|
|
163 static void drawbar(Monitor *m); |
|
|
164 static void drawbars(void); |
|
|
165 static void enternotify(XEvent *e); |
|
|
166 static void expose(XEvent *e); |
|
|
167 static void focus(Client *c); |
|
|
168 static void focusin(XEvent *e); |
|
|
169 static void focusmon(const Arg *arg); |
|
|
170 static void focusstack(const Arg *arg); |
|
|
171 static Atom getatomprop(Client *c, Atom prop); |
|
|
172 static int getrootptr(int *x, int *y); |
|
|
173 static long getstate(Window w); |
|
|
174 static int gettextprop(Window w, Atom atom, char *text, unsigned int size); |
|
|
175 static void grabbuttons(Client *c, int focused); |
|
|
176 static void grabkeys(void); |
|
|
177 static void incnmaster(const Arg *arg); |
|
|
178 static void keypress(XEvent *e); |
|
|
179 static void killclient(const Arg *arg); |
|
|
180 static void manage(Window w, XWindowAttributes *wa); |
|
|
181 static void mappingnotify(XEvent *e); |
|
|
182 static void maprequest(XEvent *e); |
|
|
183 static void monocle(Monitor *m); |
|
|
184 static void motionnotify(XEvent *e); |
|
|
185 static void movemouse(const Arg *arg); |
|
|
186 static Client *nexttiled(Client *c); |
|
|
187 static void pop(Client *c); |
|
|
188 static void propertynotify(XEvent *e); |
|
|
189 static void quit(const Arg *arg); |
|
|
190 static Monitor *recttomon(int x, int y, int w, int h); |
|
|
191 static void resize(Client *c, int x, int y, int w, int h, int interact); |
|
|
192 static void resizeclient(Client *c, int x, int y, int w, int h); |
|
|
193 static void resizemouse(const Arg *arg); |
|
|
194 static void restack(Monitor *m); |
|
|
195 static void run(void); |
|
|
196 static void scan(void); |
|
|
197 static int sendevent(Client *c, Atom proto); |
|
|
198 static void sendmon(Client *c, Monitor *m); |
|
|
199 static void setclientstate(Client *c, long state); |
|
|
200 static void setfocus(Client *c); |
|
|
201 static void setfullscreen(Client *c, int fullscreen); |
|
|
202 static void setlayout(const Arg *arg); |
|
|
203 static void setmfact(const Arg *arg); |
|
|
204 static void setup(void); |
|
|
205 static void seturgent(Client *c, int urg); |
|
|
206 static void showhide(Client *c); |
|
|
207 static void spawn(const Arg *arg); |
|
|
208 static void tag(const Arg *arg); |
|
|
209 static void tagmon(const Arg *arg); |
|
|
210 static void tile(Monitor *m); |
|
|
211 static void togglebar(const Arg *arg); |
|
|
212 static void togglefloating(const Arg *arg); |
|
|
213 static void toggletag(const Arg *arg); |
|
|
214 static void toggleview(const Arg *arg); |
|
|
215 static void unfocus(Client *c, int setfocus); |
|
|
216 static void unmanage(Client *c, int destroyed); |
|
|
217 static void unmapnotify(XEvent *e); |
|
|
218 static void updatebarpos(Monitor *m); |
|
|
219 static void updatebars(void); |
|
|
220 static void updateclientlist(void); |
|
|
221 static int updategeom(void); |
|
|
222 static void updatenumlockmask(void); |
|
|
223 static void updatesizehints(Client *c); |
|
|
224 static void updatestatus(void); |
|
|
225 static void updatetitle(Client *c); |
|
|
226 static void updatewindowtype(Client *c); |
|
|
227 static void updatewmhints(Client *c); |
|
|
228 static void view(const Arg *arg); |
|
|
229 static Client *wintoclient(Window w); |
|
|
230 static Monitor *wintomon(Window w); |
|
|
231 static int xerror(Display *dpy, XErrorEvent *ee); |
|
|
232 static int xerrordummy(Display *dpy, XErrorEvent *ee); |
|
|
233 static int xerrorstart(Display *dpy, XErrorEvent *ee); |
|
|
234 static void zoom(const Arg *arg); |
|
|
235 |
|
|
236 /* variables */ |
|
|
237 static const char broken[] = "broken"; |
|
|
238 static char stext[256]; |
|
|
239 static int screen; |
|
|
240 static int sw, sh; /* X display screen geometry width, height */ |
|
|
241 static int bh; /* bar height */ |
|
|
242 static int lrpad; /* sum of left and right padding for text */ |
|
|
243 static int (*xerrorxlib)(Display *, XErrorEvent *); |
|
|
244 static unsigned int numlockmask = 0; |
|
|
245 static void (*handler[LASTEvent]) (XEvent *) = { |
|
|
246 [ButtonPress] = buttonpress, |
|
|
247 [ClientMessage] = clientmessage, |
|
|
248 [ConfigureRequest] = configurerequest, |
|
|
249 [ConfigureNotify] = configurenotify, |
|
|
250 [DestroyNotify] = destroynotify, |
|
|
251 [EnterNotify] = enternotify, |
|
|
252 [Expose] = expose, |
|
|
253 [FocusIn] = focusin, |
|
|
254 [KeyPress] = keypress, |
|
|
255 [MappingNotify] = mappingnotify, |
|
|
256 [MapRequest] = maprequest, |
|
|
257 [MotionNotify] = motionnotify, |
|
|
258 [PropertyNotify] = propertynotify, |
|
|
259 [UnmapNotify] = unmapnotify |
|
|
260 }; |
|
|
261 static Atom wmatom[WMLast], netatom[NetLast]; |
|
|
262 static int running = 1; |
|
|
263 static Cur *cursor[CurLast]; |
|
|
264 static Clr **scheme; |
|
|
265 static Display *dpy; |
|
|
266 static Drw *drw; |
|
|
267 static Monitor *mons, *selmon; |
|
|
268 static Window root, wmcheckwin; |
|
|
269 |
|
|
270 /* configuration, allows nested code to access above variables */ |
|
|
271 #include "config.h" |
|
|
272 |
|
|
273 /* compile-time check if all tags fit into an unsigned int bit array. */ |
|
|
274 struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; |
|
|
275 |
|
|
276 /* function implementations */ |
|
|
277 void |
|
|
278 applyrules(Client *c) |
|
|
279 { |
|
|
280 const char *class, *instance; |
|
|
281 unsigned int i; |
|
|
282 const Rule *r; |
|
|
283 Monitor *m; |
|
|
284 XClassHint ch = { NULL, NULL }; |
|
|
285 |
|
|
286 /* rule matching */ |
|
|
287 c->isfloating = 0; |
|
|
288 c->tags = 0; |
|
|
289 XGetClassHint(dpy, c->win, &ch); |
|
|
290 class = ch.res_class ? ch.res_class : broken; |
|
|
291 instance = ch.res_name ? ch.res_name : broken; |
|
|
292 |
|
|
293 for (i = 0; i < LENGTH(rules); i++) { |
|
|
294 r = &rules[i]; |
|
|
295 if ((!r->title || strstr(c->name, r->title)) |
|
|
296 && (!r->class || strstr(class, r->class)) |
|
|
297 && (!r->instance || strstr(instance, r->instance))) |
|
|
298 { |
|
|
299 c->isfloating = r->isfloating; |
|
|
300 c->tags |= r->tags; |
|
|
301 for (m = mons; m && m->num != r->monitor; m = m->next); |
|
|
302 if (m) |
|
|
303 c->mon = m; |
|
|
304 } |
|
|
305 } |
|
|
306 if (ch.res_class) |
|
|
307 XFree(ch.res_class); |
|
|
308 if (ch.res_name) |
|
|
309 XFree(ch.res_name); |
|
|
310 c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; |
|
|
311 } |
|
|
312 |
|
|
313 int |
|
|
314 applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) |
|
|
315 { |
|
|
316 int baseismin; |
|
|
317 Monitor *m = c->mon; |
|
|
318 |
|
|
319 /* set minimum possible */ |
|
|
320 *w = MAX(1, *w); |
|
|
321 *h = MAX(1, *h); |
|
|
322 if (interact) { |
|
|
323 if (*x > sw) |
|
|
324 *x = sw - WIDTH(c); |
|
|
325 if (*y > sh) |
|
|
326 *y = sh - HEIGHT(c); |
|
|
327 if (*x + *w + 2 * c->bw < 0) |
|
|
328 *x = 0; |
|
|
329 if (*y + *h + 2 * c->bw < 0) |
|
|
330 *y = 0; |
|
|
331 } else { |
|
|
332 if (*x >= m->wx + m->ww) |
|
|
333 *x = m->wx + m->ww - WIDTH(c); |
|
|
334 if (*y >= m->wy + m->wh) |
|
|
335 *y = m->wy + m->wh - HEIGHT(c); |
|
|
336 if (*x + *w + 2 * c->bw <= m->wx) |
|
|
337 *x = m->wx; |
|
|
338 if (*y + *h + 2 * c->bw <= m->wy) |
|
|
339 *y = m->wy; |
|
|
340 } |
|
|
341 if (*h < bh) |
|
|
342 *h = bh; |
|
|
343 if (*w < bh) |
|
|
344 *w = bh; |
|
|
345 if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { |
|
|
346 if (!c->hintsvalid) |
|
|
347 updatesizehints(c); |
|
|
348 /* see last two sentences in ICCCM 4.1.2.3 */ |
|
|
349 baseismin = c->basew == c->minw && c->baseh == c->minh; |
|
|
350 if (!baseismin) { /* temporarily remove base dimensions */ |
|
|
351 *w -= c->basew; |
|
|
352 *h -= c->baseh; |
|
|
353 } |
|
|
354 /* adjust for aspect limits */ |
|
|
355 if (c->mina > 0 && c->maxa > 0) { |
|
|
356 if (c->maxa < (float)*w / *h) |
|
|
357 *w = *h * c->maxa + 0.5; |
|
|
358 else if (c->mina < (float)*h / *w) |
|
|
359 *h = *w * c->mina + 0.5; |
|
|
360 } |
|
|
361 if (baseismin) { /* increment calculation requires this */ |
|
|
362 *w -= c->basew; |
|
|
363 *h -= c->baseh; |
|
|
364 } |
|
|
365 /* adjust for increment value */ |
|
|
366 if (c->incw) |
|
|
367 *w -= *w % c->incw; |
|
|
368 if (c->inch) |
|
|
369 *h -= *h % c->inch; |
|
|
370 /* restore base dimensions */ |
|
|
371 *w = MAX(*w + c->basew, c->minw); |
|
|
372 *h = MAX(*h + c->baseh, c->minh); |
|
|
373 if (c->maxw) |
|
|
374 *w = MIN(*w, c->maxw); |
|
|
375 if (c->maxh) |
|
|
376 *h = MIN(*h, c->maxh); |
|
|
377 } |
|
|
378 return *x != c->x || *y != c->y || *w != c->w || *h != c->h; |
|
|
379 } |
|
|
380 |
|
|
381 void |
|
|
382 arrange(Monitor *m) |
|
|
383 { |
|
|
384 if (m) |
|
|
385 showhide(m->stack); |
|
|
386 else for (m = mons; m; m = m->next) |
|
|
387 showhide(m->stack); |
|
|
388 if (m) { |
|
|
389 arrangemon(m); |
|
|
390 restack(m); |
|
|
391 } else for (m = mons; m; m = m->next) |
|
|
392 arrangemon(m); |
|
|
393 } |
|
|
394 |
|
|
395 void |
|
|
396 arrangemon(Monitor *m) |
|
|
397 { |
|
|
398 strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); |
|
|
399 if (m->lt[m->sellt]->arrange) |
|
|
400 m->lt[m->sellt]->arrange(m); |
|
|
401 } |
|
|
402 |
|
|
403 void |
|
|
404 attach(Client *c) |
|
|
405 { |
|
|
406 c->next = c->mon->clients; |
|
|
407 c->mon->clients = c; |
|
|
408 } |
|
|
409 |
|
|
410 void |
|
|
411 attachstack(Client *c) |
|
|
412 { |
|
|
413 c->snext = c->mon->stack; |
|
|
414 c->mon->stack = c; |
|
|
415 } |
|
|
416 |
|
|
417 void |
|
|
418 buttonpress(XEvent *e) |
|
|
419 { |
|
|
420 unsigned int i, x, click; |
|
|
421 Arg arg = {0}; |
|
|
422 Client *c; |
|
|
423 Monitor *m; |
|
|
424 XButtonPressedEvent *ev = &e->xbutton; |
|
|
425 |
|
|
426 click = ClkRootWin; |
|
|
427 /* focus monitor if necessary */ |
|
|
428 if ((m = wintomon(ev->window)) && m != selmon) { |
|
|
429 unfocus(selmon->sel, 1); |
|
|
430 selmon = m; |
|
|
431 focus(NULL); |
|
|
432 } |
|
|
433 if (ev->window == selmon->barwin) { |
|
|
434 i = x = 0; |
|
|
435 do |
|
|
436 x += TEXTW(tags[i]); |
|
|
437 while (ev->x >= x && ++i < LENGTH(tags)); |
|
|
438 if (i < LENGTH(tags)) { |
|
|
439 click = ClkTagBar; |
|
|
440 arg.ui = 1 << i; |
|
|
441 } else if (ev->x < x + TEXTW(selmon->ltsymbol)) |
|
|
442 click = ClkLtSymbol; |
|
|
443 else if (ev->x > selmon->ww - (int)TEXTW(stext) + lrpad - 2) |
|
|
444 click = ClkStatusText; |
|
|
445 else |
|
|
446 click = ClkWinTitle; |
|
|
447 } else if ((c = wintoclient(ev->window))) { |
|
|
448 focus(c); |
|
|
449 restack(selmon); |
|
|
450 XAllowEvents(dpy, ReplayPointer, CurrentTime); |
|
|
451 click = ClkClientWin; |
|
|
452 } |
|
|
453 for (i = 0; i < LENGTH(buttons); i++) |
|
|
454 if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button |
|
|
455 && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) |
|
|
456 buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); |
|
|
457 } |
|
|
458 |
|
|
459 void |
|
|
460 checkotherwm(void) |
|
|
461 { |
|
|
462 xerrorxlib = XSetErrorHandler(xerrorstart); |
|
|
463 /* this causes an error if some other window manager is running */ |
|
|
464 XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); |
|
|
465 XSync(dpy, False); |
|
|
466 XSetErrorHandler(xerror); |
|
|
467 XSync(dpy, False); |
|
|
468 } |
|
|
469 |
|
|
470 void |
|
|
471 cleanup(void) |
|
|
472 { |
|
|
473 Arg a = {.ui = ~0}; |
|
|
474 Layout foo = { "", NULL }; |
|
|
475 Monitor *m; |
|
|
476 size_t i; |
|
|
477 |
|
|
478 view(&a); |
|
|
479 selmon->lt[selmon->sellt] = &foo; |
|
|
480 for (m = mons; m; m = m->next) |
|
|
481 while (m->stack) |
|
|
482 unmanage(m->stack, 0); |
|
|
483 XUngrabKey(dpy, AnyKey, AnyModifier, root); |
|
|
484 while (mons) |
|
|
485 cleanupmon(mons); |
|
|
486 for (i = 0; i < CurLast; i++) |
|
|
487 drw_cur_free(drw, cursor[i]); |
|
|
488 for (i = 0; i < LENGTH(colors); i++) |
|
|
489 drw_scm_free(drw, scheme[i], 3); |
|
|
490 free(scheme); |
|
|
491 XDestroyWindow(dpy, wmcheckwin); |
|
|
492 drw_free(drw); |
|
|
493 XSync(dpy, False); |
|
|
494 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); |
|
|
495 XDeleteProperty(dpy, root, netatom[NetActiveWindow]); |
|
|
496 } |
|
|
497 |
|
|
498 void |
|
|
499 cleanupmon(Monitor *mon) |
|
|
500 { |
|
|
501 Monitor *m; |
|
|
502 |
|
|
503 if (mon == mons) |
|
|
504 mons = mons->next; |
|
|
505 else { |
|
|
506 for (m = mons; m && m->next != mon; m = m->next); |
|
|
507 m->next = mon->next; |
|
|
508 } |
|
|
509 XUnmapWindow(dpy, mon->barwin); |
|
|
510 XDestroyWindow(dpy, mon->barwin); |
|
|
511 free(mon); |
|
|
512 } |
|
|
513 |
|
|
514 void |
|
|
515 clientmessage(XEvent *e) |
|
|
516 { |
|
|
517 XClientMessageEvent *cme = &e->xclient; |
|
|
518 Client *c = wintoclient(cme->window); |
|
|
519 |
|
|
520 if (!c) |
|
|
521 return; |
|
|
522 if (cme->message_type == netatom[NetWMState]) { |
|
|
523 if (cme->data.l[1] == netatom[NetWMFullscreen] |
|
|
524 || cme->data.l[2] == netatom[NetWMFullscreen]) |
|
|
525 setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ |
|
|
526 || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); |
|
|
527 } else if (cme->message_type == netatom[NetActiveWindow]) { |
|
|
528 if (c != selmon->sel && !c->isurgent) |
|
|
529 seturgent(c, 1); |
|
|
530 } |
|
|
531 } |
|
|
532 |
|
|
533 void |
|
|
534 configure(Client *c) |
|
|
535 { |
|
|
536 XConfigureEvent ce; |
|
|
537 |
|
|
538 ce.type = ConfigureNotify; |
|
|
539 ce.display = dpy; |
|
|
540 ce.event = c->win; |
|
|
541 ce.window = c->win; |
|
|
542 ce.x = c->x; |
|
|
543 ce.y = c->y; |
|
|
544 ce.width = c->w; |
|
|
545 ce.height = c->h; |
|
|
546 ce.border_width = c->bw; |
|
|
547 ce.above = None; |
|
|
548 ce.override_redirect = False; |
|
|
549 XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); |
|
|
550 } |
|
|
551 |
|
|
552 void |
|
|
553 configurenotify(XEvent *e) |
|
|
554 { |
|
|
555 Monitor *m; |
|
|
556 Client *c; |
|
|
557 XConfigureEvent *ev = &e->xconfigure; |
|
|
558 int dirty; |
|
|
559 |
|
|
560 /* TODO: updategeom handling sucks, needs to be simplified */ |
|
|
561 if (ev->window == root) { |
|
|
562 dirty = (sw != ev->width || sh != ev->height); |
|
|
563 sw = ev->width; |
|
|
564 sh = ev->height; |
|
|
565 if (updategeom() || dirty) { |
|
|
566 drw_resize(drw, sw, bh); |
|
|
567 updatebars(); |
|
|
568 for (m = mons; m; m = m->next) { |
|
|
569 for (c = m->clients; c; c = c->next) |
|
|
570 if (c->isfullscreen) |
|
|
571 resizeclient(c, m->mx, m->my, m->mw, m->mh); |
|
|
572 XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); |
|
|
573 } |
|
|
574 focus(NULL); |
|
|
575 arrange(NULL); |
|
|
576 } |
|
|
577 } |
|
|
578 } |
|
|
579 |
|
|
580 void |
|
|
581 configurerequest(XEvent *e) |
|
|
582 { |
|
|
583 Client *c; |
|
|
584 Monitor *m; |
|
|
585 XConfigureRequestEvent *ev = &e->xconfigurerequest; |
|
|
586 XWindowChanges wc; |
|
|
587 |
|
|
588 if ((c = wintoclient(ev->window))) { |
|
|
589 if (ev->value_mask & CWBorderWidth) |
|
|
590 c->bw = ev->border_width; |
|
|
591 else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { |
|
|
592 m = c->mon; |
|
|
593 if (ev->value_mask & CWX) { |
|
|
594 c->oldx = c->x; |
|
|
595 c->x = m->mx + ev->x; |
|
|
596 } |
|
|
597 if (ev->value_mask & CWY) { |
|
|
598 c->oldy = c->y; |
|
|
599 c->y = m->my + ev->y; |
|
|
600 } |
|
|
601 if (ev->value_mask & CWWidth) { |
|
|
602 c->oldw = c->w; |
|
|
603 c->w = ev->width; |
|
|
604 } |
|
|
605 if (ev->value_mask & CWHeight) { |
|
|
606 c->oldh = c->h; |
|
|
607 c->h = ev->height; |
|
|
608 } |
|
|
609 if ((c->x + c->w) > m->mx + m->mw && c->isfloating) |
|
|
610 c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ |
|
|
611 if ((c->y + c->h) > m->my + m->mh && c->isfloating) |
|
|
612 c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ |
|
|
613 if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) |
|
|
614 configure(c); |
|
|
615 if (ISVISIBLE(c)) |
|
|
616 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); |
|
|
617 } else |
|
|
618 configure(c); |
|
|
619 } else { |
|
|
620 wc.x = ev->x; |
|
|
621 wc.y = ev->y; |
|
|
622 wc.width = ev->width; |
|
|
623 wc.height = ev->height; |
|
|
624 wc.border_width = ev->border_width; |
|
|
625 wc.sibling = ev->above; |
|
|
626 wc.stack_mode = ev->detail; |
|
|
627 XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); |
|
|
628 } |
|
|
629 XSync(dpy, False); |
|
|
630 } |
|
|
631 |
|
|
632 Monitor * |
|
|
633 createmon(void) |
|
|
634 { |
|
|
635 Monitor *m; |
|
|
636 |
|
|
637 m = ecalloc(1, sizeof(Monitor)); |
|
|
638 m->tagset[0] = m->tagset[1] = 1; |
|
|
639 m->mfact = mfact; |
|
|
640 m->nmaster = nmaster; |
|
|
641 m->showbar = showbar; |
|
|
642 m->topbar = topbar; |
|
|
643 m->lt[0] = &layouts[0]; |
|
|
644 m->lt[1] = &layouts[1 % LENGTH(layouts)]; |
|
|
645 strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); |
|
|
646 return m; |
|
|
647 } |
|
|
648 |
|
|
649 void |
|
|
650 destroynotify(XEvent *e) |
|
|
651 { |
|
|
652 Client *c; |
|
|
653 XDestroyWindowEvent *ev = &e->xdestroywindow; |
|
|
654 |
|
|
655 if ((c = wintoclient(ev->window))) |
|
|
656 unmanage(c, 1); |
|
|
657 } |
|
|
658 |
|
|
659 void |
|
|
660 detach(Client *c) |
|
|
661 { |
|
|
662 Client **tc; |
|
|
663 |
|
|
664 for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); |
|
|
665 *tc = c->next; |
|
|
666 } |
|
|
667 |
|
|
668 void |
|
|
669 detachstack(Client *c) |
|
|
670 { |
|
|
671 Client **tc, *t; |
|
|
672 |
|
|
673 for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); |
|
|
674 *tc = c->snext; |
|
|
675 |
|
|
676 if (c == c->mon->sel) { |
|
|
677 for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); |
|
|
678 c->mon->sel = t; |
|
|
679 } |
|
|
680 } |
|
|
681 |
|
|
682 Monitor * |
|
|
683 dirtomon(int dir) |
|
|
684 { |
|
|
685 Monitor *m = NULL; |
|
|
686 |
|
|
687 if (dir > 0) { |
|
|
688 if (!(m = selmon->next)) |
|
|
689 m = mons; |
|
|
690 } else if (selmon == mons) |
|
|
691 for (m = mons; m->next; m = m->next); |
|
|
692 else |
|
|
693 for (m = mons; m->next != selmon; m = m->next); |
|
|
694 return m; |
|
|
695 } |
|
|
696 |
|
|
697 void |
|
|
698 drawbar(Monitor *m) |
|
|
699 { |
|
|
700 int x, w, tw = 0; |
|
|
701 int boxs = drw->fonts->h / 9; |
|
|
702 int boxw = drw->fonts->h / 6 + 2; |
|
|
703 unsigned int i, occ = 0, urg = 0; |
|
|
704 Client *c; |
|
|
705 |
|
|
706 if (!m->showbar) |
|
|
707 return; |
|
|
708 |
|
|
709 /* draw status first so it can be overdrawn by tags later */ |
|
|
710 if (m == selmon) { /* status is only drawn on selected monitor */ |
|
|
711 drw_setscheme(drw, scheme[SchemeNorm]); |
|
|
712 tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ |
|
|
713 drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); |
|
|
714 } |
|
|
715 |
|
|
716 for (c = m->clients; c; c = c->next) { |
|
|
717 occ |= c->tags; |
|
|
718 if (c->isurgent) |
|
|
719 urg |= c->tags; |
|
|
720 } |
|
|
721 x = 0; |
|
|
722 for (i = 0; i < LENGTH(tags); i++) { |
|
|
723 w = TEXTW(tags[i]); |
|
|
724 drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); |
|
|
725 drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); |
|
|
726 if (occ & 1 << i) |
|
|
727 drw_rect(drw, x + boxs, boxs, boxw, boxw, |
|
|
728 m == selmon && selmon->sel && selmon->sel->tags & 1 << i, |
|
|
729 urg & 1 << i); |
|
|
730 x += w; |
|
|
731 } |
|
|
732 w = TEXTW(m->ltsymbol); |
|
|
733 drw_setscheme(drw, scheme[SchemeNorm]); |
|
|
734 x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); |
|
|
735 |
|
|
736 if ((w = m->ww - tw - x) > bh) { |
|
|
737 if (m->sel) { |
|
|
738 drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); |
|
|
739 drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); |
|
|
740 if (m->sel->isfloating) |
|
|
741 drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); |
|
|
742 } else { |
|
|
743 drw_setscheme(drw, scheme[SchemeNorm]); |
|
|
744 drw_rect(drw, x, 0, w, bh, 1, 1); |
|
|
745 } |
|
|
746 } |
|
|
747 drw_map(drw, m->barwin, 0, 0, m->ww, bh); |
|
|
748 } |
|
|
749 |
|
|
750 void |
|
|
751 drawbars(void) |
|
|
752 { |
|
|
753 Monitor *m; |
|
|
754 |
|
|
755 for (m = mons; m; m = m->next) |
|
|
756 drawbar(m); |
|
|
757 } |
|
|
758 |
|
|
759 void |
|
|
760 enternotify(XEvent *e) |
|
|
761 { |
|
|
762 Client *c; |
|
|
763 Monitor *m; |
|
|
764 XCrossingEvent *ev = &e->xcrossing; |
|
|
765 |
|
|
766 if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) |
|
|
767 return; |
|
|
768 c = wintoclient(ev->window); |
|
|
769 m = c ? c->mon : wintomon(ev->window); |
|
|
770 if (m != selmon) { |
|
|
771 unfocus(selmon->sel, 1); |
|
|
772 selmon = m; |
|
|
773 } else if (!c || c == selmon->sel) |
|
|
774 return; |
|
|
775 focus(c); |
|
|
776 } |
|
|
777 |
|
|
778 void |
|
|
779 expose(XEvent *e) |
|
|
780 { |
|
|
781 Monitor *m; |
|
|
782 XExposeEvent *ev = &e->xexpose; |
|
|
783 |
|
|
784 if (ev->count == 0 && (m = wintomon(ev->window))) |
|
|
785 drawbar(m); |
|
|
786 } |
|
|
787 |
|
|
788 void |
|
|
789 focus(Client *c) |
|
|
790 { |
|
|
791 if (!c || !ISVISIBLE(c)) |
|
|
792 for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); |
|
|
793 if (selmon->sel && selmon->sel != c) |
|
|
794 unfocus(selmon->sel, 0); |
|
|
795 if (c) { |
|
|
796 if (c->mon != selmon) |
|
|
797 selmon = c->mon; |
|
|
798 if (c->isurgent) |
|
|
799 seturgent(c, 0); |
|
|
800 detachstack(c); |
|
|
801 attachstack(c); |
|
|
802 grabbuttons(c, 1); |
|
|
803 XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); |
|
|
804 setfocus(c); |
|
|
805 } else { |
|
|
806 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); |
|
|
807 XDeleteProperty(dpy, root, netatom[NetActiveWindow]); |
|
|
808 } |
|
|
809 selmon->sel = c; |
|
|
810 drawbars(); |
|
|
811 } |
|
|
812 |
|
|
813 /* there are some broken focus acquiring clients needing extra handling */ |
|
|
814 void |
|
|
815 focusin(XEvent *e) |
|
|
816 { |
|
|
817 XFocusChangeEvent *ev = &e->xfocus; |
|
|
818 |
|
|
819 if (selmon->sel && ev->window != selmon->sel->win) |
|
|
820 setfocus(selmon->sel); |
|
|
821 } |
|
|
822 |
|
|
823 void |
|
|
824 focusmon(const Arg *arg) |
|
|
825 { |
|
|
826 Monitor *m; |
|
|
827 |
|
|
828 if (!mons->next) |
|
|
829 return; |
|
|
830 if ((m = dirtomon(arg->i)) == selmon) |
|
|
831 return; |
|
|
832 unfocus(selmon->sel, 0); |
|
|
833 selmon = m; |
|
|
834 focus(NULL); |
|
|
835 } |
|
|
836 |
|
|
837 void |
|
|
838 focusstack(const Arg *arg) |
|
|
839 { |
|
|
840 Client *c = NULL, *i; |
|
|
841 |
|
|
842 if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) |
|
|
843 return; |
|
|
844 if (arg->i > 0) { |
|
|
845 for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); |
|
|
846 if (!c) |
|
|
847 for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); |
|
|
848 } else { |
|
|
849 for (i = selmon->clients; i != selmon->sel; i = i->next) |
|
|
850 if (ISVISIBLE(i)) |
|
|
851 c = i; |
|
|
852 if (!c) |
|
|
853 for (; i; i = i->next) |
|
|
854 if (ISVISIBLE(i)) |
|
|
855 c = i; |
|
|
856 } |
|
|
857 if (c) { |
|
|
858 focus(c); |
|
|
859 restack(selmon); |
|
|
860 } |
|
|
861 } |
|
|
862 |
|
|
863 Atom |
|
|
864 getatomprop(Client *c, Atom prop) |
|
|
865 { |
|
|
866 int format; |
|
|
867 unsigned long nitems, dl; |
|
|
868 unsigned char *p = NULL; |
|
|
869 Atom da, atom = None; |
|
|
870 |
|
|
871 if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, |
|
|
872 &da, &format, &nitems, &dl, &p) == Success && p) { |
|
|
873 if (nitems > 0 && format == 32) |
|
|
874 atom = *(long *)p; |
|
|
875 XFree(p); |
|
|
876 } |
|
|
877 return atom; |
|
|
878 } |
|
|
879 |
|
|
880 int |
|
|
881 getrootptr(int *x, int *y) |
|
|
882 { |
|
|
883 int di; |
|
|
884 unsigned int dui; |
|
|
885 Window dummy; |
|
|
886 |
|
|
887 return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); |
|
|
888 } |
|
|
889 |
|
|
890 long |
|
|
891 getstate(Window w) |
|
|
892 { |
|
|
893 int format; |
|
|
894 long result = -1; |
|
|
895 unsigned char *p = NULL; |
|
|
896 unsigned long n, extra; |
|
|
897 Atom real; |
|
|
898 |
|
|
899 if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], |
|
|
900 &real, &format, &n, &extra, &p) != Success) |
|
|
901 return -1; |
|
|
902 if (n != 0 && format == 32) |
|
|
903 result = *(long *)p; |
|
|
904 XFree(p); |
|
|
905 return result; |
|
|
906 } |
|
|
907 |
|
|
908 int |
|
|
909 gettextprop(Window w, Atom atom, char *text, unsigned int size) |
|
|
910 { |
|
|
911 char **list = NULL; |
|
|
912 int n; |
|
|
913 XTextProperty name; |
|
|
914 |
|
|
915 if (!text || size == 0) |
|
|
916 return 0; |
|
|
917 text[0] = '\0'; |
|
|
918 if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) |
|
|
919 return 0; |
|
|
920 if (name.encoding == XA_STRING) { |
|
|
921 strncpy(text, (char *)name.value, size - 1); |
|
|
922 } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { |
|
|
923 strncpy(text, *list, size - 1); |
|
|
924 XFreeStringList(list); |
|
|
925 } |
|
|
926 text[size - 1] = '\0'; |
|
|
927 XFree(name.value); |
|
|
928 return 1; |
|
|
929 } |
|
|
930 |
|
|
931 void |
|
|
932 grabbuttons(Client *c, int focused) |
|
|
933 { |
|
|
934 updatenumlockmask(); |
|
|
935 { |
|
|
936 unsigned int i, j; |
|
|
937 unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; |
|
|
938 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); |
|
|
939 if (!focused) |
|
|
940 XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, |
|
|
941 BUTTONMASK, GrabModeSync, GrabModeSync, None, None); |
|
|
942 for (i = 0; i < LENGTH(buttons); i++) |
|
|
943 if (buttons[i].click == ClkClientWin) |
|
|
944 for (j = 0; j < LENGTH(modifiers); j++) |
|
|
945 XGrabButton(dpy, buttons[i].button, |
|
|
946 buttons[i].mask | modifiers[j], |
|
|
947 c->win, False, BUTTONMASK, |
|
|
948 GrabModeAsync, GrabModeSync, None, None); |
|
|
949 } |
|
|
950 } |
|
|
951 |
|
|
952 void |
|
|
953 grabkeys(void) |
|
|
954 { |
|
|
955 updatenumlockmask(); |
|
|
956 { |
|
|
957 unsigned int i, j, k; |
|
|
958 unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; |
|
|
959 int start, end, skip; |
|
|
960 KeySym *syms; |
|
|
961 |
|
|
962 XUngrabKey(dpy, AnyKey, AnyModifier, root); |
|
|
963 XDisplayKeycodes(dpy, &start, &end); |
|
|
964 syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip); |
|
|
965 if (!syms) |
|
|
966 return; |
|
|
967 for (k = start; k <= end; k++) |
|
|
968 for (i = 0; i < LENGTH(keys); i++) |
|
|
969 /* skip modifier codes, we do that ourselves */ |
|
|
970 if (keys[i].keysym == syms[(k - start) * skip]) |
|
|
971 for (j = 0; j < LENGTH(modifiers); j++) |
|
|
972 XGrabKey(dpy, k, |
|
|
973 keys[i].mod | modifiers[j], |
|
|
974 root, True, |
|
|
975 GrabModeAsync, GrabModeAsync); |
|
|
976 XFree(syms); |
|
|
977 } |
|
|
978 } |
|
|
979 |
|
|
980 void |
|
|
981 incnmaster(const Arg *arg) |
|
|
982 { |
|
|
983 selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); |
|
|
984 arrange(selmon); |
|
|
985 } |
|
|
986 |
|
|
987 #ifdef XINERAMA |
|
|
988 static int |
|
|
989 isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) |
|
|
990 { |
|
|
991 while (n--) |
|
|
992 if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org |
|
|
993 && unique[n].width == info->width && unique[n].height == info->height) |
|
|
994 return 0; |
|
|
995 return 1; |
|
|
996 } |
|
|
997 #endif /* XINERAMA */ |
|
|
998 |
|
|
999 void |
|
|
1000 keypress(XEvent *e) |
|
|
1001 { |
|
|
1002 unsigned int i; |
|
|
1003 KeySym keysym; |
|
|
1004 XKeyEvent *ev; |
|
|
1005 |
|
|
1006 ev = &e->xkey; |
|
|
1007 keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); |
|
|
1008 for (i = 0; i < LENGTH(keys); i++) |
|
|
1009 if (keysym == keys[i].keysym |
|
|
1010 && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) |
|
|
1011 && keys[i].func) |
|
|
1012 keys[i].func(&(keys[i].arg)); |
|
|
1013 } |
|
|
1014 |
|
|
1015 void |
|
|
1016 killclient(const Arg *arg) |
|
|
1017 { |
|
|
1018 if (!selmon->sel) |
|
|
1019 return; |
|
|
1020 if (!sendevent(selmon->sel, wmatom[WMDelete])) { |
|
|
1021 XGrabServer(dpy); |
|
|
1022 XSetErrorHandler(xerrordummy); |
|
|
1023 XSetCloseDownMode(dpy, DestroyAll); |
|
|
1024 XKillClient(dpy, selmon->sel->win); |
|
|
1025 XSync(dpy, False); |
|
|
1026 XSetErrorHandler(xerror); |
|
|
1027 XUngrabServer(dpy); |
|
|
1028 } |
|
|
1029 } |
|
|
1030 |
|
|
1031 void |
|
|
1032 manage(Window w, XWindowAttributes *wa) |
|
|
1033 { |
|
|
1034 Client *c, *t = NULL; |
|
|
1035 Window trans = None; |
|
|
1036 XWindowChanges wc; |
|
|
1037 |
|
|
1038 c = ecalloc(1, sizeof(Client)); |
|
|
1039 c->win = w; |
|
|
1040 /* geometry */ |
|
|
1041 c->x = c->oldx = wa->x; |
|
|
1042 c->y = c->oldy = wa->y; |
|
|
1043 c->w = c->oldw = wa->width; |
|
|
1044 c->h = c->oldh = wa->height; |
|
|
1045 c->oldbw = wa->border_width; |
|
|
1046 |
|
|
1047 updatetitle(c); |
|
|
1048 if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { |
|
|
1049 c->mon = t->mon; |
|
|
1050 c->tags = t->tags; |
|
|
1051 } else { |
|
|
1052 c->mon = selmon; |
|
|
1053 applyrules(c); |
|
|
1054 } |
|
|
1055 |
|
|
1056 if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) |
|
|
1057 c->x = c->mon->wx + c->mon->ww - WIDTH(c); |
|
|
1058 if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) |
|
|
1059 c->y = c->mon->wy + c->mon->wh - HEIGHT(c); |
|
|
1060 c->x = MAX(c->x, c->mon->wx); |
|
|
1061 c->y = MAX(c->y, c->mon->wy); |
|
|
1062 c->bw = borderpx; |
|
|
1063 |
|
|
1064 wc.border_width = c->bw; |
|
|
1065 XConfigureWindow(dpy, w, CWBorderWidth, &wc); |
|
|
1066 XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); |
|
|
1067 configure(c); /* propagates border_width, if size doesn't change */ |
|
|
1068 updatewindowtype(c); |
|
|
1069 updatesizehints(c); |
|
|
1070 updatewmhints(c); |
|
|
1071 XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); |
|
|
1072 grabbuttons(c, 0); |
|
|
1073 if (!c->isfloating) |
|
|
1074 c->isfloating = c->oldstate = trans != None || c->isfixed; |
|
|
1075 if (c->isfloating) |
|
|
1076 XRaiseWindow(dpy, c->win); |
|
|
1077 attach(c); |
|
|
1078 attachstack(c); |
|
|
1079 XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, |
|
|
1080 (unsigned char *) &(c->win), 1); |
|
|
1081 XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ |
|
|
1082 setclientstate(c, NormalState); |
|
|
1083 if (c->mon == selmon) |
|
|
1084 unfocus(selmon->sel, 0); |
|
|
1085 c->mon->sel = c; |
|
|
1086 arrange(c->mon); |
|
|
1087 XMapWindow(dpy, c->win); |
|
|
1088 focus(NULL); |
|
|
1089 } |
|
|
1090 |
|
|
1091 void |
|
|
1092 mappingnotify(XEvent *e) |
|
|
1093 { |
|
|
1094 XMappingEvent *ev = &e->xmapping; |
|
|
1095 |
|
|
1096 XRefreshKeyboardMapping(ev); |
|
|
1097 if (ev->request == MappingKeyboard) |
|
|
1098 grabkeys(); |
|
|
1099 } |
|
|
1100 |
|
|
1101 void |
|
|
1102 maprequest(XEvent *e) |
|
|
1103 { |
|
|
1104 static XWindowAttributes wa; |
|
|
1105 XMapRequestEvent *ev = &e->xmaprequest; |
|
|
1106 |
|
|
1107 if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) |
|
|
1108 return; |
|
|
1109 if (!wintoclient(ev->window)) |
|
|
1110 manage(ev->window, &wa); |
|
|
1111 } |
|
|
1112 |
|
|
1113 void |
|
|
1114 monocle(Monitor *m) |
|
|
1115 { |
|
|
1116 unsigned int n = 0; |
|
|
1117 Client *c; |
|
|
1118 |
|
|
1119 for (c = m->clients; c; c = c->next) |
|
|
1120 if (ISVISIBLE(c)) |
|
|
1121 n++; |
|
|
1122 if (n > 0) /* override layout symbol */ |
|
|
1123 snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); |
|
|
1124 for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) |
|
|
1125 resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); |
|
|
1126 } |
|
|
1127 |
|
|
1128 void |
|
|
1129 motionnotify(XEvent *e) |
|
|
1130 { |
|
|
1131 static Monitor *mon = NULL; |
|
|
1132 Monitor *m; |
|
|
1133 XMotionEvent *ev = &e->xmotion; |
|
|
1134 |
|
|
1135 if (ev->window != root) |
|
|
1136 return; |
|
|
1137 if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { |
|
|
1138 unfocus(selmon->sel, 1); |
|
|
1139 selmon = m; |
|
|
1140 focus(NULL); |
|
|
1141 } |
|
|
1142 mon = m; |
|
|
1143 } |
|
|
1144 |
|
|
1145 void |
|
|
1146 movemouse(const Arg *arg) |
|
|
1147 { |
|
|
1148 int x, y, ocx, ocy, nx, ny; |
|
|
1149 Client *c; |
|
|
1150 Monitor *m; |
|
|
1151 XEvent ev; |
|
|
1152 Time lasttime = 0; |
|
|
1153 |
|
|
1154 if (!(c = selmon->sel)) |
|
|
1155 return; |
|
|
1156 if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ |
|
|
1157 return; |
|
|
1158 restack(selmon); |
|
|
1159 ocx = c->x; |
|
|
1160 ocy = c->y; |
|
|
1161 if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, |
|
|
1162 None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) |
|
|
1163 return; |
|
|
1164 if (!getrootptr(&x, &y)) |
|
|
1165 return; |
|
|
1166 do { |
|
|
1167 XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); |
|
|
1168 switch(ev.type) { |
|
|
1169 case ConfigureRequest: |
|
|
1170 case Expose: |
|
|
1171 case MapRequest: |
|
|
1172 handler[ev.type](&ev); |
|
|
1173 break; |
|
|
1174 case MotionNotify: |
|
|
1175 if ((ev.xmotion.time - lasttime) <= (1000 / refreshrate)) |
|
|
1176 continue; |
|
|
1177 lasttime = ev.xmotion.time; |
|
|
1178 |
|
|
1179 nx = ocx + (ev.xmotion.x - x); |
|
|
1180 ny = ocy + (ev.xmotion.y - y); |
|
|
1181 if (abs(selmon->wx - nx) < snap) |
|
|
1182 nx = selmon->wx; |
|
|
1183 else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) |
|
|
1184 nx = selmon->wx + selmon->ww - WIDTH(c); |
|
|
1185 if (abs(selmon->wy - ny) < snap) |
|
|
1186 ny = selmon->wy; |
|
|
1187 else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) |
|
|
1188 ny = selmon->wy + selmon->wh - HEIGHT(c); |
|
|
1189 if (!c->isfloating && selmon->lt[selmon->sellt]->arrange |
|
|
1190 && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) |
|
|
1191 togglefloating(NULL); |
|
|
1192 if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) |
|
|
1193 resize(c, nx, ny, c->w, c->h, 1); |
|
|
1194 break; |
|
|
1195 } |
|
|
1196 } while (ev.type != ButtonRelease); |
|
|
1197 XUngrabPointer(dpy, CurrentTime); |
|
|
1198 if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { |
|
|
1199 sendmon(c, m); |
|
|
1200 selmon = m; |
|
|
1201 focus(NULL); |
|
|
1202 } |
|
|
1203 } |
|
|
1204 |
|
|
1205 Client * |
|
|
1206 nexttiled(Client *c) |
|
|
1207 { |
|
|
1208 for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); |
|
|
1209 return c; |
|
|
1210 } |
|
|
1211 |
|
|
1212 void |
|
|
1213 pop(Client *c) |
|
|
1214 { |
|
|
1215 detach(c); |
|
|
1216 attach(c); |
|
|
1217 focus(c); |
|
|
1218 arrange(c->mon); |
|
|
1219 } |
|
|
1220 |
|
|
1221 void |
|
|
1222 propertynotify(XEvent *e) |
|
|
1223 { |
|
|
1224 Client *c; |
|
|
1225 Window trans; |
|
|
1226 XPropertyEvent *ev = &e->xproperty; |
|
|
1227 |
|
|
1228 if ((ev->window == root) && (ev->atom == XA_WM_NAME)) |
|
|
1229 updatestatus(); |
|
|
1230 else if (ev->state == PropertyDelete) |
|
|
1231 return; /* ignore */ |
|
|
1232 else if ((c = wintoclient(ev->window))) { |
|
|
1233 switch(ev->atom) { |
|
|
1234 default: break; |
|
|
1235 case XA_WM_TRANSIENT_FOR: |
|
|
1236 if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && |
|
|
1237 (c->isfloating = (wintoclient(trans)) != NULL)) |
|
|
1238 arrange(c->mon); |
|
|
1239 break; |
|
|
1240 case XA_WM_NORMAL_HINTS: |
|
|
1241 c->hintsvalid = 0; |
|
|
1242 break; |
|
|
1243 case XA_WM_HINTS: |
|
|
1244 updatewmhints(c); |
|
|
1245 drawbars(); |
|
|
1246 break; |
|
|
1247 } |
|
|
1248 if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { |
|
|
1249 updatetitle(c); |
|
|
1250 if (c == c->mon->sel) |
|
|
1251 drawbar(c->mon); |
|
|
1252 } |
|
|
1253 if (ev->atom == netatom[NetWMWindowType]) |
|
|
1254 updatewindowtype(c); |
|
|
1255 } |
|
|
1256 } |
|
|
1257 |
|
|
1258 void |
|
|
1259 quit(const Arg *arg) |
|
|
1260 { |
|
|
1261 running = 0; |
|
|
1262 } |
|
|
1263 |
|
|
1264 Monitor * |
|
|
1265 recttomon(int x, int y, int w, int h) |
|
|
1266 { |
|
|
1267 Monitor *m, *r = selmon; |
|
|
1268 int a, area = 0; |
|
|
1269 |
|
|
1270 for (m = mons; m; m = m->next) |
|
|
1271 if ((a = INTERSECT(x, y, w, h, m)) > area) { |
|
|
1272 area = a; |
|
|
1273 r = m; |
|
|
1274 } |
|
|
1275 return r; |
|
|
1276 } |
|
|
1277 |
|
|
1278 void |
|
|
1279 resize(Client *c, int x, int y, int w, int h, int interact) |
|
|
1280 { |
|
|
1281 if (applysizehints(c, &x, &y, &w, &h, interact)) |
|
|
1282 resizeclient(c, x, y, w, h); |
|
|
1283 } |
|
|
1284 |
|
|
1285 void |
|
|
1286 resizeclient(Client *c, int x, int y, int w, int h) |
|
|
1287 { |
|
|
1288 XWindowChanges wc; |
|
|
1289 |
|
|
1290 c->oldx = c->x; c->x = wc.x = x; |
|
|
1291 c->oldy = c->y; c->y = wc.y = y; |
|
|
1292 c->oldw = c->w; c->w = wc.width = w; |
|
|
1293 c->oldh = c->h; c->h = wc.height = h; |
|
|
1294 wc.border_width = c->bw; |
|
|
1295 XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); |
|
|
1296 configure(c); |
|
|
1297 XSync(dpy, False); |
|
|
1298 } |
|
|
1299 |
|
|
1300 void |
|
|
1301 resizemouse(const Arg *arg) |
|
|
1302 { |
|
|
1303 int ocx, ocy, nw, nh; |
|
|
1304 Client *c; |
|
|
1305 Monitor *m; |
|
|
1306 XEvent ev; |
|
|
1307 Time lasttime = 0; |
|
|
1308 |
|
|
1309 if (!(c = selmon->sel)) |
|
|
1310 return; |
|
|
1311 if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ |
|
|
1312 return; |
|
|
1313 restack(selmon); |
|
|
1314 ocx = c->x; |
|
|
1315 ocy = c->y; |
|
|
1316 if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, |
|
|
1317 None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) |
|
|
1318 return; |
|
|
1319 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); |
|
|
1320 do { |
|
|
1321 XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); |
|
|
1322 switch(ev.type) { |
|
|
1323 case ConfigureRequest: |
|
|
1324 case Expose: |
|
|
1325 case MapRequest: |
|
|
1326 handler[ev.type](&ev); |
|
|
1327 break; |
|
|
1328 case MotionNotify: |
|
|
1329 if ((ev.xmotion.time - lasttime) <= (1000 / refreshrate)) |
|
|
1330 continue; |
|
|
1331 lasttime = ev.xmotion.time; |
|
|
1332 |
|
|
1333 nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); |
|
|
1334 nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); |
|
|
1335 if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww |
|
|
1336 && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) |
|
|
1337 { |
|
|
1338 if (!c->isfloating && selmon->lt[selmon->sellt]->arrange |
|
|
1339 && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) |
|
|
1340 togglefloating(NULL); |
|
|
1341 } |
|
|
1342 if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) |
|
|
1343 resize(c, c->x, c->y, nw, nh, 1); |
|
|
1344 break; |
|
|
1345 } |
|
|
1346 } while (ev.type != ButtonRelease); |
|
|
1347 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); |
|
|
1348 XUngrabPointer(dpy, CurrentTime); |
|
|
1349 while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
|
|
1350 if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { |
|
|
1351 sendmon(c, m); |
|
|
1352 selmon = m; |
|
|
1353 focus(NULL); |
|
|
1354 } |
|
|
1355 } |
|
|
1356 |
|
|
1357 void |
|
|
1358 restack(Monitor *m) |
|
|
1359 { |
|
|
1360 Client *c; |
|
|
1361 XEvent ev; |
|
|
1362 XWindowChanges wc; |
|
|
1363 |
|
|
1364 drawbar(m); |
|
|
1365 if (!m->sel) |
|
|
1366 return; |
|
|
1367 if (m->sel->isfloating || !m->lt[m->sellt]->arrange) |
|
|
1368 XRaiseWindow(dpy, m->sel->win); |
|
|
1369 if (m->lt[m->sellt]->arrange) { |
|
|
1370 wc.stack_mode = Below; |
|
|
1371 wc.sibling = m->barwin; |
|
|
1372 for (c = m->stack; c; c = c->snext) |
|
|
1373 if (!c->isfloating && ISVISIBLE(c)) { |
|
|
1374 XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); |
|
|
1375 wc.sibling = c->win; |
|
|
1376 } |
|
|
1377 } |
|
|
1378 XSync(dpy, False); |
|
|
1379 while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
|
|
1380 } |
|
|
1381 |
|
|
1382 void |
|
|
1383 run(void) |
|
|
1384 { |
|
|
1385 XEvent ev; |
|
|
1386 /* main event loop */ |
|
|
1387 XSync(dpy, False); |
|
|
1388 while (running && !XNextEvent(dpy, &ev)) |
|
|
1389 if (handler[ev.type]) |
|
|
1390 handler[ev.type](&ev); /* call handler */ |
|
|
1391 } |
|
|
1392 |
|
|
1393 void |
|
|
1394 scan(void) |
|
|
1395 { |
|
|
1396 unsigned int i, num; |
|
|
1397 Window d1, d2, *wins = NULL; |
|
|
1398 XWindowAttributes wa; |
|
|
1399 |
|
|
1400 if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { |
|
|
1401 for (i = 0; i < num; i++) { |
|
|
1402 if (!XGetWindowAttributes(dpy, wins[i], &wa) |
|
|
1403 || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) |
|
|
1404 continue; |
|
|
1405 if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) |
|
|
1406 manage(wins[i], &wa); |
|
|
1407 } |
|
|
1408 for (i = 0; i < num; i++) { /* now the transients */ |
|
|
1409 if (!XGetWindowAttributes(dpy, wins[i], &wa)) |
|
|
1410 continue; |
|
|
1411 if (XGetTransientForHint(dpy, wins[i], &d1) |
|
|
1412 && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) |
|
|
1413 manage(wins[i], &wa); |
|
|
1414 } |
|
|
1415 if (wins) |
|
|
1416 XFree(wins); |
|
|
1417 } |
|
|
1418 } |
|
|
1419 |
|
|
1420 void |
|
|
1421 sendmon(Client *c, Monitor *m) |
|
|
1422 { |
|
|
1423 if (c->mon == m) |
|
|
1424 return; |
|
|
1425 unfocus(c, 1); |
|
|
1426 detach(c); |
|
|
1427 detachstack(c); |
|
|
1428 c->mon = m; |
|
|
1429 c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ |
|
|
1430 attach(c); |
|
|
1431 attachstack(c); |
|
|
1432 if (c->isfullscreen) |
|
|
1433 resizeclient(c, m->mx, m->my, m->mw, m->mh); |
|
|
1434 focus(NULL); |
|
|
1435 arrange(NULL); |
|
|
1436 } |
|
|
1437 |
|
|
1438 void |
|
|
1439 setclientstate(Client *c, long state) |
|
|
1440 { |
|
|
1441 long data[] = { state, None }; |
|
|
1442 |
|
|
1443 XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, |
|
|
1444 PropModeReplace, (unsigned char *)data, 2); |
|
|
1445 } |
|
|
1446 |
|
|
1447 int |
|
|
1448 sendevent(Client *c, Atom proto) |
|
|
1449 { |
|
|
1450 int n; |
|
|
1451 Atom *protocols; |
|
|
1452 int exists = 0; |
|
|
1453 XEvent ev; |
|
|
1454 |
|
|
1455 if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { |
|
|
1456 while (!exists && n--) |
|
|
1457 exists = protocols[n] == proto; |
|
|
1458 XFree(protocols); |
|
|
1459 } |
|
|
1460 if (exists) { |
|
|
1461 ev.type = ClientMessage; |
|
|
1462 ev.xclient.window = c->win; |
|
|
1463 ev.xclient.message_type = wmatom[WMProtocols]; |
|
|
1464 ev.xclient.format = 32; |
|
|
1465 ev.xclient.data.l[0] = proto; |
|
|
1466 ev.xclient.data.l[1] = CurrentTime; |
|
|
1467 XSendEvent(dpy, c->win, False, NoEventMask, &ev); |
|
|
1468 } |
|
|
1469 return exists; |
|
|
1470 } |
|
|
1471 |
|
|
1472 void |
|
|
1473 setfocus(Client *c) |
|
|
1474 { |
|
|
1475 if (!c->neverfocus) |
|
|
1476 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); |
|
|
1477 XChangeProperty(dpy, root, netatom[NetActiveWindow], XA_WINDOW, 32, |
|
|
1478 PropModeReplace, (unsigned char *)&c->win, 1); |
|
|
1479 sendevent(c, wmatom[WMTakeFocus]); |
|
|
1480 } |
|
|
1481 |
|
|
1482 void |
|
|
1483 setfullscreen(Client *c, int fullscreen) |
|
|
1484 { |
|
|
1485 if (fullscreen && !c->isfullscreen) { |
|
|
1486 XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, |
|
|
1487 PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); |
|
|
1488 c->isfullscreen = 1; |
|
|
1489 c->oldstate = c->isfloating; |
|
|
1490 c->oldbw = c->bw; |
|
|
1491 c->bw = 0; |
|
|
1492 c->isfloating = 1; |
|
|
1493 resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); |
|
|
1494 XRaiseWindow(dpy, c->win); |
|
|
1495 } else if (!fullscreen && c->isfullscreen){ |
|
|
1496 XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, |
|
|
1497 PropModeReplace, (unsigned char*)0, 0); |
|
|
1498 c->isfullscreen = 0; |
|
|
1499 c->isfloating = c->oldstate; |
|
|
1500 c->bw = c->oldbw; |
|
|
1501 c->x = c->oldx; |
|
|
1502 c->y = c->oldy; |
|
|
1503 c->w = c->oldw; |
|
|
1504 c->h = c->oldh; |
|
|
1505 resizeclient(c, c->x, c->y, c->w, c->h); |
|
|
1506 arrange(c->mon); |
|
|
1507 } |
|
|
1508 } |
|
|
1509 |
|
|
1510 void |
|
|
1511 setlayout(const Arg *arg) |
|
|
1512 { |
|
|
1513 if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) |
|
|
1514 selmon->sellt ^= 1; |
|
|
1515 if (arg && arg->v) |
|
|
1516 selmon->lt[selmon->sellt] = (Layout *)arg->v; |
|
|
1517 strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); |
|
|
1518 if (selmon->sel) |
|
|
1519 arrange(selmon); |
|
|
1520 else |
|
|
1521 drawbar(selmon); |
|
|
1522 } |
|
|
1523 |
|
|
1524 /* arg > 1.0 will set mfact absolutely */ |
|
|
1525 void |
|
|
1526 setmfact(const Arg *arg) |
|
|
1527 { |
|
|
1528 float f; |
|
|
1529 |
|
|
1530 if (!arg || !selmon->lt[selmon->sellt]->arrange) |
|
|
1531 return; |
|
|
1532 f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; |
|
|
1533 if (f < 0.05 || f > 0.95) |
|
|
1534 return; |
|
|
1535 selmon->mfact = f; |
|
|
1536 arrange(selmon); |
|
|
1537 } |
|
|
1538 |
|
|
1539 void |
|
|
1540 setup(void) |
|
|
1541 { |
|
|
1542 int i; |
|
|
1543 XSetWindowAttributes wa; |
|
|
1544 Atom utf8string; |
|
|
1545 struct sigaction sa; |
|
|
1546 |
|
|
1547 /* do not transform children into zombies when they terminate */ |
|
|
1548 sigemptyset(&sa.sa_mask); |
|
|
1549 sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART; |
|
|
1550 sa.sa_handler = SIG_IGN; |
|
|
1551 sigaction(SIGCHLD, &sa, NULL); |
|
|
1552 |
|
|
1553 /* clean up any zombies (inherited from .xinitrc etc) immediately */ |
|
|
1554 while (waitpid(-1, NULL, WNOHANG) > 0); |
|
|
1555 |
|
|
1556 /* init screen */ |
|
|
1557 screen = DefaultScreen(dpy); |
|
|
1558 sw = DisplayWidth(dpy, screen); |
|
|
1559 sh = DisplayHeight(dpy, screen); |
|
|
1560 root = RootWindow(dpy, screen); |
|
|
1561 drw = drw_create(dpy, screen, root, sw, sh); |
|
|
1562 if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) |
|
|
1563 die("no fonts could be loaded."); |
|
|
1564 lrpad = drw->fonts->h; |
|
|
1565 bh = drw->fonts->h + 2; |
|
|
1566 updategeom(); |
|
|
1567 /* init atoms */ |
|
|
1568 utf8string = XInternAtom(dpy, "UTF8_STRING", False); |
|
|
1569 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); |
|
|
1570 wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); |
|
|
1571 wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); |
|
|
1572 wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); |
|
|
1573 netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); |
|
|
1574 netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); |
|
|
1575 netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); |
|
|
1576 netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); |
|
|
1577 netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); |
|
|
1578 netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); |
|
|
1579 netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); |
|
|
1580 netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); |
|
|
1581 netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); |
|
|
1582 /* init cursors */ |
|
|
1583 cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); |
|
|
1584 cursor[CurResize] = drw_cur_create(drw, XC_sizing); |
|
|
1585 cursor[CurMove] = drw_cur_create(drw, XC_fleur); |
|
|
1586 /* init appearance */ |
|
|
1587 scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); |
|
|
1588 for (i = 0; i < LENGTH(colors); i++) |
|
|
1589 scheme[i] = drw_scm_create(drw, colors[i], 3); |
|
|
1590 /* init bars */ |
|
|
1591 updatebars(); |
|
|
1592 updatestatus(); |
|
|
1593 /* supporting window for NetWMCheck */ |
|
|
1594 wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); |
|
|
1595 XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, |
|
|
1596 PropModeReplace, (unsigned char *) &wmcheckwin, 1); |
|
|
1597 XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, |
|
|
1598 PropModeReplace, (unsigned char *) "dwm", 3); |
|
|
1599 XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, |
|
|
1600 PropModeReplace, (unsigned char *) &wmcheckwin, 1); |
|
|
1601 /* EWMH support per view */ |
|
|
1602 XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, |
|
|
1603 PropModeReplace, (unsigned char *) netatom, NetLast); |
|
|
1604 XDeleteProperty(dpy, root, netatom[NetClientList]); |
|
|
1605 /* select events */ |
|
|
1606 wa.cursor = cursor[CurNormal]->cursor; |
|
|
1607 wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask |
|
|
1608 |ButtonPressMask|PointerMotionMask|EnterWindowMask |
|
|
1609 |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; |
|
|
1610 XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); |
|
|
1611 XSelectInput(dpy, root, wa.event_mask); |
|
|
1612 grabkeys(); |
|
|
1613 focus(NULL); |
|
|
1614 } |
|
|
1615 |
|
|
1616 void |
|
|
1617 seturgent(Client *c, int urg) |
|
|
1618 { |
|
|
1619 XWMHints *wmh; |
|
|
1620 |
|
|
1621 c->isurgent = urg; |
|
|
1622 if (!(wmh = XGetWMHints(dpy, c->win))) |
|
|
1623 return; |
|
|
1624 wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); |
|
|
1625 XSetWMHints(dpy, c->win, wmh); |
|
|
1626 XFree(wmh); |
|
|
1627 } |
|
|
1628 |
|
|
1629 void |
|
|
1630 showhide(Client *c) |
|
|
1631 { |
|
|
1632 if (!c) |
|
|
1633 return; |
|
|
1634 if (ISVISIBLE(c)) { |
|
|
1635 /* show clients top down */ |
|
|
1636 XMoveWindow(dpy, c->win, c->x, c->y); |
|
|
1637 if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) |
|
|
1638 resize(c, c->x, c->y, c->w, c->h, 0); |
|
|
1639 showhide(c->snext); |
|
|
1640 } else { |
|
|
1641 /* hide clients bottom up */ |
|
|
1642 showhide(c->snext); |
|
|
1643 XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); |
|
|
1644 } |
|
|
1645 } |
|
|
1646 |
|
|
1647 void |
|
|
1648 spawn(const Arg *arg) |
|
|
1649 { |
|
|
1650 struct sigaction sa; |
|
|
1651 |
|
|
1652 if (arg->v == dmenucmd) |
|
|
1653 dmenumon[0] = '0' + selmon->num; |
|
|
1654 if (fork() == 0) { |
|
|
1655 if (dpy) |
|
|
1656 close(ConnectionNumber(dpy)); |
|
|
1657 setsid(); |
|
|
1658 |
|
|
1659 sigemptyset(&sa.sa_mask); |
|
|
1660 sa.sa_flags = 0; |
|
|
1661 sa.sa_handler = SIG_DFL; |
|
|
1662 sigaction(SIGCHLD, &sa, NULL); |
|
|
1663 |
|
|
1664 execvp(((char **)arg->v)[0], (char **)arg->v); |
|
|
1665 die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); |
|
|
1666 } |
|
|
1667 } |
|
|
1668 |
|
|
1669 void |
|
|
1670 tag(const Arg *arg) |
|
|
1671 { |
|
|
1672 if (selmon->sel && arg->ui & TAGMASK) { |
|
|
1673 selmon->sel->tags = arg->ui & TAGMASK; |
|
|
1674 focus(NULL); |
|
|
1675 arrange(selmon); |
|
|
1676 } |
|
|
1677 } |
|
|
1678 |
|
|
1679 void |
|
|
1680 tagmon(const Arg *arg) |
|
|
1681 { |
|
|
1682 if (!selmon->sel || !mons->next) |
|
|
1683 return; |
|
|
1684 sendmon(selmon->sel, dirtomon(arg->i)); |
|
|
1685 } |
|
|
1686 |
|
|
1687 void |
|
|
1688 tile(Monitor *m) |
|
|
1689 { |
|
|
1690 unsigned int i, n, h, mw, my, ty; |
|
|
1691 Client *c; |
|
|
1692 |
|
|
1693 for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); |
|
|
1694 if (n == 0) |
|
|
1695 return; |
|
|
1696 |
|
|
1697 if (n > m->nmaster) |
|
|
1698 mw = m->nmaster ? m->ww * m->mfact : 0; |
|
|
1699 else |
|
|
1700 mw = m->ww; |
|
|
1701 for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) |
|
|
1702 if (i < m->nmaster) { |
|
|
1703 h = (m->wh - my) / (MIN(n, m->nmaster) - i); |
|
|
1704 resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), 0); |
|
|
1705 if (my + HEIGHT(c) < m->wh) |
|
|
1706 my += HEIGHT(c); |
|
|
1707 } else { |
|
|
1708 h = (m->wh - ty) / (n - i); |
|
|
1709 resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), 0); |
|
|
1710 if (ty + HEIGHT(c) < m->wh) |
|
|
1711 ty += HEIGHT(c); |
|
|
1712 } |
|
|
1713 } |
|
|
1714 |
|
|
1715 void |
|
|
1716 togglebar(const Arg *arg) |
|
|
1717 { |
|
|
1718 selmon->showbar = !selmon->showbar; |
|
|
1719 updatebarpos(selmon); |
|
|
1720 XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); |
|
|
1721 arrange(selmon); |
|
|
1722 } |
|
|
1723 |
|
|
1724 void |
|
|
1725 togglefloating(const Arg *arg) |
|
|
1726 { |
|
|
1727 if (!selmon->sel) |
|
|
1728 return; |
|
|
1729 if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ |
|
|
1730 return; |
|
|
1731 selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; |
|
|
1732 if (selmon->sel->isfloating) |
|
|
1733 resize(selmon->sel, selmon->sel->x, selmon->sel->y, |
|
|
1734 selmon->sel->w, selmon->sel->h, 0); |
|
|
1735 arrange(selmon); |
|
|
1736 } |
|
|
1737 |
|
|
1738 void |
|
|
1739 toggletag(const Arg *arg) |
|
|
1740 { |
|
|
1741 unsigned int newtags; |
|
|
1742 |
|
|
1743 if (!selmon->sel) |
|
|
1744 return; |
|
|
1745 newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); |
|
|
1746 if (newtags) { |
|
|
1747 selmon->sel->tags = newtags; |
|
|
1748 focus(NULL); |
|
|
1749 arrange(selmon); |
|
|
1750 } |
|
|
1751 } |
|
|
1752 |
|
|
1753 void |
|
|
1754 toggleview(const Arg *arg) |
|
|
1755 { |
|
|
1756 unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); |
|
|
1757 |
|
|
1758 if (newtagset) { |
|
|
1759 selmon->tagset[selmon->seltags] = newtagset; |
|
|
1760 focus(NULL); |
|
|
1761 arrange(selmon); |
|
|
1762 } |
|
|
1763 } |
|
|
1764 |
|
|
1765 void |
|
|
1766 unfocus(Client *c, int setfocus) |
|
|
1767 { |
|
|
1768 if (!c) |
|
|
1769 return; |
|
|
1770 grabbuttons(c, 0); |
|
|
1771 XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); |
|
|
1772 if (setfocus) { |
|
|
1773 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); |
|
|
1774 XDeleteProperty(dpy, root, netatom[NetActiveWindow]); |
|
|
1775 } |
|
|
1776 } |
|
|
1777 |
|
|
1778 void |
|
|
1779 unmanage(Client *c, int destroyed) |
|
|
1780 { |
|
|
1781 Monitor *m = c->mon; |
|
|
1782 XWindowChanges wc; |
|
|
1783 |
|
|
1784 detach(c); |
|
|
1785 detachstack(c); |
|
|
1786 if (!destroyed) { |
|
|
1787 wc.border_width = c->oldbw; |
|
|
1788 XGrabServer(dpy); /* avoid race conditions */ |
|
|
1789 XSetErrorHandler(xerrordummy); |
|
|
1790 XSelectInput(dpy, c->win, NoEventMask); |
|
|
1791 XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ |
|
|
1792 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); |
|
|
1793 setclientstate(c, WithdrawnState); |
|
|
1794 XSync(dpy, False); |
|
|
1795 XSetErrorHandler(xerror); |
|
|
1796 XUngrabServer(dpy); |
|
|
1797 } |
|
|
1798 free(c); |
|
|
1799 focus(NULL); |
|
|
1800 updateclientlist(); |
|
|
1801 arrange(m); |
|
|
1802 } |
|
|
1803 |
|
|
1804 void |
|
|
1805 unmapnotify(XEvent *e) |
|
|
1806 { |
|
|
1807 Client *c; |
|
|
1808 XUnmapEvent *ev = &e->xunmap; |
|
|
1809 |
|
|
1810 if ((c = wintoclient(ev->window))) { |
|
|
1811 if (ev->send_event) |
|
|
1812 setclientstate(c, WithdrawnState); |
|
|
1813 else |
|
|
1814 unmanage(c, 0); |
|
|
1815 } |
|
|
1816 } |
|
|
1817 |
|
|
1818 void |
|
|
1819 updatebars(void) |
|
|
1820 { |
|
|
1821 Monitor *m; |
|
|
1822 XSetWindowAttributes wa = { |
|
|
1823 .override_redirect = True, |
|
|
1824 .background_pixmap = ParentRelative, |
|
|
1825 .event_mask = ButtonPressMask|ExposureMask |
|
|
1826 }; |
|
|
1827 XClassHint ch = {"dwm", "dwm"}; |
|
|
1828 for (m = mons; m; m = m->next) { |
|
|
1829 if (m->barwin) |
|
|
1830 continue; |
|
|
1831 m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen), |
|
|
1832 CopyFromParent, DefaultVisual(dpy, screen), |
|
|
1833 CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); |
|
|
1834 XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); |
|
|
1835 XMapRaised(dpy, m->barwin); |
|
|
1836 XSetClassHint(dpy, m->barwin, &ch); |
|
|
1837 } |
|
|
1838 } |
|
|
1839 |
|
|
1840 void |
|
|
1841 updatebarpos(Monitor *m) |
|
|
1842 { |
|
|
1843 m->wy = m->my; |
|
|
1844 m->wh = m->mh; |
|
|
1845 if (m->showbar) { |
|
|
1846 m->wh -= bh; |
|
|
1847 m->by = m->topbar ? m->wy : m->wy + m->wh; |
|
|
1848 m->wy = m->topbar ? m->wy + bh : m->wy; |
|
|
1849 } else |
|
|
1850 m->by = -bh; |
|
|
1851 } |
|
|
1852 |
|
|
1853 void |
|
|
1854 updateclientlist(void) |
|
|
1855 { |
|
|
1856 Client *c; |
|
|
1857 Monitor *m; |
|
|
1858 |
|
|
1859 XDeleteProperty(dpy, root, netatom[NetClientList]); |
|
|
1860 for (m = mons; m; m = m->next) |
|
|
1861 for (c = m->clients; c; c = c->next) |
|
|
1862 XChangeProperty(dpy, root, netatom[NetClientList], |
|
|
1863 XA_WINDOW, 32, PropModeAppend, |
|
|
1864 (unsigned char *) &(c->win), 1); |
|
|
1865 } |
|
|
1866 |
|
|
1867 int |
|
|
1868 updategeom(void) |
|
|
1869 { |
|
|
1870 int dirty = 0; |
|
|
1871 |
|
|
1872 #ifdef XINERAMA |
|
|
1873 if (XineramaIsActive(dpy)) { |
|
|
1874 int i, j, n, nn; |
|
|
1875 Client *c; |
|
|
1876 Monitor *m; |
|
|
1877 XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); |
|
|
1878 XineramaScreenInfo *unique = NULL; |
|
|
1879 |
|
|
1880 for (n = 0, m = mons; m; m = m->next, n++); |
|
|
1881 /* only consider unique geometries as separate screens */ |
|
|
1882 unique = ecalloc(nn, sizeof(XineramaScreenInfo)); |
|
|
1883 for (i = 0, j = 0; i < nn; i++) |
|
|
1884 if (isuniquegeom(unique, j, &info[i])) |
|
|
1885 memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); |
|
|
1886 XFree(info); |
|
|
1887 nn = j; |
|
|
1888 |
|
|
1889 /* new monitors if nn > n */ |
|
|
1890 for (i = n; i < nn; i++) { |
|
|
1891 for (m = mons; m && m->next; m = m->next); |
|
|
1892 if (m) |
|
|
1893 m->next = createmon(); |
|
|
1894 else |
|
|
1895 mons = createmon(); |
|
|
1896 } |
|
|
1897 for (i = 0, m = mons; i < nn && m; m = m->next, i++) |
|
|
1898 if (i >= n |
|
|
1899 || unique[i].x_org != m->mx || unique[i].y_org != m->my |
|
|
1900 || unique[i].width != m->mw || unique[i].height != m->mh) |
|
|
1901 { |
|
|
1902 dirty = 1; |
|
|
1903 m->num = i; |
|
|
1904 m->mx = m->wx = unique[i].x_org; |
|
|
1905 m->my = m->wy = unique[i].y_org; |
|
|
1906 m->mw = m->ww = unique[i].width; |
|
|
1907 m->mh = m->wh = unique[i].height; |
|
|
1908 updatebarpos(m); |
|
|
1909 } |
|
|
1910 /* removed monitors if n > nn */ |
|
|
1911 for (i = nn; i < n; i++) { |
|
|
1912 for (m = mons; m && m->next; m = m->next); |
|
|
1913 while ((c = m->clients)) { |
|
|
1914 dirty = 1; |
|
|
1915 m->clients = c->next; |
|
|
1916 detachstack(c); |
|
|
1917 c->mon = mons; |
|
|
1918 attach(c); |
|
|
1919 attachstack(c); |
|
|
1920 } |
|
|
1921 if (m == selmon) |
|
|
1922 selmon = mons; |
|
|
1923 cleanupmon(m); |
|
|
1924 } |
|
|
1925 free(unique); |
|
|
1926 } else |
|
|
1927 #endif /* XINERAMA */ |
|
|
1928 { /* default monitor setup */ |
|
|
1929 if (!mons) |
|
|
1930 mons = createmon(); |
|
|
1931 if (mons->mw != sw || mons->mh != sh) { |
|
|
1932 dirty = 1; |
|
|
1933 mons->mw = mons->ww = sw; |
|
|
1934 mons->mh = mons->wh = sh; |
|
|
1935 updatebarpos(mons); |
|
|
1936 } |
|
|
1937 } |
|
|
1938 if (dirty) { |
|
|
1939 selmon = mons; |
|
|
1940 selmon = wintomon(root); |
|
|
1941 } |
|
|
1942 return dirty; |
|
|
1943 } |
|
|
1944 |
|
|
1945 void |
|
|
1946 updatenumlockmask(void) |
|
|
1947 { |
|
|
1948 unsigned int i, j; |
|
|
1949 XModifierKeymap *modmap; |
|
|
1950 |
|
|
1951 numlockmask = 0; |
|
|
1952 modmap = XGetModifierMapping(dpy); |
|
|
1953 for (i = 0; i < 8; i++) |
|
|
1954 for (j = 0; j < modmap->max_keypermod; j++) |
|
|
1955 if (modmap->modifiermap[i * modmap->max_keypermod + j] |
|
|
1956 == XKeysymToKeycode(dpy, XK_Num_Lock)) |
|
|
1957 numlockmask = (1 << i); |
|
|
1958 XFreeModifiermap(modmap); |
|
|
1959 } |
|
|
1960 |
|
|
1961 void |
|
|
1962 updatesizehints(Client *c) |
|
|
1963 { |
|
|
1964 long msize; |
|
|
1965 XSizeHints size; |
|
|
1966 |
|
|
1967 if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) |
|
|
1968 /* size is uninitialized, ensure that size.flags aren't used */ |
|
|
1969 size.flags = PSize; |
|
|
1970 if (size.flags & PBaseSize) { |
|
|
1971 c->basew = size.base_width; |
|
|
1972 c->baseh = size.base_height; |
|
|
1973 } else if (size.flags & PMinSize) { |
|
|
1974 c->basew = size.min_width; |
|
|
1975 c->baseh = size.min_height; |
|
|
1976 } else |
|
|
1977 c->basew = c->baseh = 0; |
|
|
1978 if (size.flags & PResizeInc) { |
|
|
1979 c->incw = size.width_inc; |
|
|
1980 c->inch = size.height_inc; |
|
|
1981 } else |
|
|
1982 c->incw = c->inch = 0; |
|
|
1983 if (size.flags & PMaxSize) { |
|
|
1984 c->maxw = size.max_width; |
|
|
1985 c->maxh = size.max_height; |
|
|
1986 } else |
|
|
1987 c->maxw = c->maxh = 0; |
|
|
1988 if (size.flags & PMinSize) { |
|
|
1989 c->minw = size.min_width; |
|
|
1990 c->minh = size.min_height; |
|
|
1991 } else if (size.flags & PBaseSize) { |
|
|
1992 c->minw = size.base_width; |
|
|
1993 c->minh = size.base_height; |
|
|
1994 } else |
|
|
1995 c->minw = c->minh = 0; |
|
|
1996 if (size.flags & PAspect) { |
|
|
1997 c->mina = (float)size.min_aspect.y / size.min_aspect.x; |
|
|
1998 c->maxa = (float)size.max_aspect.x / size.max_aspect.y; |
|
|
1999 } else |
|
|
2000 c->maxa = c->mina = 0.0; |
|
|
2001 c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); |
|
|
2002 c->hintsvalid = 1; |
|
|
2003 } |
|
|
2004 |
|
|
2005 void |
|
|
2006 updatestatus(void) |
|
|
2007 { |
|
|
2008 if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) |
|
|
2009 strcpy(stext, "dwm-"VERSION); |
|
|
2010 drawbar(selmon); |
|
|
2011 } |
|
|
2012 |
|
|
2013 void |
|
|
2014 updatetitle(Client *c) |
|
|
2015 { |
|
|
2016 if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) |
|
|
2017 gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); |
|
|
2018 if (c->name[0] == '\0') /* hack to mark broken clients */ |
|
|
2019 strcpy(c->name, broken); |
|
|
2020 } |
|
|
2021 |
|
|
2022 void |
|
|
2023 updatewindowtype(Client *c) |
|
|
2024 { |
|
|
2025 Atom state = getatomprop(c, netatom[NetWMState]); |
|
|
2026 Atom wtype = getatomprop(c, netatom[NetWMWindowType]); |
|
|
2027 |
|
|
2028 if (state == netatom[NetWMFullscreen]) |
|
|
2029 setfullscreen(c, 1); |
|
|
2030 if (wtype == netatom[NetWMWindowTypeDialog]) |
|
|
2031 c->isfloating = 1; |
|
|
2032 } |
|
|
2033 |
|
|
2034 void |
|
|
2035 updatewmhints(Client *c) |
|
|
2036 { |
|
|
2037 XWMHints *wmh; |
|
|
2038 |
|
|
2039 if ((wmh = XGetWMHints(dpy, c->win))) { |
|
|
2040 if (c == selmon->sel && wmh->flags & XUrgencyHint) { |
|
|
2041 wmh->flags &= ~XUrgencyHint; |
|
|
2042 XSetWMHints(dpy, c->win, wmh); |
|
|
2043 } else |
|
|
2044 c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; |
|
|
2045 if (wmh->flags & InputHint) |
|
|
2046 c->neverfocus = !wmh->input; |
|
|
2047 else |
|
|
2048 c->neverfocus = 0; |
|
|
2049 XFree(wmh); |
|
|
2050 } |
|
|
2051 } |
|
|
2052 |
|
|
2053 void |
|
|
2054 view(const Arg *arg) |
|
|
2055 { |
|
|
2056 if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) |
|
|
2057 return; |
|
|
2058 selmon->seltags ^= 1; /* toggle sel tagset */ |
|
|
2059 if (arg->ui & TAGMASK) |
|
|
2060 selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; |
|
|
2061 focus(NULL); |
|
|
2062 arrange(selmon); |
|
|
2063 } |
|
|
2064 |
|
|
2065 Client * |
|
|
2066 wintoclient(Window w) |
|
|
2067 { |
|
|
2068 Client *c; |
|
|
2069 Monitor *m; |
|
|
2070 |
|
|
2071 for (m = mons; m; m = m->next) |
|
|
2072 for (c = m->clients; c; c = c->next) |
|
|
2073 if (c->win == w) |
|
|
2074 return c; |
|
|
2075 return NULL; |
|
|
2076 } |
|
|
2077 |
|
|
2078 Monitor * |
|
|
2079 wintomon(Window w) |
|
|
2080 { |
|
|
2081 int x, y; |
|
|
2082 Client *c; |
|
|
2083 Monitor *m; |
|
|
2084 |
|
|
2085 if (w == root && getrootptr(&x, &y)) |
|
|
2086 return recttomon(x, y, 1, 1); |
|
|
2087 for (m = mons; m; m = m->next) |
|
|
2088 if (w == m->barwin) |
|
|
2089 return m; |
|
|
2090 if ((c = wintoclient(w))) |
|
|
2091 return c->mon; |
|
|
2092 return selmon; |
|
|
2093 } |
|
|
2094 |
|
|
2095 /* There's no way to check accesses to destroyed windows, thus those cases are |
|
|
2096 * ignored (especially on UnmapNotify's). Other types of errors call Xlibs |
|
|
2097 * default error handler, which may call exit. */ |
|
|
2098 int |
|
|
2099 xerror(Display *dpy, XErrorEvent *ee) |
|
|
2100 { |
|
|
2101 if (ee->error_code == BadWindow |
|
|
2102 || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) |
|
|
2103 || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) |
|
|
2104 || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) |
|
|
2105 || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) |
|
|
2106 || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) |
|
|
2107 || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) |
|
|
2108 || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) |
|
|
2109 || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) |
|
|
2110 return 0; |
|
|
2111 fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", |
|
|
2112 ee->request_code, ee->error_code); |
|
|
2113 return xerrorxlib(dpy, ee); /* may call exit */ |
|
|
2114 } |
|
|
2115 |
|
|
2116 int |
|
|
2117 xerrordummy(Display *dpy, XErrorEvent *ee) |
|
|
2118 { |
|
|
2119 return 0; |
|
|
2120 } |
|
|
2121 |
|
|
2122 /* Startup Error handler to check if another window manager |
|
|
2123 * is already running. */ |
|
|
2124 int |
|
|
2125 xerrorstart(Display *dpy, XErrorEvent *ee) |
|
|
2126 { |
|
|
2127 die("dwm: another window manager is already running"); |
|
|
2128 return -1; |
|
|
2129 } |
|
|
2130 |
|
|
2131 void |
|
|
2132 zoom(const Arg *arg) |
|
|
2133 { |
|
|
2134 Client *c = selmon->sel; |
|
|
2135 |
|
|
2136 if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating) |
|
|
2137 return; |
|
|
2138 if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next))) |
|
|
2139 return; |
|
|
2140 pop(c); |
|
|
2141 } |
|
|
2142 |
|
|
2143 int |
|
|
2144 main(int argc, char *argv[]) |
|
|
2145 { |
|
|
2146 if (argc == 2 && !strcmp("-v", argv[1])) |
|
|
2147 die("dwm-"VERSION); |
|
|
2148 else if (argc != 1) |
|
|
2149 die("usage: dwm [-v]"); |
|
|
2150 if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) |
|
|
2151 fputs("warning: no locale support\n", stderr); |
|
|
2152 if (!(dpy = XOpenDisplay(NULL))) |
|
|
2153 die("dwm: cannot open display"); |
|
|
2154 checkotherwm(); |
|
|
2155 setup(); |
|
|
2156 #ifdef __OpenBSD__ |
|
|
2157 if (pledge("stdio rpath proc exec", NULL) == -1) |
|
|
2158 die("pledge"); |
|
|
2159 #endif /* __OpenBSD__ */ |
|
|
2160 scan(); |
|
|
2161 run(); |
|
|
2162 cleanup(); |
|
|
2163 XCloseDisplay(dpy); |
|
|
2164 return EXIT_SUCCESS; |
|
|
2165 } |