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