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