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