JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
made status bar drawing more robust, implemented togglemax and togglemode, works...
[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 #include "dwm.h"
6
7 #include <stdlib.h>
8 #include <string.h>
9 #include <X11/Xatom.h>
10 #include <X11/Xutil.h>
11
12 /* static functions */
13
14 static void
15 resizetitle(Client *c)
16 {
17         int i;
18
19         c->tw = 0;
20         for(i = 0; i < TLast; i++)
21                 if(c->tags[i])
22                         c->tw += textw(c->tags[i]);
23         c->tw += textw(c->name);
24         if(c->tw > c->w)
25                 c->tw = c->w + 2;
26         c->tx = c->x + c->w - c->tw + 2;
27         c->ty = c->y;
28         if(c->tags[tsel])
29                 XMoveResizeWindow(dpy, c->title, c->tx, c->ty, c->tw, c->th);
30         else
31                 XMoveResizeWindow(dpy, c->title, c->tx + 2 * sw, c->ty, c->tw, c->th);
32
33 }
34
35 static int
36 xerrordummy(Display *dsply, XErrorEvent *ee)
37 {
38         return 0;
39 }
40
41 /* extern functions */
42
43 void
44 ban(Client *c)
45 {
46         XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
47         XMoveWindow(dpy, c->title, c->tx + 2 * sw, c->ty);
48 }
49
50 void
51 focus(Client *c)
52 {
53         Client *old = sel;
54         XEvent ev;
55
56         sel = c;
57         if(old && old != c)
58                 drawtitle(old);
59         drawtitle(c);
60         XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
61         XSync(dpy, False);
62         while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
63 }
64
65 void
66 focusnext(Arg *arg)
67 {
68         Client *c;
69    
70         if(!sel)
71                 return;
72
73         if(sel->ismax)
74                 togglemax(NULL);
75
76         if(!(c = getnext(sel->next, tsel)))
77                 c = getnext(clients, tsel);
78         if(c) {
79                 higher(c);
80                 c->revert = sel;
81                 focus(c);
82         }
83 }
84
85 void
86 focusprev(Arg *arg)
87 {
88         Client *c;
89
90         if(!sel)
91                 return;
92
93         if(sel->ismax)
94                 togglemax(NULL);
95
96         if((c = sel->revert && sel->revert->tags[tsel] ? sel->revert : NULL)) {
97                 higher(c);
98                 focus(c);
99         }
100 }
101
102 Client *
103 getclient(Window w)
104 {
105         Client *c;
106
107         for(c = clients; c; c = c->next)
108                 if(c->win == w)
109                         return c;
110         return NULL;
111 }
112
113 Client *
114 getctitle(Window w)
115 {
116         Client *c;
117
118         for(c = clients; c; c = c->next)
119                 if(c->title == w)
120                         return c;
121         return NULL;
122 }
123
124 void
125 gravitate(Client *c, Bool invert)
126 {
127         int dx = 0, dy = 0;
128
129         switch(c->grav) {
130         case StaticGravity:
131         case NorthWestGravity:
132         case NorthGravity:
133         case NorthEastGravity:
134                 dy = c->border;
135                 break;
136         case EastGravity:
137         case CenterGravity:
138         case WestGravity:
139                 dy = -(c->h / 2) + c->border;
140                 break;
141         case SouthEastGravity:
142         case SouthGravity:
143         case SouthWestGravity:
144                 dy = -(c->h);
145                 break;
146         default:
147                 break;
148         }
149
150         switch (c->grav) {
151         case StaticGravity:
152         case NorthWestGravity:
153         case WestGravity:
154         case SouthWestGravity:
155                 dx = c->border;
156                 break;
157         case NorthGravity:
158         case CenterGravity:
159         case SouthGravity:
160                 dx = -(c->w / 2) + c->border;
161                 break;
162         case NorthEastGravity:
163         case EastGravity:
164         case SouthEastGravity:
165                 dx = -(c->w + c->border);
166                 break;
167         default:
168                 break;
169         }
170
171         if(invert) {
172                 dx = -dx;
173                 dy = -dy;
174         }
175         c->x += dx;
176         c->y += dy;
177 }
178
179 void
180 higher(Client *c)
181 {
182         XRaiseWindow(dpy, c->win);
183         XRaiseWindow(dpy, c->title);
184 }
185
186 void
187 killclient(Arg *arg)
188 {
189         if(!sel)
190                 return;
191         if(sel->proto & WM_PROTOCOL_DELWIN)
192                 sendevent(sel->win, wmatom[WMProtocols], wmatom[WMDelete]);
193         else
194                 XKillClient(dpy, sel->win);
195 }
196
197 void
198 lower(Client *c)
199 {
200         XLowerWindow(dpy, c->title);
201         XLowerWindow(dpy, c->win);
202 }
203
204 void
205 manage(Window w, XWindowAttributes *wa)
206 {
207         int diff;
208         Client *c;
209         Window trans;
210         XSetWindowAttributes twa;
211
212         c = emallocz(sizeof(Client));
213         c->win = w;
214         c->x = c->tx = wa->x;
215         c->y = c->ty = wa->y;
216         c->w = c->tw = wa->width;
217         c->h = wa->height;
218         c->th = bh;
219
220         if(c->y < bh)
221                 c->y = c->ty = bh;
222
223         c->border = 1;
224         c->proto = getproto(c->win);
225         setsize(c);
226         XSelectInput(dpy, c->win,
227                         StructureNotifyMask | PropertyChangeMask | EnterWindowMask);
228         XGetTransientForHint(dpy, c->win, &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
238         settags(c);
239
240         c->next = clients;
241         clients = c;
242
243         XGrabButton(dpy, Button1, MODKEY, c->win, False, ButtonPressMask,
244                         GrabModeAsync, GrabModeSync, None, None);
245         XGrabButton(dpy, Button2, MODKEY, c->win, False, ButtonPressMask,
246                         GrabModeAsync, GrabModeSync, None, None);
247         XGrabButton(dpy, Button3, MODKEY, c->win, False, ButtonPressMask,
248                         GrabModeAsync, GrabModeSync, None, None);
249
250         if(!c->isfloat)
251                 c->isfloat = trans || (c->maxw && c->minw &&
252                                 (c->maxw == c->minw) && (c->maxh == c->minh));
253
254
255         settitle(c);
256         arrange(NULL);
257
258         /* mapping the window now prevents flicker */
259         if(c->tags[tsel]) {
260                 XMapRaised(dpy, c->win);
261                 XMapRaised(dpy, c->title);
262                 focus(c);
263         }
264         else {
265                 XMapRaised(dpy, c->win);
266                 XMapRaised(dpy, c->title);
267         }
268 }
269
270 void
271 pop(Client *c)
272 {
273         Client **l;
274
275         for(l = &clients; *l && *l != c; l = &(*l)->next);
276         *l = c->next;
277
278         c->next = clients; /* pop */
279         clients = c;
280         arrange(NULL);
281 }
282
283 void
284 resize(Client *c, Bool inc, Corner sticky)
285 {
286         int bottom = c->y + c->h;
287         int right = c->x + c->w;
288         XConfigureEvent e;
289
290         if(inc) {
291                 if(c->incw)
292                         c->w -= (c->w - c->basew) % c->incw;
293                 if(c->inch)
294                         c->h -= (c->h - c->baseh) % c->inch;
295         }
296         if(c->x > sw) /* might happen on restart */
297                 c->x = sw - c->w;
298         if(c->y > sh)
299                 c->y = sh - c->h;
300         if(c->minw && c->w < c->minw)
301                 c->w = c->minw;
302         if(c->minh && c->h < c->minh)
303                 c->h = c->minh;
304         if(c->maxw && c->w > c->maxw)
305                 c->w = c->maxw;
306         if(c->maxh && c->h > c->maxh)
307                 c->h = c->maxh;
308         if(sticky == TopRight || sticky == BotRight)
309                 c->x = right - c->w;
310         if(sticky == BotLeft || sticky == BotRight)
311                 c->y = bottom - c->h;
312
313         resizetitle(c);
314         XSetWindowBorderWidth(dpy, c->win, 1);
315         XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
316
317         e.type = ConfigureNotify;
318         e.event = c->win;
319         e.window = c->win;
320         e.x = c->x;
321         e.y = c->y;
322         e.width = c->w;
323         e.height = c->h;
324         e.border_width = c->border;
325         e.above = None;
326         e.override_redirect = False;
327         XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&e);
328         XSync(dpy, False);
329 }
330
331 void
332 setsize(Client *c)
333 {
334         long msize;
335         XSizeHints size;
336
337         if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
338                 size.flags = PSize;
339         c->flags = size.flags;
340         if(c->flags & PBaseSize) {
341                 c->basew = size.base_width;
342                 c->baseh = size.base_height;
343         }
344         else
345                 c->basew = c->baseh = 0;
346         if(c->flags & PResizeInc) {
347                 c->incw = size.width_inc;
348                 c->inch = size.height_inc;
349         }
350         else
351                 c->incw = c->inch = 0;
352         if(c->flags & PMaxSize) {
353                 c->maxw = size.max_width;
354                 c->maxh = size.max_height;
355         }
356         else
357                 c->maxw = c->maxh = 0;
358         if(c->flags & PMinSize) {
359                 c->minw = size.min_width;
360                 c->minh = size.min_height;
361         }
362         else
363                 c->minw = c->minh = 0;
364         if(c->flags & PWinGravity)
365                 c->grav = size.win_gravity;
366         else
367                 c->grav = NorthWestGravity;
368 }
369
370 void
371 settitle(Client *c)
372 {
373         char **list = NULL;
374         int n;
375         XTextProperty name;
376
377         name.nitems = 0;
378         c->name[0] = 0;
379         XGetTextProperty(dpy, c->win, &name, netatom[NetWMName]);
380         if(!name.nitems)
381                 XGetWMName(dpy, c->win, &name);
382         if(!name.nitems)
383                 return;
384         if(name.encoding == XA_STRING)
385                 strncpy(c->name, (char *)name.value, sizeof(c->name));
386         else {
387                 if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
388                                 && n > 0 && *list)
389                 {
390                         strncpy(c->name, *list, sizeof(c->name));
391                         XFreeStringList(list);
392                 }
393         }
394         XFree(name.value);
395         resizetitle(c);
396 }
397
398 void
399 togglemax(Arg *arg)
400 {
401         int ox, oy, ow, oh;
402         XEvent ev;
403
404         if(!sel)
405                 return;
406
407         if((sel->ismax = !sel->ismax)) {
408                 ox = sel->x;
409                 oy = sel->y;
410                 ow = sel->w;
411                 oh = sel->h;
412                 sel->x = sx;
413                 sel->y = sy + bh;
414                 sel->w = sw - 2 * sel->border;
415                 sel->h = sh - 2 * sel->border - bh;
416
417                 higher(sel);
418                 resize(sel, False, TopLeft);
419
420                 sel->x = ox;
421                 sel->y = oy;
422                 sel->w = ow;
423                 sel->h = oh;
424         }
425         else
426                 resize(sel, False, TopLeft);
427         while(XCheckMaskEvent(dpy, EnterWindowMask, &ev));
428 }
429
430 void
431 unmanage(Client *c)
432 {
433         Client **l;
434
435         XGrabServer(dpy);
436         XSetErrorHandler(xerrordummy);
437
438         XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
439         XDestroyWindow(dpy, c->title);
440
441         for(l = &clients; *l && *l != c; l = &(*l)->next);
442         *l = c->next;
443         for(l = &clients; *l; l = &(*l)->next)
444                 if((*l)->revert == c)
445                         (*l)->revert = NULL;
446         if(sel == c)
447                 sel = sel->revert ? sel->revert : clients;
448
449         free(c);
450
451         XSync(dpy, False);
452         XSetErrorHandler(xerror);
453         XUngrabServer(dpy);
454         arrange(NULL);
455         if(sel)
456                 focus(sel);
457 }
458
459 void
460 zoom(Arg *arg)
461 {
462         Client *c;
463
464         if(!sel)
465                 return;
466
467         if(sel == getnext(clients, tsel) && sel->next)  {
468                 if((c = getnext(sel->next, tsel)))
469                         sel = c;
470         }
471
472         pop(sel);
473         focus(sel);
474 }