rev |
line source |
garbeam@5
|
1 /*
|
garbeam@5
|
2 * (C)opyright MMVI Anselm R. Garbe <garbeam at gmail dot com>
|
garbeam@5
|
3 * See LICENSE file for license details.
|
garbeam@5
|
4 */
|
garbeam@5
|
5
|
garbeam@27
|
6 #include <math.h>
|
garbeam@10
|
7 #include <stdlib.h>
|
garbeam@5
|
8 #include <string.h>
|
garbeam@5
|
9 #include <X11/Xatom.h>
|
garbeam@5
|
10
|
garbeam@5
|
11 #include "util.h"
|
garbeam@5
|
12 #include "wm.h"
|
garbeam@5
|
13
|
garbeam@27
|
14 void
|
garbeam@28
|
15 max(void *aux)
|
garbeam@28
|
16 {
|
garbeam@28
|
17 if(!stack)
|
garbeam@28
|
18 return;
|
garbeam@28
|
19 stack->x = sx;
|
garbeam@28
|
20 stack->y = bh;
|
garbeam@29
|
21 stack->w = sw - 2 * stack->border;
|
garbeam@29
|
22 stack->h = sh - bh - 2 * stack->border;
|
garbeam@28
|
23 resize(stack);
|
garbeam@29
|
24 discard_events(EnterWindowMask);
|
garbeam@28
|
25 }
|
garbeam@28
|
26
|
garbeam@28
|
27 void
|
garbeam@27
|
28 arrange(void *aux)
|
garbeam@27
|
29 {
|
garbeam@27
|
30 Client *c;
|
garbeam@27
|
31 int n, cols, rows, gw, gh, i, j;
|
garbeam@27
|
32 float rt, fd;
|
garbeam@27
|
33
|
garbeam@27
|
34 if(!clients)
|
garbeam@27
|
35 return;
|
garbeam@27
|
36 for(n = 0, c = clients; c; c = c->next, n++);
|
garbeam@27
|
37 rt = sqrt(n);
|
garbeam@27
|
38 if(modff(rt, &fd) < 0.5)
|
garbeam@27
|
39 rows = floor(rt);
|
garbeam@27
|
40 else
|
garbeam@27
|
41 rows = ceil(rt);
|
garbeam@27
|
42 if(rows * rows < n)
|
garbeam@27
|
43 cols = rows + 1;
|
garbeam@27
|
44 else
|
garbeam@27
|
45 cols = rows;
|
garbeam@27
|
46
|
garbeam@29
|
47 gw = (sw - 2 * c->border) / cols;
|
garbeam@29
|
48 gh = (sh - bh - 2 * c->border) / rows;
|
garbeam@27
|
49
|
garbeam@27
|
50 for(i = j = 0, c = clients; c; c = c->next) {
|
garbeam@27
|
51 c->x = i * gw;
|
garbeam@27
|
52 c->y = j * gh + bh;
|
garbeam@27
|
53 c->w = gw;
|
garbeam@27
|
54 c->h = gh;
|
garbeam@27
|
55 resize(c);
|
garbeam@27
|
56 if(++i == cols) {
|
garbeam@27
|
57 j++;
|
garbeam@27
|
58 i = 0;
|
garbeam@27
|
59 }
|
garbeam@27
|
60 }
|
garbeam@29
|
61 discard_events(EnterWindowMask);
|
garbeam@27
|
62 }
|
garbeam@27
|
63
|
garbeam@27
|
64 void
|
garbeam@27
|
65 sel(void *aux)
|
garbeam@27
|
66 {
|
garbeam@27
|
67 const char *arg = aux;
|
garbeam@27
|
68 Client *c = NULL;
|
garbeam@27
|
69
|
garbeam@27
|
70 if(!arg || !stack)
|
garbeam@27
|
71 return;
|
garbeam@27
|
72 if(!strncmp(arg, "next", 5))
|
garbeam@27
|
73 c = stack->snext ? stack->snext : stack;
|
garbeam@27
|
74 else if(!strncmp(arg, "prev", 5))
|
garbeam@27
|
75 for(c = stack; c && c->snext; c = c->snext);
|
garbeam@27
|
76 if(!c)
|
garbeam@27
|
77 c = stack;
|
garbeam@27
|
78 raise(c);
|
garbeam@27
|
79 focus(c);
|
garbeam@27
|
80 }
|
garbeam@27
|
81
|
garbeam@27
|
82 void
|
garbeam@27
|
83 kill(void *aux)
|
garbeam@27
|
84 {
|
garbeam@27
|
85 Client *c = stack;
|
garbeam@27
|
86
|
garbeam@27
|
87 if(!c)
|
garbeam@27
|
88 return;
|
garbeam@27
|
89 if(c->proto & WM_PROTOCOL_DELWIN)
|
garbeam@27
|
90 send_message(c->win, wm_atom[WMProtocols], wm_atom[WMDelete]);
|
garbeam@27
|
91 else
|
garbeam@27
|
92 XKillClient(dpy, c->win);
|
garbeam@27
|
93 }
|
garbeam@27
|
94
|
garbeam@26
|
95 static void
|
garbeam@26
|
96 resize_title(Client *c)
|
garbeam@26
|
97 {
|
garbeam@26
|
98 c->tw = textw(&brush.font, c->name) + bh;
|
garbeam@26
|
99 if(c->tw > c->w)
|
garbeam@26
|
100 c->tw = c->w + 2;
|
garbeam@26
|
101 c->tx = c->x + c->w - c->tw + 2;
|
garbeam@26
|
102 c->ty = c->y;
|
garbeam@26
|
103 XMoveResizeWindow(dpy, c->title, c->tx, c->ty, c->tw, c->th);
|
garbeam@26
|
104 }
|
garbeam@18
|
105
|
garbeam@13
|
106 void
|
garbeam@13
|
107 update_name(Client *c)
|
garbeam@5
|
108 {
|
garbeam@5
|
109 XTextProperty name;
|
garbeam@5
|
110 int n;
|
garbeam@7
|
111 char **list = NULL;
|
garbeam@5
|
112
|
garbeam@5
|
113 name.nitems = 0;
|
garbeam@5
|
114 c->name[0] = 0;
|
garbeam@5
|
115 XGetTextProperty(dpy, c->win, &name, net_atom[NetWMName]);
|
garbeam@5
|
116 if(!name.nitems)
|
garbeam@5
|
117 XGetWMName(dpy, c->win, &name);
|
garbeam@5
|
118 if(!name.nitems)
|
garbeam@5
|
119 return;
|
garbeam@5
|
120 if(name.encoding == XA_STRING)
|
garbeam@5
|
121 strncpy(c->name, (char *)name.value, sizeof(c->name));
|
garbeam@5
|
122 else {
|
garbeam@5
|
123 if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
|
garbeam@5
|
124 && n > 0 && *list)
|
garbeam@5
|
125 {
|
garbeam@5
|
126 strncpy(c->name, *list, sizeof(c->name));
|
garbeam@5
|
127 XFreeStringList(list);
|
garbeam@5
|
128 }
|
garbeam@5
|
129 }
|
garbeam@5
|
130 XFree(name.value);
|
garbeam@26
|
131 resize_title(c);
|
garbeam@5
|
132 }
|
garbeam@5
|
133
|
garbeam@10
|
134 void
|
garbeam@20
|
135 update_size(Client *c)
|
garbeam@20
|
136 {
|
garbeam@20
|
137 XSizeHints size;
|
garbeam@20
|
138 long msize;
|
garbeam@20
|
139 if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
|
garbeam@20
|
140 size.flags = PSize;
|
garbeam@20
|
141 c->flags = size.flags;
|
garbeam@21
|
142 if(c->flags & PBaseSize) {
|
garbeam@21
|
143 c->basew = size.base_width;
|
garbeam@21
|
144 c->baseh = size.base_height;
|
garbeam@21
|
145 }
|
garbeam@21
|
146 else
|
garbeam@21
|
147 c->basew = c->baseh = 0;
|
garbeam@21
|
148 if(c->flags & PResizeInc) {
|
garbeam@21
|
149 c->incw = size.width_inc;
|
garbeam@21
|
150 c->inch = size.height_inc;
|
garbeam@21
|
151 }
|
garbeam@21
|
152 else
|
garbeam@21
|
153 c->incw = c->inch = 0;
|
garbeam@21
|
154 if(c->flags & PMaxSize) {
|
garbeam@21
|
155 c->maxw = size.max_width;
|
garbeam@21
|
156 c->maxh = size.max_height;
|
garbeam@21
|
157 }
|
garbeam@21
|
158 else
|
garbeam@21
|
159 c->maxw = c->maxh = 0;
|
garbeam@21
|
160 if(c->flags & PMinSize) {
|
garbeam@21
|
161 c->minw = size.min_width;
|
garbeam@21
|
162 c->minh = size.min_height;
|
garbeam@21
|
163 }
|
garbeam@21
|
164 else
|
garbeam@21
|
165 c->minw = c->minh = 0;
|
garbeam@29
|
166 if(c->flags & PWinGravity)
|
garbeam@29
|
167 c->grav = size.win_gravity;
|
garbeam@29
|
168 else
|
garbeam@29
|
169 c->grav = NorthWestGravity;
|
garbeam@20
|
170 }
|
garbeam@20
|
171
|
garbeam@20
|
172 void
|
garbeam@26
|
173 raise(Client *c)
|
garbeam@26
|
174 {
|
garbeam@26
|
175 XRaiseWindow(dpy, c->win);
|
garbeam@26
|
176 XRaiseWindow(dpy, c->title);
|
garbeam@26
|
177 }
|
garbeam@26
|
178
|
garbeam@26
|
179 void
|
garbeam@26
|
180 lower(Client *c)
|
garbeam@26
|
181 {
|
garbeam@26
|
182 XLowerWindow(dpy, c->title);
|
garbeam@26
|
183 XLowerWindow(dpy, c->win);
|
garbeam@26
|
184 }
|
garbeam@26
|
185
|
garbeam@26
|
186 void
|
garbeam@13
|
187 focus(Client *c)
|
garbeam@13
|
188 {
|
garbeam@21
|
189 Client **l, *old;
|
garbeam@21
|
190
|
garbeam@21
|
191 old = stack;
|
garbeam@26
|
192 for(l = &stack; *l && *l != c; l = &(*l)->snext);
|
garbeam@13
|
193 eassert(*l == c);
|
garbeam@13
|
194 *l = c->snext;
|
garbeam@13
|
195 c->snext = stack;
|
garbeam@13
|
196 stack = c;
|
garbeam@21
|
197 if(old && old != c) {
|
garbeam@21
|
198 XMapWindow(dpy, old->title);
|
garbeam@21
|
199 draw_client(old);
|
garbeam@21
|
200 }
|
garbeam@21
|
201 XUnmapWindow(dpy, c->title);
|
garbeam@27
|
202 draw_client(c);
|
garbeam@26
|
203 XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
|
garbeam@13
|
204 XFlush(dpy);
|
garbeam@13
|
205 }
|
garbeam@13
|
206
|
garbeam@13
|
207 void
|
garbeam@10
|
208 manage(Window w, XWindowAttributes *wa)
|
garbeam@5
|
209 {
|
garbeam@10
|
210 Client *c, **l;
|
garbeam@5
|
211 XSetWindowAttributes twa;
|
garbeam@5
|
212
|
garbeam@5
|
213 c = emallocz(sizeof(Client));
|
garbeam@5
|
214 c->win = w;
|
garbeam@22
|
215 c->tx = c->x = wa->x;
|
garbeam@22
|
216 c->ty = c->y = wa->y;
|
garbeam@26
|
217 if(c->y < bh)
|
garbeam@26
|
218 c->ty = c->y += bh;
|
garbeam@22
|
219 c->tw = c->w = wa->width;
|
garbeam@20
|
220 c->h = wa->height;
|
garbeam@26
|
221 c->th = bh;
|
garbeam@29
|
222 c->border = 1;
|
garbeam@20
|
223 update_size(c);
|
garbeam@19
|
224 XSetWindowBorderWidth(dpy, c->win, 1);
|
garbeam@22
|
225 XSetWindowBorder(dpy, c->win, brush.border);
|
garbeam@26
|
226 XSelectInput(dpy, c->win,
|
garbeam@26
|
227 StructureNotifyMask | PropertyChangeMask | EnterWindowMask);
|
garbeam@5
|
228 XGetTransientForHint(dpy, c->win, &c->trans);
|
garbeam@5
|
229 twa.override_redirect = 1;
|
garbeam@5
|
230 twa.background_pixmap = ParentRelative;
|
garbeam@23
|
231 twa.event_mask = ExposureMask;
|
garbeam@5
|
232
|
garbeam@22
|
233 c->title = XCreateWindow(dpy, root, c->tx, c->ty, c->tw, c->th,
|
garbeam@20
|
234 0, DefaultDepth(dpy, screen), CopyFromParent,
|
garbeam@5
|
235 DefaultVisual(dpy, screen),
|
garbeam@5
|
236 CWOverrideRedirect | CWBackPixmap | CWEventMask, &twa);
|
garbeam@21
|
237 update_name(c);
|
garbeam@5
|
238
|
garbeam@10
|
239 for(l=&clients; *l; l=&(*l)->next);
|
garbeam@10
|
240 c->next = *l; /* *l == nil */
|
garbeam@10
|
241 *l = c;
|
garbeam@13
|
242 c->snext = stack;
|
garbeam@13
|
243 stack = c;
|
garbeam@26
|
244 XMapRaised(dpy, c->win);
|
garbeam@26
|
245 XMapRaised(dpy, c->title);
|
garbeam@19
|
246 XGrabButton(dpy, Button1, Mod1Mask, c->win, False, ButtonPressMask,
|
garbeam@19
|
247 GrabModeAsync, GrabModeSync, None, None);
|
garbeam@19
|
248 XGrabButton(dpy, Button2, Mod1Mask, c->win, False, ButtonPressMask,
|
garbeam@19
|
249 GrabModeAsync, GrabModeSync, None, None);
|
garbeam@19
|
250 XGrabButton(dpy, Button3, Mod1Mask, c->win, False, ButtonPressMask,
|
garbeam@18
|
251 GrabModeAsync, GrabModeSync, None, None);
|
garbeam@21
|
252 resize(c);
|
garbeam@13
|
253 focus(c);
|
garbeam@5
|
254 }
|
garbeam@9
|
255
|
garbeam@18
|
256 void
|
garbeam@29
|
257 gravitate(Client *c, Bool invert)
|
garbeam@29
|
258 {
|
garbeam@29
|
259 int dx = 0, dy = 0;
|
garbeam@29
|
260
|
garbeam@29
|
261 switch(c->grav) {
|
garbeam@29
|
262 case StaticGravity:
|
garbeam@29
|
263 case NorthWestGravity:
|
garbeam@29
|
264 case NorthGravity:
|
garbeam@29
|
265 case NorthEastGravity:
|
garbeam@29
|
266 dy = c->border;
|
garbeam@29
|
267 break;
|
garbeam@29
|
268 case EastGravity:
|
garbeam@29
|
269 case CenterGravity:
|
garbeam@29
|
270 case WestGravity:
|
garbeam@29
|
271 dy = -(c->h / 2) + c->border;
|
garbeam@29
|
272 break;
|
garbeam@29
|
273 case SouthEastGravity:
|
garbeam@29
|
274 case SouthGravity:
|
garbeam@29
|
275 case SouthWestGravity:
|
garbeam@29
|
276 dy = -c->h;
|
garbeam@29
|
277 break;
|
garbeam@29
|
278 default:
|
garbeam@29
|
279 break;
|
garbeam@29
|
280 }
|
garbeam@29
|
281
|
garbeam@29
|
282 switch (c->grav) {
|
garbeam@29
|
283 case StaticGravity:
|
garbeam@29
|
284 case NorthWestGravity:
|
garbeam@29
|
285 case WestGravity:
|
garbeam@29
|
286 case SouthWestGravity:
|
garbeam@29
|
287 dx = c->border;
|
garbeam@29
|
288 break;
|
garbeam@29
|
289 case NorthGravity:
|
garbeam@29
|
290 case CenterGravity:
|
garbeam@29
|
291 case SouthGravity:
|
garbeam@29
|
292 dx = -(c->w / 2) + c->border;
|
garbeam@29
|
293 break;
|
garbeam@29
|
294 case NorthEastGravity:
|
garbeam@29
|
295 case EastGravity:
|
garbeam@29
|
296 case SouthEastGravity:
|
garbeam@29
|
297 dx = -(c->w + c->border);
|
garbeam@29
|
298 break;
|
garbeam@29
|
299 default:
|
garbeam@29
|
300 break;
|
garbeam@29
|
301 }
|
garbeam@29
|
302
|
garbeam@29
|
303 if(invert) {
|
garbeam@29
|
304 dx = -dx;
|
garbeam@29
|
305 dy = -dy;
|
garbeam@29
|
306 }
|
garbeam@29
|
307 c->x += dx;
|
garbeam@29
|
308 c->y += dy;
|
garbeam@29
|
309 }
|
garbeam@29
|
310
|
garbeam@29
|
311 void
|
garbeam@18
|
312 resize(Client *c)
|
garbeam@18
|
313 {
|
garbeam@18
|
314 XConfigureEvent e;
|
garbeam@18
|
315
|
garbeam@26
|
316 resize_title(c);
|
garbeam@20
|
317 XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
|
garbeam@18
|
318 e.type = ConfigureNotify;
|
garbeam@18
|
319 e.event = c->win;
|
garbeam@18
|
320 e.window = c->win;
|
garbeam@20
|
321 e.x = c->x;
|
garbeam@20
|
322 e.y = c->y;
|
garbeam@20
|
323 e.width = c->w;
|
garbeam@20
|
324 e.height = c->h;
|
garbeam@29
|
325 e.border_width = c->border;
|
garbeam@18
|
326 e.above = None;
|
garbeam@18
|
327 e.override_redirect = False;
|
garbeam@18
|
328 XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&e);
|
garbeam@18
|
329 XFlush(dpy);
|
garbeam@18
|
330 }
|
garbeam@18
|
331
|
garbeam@10
|
332 static int
|
garbeam@10
|
333 dummy_error_handler(Display *dpy, XErrorEvent *error)
|
garbeam@10
|
334 {
|
garbeam@10
|
335 return 0;
|
garbeam@10
|
336 }
|
garbeam@10
|
337
|
garbeam@10
|
338 void
|
garbeam@10
|
339 unmanage(Client *c)
|
garbeam@10
|
340 {
|
garbeam@10
|
341 Client **l;
|
garbeam@10
|
342
|
garbeam@10
|
343 XGrabServer(dpy);
|
garbeam@10
|
344 XSetErrorHandler(dummy_error_handler);
|
garbeam@10
|
345
|
garbeam@18
|
346 XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
|
garbeam@10
|
347 XDestroyWindow(dpy, c->title);
|
garbeam@10
|
348
|
garbeam@10
|
349 for(l=&clients; *l && *l != c; l=&(*l)->next);
|
garbeam@10
|
350 eassert(*l == c);
|
garbeam@10
|
351 *l = c->next;
|
garbeam@13
|
352 for(l=&stack; *l && *l != c; l=&(*l)->snext);
|
garbeam@13
|
353 eassert(*l == c);
|
garbeam@13
|
354 *l = c->snext;
|
garbeam@10
|
355 free(c);
|
garbeam@10
|
356
|
garbeam@10
|
357 XFlush(dpy);
|
garbeam@10
|
358 XSetErrorHandler(error_handler);
|
garbeam@10
|
359 XUngrabServer(dpy);
|
garbeam@14
|
360 if(stack)
|
garbeam@14
|
361 focus(stack);
|
garbeam@10
|
362 }
|
garbeam@10
|
363
|
garbeam@23
|
364 Client *
|
garbeam@23
|
365 gettitle(Window w)
|
garbeam@23
|
366 {
|
garbeam@23
|
367 Client *c;
|
garbeam@23
|
368 for(c = clients; c; c = c->next)
|
garbeam@23
|
369 if(c->title == w)
|
garbeam@23
|
370 return c;
|
garbeam@23
|
371 return NULL;
|
garbeam@23
|
372 }
|
garbeam@10
|
373
|
garbeam@9
|
374 Client *
|
garbeam@9
|
375 getclient(Window w)
|
garbeam@9
|
376 {
|
garbeam@9
|
377 Client *c;
|
garbeam@9
|
378 for(c = clients; c; c = c->next)
|
garbeam@9
|
379 if(c->win == w)
|
garbeam@9
|
380 return c;
|
garbeam@9
|
381 return NULL;
|
garbeam@9
|
382 }
|
garbeam@13
|
383
|
garbeam@14
|
384 void
|
garbeam@14
|
385 draw_client(Client *c)
|
garbeam@14
|
386 {
|
garbeam@26
|
387 if(c == stack) {
|
garbeam@21
|
388 draw_bar();
|
garbeam@26
|
389 return;
|
garbeam@26
|
390 }
|
garbeam@14
|
391
|
garbeam@26
|
392 brush.x = brush.y = 0;
|
garbeam@26
|
393 brush.w = c->tw;
|
garbeam@26
|
394 brush.h = c->th;
|
garbeam@14
|
395
|
garbeam@21
|
396 draw(dpy, &brush, True, c->name);
|
garbeam@22
|
397 XCopyArea(dpy, brush.drawable, c->title, brush.gc,
|
garbeam@22
|
398 0, 0, c->tw, c->th, 0, 0);
|
garbeam@21
|
399 XFlush(dpy);
|
garbeam@14
|
400 }
|