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