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