|
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; |
|
2
|
89 float cfact; |
|
0
|
90 int x, y, w, h; |
|
|
91 int oldx, oldy, oldw, oldh; |
|
|
92 int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; |
|
|
93 int bw, oldbw; |
|
|
94 unsigned int tags; |
|
|
95 int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; |
|
|
96 Client *next; |
|
|
97 Client *snext; |
|
|
98 Monitor *mon; |
|
|
99 Window win; |
|
|
100 }; |
|
|
101 |
|
|
102 typedef struct { |
|
|
103 unsigned int mod; |
|
|
104 KeySym keysym; |
|
|
105 void (*func)(const Arg *); |
|
|
106 const Arg arg; |
|
|
107 } Key; |
|
|
108 |
|
|
109 typedef struct { |
|
|
110 const char *symbol; |
|
|
111 void (*arrange)(Monitor *); |
|
|
112 } Layout; |
|
|
113 |
|
|
114 struct Monitor { |
|
|
115 char ltsymbol[16]; |
|
|
116 float mfact; |
|
|
117 int nmaster; |
|
|
118 int num; |
|
|
119 int by; /* bar geometry */ |
|
|
120 int mx, my, mw, mh; /* screen size */ |
|
|
121 int wx, wy, ww, wh; /* window area */ |
|
2
|
122 int gappih; /* horizontal gap between windows */ |
|
|
123 int gappiv; /* vertical gap between windows */ |
|
|
124 int gappoh; /* horizontal outer gaps */ |
|
|
125 int gappov; /* vertical outer gaps */ |
|
0
|
126 unsigned int seltags; |
|
|
127 unsigned int sellt; |
|
|
128 unsigned int tagset[2]; |
|
|
129 int showbar; |
|
|
130 int topbar; |
|
|
131 Client *clients; |
|
|
132 Client *sel; |
|
|
133 Client *stack; |
|
|
134 Monitor *next; |
|
|
135 Window barwin; |
|
|
136 const Layout *lt[2]; |
|
|
137 }; |
|
|
138 |
|
|
139 typedef struct { |
|
|
140 const char *class; |
|
|
141 const char *instance; |
|
|
142 const char *title; |
|
|
143 unsigned int tags; |
|
|
144 int isfloating; |
|
|
145 int monitor; |
|
|
146 } Rule; |
|
|
147 |
|
|
148 /* function declarations */ |
|
|
149 static void applyrules(Client *c); |
|
|
150 static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); |
|
|
151 static void arrange(Monitor *m); |
|
|
152 static void arrangemon(Monitor *m); |
|
|
153 static void attach(Client *c); |
|
|
154 static void attachstack(Client *c); |
|
|
155 static void buttonpress(XEvent *e); |
|
|
156 static void checkotherwm(void); |
|
|
157 static void cleanup(void); |
|
|
158 static void cleanupmon(Monitor *mon); |
|
|
159 static void clientmessage(XEvent *e); |
|
|
160 static void configure(Client *c); |
|
|
161 static void configurenotify(XEvent *e); |
|
|
162 static void configurerequest(XEvent *e); |
|
|
163 static Monitor *createmon(void); |
|
|
164 static void destroynotify(XEvent *e); |
|
|
165 static void detach(Client *c); |
|
|
166 static void detachstack(Client *c); |
|
|
167 static Monitor *dirtomon(int dir); |
|
|
168 static void drawbar(Monitor *m); |
|
|
169 static void drawbars(void); |
|
|
170 static void enternotify(XEvent *e); |
|
|
171 static void expose(XEvent *e); |
|
|
172 static void focus(Client *c); |
|
|
173 static void focusin(XEvent *e); |
|
|
174 static void focusmon(const Arg *arg); |
|
|
175 static void focusstack(const Arg *arg); |
|
|
176 static Atom getatomprop(Client *c, Atom prop); |
|
|
177 static int getrootptr(int *x, int *y); |
|
|
178 static long getstate(Window w); |
|
|
179 static int gettextprop(Window w, Atom atom, char *text, unsigned int size); |
|
|
180 static void grabbuttons(Client *c, int focused); |
|
|
181 static void grabkeys(void); |
|
|
182 static void incnmaster(const Arg *arg); |
|
|
183 static void keypress(XEvent *e); |
|
|
184 static void killclient(const Arg *arg); |
|
|
185 static void manage(Window w, XWindowAttributes *wa); |
|
|
186 static void mappingnotify(XEvent *e); |
|
|
187 static void maprequest(XEvent *e); |
|
|
188 static void monocle(Monitor *m); |
|
|
189 static void motionnotify(XEvent *e); |
|
|
190 static void movemouse(const Arg *arg); |
|
|
191 static Client *nexttiled(Client *c); |
|
|
192 static void pop(Client *c); |
|
|
193 static void propertynotify(XEvent *e); |
|
|
194 static void quit(const Arg *arg); |
|
|
195 static Monitor *recttomon(int x, int y, int w, int h); |
|
|
196 static void resize(Client *c, int x, int y, int w, int h, int interact); |
|
|
197 static void resizeclient(Client *c, int x, int y, int w, int h); |
|
|
198 static void resizemouse(const Arg *arg); |
|
|
199 static void restack(Monitor *m); |
|
|
200 static void run(void); |
|
|
201 static void scan(void); |
|
|
202 static int sendevent(Client *c, Atom proto); |
|
|
203 static void sendmon(Client *c, Monitor *m); |
|
|
204 static void setclientstate(Client *c, long state); |
|
|
205 static void setfocus(Client *c); |
|
|
206 static void setfullscreen(Client *c, int fullscreen); |
|
|
207 static void setlayout(const Arg *arg); |
|
2
|
208 static void setcfact(const Arg *arg); |
|
0
|
209 static void setmfact(const Arg *arg); |
|
|
210 static void setup(void); |
|
|
211 static void seturgent(Client *c, int urg); |
|
|
212 static void showhide(Client *c); |
|
|
213 static void spawn(const Arg *arg); |
|
|
214 static void tag(const Arg *arg); |
|
|
215 static void tagmon(const Arg *arg); |
|
|
216 static void togglebar(const Arg *arg); |
|
|
217 static void togglefloating(const Arg *arg); |
|
|
218 static void toggletag(const Arg *arg); |
|
|
219 static void toggleview(const Arg *arg); |
|
|
220 static void unfocus(Client *c, int setfocus); |
|
|
221 static void unmanage(Client *c, int destroyed); |
|
|
222 static void unmapnotify(XEvent *e); |
|
|
223 static void updatebarpos(Monitor *m); |
|
|
224 static void updatebars(void); |
|
|
225 static void updateclientlist(void); |
|
|
226 static int updategeom(void); |
|
|
227 static void updatenumlockmask(void); |
|
|
228 static void updatesizehints(Client *c); |
|
|
229 static void updatestatus(void); |
|
|
230 static void updatetitle(Client *c); |
|
|
231 static void updatewindowtype(Client *c); |
|
|
232 static void updatewmhints(Client *c); |
|
|
233 static void view(const Arg *arg); |
|
|
234 static Client *wintoclient(Window w); |
|
|
235 static Monitor *wintomon(Window w); |
|
|
236 static int xerror(Display *dpy, XErrorEvent *ee); |
|
|
237 static int xerrordummy(Display *dpy, XErrorEvent *ee); |
|
|
238 static int xerrorstart(Display *dpy, XErrorEvent *ee); |
|
|
239 static void zoom(const Arg *arg); |
|
|
240 |
|
|
241 /* variables */ |
|
|
242 static const char broken[] = "broken"; |
|
|
243 static char stext[256]; |
|
|
244 static int screen; |
|
|
245 static int sw, sh; /* X display screen geometry width, height */ |
|
|
246 static int bh; /* bar height */ |
|
|
247 static int lrpad; /* sum of left and right padding for text */ |
|
|
248 static int (*xerrorxlib)(Display *, XErrorEvent *); |
|
|
249 static unsigned int numlockmask = 0; |
|
|
250 static void (*handler[LASTEvent]) (XEvent *) = { |
|
|
251 [ButtonPress] = buttonpress, |
|
|
252 [ClientMessage] = clientmessage, |
|
|
253 [ConfigureRequest] = configurerequest, |
|
|
254 [ConfigureNotify] = configurenotify, |
|
|
255 [DestroyNotify] = destroynotify, |
|
|
256 [EnterNotify] = enternotify, |
|
|
257 [Expose] = expose, |
|
|
258 [FocusIn] = focusin, |
|
|
259 [KeyPress] = keypress, |
|
|
260 [MappingNotify] = mappingnotify, |
|
|
261 [MapRequest] = maprequest, |
|
|
262 [MotionNotify] = motionnotify, |
|
|
263 [PropertyNotify] = propertynotify, |
|
|
264 [UnmapNotify] = unmapnotify |
|
|
265 }; |
|
|
266 static Atom wmatom[WMLast], netatom[NetLast]; |
|
|
267 static int running = 1; |
|
|
268 static Cur *cursor[CurLast]; |
|
|
269 static Clr **scheme; |
|
|
270 static Display *dpy; |
|
|
271 static Drw *drw; |
|
|
272 static Monitor *mons, *selmon; |
|
|
273 static Window root, wmcheckwin; |
|
|
274 |
|
|
275 /* configuration, allows nested code to access above variables */ |
|
|
276 #include "config.h" |
|
|
277 |
|
|
278 /* compile-time check if all tags fit into an unsigned int bit array. */ |
|
|
279 struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; |
|
|
280 |
|
|
281 /* function implementations */ |
|
|
282 void |
|
|
283 applyrules(Client *c) |
|
|
284 { |
|
|
285 const char *class, *instance; |
|
|
286 unsigned int i; |
|
|
287 const Rule *r; |
|
|
288 Monitor *m; |
|
|
289 XClassHint ch = { NULL, NULL }; |
|
|
290 |
|
|
291 /* rule matching */ |
|
|
292 c->isfloating = 0; |
|
|
293 c->tags = 0; |
|
|
294 XGetClassHint(dpy, c->win, &ch); |
|
|
295 class = ch.res_class ? ch.res_class : broken; |
|
|
296 instance = ch.res_name ? ch.res_name : broken; |
|
|
297 |
|
|
298 for (i = 0; i < LENGTH(rules); i++) { |
|
|
299 r = &rules[i]; |
|
|
300 if ((!r->title || strstr(c->name, r->title)) |
|
|
301 && (!r->class || strstr(class, r->class)) |
|
|
302 && (!r->instance || strstr(instance, r->instance))) |
|
|
303 { |
|
|
304 c->isfloating = r->isfloating; |
|
|
305 c->tags |= r->tags; |
|
|
306 for (m = mons; m && m->num != r->monitor; m = m->next); |
|
|
307 if (m) |
|
|
308 c->mon = m; |
|
|
309 } |
|
|
310 } |
|
|
311 if (ch.res_class) |
|
|
312 XFree(ch.res_class); |
|
|
313 if (ch.res_name) |
|
|
314 XFree(ch.res_name); |
|
|
315 c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; |
|
|
316 } |
|
|
317 |
|
|
318 int |
|
|
319 applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) |
|
|
320 { |
|
|
321 int baseismin; |
|
|
322 Monitor *m = c->mon; |
|
|
323 |
|
|
324 /* set minimum possible */ |
|
|
325 *w = MAX(1, *w); |
|
|
326 *h = MAX(1, *h); |
|
|
327 if (interact) { |
|
|
328 if (*x > sw) |
|
|
329 *x = sw - WIDTH(c); |
|
|
330 if (*y > sh) |
|
|
331 *y = sh - HEIGHT(c); |
|
|
332 if (*x + *w + 2 * c->bw < 0) |
|
|
333 *x = 0; |
|
|
334 if (*y + *h + 2 * c->bw < 0) |
|
|
335 *y = 0; |
|
|
336 } else { |
|
|
337 if (*x >= m->wx + m->ww) |
|
|
338 *x = m->wx + m->ww - WIDTH(c); |
|
|
339 if (*y >= m->wy + m->wh) |
|
|
340 *y = m->wy + m->wh - HEIGHT(c); |
|
|
341 if (*x + *w + 2 * c->bw <= m->wx) |
|
|
342 *x = m->wx; |
|
|
343 if (*y + *h + 2 * c->bw <= m->wy) |
|
|
344 *y = m->wy; |
|
|
345 } |
|
|
346 if (*h < bh) |
|
|
347 *h = bh; |
|
|
348 if (*w < bh) |
|
|
349 *w = bh; |
|
|
350 if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { |
|
|
351 if (!c->hintsvalid) |
|
|
352 updatesizehints(c); |
|
|
353 /* see last two sentences in ICCCM 4.1.2.3 */ |
|
|
354 baseismin = c->basew == c->minw && c->baseh == c->minh; |
|
|
355 if (!baseismin) { /* temporarily remove base dimensions */ |
|
|
356 *w -= c->basew; |
|
|
357 *h -= c->baseh; |
|
|
358 } |
|
|
359 /* adjust for aspect limits */ |
|
|
360 if (c->mina > 0 && c->maxa > 0) { |
|
|
361 if (c->maxa < (float)*w / *h) |
|
|
362 *w = *h * c->maxa + 0.5; |
|
|
363 else if (c->mina < (float)*h / *w) |
|
|
364 *h = *w * c->mina + 0.5; |
|
|
365 } |
|
|
366 if (baseismin) { /* increment calculation requires this */ |
|
|
367 *w -= c->basew; |
|
|
368 *h -= c->baseh; |
|
|
369 } |
|
|
370 /* adjust for increment value */ |
|
|
371 if (c->incw) |
|
|
372 *w -= *w % c->incw; |
|
|
373 if (c->inch) |
|
|
374 *h -= *h % c->inch; |
|
|
375 /* restore base dimensions */ |
|
|
376 *w = MAX(*w + c->basew, c->minw); |
|
|
377 *h = MAX(*h + c->baseh, c->minh); |
|
|
378 if (c->maxw) |
|
|
379 *w = MIN(*w, c->maxw); |
|
|
380 if (c->maxh) |
|
|
381 *h = MIN(*h, c->maxh); |
|
|
382 } |
|
|
383 return *x != c->x || *y != c->y || *w != c->w || *h != c->h; |
|
|
384 } |
|
|
385 |
|
|
386 void |
|
|
387 arrange(Monitor *m) |
|
|
388 { |
|
|
389 if (m) |
|
|
390 showhide(m->stack); |
|
|
391 else for (m = mons; m; m = m->next) |
|
|
392 showhide(m->stack); |
|
|
393 if (m) { |
|
|
394 arrangemon(m); |
|
|
395 restack(m); |
|
|
396 } else for (m = mons; m; m = m->next) |
|
|
397 arrangemon(m); |
|
|
398 } |
|
|
399 |
|
|
400 void |
|
|
401 arrangemon(Monitor *m) |
|
|
402 { |
|
|
403 strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); |
|
|
404 if (m->lt[m->sellt]->arrange) |
|
|
405 m->lt[m->sellt]->arrange(m); |
|
|
406 } |
|
|
407 |
|
|
408 void |
|
|
409 attach(Client *c) |
|
|
410 { |
|
|
411 c->next = c->mon->clients; |
|
|
412 c->mon->clients = c; |
|
|
413 } |
|
|
414 |
|
|
415 void |
|
|
416 attachstack(Client *c) |
|
|
417 { |
|
|
418 c->snext = c->mon->stack; |
|
|
419 c->mon->stack = c; |
|
|
420 } |
|
|
421 |
|
|
422 void |
|
|
423 buttonpress(XEvent *e) |
|
|
424 { |
|
|
425 unsigned int i, x, click; |
|
|
426 Arg arg = {0}; |
|
|
427 Client *c; |
|
|
428 Monitor *m; |
|
|
429 XButtonPressedEvent *ev = &e->xbutton; |
|
|
430 |
|
|
431 click = ClkRootWin; |
|
|
432 /* focus monitor if necessary */ |
|
|
433 if ((m = wintomon(ev->window)) && m != selmon) { |
|
|
434 unfocus(selmon->sel, 1); |
|
|
435 selmon = m; |
|
|
436 focus(NULL); |
|
|
437 } |
|
|
438 if (ev->window == selmon->barwin) { |
|
|
439 i = x = 0; |
|
|
440 do |
|
|
441 x += TEXTW(tags[i]); |
|
|
442 while (ev->x >= x && ++i < LENGTH(tags)); |
|
|
443 if (i < LENGTH(tags)) { |
|
|
444 click = ClkTagBar; |
|
|
445 arg.ui = 1 << i; |
|
|
446 } else if (ev->x < x + TEXTW(selmon->ltsymbol)) |
|
|
447 click = ClkLtSymbol; |
|
|
448 else if (ev->x > selmon->ww - (int)TEXTW(stext) + lrpad - 2) |
|
|
449 click = ClkStatusText; |
|
|
450 else |
|
|
451 click = ClkWinTitle; |
|
|
452 } else if ((c = wintoclient(ev->window))) { |
|
|
453 focus(c); |
|
|
454 restack(selmon); |
|
|
455 XAllowEvents(dpy, ReplayPointer, CurrentTime); |
|
|
456 click = ClkClientWin; |
|
|
457 } |
|
|
458 for (i = 0; i < LENGTH(buttons); i++) |
|
|
459 if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button |
|
|
460 && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) |
|
|
461 buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); |
|
|
462 } |
|
|
463 |
|
|
464 void |
|
|
465 checkotherwm(void) |
|
|
466 { |
|
|
467 xerrorxlib = XSetErrorHandler(xerrorstart); |
|
|
468 /* this causes an error if some other window manager is running */ |
|
|
469 XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); |
|
|
470 XSync(dpy, False); |
|
|
471 XSetErrorHandler(xerror); |
|
|
472 XSync(dpy, False); |
|
|
473 } |
|
|
474 |
|
|
475 void |
|
|
476 cleanup(void) |
|
|
477 { |
|
|
478 Arg a = {.ui = ~0}; |
|
|
479 Layout foo = { "", NULL }; |
|
|
480 Monitor *m; |
|
|
481 size_t i; |
|
|
482 |
|
|
483 view(&a); |
|
|
484 selmon->lt[selmon->sellt] = &foo; |
|
|
485 for (m = mons; m; m = m->next) |
|
|
486 while (m->stack) |
|
|
487 unmanage(m->stack, 0); |
|
|
488 XUngrabKey(dpy, AnyKey, AnyModifier, root); |
|
|
489 while (mons) |
|
|
490 cleanupmon(mons); |
|
|
491 for (i = 0; i < CurLast; i++) |
|
|
492 drw_cur_free(drw, cursor[i]); |
|
|
493 for (i = 0; i < LENGTH(colors); i++) |
|
|
494 drw_scm_free(drw, scheme[i], 3); |
|
|
495 free(scheme); |
|
|
496 XDestroyWindow(dpy, wmcheckwin); |
|
|
497 drw_free(drw); |
|
|
498 XSync(dpy, False); |
|
|
499 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); |
|
|
500 XDeleteProperty(dpy, root, netatom[NetActiveWindow]); |
|
|
501 } |
|
|
502 |
|
|
503 void |
|
|
504 cleanupmon(Monitor *mon) |
|
|
505 { |
|
|
506 Monitor *m; |
|
|
507 |
|
|
508 if (mon == mons) |
|
|
509 mons = mons->next; |
|
|
510 else { |
|
|
511 for (m = mons; m && m->next != mon; m = m->next); |
|
|
512 m->next = mon->next; |
|
|
513 } |
|
|
514 XUnmapWindow(dpy, mon->barwin); |
|
|
515 XDestroyWindow(dpy, mon->barwin); |
|
|
516 free(mon); |
|
|
517 } |
|
|
518 |
|
|
519 void |
|
|
520 clientmessage(XEvent *e) |
|
|
521 { |
|
|
522 XClientMessageEvent *cme = &e->xclient; |
|
|
523 Client *c = wintoclient(cme->window); |
|
|
524 |
|
|
525 if (!c) |
|
|
526 return; |
|
|
527 if (cme->message_type == netatom[NetWMState]) { |
|
|
528 if (cme->data.l[1] == netatom[NetWMFullscreen] |
|
|
529 || cme->data.l[2] == netatom[NetWMFullscreen]) |
|
|
530 setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ |
|
|
531 || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); |
|
|
532 } else if (cme->message_type == netatom[NetActiveWindow]) { |
|
|
533 if (c != selmon->sel && !c->isurgent) |
|
|
534 seturgent(c, 1); |
|
|
535 } |
|
|
536 } |
|
|
537 |
|
|
538 void |
|
|
539 configure(Client *c) |
|
|
540 { |
|
|
541 XConfigureEvent ce; |
|
|
542 |
|
|
543 ce.type = ConfigureNotify; |
|
|
544 ce.display = dpy; |
|
|
545 ce.event = c->win; |
|
|
546 ce.window = c->win; |
|
|
547 ce.x = c->x; |
|
|
548 ce.y = c->y; |
|
|
549 ce.width = c->w; |
|
|
550 ce.height = c->h; |
|
|
551 ce.border_width = c->bw; |
|
|
552 ce.above = None; |
|
|
553 ce.override_redirect = False; |
|
|
554 XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); |
|
|
555 } |
|
|
556 |
|
|
557 void |
|
|
558 configurenotify(XEvent *e) |
|
|
559 { |
|
|
560 Monitor *m; |
|
|
561 Client *c; |
|
|
562 XConfigureEvent *ev = &e->xconfigure; |
|
|
563 int dirty; |
|
|
564 |
|
|
565 /* TODO: updategeom handling sucks, needs to be simplified */ |
|
|
566 if (ev->window == root) { |
|
|
567 dirty = (sw != ev->width || sh != ev->height); |
|
|
568 sw = ev->width; |
|
|
569 sh = ev->height; |
|
|
570 if (updategeom() || dirty) { |
|
|
571 drw_resize(drw, sw, bh); |
|
|
572 updatebars(); |
|
|
573 for (m = mons; m; m = m->next) { |
|
|
574 for (c = m->clients; c; c = c->next) |
|
|
575 if (c->isfullscreen) |
|
|
576 resizeclient(c, m->mx, m->my, m->mw, m->mh); |
|
|
577 XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh); |
|
|
578 } |
|
|
579 focus(NULL); |
|
|
580 arrange(NULL); |
|
|
581 } |
|
|
582 } |
|
|
583 } |
|
|
584 |
|
|
585 void |
|
|
586 configurerequest(XEvent *e) |
|
|
587 { |
|
|
588 Client *c; |
|
|
589 Monitor *m; |
|
|
590 XConfigureRequestEvent *ev = &e->xconfigurerequest; |
|
|
591 XWindowChanges wc; |
|
|
592 |
|
|
593 if ((c = wintoclient(ev->window))) { |
|
|
594 if (ev->value_mask & CWBorderWidth) |
|
|
595 c->bw = ev->border_width; |
|
|
596 else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { |
|
|
597 m = c->mon; |
|
|
598 if (ev->value_mask & CWX) { |
|
|
599 c->oldx = c->x; |
|
|
600 c->x = m->mx + ev->x; |
|
|
601 } |
|
|
602 if (ev->value_mask & CWY) { |
|
|
603 c->oldy = c->y; |
|
|
604 c->y = m->my + ev->y; |
|
|
605 } |
|
|
606 if (ev->value_mask & CWWidth) { |
|
|
607 c->oldw = c->w; |
|
|
608 c->w = ev->width; |
|
|
609 } |
|
|
610 if (ev->value_mask & CWHeight) { |
|
|
611 c->oldh = c->h; |
|
|
612 c->h = ev->height; |
|
|
613 } |
|
|
614 if ((c->x + c->w) > m->mx + m->mw && c->isfloating) |
|
|
615 c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ |
|
|
616 if ((c->y + c->h) > m->my + m->mh && c->isfloating) |
|
|
617 c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ |
|
|
618 if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) |
|
|
619 configure(c); |
|
|
620 if (ISVISIBLE(c)) |
|
|
621 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); |
|
|
622 } else |
|
|
623 configure(c); |
|
|
624 } else { |
|
|
625 wc.x = ev->x; |
|
|
626 wc.y = ev->y; |
|
|
627 wc.width = ev->width; |
|
|
628 wc.height = ev->height; |
|
|
629 wc.border_width = ev->border_width; |
|
|
630 wc.sibling = ev->above; |
|
|
631 wc.stack_mode = ev->detail; |
|
|
632 XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); |
|
|
633 } |
|
|
634 XSync(dpy, False); |
|
|
635 } |
|
|
636 |
|
|
637 Monitor * |
|
|
638 createmon(void) |
|
|
639 { |
|
|
640 Monitor *m; |
|
|
641 |
|
|
642 m = ecalloc(1, sizeof(Monitor)); |
|
|
643 m->tagset[0] = m->tagset[1] = 1; |
|
|
644 m->mfact = mfact; |
|
|
645 m->nmaster = nmaster; |
|
|
646 m->showbar = showbar; |
|
|
647 m->topbar = topbar; |
|
2
|
648 m->gappih = gappih; |
|
|
649 m->gappiv = gappiv; |
|
|
650 m->gappoh = gappoh; |
|
|
651 m->gappov = gappov; |
|
0
|
652 m->lt[0] = &layouts[0]; |
|
|
653 m->lt[1] = &layouts[1 % LENGTH(layouts)]; |
|
|
654 strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); |
|
|
655 return m; |
|
|
656 } |
|
|
657 |
|
|
658 void |
|
|
659 destroynotify(XEvent *e) |
|
|
660 { |
|
|
661 Client *c; |
|
|
662 XDestroyWindowEvent *ev = &e->xdestroywindow; |
|
|
663 |
|
|
664 if ((c = wintoclient(ev->window))) |
|
|
665 unmanage(c, 1); |
|
|
666 } |
|
|
667 |
|
|
668 void |
|
|
669 detach(Client *c) |
|
|
670 { |
|
|
671 Client **tc; |
|
|
672 |
|
|
673 for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); |
|
|
674 *tc = c->next; |
|
|
675 } |
|
|
676 |
|
|
677 void |
|
|
678 detachstack(Client *c) |
|
|
679 { |
|
|
680 Client **tc, *t; |
|
|
681 |
|
|
682 for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); |
|
|
683 *tc = c->snext; |
|
|
684 |
|
|
685 if (c == c->mon->sel) { |
|
|
686 for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); |
|
|
687 c->mon->sel = t; |
|
|
688 } |
|
|
689 } |
|
|
690 |
|
|
691 Monitor * |
|
|
692 dirtomon(int dir) |
|
|
693 { |
|
|
694 Monitor *m = NULL; |
|
|
695 |
|
|
696 if (dir > 0) { |
|
|
697 if (!(m = selmon->next)) |
|
|
698 m = mons; |
|
|
699 } else if (selmon == mons) |
|
|
700 for (m = mons; m->next; m = m->next); |
|
|
701 else |
|
|
702 for (m = mons; m->next != selmon; m = m->next); |
|
|
703 return m; |
|
|
704 } |
|
|
705 |
|
|
706 void |
|
|
707 drawbar(Monitor *m) |
|
|
708 { |
|
|
709 int x, w, tw = 0; |
|
|
710 int boxs = drw->fonts->h / 9; |
|
|
711 int boxw = drw->fonts->h / 6 + 2; |
|
|
712 unsigned int i, occ = 0, urg = 0; |
|
|
713 Client *c; |
|
|
714 |
|
|
715 if (!m->showbar) |
|
|
716 return; |
|
|
717 |
|
|
718 /* draw status first so it can be overdrawn by tags later */ |
|
|
719 if (m == selmon) { /* status is only drawn on selected monitor */ |
|
|
720 drw_setscheme(drw, scheme[SchemeNorm]); |
|
|
721 tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */ |
|
|
722 drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0); |
|
|
723 } |
|
|
724 |
|
|
725 for (c = m->clients; c; c = c->next) { |
|
|
726 occ |= c->tags; |
|
|
727 if (c->isurgent) |
|
|
728 urg |= c->tags; |
|
|
729 } |
|
|
730 x = 0; |
|
|
731 for (i = 0; i < LENGTH(tags); i++) { |
|
|
732 w = TEXTW(tags[i]); |
|
|
733 drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); |
|
|
734 drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); |
|
|
735 if (occ & 1 << i) |
|
|
736 drw_rect(drw, x + boxs, boxs, boxw, boxw, |
|
|
737 m == selmon && selmon->sel && selmon->sel->tags & 1 << i, |
|
|
738 urg & 1 << i); |
|
|
739 x += w; |
|
|
740 } |
|
|
741 w = TEXTW(m->ltsymbol); |
|
|
742 drw_setscheme(drw, scheme[SchemeNorm]); |
|
|
743 x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); |
|
|
744 |
|
|
745 if ((w = m->ww - tw - x) > bh) { |
|
|
746 if (m->sel) { |
|
|
747 drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); |
|
|
748 drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); |
|
|
749 if (m->sel->isfloating) |
|
|
750 drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); |
|
|
751 } else { |
|
|
752 drw_setscheme(drw, scheme[SchemeNorm]); |
|
|
753 drw_rect(drw, x, 0, w, bh, 1, 1); |
|
|
754 } |
|
|
755 } |
|
|
756 drw_map(drw, m->barwin, 0, 0, m->ww, bh); |
|
|
757 } |
|
|
758 |
|
|
759 void |
|
|
760 drawbars(void) |
|
|
761 { |
|
|
762 Monitor *m; |
|
|
763 |
|
|
764 for (m = mons; m; m = m->next) |
|
|
765 drawbar(m); |
|
|
766 } |
|
|
767 |
|
|
768 void |
|
|
769 enternotify(XEvent *e) |
|
|
770 { |
|
|
771 Client *c; |
|
|
772 Monitor *m; |
|
|
773 XCrossingEvent *ev = &e->xcrossing; |
|
|
774 |
|
|
775 if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) |
|
|
776 return; |
|
|
777 c = wintoclient(ev->window); |
|
|
778 m = c ? c->mon : wintomon(ev->window); |
|
|
779 if (m != selmon) { |
|
|
780 unfocus(selmon->sel, 1); |
|
|
781 selmon = m; |
|
|
782 } else if (!c || c == selmon->sel) |
|
|
783 return; |
|
|
784 focus(c); |
|
|
785 } |
|
|
786 |
|
|
787 void |
|
|
788 expose(XEvent *e) |
|
|
789 { |
|
|
790 Monitor *m; |
|
|
791 XExposeEvent *ev = &e->xexpose; |
|
|
792 |
|
|
793 if (ev->count == 0 && (m = wintomon(ev->window))) |
|
|
794 drawbar(m); |
|
|
795 } |
|
|
796 |
|
|
797 void |
|
|
798 focus(Client *c) |
|
|
799 { |
|
|
800 if (!c || !ISVISIBLE(c)) |
|
|
801 for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); |
|
|
802 if (selmon->sel && selmon->sel != c) |
|
|
803 unfocus(selmon->sel, 0); |
|
|
804 if (c) { |
|
|
805 if (c->mon != selmon) |
|
|
806 selmon = c->mon; |
|
|
807 if (c->isurgent) |
|
|
808 seturgent(c, 0); |
|
|
809 detachstack(c); |
|
|
810 attachstack(c); |
|
|
811 grabbuttons(c, 1); |
|
|
812 XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); |
|
|
813 setfocus(c); |
|
|
814 } else { |
|
|
815 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); |
|
|
816 XDeleteProperty(dpy, root, netatom[NetActiveWindow]); |
|
|
817 } |
|
|
818 selmon->sel = c; |
|
|
819 drawbars(); |
|
|
820 } |
|
|
821 |
|
|
822 /* there are some broken focus acquiring clients needing extra handling */ |
|
|
823 void |
|
|
824 focusin(XEvent *e) |
|
|
825 { |
|
|
826 XFocusChangeEvent *ev = &e->xfocus; |
|
|
827 |
|
|
828 if (selmon->sel && ev->window != selmon->sel->win) |
|
|
829 setfocus(selmon->sel); |
|
|
830 } |
|
|
831 |
|
|
832 void |
|
|
833 focusmon(const Arg *arg) |
|
|
834 { |
|
|
835 Monitor *m; |
|
|
836 |
|
|
837 if (!mons->next) |
|
|
838 return; |
|
|
839 if ((m = dirtomon(arg->i)) == selmon) |
|
|
840 return; |
|
|
841 unfocus(selmon->sel, 0); |
|
|
842 selmon = m; |
|
|
843 focus(NULL); |
|
|
844 } |
|
|
845 |
|
|
846 void |
|
|
847 focusstack(const Arg *arg) |
|
|
848 { |
|
|
849 Client *c = NULL, *i; |
|
|
850 |
|
|
851 if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) |
|
|
852 return; |
|
|
853 if (arg->i > 0) { |
|
|
854 for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); |
|
|
855 if (!c) |
|
|
856 for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); |
|
|
857 } else { |
|
|
858 for (i = selmon->clients; i != selmon->sel; i = i->next) |
|
|
859 if (ISVISIBLE(i)) |
|
|
860 c = i; |
|
|
861 if (!c) |
|
|
862 for (; i; i = i->next) |
|
|
863 if (ISVISIBLE(i)) |
|
|
864 c = i; |
|
|
865 } |
|
|
866 if (c) { |
|
|
867 focus(c); |
|
|
868 restack(selmon); |
|
|
869 } |
|
|
870 } |
|
|
871 |
|
|
872 Atom |
|
|
873 getatomprop(Client *c, Atom prop) |
|
|
874 { |
|
|
875 int format; |
|
|
876 unsigned long nitems, dl; |
|
|
877 unsigned char *p = NULL; |
|
|
878 Atom da, atom = None; |
|
|
879 |
|
|
880 if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, |
|
|
881 &da, &format, &nitems, &dl, &p) == Success && p) { |
|
|
882 if (nitems > 0 && format == 32) |
|
|
883 atom = *(long *)p; |
|
|
884 XFree(p); |
|
|
885 } |
|
|
886 return atom; |
|
|
887 } |
|
|
888 |
|
|
889 int |
|
|
890 getrootptr(int *x, int *y) |
|
|
891 { |
|
|
892 int di; |
|
|
893 unsigned int dui; |
|
|
894 Window dummy; |
|
|
895 |
|
|
896 return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); |
|
|
897 } |
|
|
898 |
|
|
899 long |
|
|
900 getstate(Window w) |
|
|
901 { |
|
|
902 int format; |
|
|
903 long result = -1; |
|
|
904 unsigned char *p = NULL; |
|
|
905 unsigned long n, extra; |
|
|
906 Atom real; |
|
|
907 |
|
|
908 if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], |
|
|
909 &real, &format, &n, &extra, &p) != Success) |
|
|
910 return -1; |
|
|
911 if (n != 0 && format == 32) |
|
|
912 result = *(long *)p; |
|
|
913 XFree(p); |
|
|
914 return result; |
|
|
915 } |
|
|
916 |
|
|
917 int |
|
|
918 gettextprop(Window w, Atom atom, char *text, unsigned int size) |
|
|
919 { |
|
|
920 char **list = NULL; |
|
|
921 int n; |
|
|
922 XTextProperty name; |
|
|
923 |
|
|
924 if (!text || size == 0) |
|
|
925 return 0; |
|
|
926 text[0] = '\0'; |
|
|
927 if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) |
|
|
928 return 0; |
|
|
929 if (name.encoding == XA_STRING) { |
|
|
930 strncpy(text, (char *)name.value, size - 1); |
|
|
931 } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { |
|
|
932 strncpy(text, *list, size - 1); |
|
|
933 XFreeStringList(list); |
|
|
934 } |
|
|
935 text[size - 1] = '\0'; |
|
|
936 XFree(name.value); |
|
|
937 return 1; |
|
|
938 } |
|
|
939 |
|
|
940 void |
|
|
941 grabbuttons(Client *c, int focused) |
|
|
942 { |
|
|
943 updatenumlockmask(); |
|
|
944 { |
|
|
945 unsigned int i, j; |
|
|
946 unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; |
|
|
947 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); |
|
|
948 if (!focused) |
|
|
949 XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, |
|
|
950 BUTTONMASK, GrabModeSync, GrabModeSync, None, None); |
|
|
951 for (i = 0; i < LENGTH(buttons); i++) |
|
|
952 if (buttons[i].click == ClkClientWin) |
|
|
953 for (j = 0; j < LENGTH(modifiers); j++) |
|
|
954 XGrabButton(dpy, buttons[i].button, |
|
|
955 buttons[i].mask | modifiers[j], |
|
|
956 c->win, False, BUTTONMASK, |
|
|
957 GrabModeAsync, GrabModeSync, None, None); |
|
|
958 } |
|
|
959 } |
|
|
960 |
|
|
961 void |
|
|
962 grabkeys(void) |
|
|
963 { |
|
|
964 updatenumlockmask(); |
|
|
965 { |
|
|
966 unsigned int i, j, k; |
|
|
967 unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; |
|
|
968 int start, end, skip; |
|
|
969 KeySym *syms; |
|
|
970 |
|
|
971 XUngrabKey(dpy, AnyKey, AnyModifier, root); |
|
|
972 XDisplayKeycodes(dpy, &start, &end); |
|
|
973 syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip); |
|
|
974 if (!syms) |
|
|
975 return; |
|
|
976 for (k = start; k <= end; k++) |
|
|
977 for (i = 0; i < LENGTH(keys); i++) |
|
|
978 /* skip modifier codes, we do that ourselves */ |
|
|
979 if (keys[i].keysym == syms[(k - start) * skip]) |
|
|
980 for (j = 0; j < LENGTH(modifiers); j++) |
|
|
981 XGrabKey(dpy, k, |
|
|
982 keys[i].mod | modifiers[j], |
|
|
983 root, True, |
|
|
984 GrabModeAsync, GrabModeAsync); |
|
|
985 XFree(syms); |
|
|
986 } |
|
|
987 } |
|
|
988 |
|
|
989 void |
|
|
990 incnmaster(const Arg *arg) |
|
|
991 { |
|
|
992 selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); |
|
|
993 arrange(selmon); |
|
|
994 } |
|
|
995 |
|
|
996 #ifdef XINERAMA |
|
|
997 static int |
|
|
998 isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) |
|
|
999 { |
|
|
1000 while (n--) |
|
|
1001 if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org |
|
|
1002 && unique[n].width == info->width && unique[n].height == info->height) |
|
|
1003 return 0; |
|
|
1004 return 1; |
|
|
1005 } |
|
|
1006 #endif /* XINERAMA */ |
|
|
1007 |
|
|
1008 void |
|
|
1009 keypress(XEvent *e) |
|
|
1010 { |
|
|
1011 unsigned int i; |
|
|
1012 KeySym keysym; |
|
|
1013 XKeyEvent *ev; |
|
|
1014 |
|
|
1015 ev = &e->xkey; |
|
|
1016 keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); |
|
|
1017 for (i = 0; i < LENGTH(keys); i++) |
|
|
1018 if (keysym == keys[i].keysym |
|
|
1019 && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) |
|
|
1020 && keys[i].func) |
|
|
1021 keys[i].func(&(keys[i].arg)); |
|
|
1022 } |
|
|
1023 |
|
|
1024 void |
|
|
1025 killclient(const Arg *arg) |
|
|
1026 { |
|
|
1027 if (!selmon->sel) |
|
|
1028 return; |
|
|
1029 if (!sendevent(selmon->sel, wmatom[WMDelete])) { |
|
|
1030 XGrabServer(dpy); |
|
|
1031 XSetErrorHandler(xerrordummy); |
|
|
1032 XSetCloseDownMode(dpy, DestroyAll); |
|
|
1033 XKillClient(dpy, selmon->sel->win); |
|
|
1034 XSync(dpy, False); |
|
|
1035 XSetErrorHandler(xerror); |
|
|
1036 XUngrabServer(dpy); |
|
|
1037 } |
|
|
1038 } |
|
|
1039 |
|
|
1040 void |
|
|
1041 manage(Window w, XWindowAttributes *wa) |
|
|
1042 { |
|
|
1043 Client *c, *t = NULL; |
|
|
1044 Window trans = None; |
|
|
1045 XWindowChanges wc; |
|
|
1046 |
|
|
1047 c = ecalloc(1, sizeof(Client)); |
|
|
1048 c->win = w; |
|
|
1049 /* geometry */ |
|
|
1050 c->x = c->oldx = wa->x; |
|
|
1051 c->y = c->oldy = wa->y; |
|
|
1052 c->w = c->oldw = wa->width; |
|
|
1053 c->h = c->oldh = wa->height; |
|
|
1054 c->oldbw = wa->border_width; |
|
2
|
1055 c->cfact = 1.0; |
|
0
|
1056 |
|
|
1057 updatetitle(c); |
|
|
1058 if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { |
|
|
1059 c->mon = t->mon; |
|
|
1060 c->tags = t->tags; |
|
|
1061 } else { |
|
|
1062 c->mon = selmon; |
|
|
1063 applyrules(c); |
|
|
1064 } |
|
|
1065 |
|
|
1066 if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) |
|
|
1067 c->x = c->mon->wx + c->mon->ww - WIDTH(c); |
|
|
1068 if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) |
|
|
1069 c->y = c->mon->wy + c->mon->wh - HEIGHT(c); |
|
|
1070 c->x = MAX(c->x, c->mon->wx); |
|
|
1071 c->y = MAX(c->y, c->mon->wy); |
|
|
1072 c->bw = borderpx; |
|
|
1073 |
|
|
1074 wc.border_width = c->bw; |
|
|
1075 XConfigureWindow(dpy, w, CWBorderWidth, &wc); |
|
|
1076 XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); |
|
|
1077 configure(c); /* propagates border_width, if size doesn't change */ |
|
|
1078 updatewindowtype(c); |
|
|
1079 updatesizehints(c); |
|
|
1080 updatewmhints(c); |
|
|
1081 XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); |
|
|
1082 grabbuttons(c, 0); |
|
|
1083 if (!c->isfloating) |
|
|
1084 c->isfloating = c->oldstate = trans != None || c->isfixed; |
|
|
1085 if (c->isfloating) |
|
|
1086 XRaiseWindow(dpy, c->win); |
|
|
1087 attach(c); |
|
|
1088 attachstack(c); |
|
|
1089 XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, |
|
|
1090 (unsigned char *) &(c->win), 1); |
|
|
1091 XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ |
|
|
1092 setclientstate(c, NormalState); |
|
|
1093 if (c->mon == selmon) |
|
|
1094 unfocus(selmon->sel, 0); |
|
|
1095 c->mon->sel = c; |
|
|
1096 arrange(c->mon); |
|
|
1097 XMapWindow(dpy, c->win); |
|
|
1098 focus(NULL); |
|
|
1099 } |
|
|
1100 |
|
|
1101 void |
|
|
1102 mappingnotify(XEvent *e) |
|
|
1103 { |
|
|
1104 XMappingEvent *ev = &e->xmapping; |
|
|
1105 |
|
|
1106 XRefreshKeyboardMapping(ev); |
|
|
1107 if (ev->request == MappingKeyboard) |
|
|
1108 grabkeys(); |
|
|
1109 } |
|
|
1110 |
|
|
1111 void |
|
|
1112 maprequest(XEvent *e) |
|
|
1113 { |
|
|
1114 static XWindowAttributes wa; |
|
|
1115 XMapRequestEvent *ev = &e->xmaprequest; |
|
|
1116 |
|
|
1117 if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) |
|
|
1118 return; |
|
|
1119 if (!wintoclient(ev->window)) |
|
|
1120 manage(ev->window, &wa); |
|
|
1121 } |
|
|
1122 |
|
|
1123 void |
|
|
1124 monocle(Monitor *m) |
|
|
1125 { |
|
|
1126 unsigned int n = 0; |
|
|
1127 Client *c; |
|
|
1128 |
|
|
1129 for (c = m->clients; c; c = c->next) |
|
|
1130 if (ISVISIBLE(c)) |
|
|
1131 n++; |
|
|
1132 if (n > 0) /* override layout symbol */ |
|
|
1133 snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); |
|
|
1134 for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) |
|
|
1135 resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); |
|
|
1136 } |
|
|
1137 |
|
|
1138 void |
|
|
1139 motionnotify(XEvent *e) |
|
|
1140 { |
|
|
1141 static Monitor *mon = NULL; |
|
|
1142 Monitor *m; |
|
|
1143 XMotionEvent *ev = &e->xmotion; |
|
|
1144 |
|
|
1145 if (ev->window != root) |
|
|
1146 return; |
|
|
1147 if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { |
|
|
1148 unfocus(selmon->sel, 1); |
|
|
1149 selmon = m; |
|
|
1150 focus(NULL); |
|
|
1151 } |
|
|
1152 mon = m; |
|
|
1153 } |
|
|
1154 |
|
|
1155 void |
|
|
1156 movemouse(const Arg *arg) |
|
|
1157 { |
|
|
1158 int x, y, ocx, ocy, nx, ny; |
|
|
1159 Client *c; |
|
|
1160 Monitor *m; |
|
|
1161 XEvent ev; |
|
|
1162 Time lasttime = 0; |
|
|
1163 |
|
|
1164 if (!(c = selmon->sel)) |
|
|
1165 return; |
|
|
1166 if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ |
|
|
1167 return; |
|
|
1168 restack(selmon); |
|
|
1169 ocx = c->x; |
|
|
1170 ocy = c->y; |
|
|
1171 if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, |
|
|
1172 None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) |
|
|
1173 return; |
|
|
1174 if (!getrootptr(&x, &y)) |
|
|
1175 return; |
|
|
1176 do { |
|
|
1177 XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); |
|
|
1178 switch(ev.type) { |
|
|
1179 case ConfigureRequest: |
|
|
1180 case Expose: |
|
|
1181 case MapRequest: |
|
|
1182 handler[ev.type](&ev); |
|
|
1183 break; |
|
|
1184 case MotionNotify: |
|
|
1185 if ((ev.xmotion.time - lasttime) <= (1000 / refreshrate)) |
|
|
1186 continue; |
|
|
1187 lasttime = ev.xmotion.time; |
|
|
1188 |
|
|
1189 nx = ocx + (ev.xmotion.x - x); |
|
|
1190 ny = ocy + (ev.xmotion.y - y); |
|
|
1191 if (abs(selmon->wx - nx) < snap) |
|
|
1192 nx = selmon->wx; |
|
|
1193 else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) |
|
|
1194 nx = selmon->wx + selmon->ww - WIDTH(c); |
|
|
1195 if (abs(selmon->wy - ny) < snap) |
|
|
1196 ny = selmon->wy; |
|
|
1197 else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) |
|
|
1198 ny = selmon->wy + selmon->wh - HEIGHT(c); |
|
|
1199 if (!c->isfloating && selmon->lt[selmon->sellt]->arrange |
|
|
1200 && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) |
|
|
1201 togglefloating(NULL); |
|
|
1202 if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) |
|
|
1203 resize(c, nx, ny, c->w, c->h, 1); |
|
|
1204 break; |
|
|
1205 } |
|
|
1206 } while (ev.type != ButtonRelease); |
|
|
1207 XUngrabPointer(dpy, CurrentTime); |
|
|
1208 if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { |
|
|
1209 sendmon(c, m); |
|
|
1210 selmon = m; |
|
|
1211 focus(NULL); |
|
|
1212 } |
|
|
1213 } |
|
|
1214 |
|
|
1215 Client * |
|
|
1216 nexttiled(Client *c) |
|
|
1217 { |
|
|
1218 for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); |
|
|
1219 return c; |
|
|
1220 } |
|
|
1221 |
|
|
1222 void |
|
|
1223 pop(Client *c) |
|
|
1224 { |
|
|
1225 detach(c); |
|
|
1226 attach(c); |
|
|
1227 focus(c); |
|
|
1228 arrange(c->mon); |
|
|
1229 } |
|
|
1230 |
|
|
1231 void |
|
|
1232 propertynotify(XEvent *e) |
|
|
1233 { |
|
|
1234 Client *c; |
|
|
1235 Window trans; |
|
|
1236 XPropertyEvent *ev = &e->xproperty; |
|
|
1237 |
|
|
1238 if ((ev->window == root) && (ev->atom == XA_WM_NAME)) |
|
|
1239 updatestatus(); |
|
|
1240 else if (ev->state == PropertyDelete) |
|
|
1241 return; /* ignore */ |
|
|
1242 else if ((c = wintoclient(ev->window))) { |
|
|
1243 switch(ev->atom) { |
|
|
1244 default: break; |
|
|
1245 case XA_WM_TRANSIENT_FOR: |
|
|
1246 if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && |
|
|
1247 (c->isfloating = (wintoclient(trans)) != NULL)) |
|
|
1248 arrange(c->mon); |
|
|
1249 break; |
|
|
1250 case XA_WM_NORMAL_HINTS: |
|
|
1251 c->hintsvalid = 0; |
|
|
1252 break; |
|
|
1253 case XA_WM_HINTS: |
|
|
1254 updatewmhints(c); |
|
|
1255 drawbars(); |
|
|
1256 break; |
|
|
1257 } |
|
|
1258 if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { |
|
|
1259 updatetitle(c); |
|
|
1260 if (c == c->mon->sel) |
|
|
1261 drawbar(c->mon); |
|
|
1262 } |
|
|
1263 if (ev->atom == netatom[NetWMWindowType]) |
|
|
1264 updatewindowtype(c); |
|
|
1265 } |
|
|
1266 } |
|
|
1267 |
|
|
1268 void |
|
|
1269 quit(const Arg *arg) |
|
|
1270 { |
|
|
1271 running = 0; |
|
|
1272 } |
|
|
1273 |
|
|
1274 Monitor * |
|
|
1275 recttomon(int x, int y, int w, int h) |
|
|
1276 { |
|
|
1277 Monitor *m, *r = selmon; |
|
|
1278 int a, area = 0; |
|
|
1279 |
|
|
1280 for (m = mons; m; m = m->next) |
|
|
1281 if ((a = INTERSECT(x, y, w, h, m)) > area) { |
|
|
1282 area = a; |
|
|
1283 r = m; |
|
|
1284 } |
|
|
1285 return r; |
|
|
1286 } |
|
|
1287 |
|
|
1288 void |
|
|
1289 resize(Client *c, int x, int y, int w, int h, int interact) |
|
|
1290 { |
|
|
1291 if (applysizehints(c, &x, &y, &w, &h, interact)) |
|
|
1292 resizeclient(c, x, y, w, h); |
|
|
1293 } |
|
|
1294 |
|
|
1295 void |
|
|
1296 resizeclient(Client *c, int x, int y, int w, int h) |
|
|
1297 { |
|
|
1298 XWindowChanges wc; |
|
|
1299 |
|
|
1300 c->oldx = c->x; c->x = wc.x = x; |
|
|
1301 c->oldy = c->y; c->y = wc.y = y; |
|
|
1302 c->oldw = c->w; c->w = wc.width = w; |
|
|
1303 c->oldh = c->h; c->h = wc.height = h; |
|
|
1304 wc.border_width = c->bw; |
|
|
1305 XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); |
|
|
1306 configure(c); |
|
|
1307 XSync(dpy, False); |
|
|
1308 } |
|
|
1309 |
|
|
1310 void |
|
|
1311 resizemouse(const Arg *arg) |
|
|
1312 { |
|
|
1313 int ocx, ocy, nw, nh; |
|
|
1314 Client *c; |
|
|
1315 Monitor *m; |
|
|
1316 XEvent ev; |
|
|
1317 Time lasttime = 0; |
|
|
1318 |
|
|
1319 if (!(c = selmon->sel)) |
|
|
1320 return; |
|
|
1321 if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ |
|
|
1322 return; |
|
|
1323 restack(selmon); |
|
|
1324 ocx = c->x; |
|
|
1325 ocy = c->y; |
|
|
1326 if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, |
|
|
1327 None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) |
|
|
1328 return; |
|
|
1329 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); |
|
|
1330 do { |
|
|
1331 XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); |
|
|
1332 switch(ev.type) { |
|
|
1333 case ConfigureRequest: |
|
|
1334 case Expose: |
|
|
1335 case MapRequest: |
|
|
1336 handler[ev.type](&ev); |
|
|
1337 break; |
|
|
1338 case MotionNotify: |
|
|
1339 if ((ev.xmotion.time - lasttime) <= (1000 / refreshrate)) |
|
|
1340 continue; |
|
|
1341 lasttime = ev.xmotion.time; |
|
|
1342 |
|
|
1343 nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); |
|
|
1344 nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); |
|
|
1345 if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww |
|
|
1346 && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) |
|
|
1347 { |
|
|
1348 if (!c->isfloating && selmon->lt[selmon->sellt]->arrange |
|
|
1349 && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) |
|
|
1350 togglefloating(NULL); |
|
|
1351 } |
|
|
1352 if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) |
|
|
1353 resize(c, c->x, c->y, nw, nh, 1); |
|
|
1354 break; |
|
|
1355 } |
|
|
1356 } while (ev.type != ButtonRelease); |
|
|
1357 XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); |
|
|
1358 XUngrabPointer(dpy, CurrentTime); |
|
|
1359 while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
|
|
1360 if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { |
|
|
1361 sendmon(c, m); |
|
|
1362 selmon = m; |
|
|
1363 focus(NULL); |
|
|
1364 } |
|
|
1365 } |
|
|
1366 |
|
|
1367 void |
|
|
1368 restack(Monitor *m) |
|
|
1369 { |
|
|
1370 Client *c; |
|
|
1371 XEvent ev; |
|
|
1372 XWindowChanges wc; |
|
|
1373 |
|
|
1374 drawbar(m); |
|
|
1375 if (!m->sel) |
|
|
1376 return; |
|
|
1377 if (m->sel->isfloating || !m->lt[m->sellt]->arrange) |
|
|
1378 XRaiseWindow(dpy, m->sel->win); |
|
|
1379 if (m->lt[m->sellt]->arrange) { |
|
|
1380 wc.stack_mode = Below; |
|
|
1381 wc.sibling = m->barwin; |
|
|
1382 for (c = m->stack; c; c = c->snext) |
|
|
1383 if (!c->isfloating && ISVISIBLE(c)) { |
|
|
1384 XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); |
|
|
1385 wc.sibling = c->win; |
|
|
1386 } |
|
|
1387 } |
|
|
1388 XSync(dpy, False); |
|
|
1389 while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
|
|
1390 } |
|
|
1391 |
|
|
1392 void |
|
|
1393 run(void) |
|
|
1394 { |
|
|
1395 XEvent ev; |
|
|
1396 /* main event loop */ |
|
|
1397 XSync(dpy, False); |
|
|
1398 while (running && !XNextEvent(dpy, &ev)) |
|
|
1399 if (handler[ev.type]) |
|
|
1400 handler[ev.type](&ev); /* call handler */ |
|
|
1401 } |
|
|
1402 |
|
|
1403 void |
|
|
1404 scan(void) |
|
|
1405 { |
|
|
1406 unsigned int i, num; |
|
|
1407 Window d1, d2, *wins = NULL; |
|
|
1408 XWindowAttributes wa; |
|
|
1409 |
|
|
1410 if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { |
|
|
1411 for (i = 0; i < num; i++) { |
|
|
1412 if (!XGetWindowAttributes(dpy, wins[i], &wa) |
|
|
1413 || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) |
|
|
1414 continue; |
|
|
1415 if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) |
|
|
1416 manage(wins[i], &wa); |
|
|
1417 } |
|
|
1418 for (i = 0; i < num; i++) { /* now the transients */ |
|
|
1419 if (!XGetWindowAttributes(dpy, wins[i], &wa)) |
|
|
1420 continue; |
|
|
1421 if (XGetTransientForHint(dpy, wins[i], &d1) |
|
|
1422 && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) |
|
|
1423 manage(wins[i], &wa); |
|
|
1424 } |
|
|
1425 if (wins) |
|
|
1426 XFree(wins); |
|
|
1427 } |
|
|
1428 } |
|
|
1429 |
|
|
1430 void |
|
|
1431 sendmon(Client *c, Monitor *m) |
|
|
1432 { |
|
|
1433 if (c->mon == m) |
|
|
1434 return; |
|
|
1435 unfocus(c, 1); |
|
|
1436 detach(c); |
|
|
1437 detachstack(c); |
|
|
1438 c->mon = m; |
|
|
1439 c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ |
|
|
1440 attach(c); |
|
|
1441 attachstack(c); |
|
|
1442 if (c->isfullscreen) |
|
|
1443 resizeclient(c, m->mx, m->my, m->mw, m->mh); |
|
|
1444 focus(NULL); |
|
|
1445 arrange(NULL); |
|
|
1446 } |
|
|
1447 |
|
|
1448 void |
|
|
1449 setclientstate(Client *c, long state) |
|
|
1450 { |
|
|
1451 long data[] = { state, None }; |
|
|
1452 |
|
|
1453 XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, |
|
|
1454 PropModeReplace, (unsigned char *)data, 2); |
|
|
1455 } |
|
|
1456 |
|
|
1457 int |
|
|
1458 sendevent(Client *c, Atom proto) |
|
|
1459 { |
|
|
1460 int n; |
|
|
1461 Atom *protocols; |
|
|
1462 int exists = 0; |
|
|
1463 XEvent ev; |
|
|
1464 |
|
|
1465 if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { |
|
|
1466 while (!exists && n--) |
|
|
1467 exists = protocols[n] == proto; |
|
|
1468 XFree(protocols); |
|
|
1469 } |
|
|
1470 if (exists) { |
|
|
1471 ev.type = ClientMessage; |
|
|
1472 ev.xclient.window = c->win; |
|
|
1473 ev.xclient.message_type = wmatom[WMProtocols]; |
|
|
1474 ev.xclient.format = 32; |
|
|
1475 ev.xclient.data.l[0] = proto; |
|
|
1476 ev.xclient.data.l[1] = CurrentTime; |
|
|
1477 XSendEvent(dpy, c->win, False, NoEventMask, &ev); |
|
|
1478 } |
|
|
1479 return exists; |
|
|
1480 } |
|
|
1481 |
|
|
1482 void |
|
|
1483 setfocus(Client *c) |
|
|
1484 { |
|
|
1485 if (!c->neverfocus) |
|
|
1486 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); |
|
|
1487 XChangeProperty(dpy, root, netatom[NetActiveWindow], XA_WINDOW, 32, |
|
|
1488 PropModeReplace, (unsigned char *)&c->win, 1); |
|
|
1489 sendevent(c, wmatom[WMTakeFocus]); |
|
|
1490 } |
|
|
1491 |
|
|
1492 void |
|
|
1493 setfullscreen(Client *c, int fullscreen) |
|
|
1494 { |
|
|
1495 if (fullscreen && !c->isfullscreen) { |
|
|
1496 XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, |
|
|
1497 PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); |
|
|
1498 c->isfullscreen = 1; |
|
|
1499 c->oldstate = c->isfloating; |
|
|
1500 c->oldbw = c->bw; |
|
|
1501 c->bw = 0; |
|
|
1502 c->isfloating = 1; |
|
|
1503 resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); |
|
|
1504 XRaiseWindow(dpy, c->win); |
|
|
1505 } else if (!fullscreen && c->isfullscreen){ |
|
|
1506 XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, |
|
|
1507 PropModeReplace, (unsigned char*)0, 0); |
|
|
1508 c->isfullscreen = 0; |
|
|
1509 c->isfloating = c->oldstate; |
|
|
1510 c->bw = c->oldbw; |
|
|
1511 c->x = c->oldx; |
|
|
1512 c->y = c->oldy; |
|
|
1513 c->w = c->oldw; |
|
|
1514 c->h = c->oldh; |
|
|
1515 resizeclient(c, c->x, c->y, c->w, c->h); |
|
|
1516 arrange(c->mon); |
|
|
1517 } |
|
|
1518 } |
|
|
1519 |
|
|
1520 void |
|
|
1521 setlayout(const Arg *arg) |
|
|
1522 { |
|
|
1523 if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) |
|
|
1524 selmon->sellt ^= 1; |
|
|
1525 if (arg && arg->v) |
|
|
1526 selmon->lt[selmon->sellt] = (Layout *)arg->v; |
|
|
1527 strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); |
|
|
1528 if (selmon->sel) |
|
|
1529 arrange(selmon); |
|
|
1530 else |
|
|
1531 drawbar(selmon); |
|
|
1532 } |
|
|
1533 |
|
2
|
1534 void |
|
|
1535 setcfact(const Arg *arg) { |
|
|
1536 float f; |
|
|
1537 Client *c; |
|
|
1538 |
|
|
1539 c = selmon->sel; |
|
|
1540 |
|
|
1541 if(!arg || !c || !selmon->lt[selmon->sellt]->arrange) |
|
|
1542 return; |
|
|
1543 f = arg->f + c->cfact; |
|
|
1544 if(arg->f == 0.0) |
|
|
1545 f = 1.0; |
|
|
1546 else if(f < 0.25 || f > 4.0) |
|
|
1547 return; |
|
|
1548 c->cfact = f; |
|
|
1549 arrange(selmon); |
|
|
1550 } |
|
|
1551 |
|
0
|
1552 /* arg > 1.0 will set mfact absolutely */ |
|
|
1553 void |
|
|
1554 setmfact(const Arg *arg) |
|
|
1555 { |
|
|
1556 float f; |
|
|
1557 |
|
|
1558 if (!arg || !selmon->lt[selmon->sellt]->arrange) |
|
|
1559 return; |
|
|
1560 f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; |
|
|
1561 if (f < 0.05 || f > 0.95) |
|
|
1562 return; |
|
|
1563 selmon->mfact = f; |
|
|
1564 arrange(selmon); |
|
|
1565 } |
|
|
1566 |
|
|
1567 void |
|
|
1568 setup(void) |
|
|
1569 { |
|
|
1570 int i; |
|
|
1571 XSetWindowAttributes wa; |
|
|
1572 Atom utf8string; |
|
|
1573 struct sigaction sa; |
|
|
1574 |
|
|
1575 /* do not transform children into zombies when they terminate */ |
|
|
1576 sigemptyset(&sa.sa_mask); |
|
|
1577 sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART; |
|
|
1578 sa.sa_handler = SIG_IGN; |
|
|
1579 sigaction(SIGCHLD, &sa, NULL); |
|
|
1580 |
|
|
1581 /* clean up any zombies (inherited from .xinitrc etc) immediately */ |
|
|
1582 while (waitpid(-1, NULL, WNOHANG) > 0); |
|
|
1583 |
|
|
1584 /* init screen */ |
|
|
1585 screen = DefaultScreen(dpy); |
|
|
1586 sw = DisplayWidth(dpy, screen); |
|
|
1587 sh = DisplayHeight(dpy, screen); |
|
|
1588 root = RootWindow(dpy, screen); |
|
|
1589 drw = drw_create(dpy, screen, root, sw, sh); |
|
|
1590 if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) |
|
|
1591 die("no fonts could be loaded."); |
|
|
1592 lrpad = drw->fonts->h; |
|
|
1593 bh = drw->fonts->h + 2; |
|
|
1594 updategeom(); |
|
|
1595 /* init atoms */ |
|
|
1596 utf8string = XInternAtom(dpy, "UTF8_STRING", False); |
|
|
1597 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); |
|
|
1598 wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); |
|
|
1599 wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); |
|
|
1600 wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); |
|
|
1601 netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); |
|
|
1602 netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); |
|
|
1603 netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); |
|
|
1604 netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); |
|
|
1605 netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); |
|
|
1606 netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); |
|
|
1607 netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); |
|
|
1608 netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False); |
|
|
1609 netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); |
|
|
1610 /* init cursors */ |
|
|
1611 cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); |
|
|
1612 cursor[CurResize] = drw_cur_create(drw, XC_sizing); |
|
|
1613 cursor[CurMove] = drw_cur_create(drw, XC_fleur); |
|
|
1614 /* init appearance */ |
|
|
1615 scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); |
|
|
1616 for (i = 0; i < LENGTH(colors); i++) |
|
|
1617 scheme[i] = drw_scm_create(drw, colors[i], 3); |
|
|
1618 /* init bars */ |
|
|
1619 updatebars(); |
|
|
1620 updatestatus(); |
|
|
1621 /* supporting window for NetWMCheck */ |
|
|
1622 wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); |
|
|
1623 XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, |
|
|
1624 PropModeReplace, (unsigned char *) &wmcheckwin, 1); |
|
|
1625 XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, |
|
|
1626 PropModeReplace, (unsigned char *) "dwm", 3); |
|
|
1627 XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, |
|
|
1628 PropModeReplace, (unsigned char *) &wmcheckwin, 1); |
|
|
1629 /* EWMH support per view */ |
|
|
1630 XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, |
|
|
1631 PropModeReplace, (unsigned char *) netatom, NetLast); |
|
|
1632 XDeleteProperty(dpy, root, netatom[NetClientList]); |
|
|
1633 /* select events */ |
|
|
1634 wa.cursor = cursor[CurNormal]->cursor; |
|
|
1635 wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask |
|
|
1636 |ButtonPressMask|PointerMotionMask|EnterWindowMask |
|
|
1637 |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; |
|
|
1638 XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); |
|
|
1639 XSelectInput(dpy, root, wa.event_mask); |
|
|
1640 grabkeys(); |
|
|
1641 focus(NULL); |
|
|
1642 } |
|
|
1643 |
|
|
1644 void |
|
|
1645 seturgent(Client *c, int urg) |
|
|
1646 { |
|
|
1647 XWMHints *wmh; |
|
|
1648 |
|
|
1649 c->isurgent = urg; |
|
|
1650 if (!(wmh = XGetWMHints(dpy, c->win))) |
|
|
1651 return; |
|
|
1652 wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); |
|
|
1653 XSetWMHints(dpy, c->win, wmh); |
|
|
1654 XFree(wmh); |
|
|
1655 } |
|
|
1656 |
|
|
1657 void |
|
|
1658 showhide(Client *c) |
|
|
1659 { |
|
|
1660 if (!c) |
|
|
1661 return; |
|
|
1662 if (ISVISIBLE(c)) { |
|
|
1663 /* show clients top down */ |
|
|
1664 XMoveWindow(dpy, c->win, c->x, c->y); |
|
|
1665 if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) |
|
|
1666 resize(c, c->x, c->y, c->w, c->h, 0); |
|
|
1667 showhide(c->snext); |
|
|
1668 } else { |
|
|
1669 /* hide clients bottom up */ |
|
|
1670 showhide(c->snext); |
|
|
1671 XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); |
|
|
1672 } |
|
|
1673 } |
|
|
1674 |
|
|
1675 void |
|
|
1676 spawn(const Arg *arg) |
|
|
1677 { |
|
|
1678 struct sigaction sa; |
|
|
1679 |
|
|
1680 if (arg->v == dmenucmd) |
|
|
1681 dmenumon[0] = '0' + selmon->num; |
|
|
1682 if (fork() == 0) { |
|
|
1683 if (dpy) |
|
|
1684 close(ConnectionNumber(dpy)); |
|
|
1685 setsid(); |
|
|
1686 |
|
|
1687 sigemptyset(&sa.sa_mask); |
|
|
1688 sa.sa_flags = 0; |
|
|
1689 sa.sa_handler = SIG_DFL; |
|
|
1690 sigaction(SIGCHLD, &sa, NULL); |
|
|
1691 |
|
|
1692 execvp(((char **)arg->v)[0], (char **)arg->v); |
|
|
1693 die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); |
|
|
1694 } |
|
|
1695 } |
|
|
1696 |
|
|
1697 void |
|
|
1698 tag(const Arg *arg) |
|
|
1699 { |
|
|
1700 if (selmon->sel && arg->ui & TAGMASK) { |
|
|
1701 selmon->sel->tags = arg->ui & TAGMASK; |
|
|
1702 focus(NULL); |
|
|
1703 arrange(selmon); |
|
|
1704 } |
|
|
1705 } |
|
|
1706 |
|
|
1707 void |
|
|
1708 tagmon(const Arg *arg) |
|
|
1709 { |
|
|
1710 if (!selmon->sel || !mons->next) |
|
|
1711 return; |
|
|
1712 sendmon(selmon->sel, dirtomon(arg->i)); |
|
|
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 } |