1 /* See LICENSE file for copyright and license details. */ |
|
2 #include "dwm.h" |
|
3 #include <stdlib.h> |
|
4 #include <X11/Xutil.h> |
|
5 |
|
6 /* static */ |
|
7 |
|
8 static void |
|
9 attachstack(Client *c) { |
|
10 c->snext = stack; |
|
11 stack = c; |
|
12 } |
|
13 |
|
14 static void |
|
15 detachstack(Client *c) { |
|
16 Client **tc; |
|
17 |
|
18 for(tc=&stack; *tc && *tc != c; tc=&(*tc)->snext); |
|
19 *tc = c->snext; |
|
20 } |
|
21 |
|
22 static void |
|
23 grabbuttons(Client *c, Bool focused) { |
|
24 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); |
|
25 |
|
26 if(focused) { |
|
27 XGrabButton(dpy, Button1, MODKEY, c->win, False, BUTTONMASK, |
|
28 GrabModeAsync, GrabModeSync, None, None); |
|
29 XGrabButton(dpy, Button1, MODKEY | LockMask, c->win, False, BUTTONMASK, |
|
30 GrabModeAsync, GrabModeSync, None, None); |
|
31 XGrabButton(dpy, Button1, MODKEY | numlockmask, c->win, False, BUTTONMASK, |
|
32 GrabModeAsync, GrabModeSync, None, None); |
|
33 XGrabButton(dpy, Button1, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, |
|
34 GrabModeAsync, GrabModeSync, None, None); |
|
35 |
|
36 XGrabButton(dpy, Button2, MODKEY, c->win, False, BUTTONMASK, |
|
37 GrabModeAsync, GrabModeSync, None, None); |
|
38 XGrabButton(dpy, Button2, MODKEY | LockMask, c->win, False, BUTTONMASK, |
|
39 GrabModeAsync, GrabModeSync, None, None); |
|
40 XGrabButton(dpy, Button2, MODKEY | numlockmask, c->win, False, BUTTONMASK, |
|
41 GrabModeAsync, GrabModeSync, None, None); |
|
42 XGrabButton(dpy, Button2, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, |
|
43 GrabModeAsync, GrabModeSync, None, None); |
|
44 |
|
45 XGrabButton(dpy, Button3, MODKEY, c->win, False, BUTTONMASK, |
|
46 GrabModeAsync, GrabModeSync, None, None); |
|
47 XGrabButton(dpy, Button3, MODKEY | LockMask, c->win, False, BUTTONMASK, |
|
48 GrabModeAsync, GrabModeSync, None, None); |
|
49 XGrabButton(dpy, Button3, MODKEY | numlockmask, c->win, False, BUTTONMASK, |
|
50 GrabModeAsync, GrabModeSync, None, None); |
|
51 XGrabButton(dpy, Button3, MODKEY | numlockmask | LockMask, c->win, False, BUTTONMASK, |
|
52 GrabModeAsync, GrabModeSync, None, None); |
|
53 } |
|
54 else |
|
55 XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, BUTTONMASK, |
|
56 GrabModeAsync, GrabModeSync, None, None); |
|
57 } |
|
58 |
|
59 static Bool |
|
60 isprotodel(Client *c) { |
|
61 int i, n; |
|
62 Atom *protocols; |
|
63 Bool ret = False; |
|
64 |
|
65 if(XGetWMProtocols(dpy, c->win, &protocols, &n)) { |
|
66 for(i = 0; !ret && i < n; i++) |
|
67 if(protocols[i] == wmatom[WMDelete]) |
|
68 ret = True; |
|
69 XFree(protocols); |
|
70 } |
|
71 return ret; |
|
72 } |
|
73 |
|
74 static void |
|
75 setclientstate(Client *c, long state) { |
|
76 long data[] = {state, None}; |
|
77 |
|
78 XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, |
|
79 PropModeReplace, (unsigned char *)data, 2); |
|
80 } |
|
81 |
|
82 static int |
|
83 xerrordummy(Display *dsply, XErrorEvent *ee) { |
|
84 return 0; |
|
85 } |
|
86 |
|
87 /* extern */ |
|
88 |
|
89 void |
|
90 attach(Client *c) { |
|
91 if(clients) |
|
92 clients->prev = c; |
|
93 c->next = clients; |
|
94 clients = c; |
|
95 } |
|
96 |
|
97 void |
|
98 ban(Client *c) { |
|
99 if(c->isbanned) |
|
100 return; |
|
101 XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y); |
|
102 c->isbanned = True; |
|
103 } |
|
104 |
|
105 void |
|
106 configure(Client *c) { |
|
107 XConfigureEvent ce; |
|
108 |
|
109 ce.type = ConfigureNotify; |
|
110 ce.display = dpy; |
|
111 ce.event = c->win; |
|
112 ce.window = c->win; |
|
113 ce.x = c->x; |
|
114 ce.y = c->y; |
|
115 ce.width = c->w; |
|
116 ce.height = c->h; |
|
117 ce.border_width = c->border; |
|
118 ce.above = None; |
|
119 ce.override_redirect = False; |
|
120 XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); |
|
121 } |
|
122 |
|
123 void |
|
124 detach(Client *c) { |
|
125 if(c->prev) |
|
126 c->prev->next = c->next; |
|
127 if(c->next) |
|
128 c->next->prev = c->prev; |
|
129 if(c == clients) |
|
130 clients = c->next; |
|
131 c->next = c->prev = NULL; |
|
132 } |
|
133 |
|
134 void |
|
135 focus(Client *c) { |
|
136 if((!c && selscreen) || (c && !isvisible(c))) |
|
137 for(c = stack; c && !isvisible(c); c = c->snext); |
|
138 if(sel && sel != c) { |
|
139 grabbuttons(sel, False); |
|
140 XSetWindowBorder(dpy, sel->win, dc.norm[ColBorder]); |
|
141 } |
|
142 if(c) { |
|
143 detachstack(c); |
|
144 attachstack(c); |
|
145 grabbuttons(c, True); |
|
146 } |
|
147 sel = c; |
|
148 drawbar(); |
|
149 if(!selscreen) |
|
150 return; |
|
151 if(c) { |
|
152 XSetWindowBorder(dpy, c->win, dc.sel[ColBorder]); |
|
153 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); |
|
154 } |
|
155 else |
|
156 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); |
|
157 } |
|
158 |
|
159 void |
|
160 killclient(const char *arg) { |
|
161 XEvent ev; |
|
162 |
|
163 if(!sel) |
|
164 return; |
|
165 if(isprotodel(sel)) { |
|
166 ev.type = ClientMessage; |
|
167 ev.xclient.window = sel->win; |
|
168 ev.xclient.message_type = wmatom[WMProtocols]; |
|
169 ev.xclient.format = 32; |
|
170 ev.xclient.data.l[0] = wmatom[WMDelete]; |
|
171 ev.xclient.data.l[1] = CurrentTime; |
|
172 XSendEvent(dpy, sel->win, False, NoEventMask, &ev); |
|
173 } |
|
174 else |
|
175 XKillClient(dpy, sel->win); |
|
176 } |
|
177 |
|
178 void |
|
179 manage(Window w, XWindowAttributes *wa) { |
|
180 unsigned int i; |
|
181 Client *c, *t = NULL; |
|
182 Window trans; |
|
183 Status rettrans; |
|
184 XWindowChanges wc; |
|
185 |
|
186 c = emallocz(sizeof(Client)); |
|
187 c->tags = emallocz(ntags * sizeof(Bool)); |
|
188 c->win = w; |
|
189 c->x = wa->x; |
|
190 c->y = wa->y; |
|
191 c->w = wa->width; |
|
192 c->h = wa->height; |
|
193 c->oldborder = wa->border_width; |
|
194 if(c->w == sw && c->h == sh) { |
|
195 c->x = sx; |
|
196 c->y = sy; |
|
197 c->border = wa->border_width; |
|
198 } |
|
199 else { |
|
200 if(c->x + c->w + 2 * c->border > wax + waw) |
|
201 c->x = wax + waw - c->w - 2 * c->border; |
|
202 if(c->y + c->h + 2 * c->border > way + wah) |
|
203 c->y = way + wah - c->h - 2 * c->border; |
|
204 if(c->x < wax) |
|
205 c->x = wax; |
|
206 if(c->y < way) |
|
207 c->y = way; |
|
208 c->border = BORDERPX; |
|
209 } |
|
210 wc.border_width = c->border; |
|
211 XConfigureWindow(dpy, w, CWBorderWidth, &wc); |
|
212 XSetWindowBorder(dpy, w, dc.norm[ColBorder]); |
|
213 configure(c); /* propagates border_width, if size doesn't change */ |
|
214 updatesizehints(c); |
|
215 XSelectInput(dpy, w, |
|
216 StructureNotifyMask | PropertyChangeMask | EnterWindowMask); |
|
217 grabbuttons(c, False); |
|
218 updatetitle(c); |
|
219 if((rettrans = XGetTransientForHint(dpy, w, &trans) == Success)) |
|
220 for(t = clients; t && t->win != trans; t = t->next); |
|
221 if(t) |
|
222 for(i = 0; i < ntags; i++) |
|
223 c->tags[i] = t->tags[i]; |
|
224 applyrules(c); |
|
225 if(!c->isfloating) |
|
226 c->isfloating = (rettrans == Success) || c->isfixed; |
|
227 attach(c); |
|
228 attachstack(c); |
|
229 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); /* some windows require this */ |
|
230 ban(c); |
|
231 XMapWindow(dpy, c->win); |
|
232 setclientstate(c, NormalState); |
|
233 arrange(); |
|
234 } |
|
235 |
|
236 void |
|
237 resize(Client *c, int x, int y, int w, int h, Bool sizehints) { |
|
238 double dx, dy, max, min, ratio; |
|
239 XWindowChanges wc; |
|
240 |
|
241 if(sizehints) { |
|
242 if(c->minay > 0 && c->maxay > 0 && (h - c->baseh) > 0 && (w - c->basew) > 0) { |
|
243 dx = (double)(w - c->basew); |
|
244 dy = (double)(h - c->baseh); |
|
245 min = (double)(c->minax) / (double)(c->minay); |
|
246 max = (double)(c->maxax) / (double)(c->maxay); |
|
247 ratio = dx / dy; |
|
248 if(max > 0 && min > 0 && ratio > 0) { |
|
249 if(ratio < min) { |
|
250 dy = (dx * min + dy) / (min * min + 1); |
|
251 dx = dy * min; |
|
252 w = (int)dx + c->basew; |
|
253 h = (int)dy + c->baseh; |
|
254 } |
|
255 else if(ratio > max) { |
|
256 dy = (dx * min + dy) / (max * max + 1); |
|
257 dx = dy * min; |
|
258 w = (int)dx + c->basew; |
|
259 h = (int)dy + c->baseh; |
|
260 } |
|
261 } |
|
262 } |
|
263 if(c->minw && w < c->minw) |
|
264 w = c->minw; |
|
265 if(c->minh && h < c->minh) |
|
266 h = c->minh; |
|
267 if(c->maxw && w > c->maxw) |
|
268 w = c->maxw; |
|
269 if(c->maxh && h > c->maxh) |
|
270 h = c->maxh; |
|
271 if(c->incw) |
|
272 w -= (w - c->basew) % c->incw; |
|
273 if(c->inch) |
|
274 h -= (h - c->baseh) % c->inch; |
|
275 } |
|
276 if(w <= 0 || h <= 0) |
|
277 return; |
|
278 /* offscreen appearance fixes */ |
|
279 if(x > sw) |
|
280 x = sw - w - 2 * c->border; |
|
281 if(y > sh) |
|
282 y = sh - h - 2 * c->border; |
|
283 if(x + w + 2 * c->border < sx) |
|
284 x = sx; |
|
285 if(y + h + 2 * c->border < sy) |
|
286 y = sy; |
|
287 if(c->x != x || c->y != y || c->w != w || c->h != h) { |
|
288 c->x = wc.x = x; |
|
289 c->y = wc.y = y; |
|
290 c->w = wc.width = w; |
|
291 c->h = wc.height = h; |
|
292 wc.border_width = c->border; |
|
293 XConfigureWindow(dpy, c->win, CWX | CWY | CWWidth | CWHeight | CWBorderWidth, &wc); |
|
294 configure(c); |
|
295 XSync(dpy, False); |
|
296 } |
|
297 } |
|
298 |
|
299 void |
|
300 unban(Client *c) { |
|
301 if(!c->isbanned) |
|
302 return; |
|
303 XMoveWindow(dpy, c->win, c->x, c->y); |
|
304 c->isbanned = False; |
|
305 } |
|
306 |
|
307 void |
|
308 unmanage(Client *c) { |
|
309 XWindowChanges wc; |
|
310 |
|
311 wc.border_width = c->oldborder; |
|
312 /* The server grab construct avoids race conditions. */ |
|
313 XGrabServer(dpy); |
|
314 XSetErrorHandler(xerrordummy); |
|
315 XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ |
|
316 detach(c); |
|
317 detachstack(c); |
|
318 if(sel == c) |
|
319 focus(NULL); |
|
320 XUngrabButton(dpy, AnyButton, AnyModifier, c->win); |
|
321 setclientstate(c, WithdrawnState); |
|
322 free(c->tags); |
|
323 free(c); |
|
324 XSync(dpy, False); |
|
325 XSetErrorHandler(xerror); |
|
326 XUngrabServer(dpy); |
|
327 arrange(); |
|
328 } |
|
329 |
|
330 void |
|
331 updatesizehints(Client *c) { |
|
332 long msize; |
|
333 XSizeHints size; |
|
334 |
|
335 if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags) |
|
336 size.flags = PSize; |
|
337 c->flags = size.flags; |
|
338 if(c->flags & PBaseSize) { |
|
339 c->basew = size.base_width; |
|
340 c->baseh = size.base_height; |
|
341 } |
|
342 else if(c->flags & PMinSize) { |
|
343 c->basew = size.min_width; |
|
344 c->baseh = size.min_height; |
|
345 } |
|
346 else |
|
347 c->basew = c->baseh = 0; |
|
348 if(c->flags & PResizeInc) { |
|
349 c->incw = size.width_inc; |
|
350 c->inch = size.height_inc; |
|
351 } |
|
352 else |
|
353 c->incw = c->inch = 0; |
|
354 if(c->flags & PMaxSize) { |
|
355 c->maxw = size.max_width; |
|
356 c->maxh = size.max_height; |
|
357 } |
|
358 else |
|
359 c->maxw = c->maxh = 0; |
|
360 if(c->flags & PMinSize) { |
|
361 c->minw = size.min_width; |
|
362 c->minh = size.min_height; |
|
363 } |
|
364 else if(c->flags & PBaseSize) { |
|
365 c->minw = size.base_width; |
|
366 c->minh = size.base_height; |
|
367 } |
|
368 else |
|
369 c->minw = c->minh = 0; |
|
370 if(c->flags & PAspect) { |
|
371 c->minax = size.min_aspect.x; |
|
372 c->maxax = size.max_aspect.x; |
|
373 c->minay = size.min_aspect.y; |
|
374 c->maxay = size.max_aspect.y; |
|
375 } |
|
376 else |
|
377 c->minax = c->maxax = c->minay = c->maxay = 0; |
|
378 c->isfixed = (c->maxw && c->minw && c->maxh && c->minh |
|
379 && c->maxw == c->minw && c->maxh == c->minh); |
|
380 } |
|
381 |
|
382 void |
|
383 updatetitle(Client *c) { |
|
384 if(!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) |
|
385 gettextprop(c->win, wmatom[WMName], c->name, sizeof c->name); |
|
386 } |
|