|
1 /* See LICENSE file for copyright and license details. */ |
|
2 #include "dwm.h" |
|
3 #include <regex.h> |
|
4 #include <stdio.h> |
|
5 #include <stdlib.h> |
|
6 #include <string.h> |
|
7 #include <X11/Xatom.h> |
|
8 #include <X11/Xutil.h> |
|
9 |
|
10 /* static */ |
|
11 |
|
12 typedef struct { |
|
13 const char *symbol; |
|
14 void (*arrange)(void); |
|
15 } Layout; |
|
16 |
|
17 typedef struct { |
|
18 const char *prop; |
|
19 const char *tags; |
|
20 Bool isfloating; |
|
21 } Rule; |
|
22 |
|
23 typedef struct { |
|
24 regex_t *propregex; |
|
25 regex_t *tagregex; |
|
26 } Regs; |
|
27 |
|
28 TAGS |
|
29 RULES |
|
30 |
|
31 static char prop[512]; |
|
32 static unsigned int nrules = 0; |
|
33 static unsigned int nlayouts = 0; |
|
34 static unsigned int ltidx = 0; /* default */ |
|
35 static Regs *regs = NULL; |
|
36 |
|
37 static unsigned int |
|
38 idxoftag(const char *tag) { |
|
39 unsigned int i; |
|
40 |
|
41 for(i = 0; i < ntags; i++) |
|
42 if(tags[i] == tag) |
|
43 return i; |
|
44 return 0; |
|
45 } |
|
46 |
|
47 static void |
|
48 floating(void) { /* default floating layout */ |
|
49 Client *c; |
|
50 |
|
51 for(c = clients; c; c = c->next) |
|
52 if(isvisible(c)) |
|
53 resize(c, c->x, c->y, c->w, c->h, True); |
|
54 } |
|
55 |
|
56 static void |
|
57 setdwmprops(void) { |
|
58 unsigned int i; |
|
59 |
|
60 for(i = 0; i < ntags && i < sizeof prop - 1; i++) |
|
61 prop[i] = seltags[i] ? '1' : '0'; |
|
62 if(i < sizeof prop - 1) |
|
63 prop[i++] = (char)ltidx; |
|
64 prop[i] = '\0'; |
|
65 XChangeProperty(dpy, root, dwmprops, XA_STRING, 8, |
|
66 PropModeReplace, (unsigned char *)prop, i); |
|
67 } |
|
68 |
|
69 LAYOUTS |
|
70 |
|
71 /* extern */ |
|
72 |
|
73 unsigned int blw = 0; |
|
74 |
|
75 void |
|
76 applyrules(Client *c) { |
|
77 unsigned int i, j; |
|
78 regmatch_t tmp; |
|
79 Bool matched = False; |
|
80 XClassHint ch = { 0 }; |
|
81 |
|
82 /* rule matching */ |
|
83 XGetClassHint(dpy, c->win, &ch); |
|
84 snprintf(prop, sizeof prop, "%s:%s:%s", |
|
85 ch.res_class ? ch.res_class : "", |
|
86 ch.res_name ? ch.res_name : "", c->name); |
|
87 for(i = 0; i < nrules; i++) |
|
88 if(regs[i].propregex && !regexec(regs[i].propregex, prop, 1, &tmp, 0)) { |
|
89 c->isfloating = rules[i].isfloating; |
|
90 for(j = 0; regs[i].tagregex && j < ntags; j++) { |
|
91 if(!regexec(regs[i].tagregex, tags[j], 1, &tmp, 0)) { |
|
92 matched = True; |
|
93 c->tags[j] = True; |
|
94 } |
|
95 } |
|
96 } |
|
97 if(ch.res_class) |
|
98 XFree(ch.res_class); |
|
99 if(ch.res_name) |
|
100 XFree(ch.res_name); |
|
101 if(!matched) |
|
102 for(i = 0; i < ntags; i++) |
|
103 c->tags[i] = seltags[i]; |
|
104 } |
|
105 |
|
106 void |
|
107 arrange(void) { |
|
108 Client *c; |
|
109 |
|
110 for(c = clients; c; c = c->next) |
|
111 if(isvisible(c)) |
|
112 unban(c); |
|
113 else |
|
114 ban(c); |
|
115 layouts[ltidx].arrange(); |
|
116 focus(NULL); |
|
117 restack(); |
|
118 } |
|
119 |
|
120 void |
|
121 compileregs(void) { |
|
122 unsigned int i; |
|
123 regex_t *reg; |
|
124 |
|
125 if(regs) |
|
126 return; |
|
127 nrules = sizeof rules / sizeof rules[0]; |
|
128 regs = emallocz(nrules * sizeof(Regs)); |
|
129 for(i = 0; i < nrules; i++) { |
|
130 if(rules[i].prop) { |
|
131 reg = emallocz(sizeof(regex_t)); |
|
132 if(regcomp(reg, rules[i].prop, REG_EXTENDED)) |
|
133 free(reg); |
|
134 else |
|
135 regs[i].propregex = reg; |
|
136 } |
|
137 if(rules[i].tags) { |
|
138 reg = emallocz(sizeof(regex_t)); |
|
139 if(regcomp(reg, rules[i].tags, REG_EXTENDED)) |
|
140 free(reg); |
|
141 else |
|
142 regs[i].tagregex = reg; |
|
143 } |
|
144 } |
|
145 } |
|
146 |
|
147 void |
|
148 focusnext(const char *arg) { |
|
149 Client *c; |
|
150 |
|
151 if(!sel) |
|
152 return; |
|
153 for(c = sel->next; c && !isvisible(c); c = c->next); |
|
154 if(!c) |
|
155 for(c = clients; c && !isvisible(c); c = c->next); |
|
156 if(c) { |
|
157 focus(c); |
|
158 restack(); |
|
159 } |
|
160 } |
|
161 |
|
162 void |
|
163 focusprev(const char *arg) { |
|
164 Client *c; |
|
165 |
|
166 if(!sel) |
|
167 return; |
|
168 for(c = sel->prev; c && !isvisible(c); c = c->prev); |
|
169 if(!c) { |
|
170 for(c = clients; c && c->next; c = c->next); |
|
171 for(; c && !isvisible(c); c = c->prev); |
|
172 } |
|
173 if(c) { |
|
174 focus(c); |
|
175 restack(); |
|
176 } |
|
177 } |
|
178 |
|
179 const char * |
|
180 getsymbol(void) |
|
181 { |
|
182 return layouts[ltidx].symbol; |
|
183 } |
|
184 |
|
185 void |
|
186 initlayouts(void) { |
|
187 unsigned int i, w; |
|
188 |
|
189 nlayouts = sizeof layouts / sizeof layouts[0]; |
|
190 for(blw = i = 0; i < nlayouts; i++) { |
|
191 w = textw(layouts[i].symbol); |
|
192 if(w > blw) |
|
193 blw = w; |
|
194 } |
|
195 } |
|
196 |
|
197 Bool |
|
198 isfloating(void) { |
|
199 return layouts[ltidx].arrange == floating; |
|
200 } |
|
201 |
|
202 Bool |
|
203 isarrange(void (*func)()) |
|
204 { |
|
205 return func == layouts[ltidx].arrange; |
|
206 } |
|
207 |
|
208 Bool |
|
209 isvisible(Client *c) { |
|
210 unsigned int i; |
|
211 |
|
212 for(i = 0; i < ntags; i++) |
|
213 if(c->tags[i] && seltags[i]) |
|
214 return True; |
|
215 return False; |
|
216 } |
|
217 |
|
218 void |
|
219 getdwmprops(void) { |
|
220 unsigned int i; |
|
221 |
|
222 if(gettextprop(root, dwmprops, prop, sizeof prop)) { |
|
223 for(i = 0; i < ntags && i < sizeof prop - 1 && prop[i] != '\0'; i++) |
|
224 seltags[i] = prop[i] == '1'; |
|
225 if(i < sizeof prop - 1 && prop[i] != '\0') { |
|
226 if(prop[i] < nlayouts) |
|
227 ltidx = prop[i]; |
|
228 } |
|
229 } |
|
230 } |
|
231 |
|
232 Client * |
|
233 nexttiled(Client *c) { |
|
234 for(; c && (c->isfloating || !isvisible(c)); c = c->next); |
|
235 return c; |
|
236 } |
|
237 |
|
238 void |
|
239 restack(void) { |
|
240 Client *c; |
|
241 XEvent ev; |
|
242 XWindowChanges wc; |
|
243 |
|
244 drawstatus(); |
|
245 if(!sel) |
|
246 return; |
|
247 if(sel->isfloating || isfloating()) |
|
248 XRaiseWindow(dpy, sel->win); |
|
249 if(!isfloating()) { |
|
250 wc.stack_mode = Below; |
|
251 wc.sibling = barwin; |
|
252 if(!sel->isfloating) { |
|
253 XConfigureWindow(dpy, sel->win, CWSibling | CWStackMode, &wc); |
|
254 wc.sibling = sel->win; |
|
255 } |
|
256 for(c = nexttiled(clients); c; c = nexttiled(c->next)) { |
|
257 if(c == sel) |
|
258 continue; |
|
259 XConfigureWindow(dpy, c->win, CWSibling | CWStackMode, &wc); |
|
260 wc.sibling = c->win; |
|
261 } |
|
262 } |
|
263 XSync(dpy, False); |
|
264 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
|
265 } |
|
266 |
|
267 void |
|
268 setlayout(const char *arg) { |
|
269 int i; |
|
270 |
|
271 if(!arg) { |
|
272 if(++ltidx == nlayouts) |
|
273 ltidx = 0;; |
|
274 } |
|
275 else { |
|
276 i = atoi(arg); |
|
277 if(i < 0 || i >= nlayouts) |
|
278 return; |
|
279 ltidx = i; |
|
280 } |
|
281 if(sel) |
|
282 arrange(); |
|
283 else |
|
284 drawstatus(); |
|
285 setdwmprops(); |
|
286 } |
|
287 |
|
288 void |
|
289 tag(const char *arg) { |
|
290 unsigned int i; |
|
291 |
|
292 if(!sel) |
|
293 return; |
|
294 for(i = 0; i < ntags; i++) |
|
295 sel->tags[i] = arg == NULL; |
|
296 i = idxoftag(arg); |
|
297 if(i >= 0 && i < ntags) |
|
298 sel->tags[i] = True; |
|
299 setprops(sel); |
|
300 arrange(); |
|
301 } |
|
302 |
|
303 void |
|
304 togglebar(const char *arg) { |
|
305 if(bpos == BarOff) |
|
306 bpos = (BARPOS == BarOff) ? BarTop : BARPOS; |
|
307 else |
|
308 bpos = BarOff; |
|
309 updatebarpos(); |
|
310 arrange(); |
|
311 } |
|
312 |
|
313 void |
|
314 togglefloating(const char *arg) { |
|
315 if(!sel || isfloating()) |
|
316 return; |
|
317 sel->isfloating = !sel->isfloating; |
|
318 if(sel->isfloating) { |
|
319 resize(sel, sel->x, sel->y, sel->w, sel->h, True); |
|
320 setprops(sel); |
|
321 } |
|
322 arrange(); |
|
323 } |
|
324 |
|
325 void |
|
326 togglemax(const char *arg) { |
|
327 XEvent ev; |
|
328 |
|
329 if(!sel || (!isfloating() && !sel->isfloating) || sel->isfixed) |
|
330 return; |
|
331 if((sel->ismax = !sel->ismax)) { |
|
332 sel->rx = sel->x; |
|
333 sel->ry = sel->y; |
|
334 sel->rw = sel->w; |
|
335 sel->rh = sel->h; |
|
336 resize(sel, wax, way, waw - 2 * sel->border, wah - 2 * sel->border, True); |
|
337 } |
|
338 else |
|
339 resize(sel, sel->rx, sel->ry, sel->rw, sel->rh, True); |
|
340 drawstatus(); |
|
341 while(XCheckMaskEvent(dpy, EnterWindowMask, &ev)); |
|
342 } |
|
343 |
|
344 void |
|
345 toggletag(const char *arg) { |
|
346 unsigned int i, j; |
|
347 |
|
348 if(!sel) |
|
349 return; |
|
350 i = idxoftag(arg); |
|
351 sel->tags[i] = !sel->tags[i]; |
|
352 for(j = 0; j < ntags && !sel->tags[j]; j++); |
|
353 if(j == ntags) |
|
354 sel->tags[i] = True; |
|
355 setprops(sel); |
|
356 arrange(); |
|
357 } |
|
358 |
|
359 void |
|
360 toggleview(const char *arg) { |
|
361 unsigned int i, j; |
|
362 |
|
363 i = idxoftag(arg); |
|
364 seltags[i] = !seltags[i]; |
|
365 for(j = 0; j < ntags && !seltags[j]; j++); |
|
366 if(j == ntags) |
|
367 seltags[i] = True; /* cannot toggle last view */ |
|
368 setdwmprops(); |
|
369 arrange(); |
|
370 } |
|
371 |
|
372 void |
|
373 view(const char *arg) { |
|
374 unsigned int i; |
|
375 |
|
376 for(i = 0; i < ntags; i++) |
|
377 seltags[i] = arg == NULL; |
|
378 i = idxoftag(arg); |
|
379 if(i >= 0 && i < ntags) |
|
380 seltags[i] = True; |
|
381 setdwmprops(); |
|
382 arrange(); |
|
383 } |