JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
new 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 <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 void (*arrange)(Arg *) = tiling;
15
16 static Rule rule[] = {
17         { "Firefox-bin", "Gecko", { [Twww] = "www" } },
18 };
19
20 static Client *
21 next(Client *c)
22 {
23         for(; c && !c->tags[tsel]; c = c->next);
24         return c;
25 }
26
27 void
28 zoom(Arg *arg)
29 {
30         Client **l, *old;
31
32         if(!(old = sel))
33                 return;
34
35         for(l = &clients; *l && *l != sel; l = &(*l)->next);
36         *l = sel->next;
37
38         old->next = clients; /* pop */
39         clients = old;
40         sel = old;
41         arrange(NULL);
42         focus(sel);
43 }
44
45 void
46 max(Arg *arg)
47 {
48         if(!sel)
49                 return;
50         sel->x = sx;
51         sel->y = sy;
52         sel->w = sw - 2 * sel->border;
53         sel->h = sh - 2 * sel->border;
54         craise(sel);
55         resize(sel);
56         discard_events(EnterWindowMask);
57 }
58
59 void
60 view(Arg *arg)
61 {
62         tsel = arg->i;
63         arrange(NULL);
64 }
65
66 void
67 tag(Arg *arg)
68 {
69         int i, n;
70         if(!sel)
71                 return;
72
73         if(arg->i == tsel) {
74                 for(n = i = 0; i < TLast; i++)
75                         if(sel->tags[i])
76                                 n++;
77                 if(n < 2)
78                         return;
79         }
80
81         if(sel->tags[arg->i])
82                 sel->tags[arg->i] = NULL; /* toggle tag */
83         else
84                 sel->tags[arg->i] = tags[arg->i];
85         arrange(NULL);
86 }
87
88 static void
89 ban_client(Client *c)
90 {
91         XMoveWindow(dpy, c->win, c->x + 2 * sw, c->y);
92         XMoveWindow(dpy, c->title, c->tx + 2 * sw, c->ty);
93 }
94
95 void
96 floating(Arg *arg)
97 {
98         Client *c;
99
100         arrange = floating;
101         for(c = clients; c; c = c->next) {
102                 if(c->tags[tsel])
103                         resize(c);
104                 else
105                         ban_client(c);
106         }
107         if(sel && !sel->tags[tsel]) {
108                 if((sel = next(clients))) {
109                         craise(sel);
110                         focus(sel);
111                 }
112         }
113         discard_events(EnterWindowMask);
114 }
115
116 void
117 tiling(Arg *arg)
118 {
119         Client *c;
120         int n, i, w, h;
121
122         w = sw - mw;
123         arrange = tiling;
124         for(n = 0, c = clients; c; c = c->next)
125                 if(c->tags[tsel])
126                         n++;
127
128         h = (n > 2) ? sh / (n - 2) : sh;
129
130         for(i = 0, c = clients; c; c = c->next) {
131                 if(c->tags[tsel]) {
132                         if(n == 1) {
133                                 c->x = sx;
134                                 c->y = sy;
135                                 c->w = sw;
136                                 c->h = sh;
137                         }
138                         else if(i == 1) {
139                                 c->x = sx;
140                                 c->y = sy;
141                                 c->w = mw;
142                                 c->h = sh;
143                         }
144                         else {
145                                 c->x = sx + mw;
146                                 c->y = sy + (i - 2) * h;
147                                 c->w = w;
148                                 c->h = h;
149                         }
150                         resize(c);
151                         i++;
152                 }
153                 else
154                         ban_client(c);
155         }
156         if(sel && !sel->tags[tsel]) {
157                 if((sel = next(clients))) {
158                         craise(sel);
159                         focus(sel);
160                 }
161         }
162         discard_events(EnterWindowMask);
163 }
164
165 void
166 prevc(Arg *arg)
167 {
168         Client *c;
169
170         if(!sel)
171                 return;
172
173         if((c = sel->revert && sel->revert->tags[tsel] ? sel->revert : NULL)) {
174                 craise(c);
175                 focus(c);
176         }
177 }
178
179 void
180 nextc(Arg *arg)
181 {
182         Client *c;
183    
184         if(!sel)
185                 return;
186
187         if(!(c = next(sel->next)))
188                 c = next(clients);
189         if(c) {
190                 craise(c);
191                 c->revert = sel;
192                 focus(c);
193         }
194 }
195
196 void
197 ckill(Arg *arg)
198 {
199         if(!sel)
200                 return;
201         if(sel->proto & WM_PROTOCOL_DELWIN)
202                 send_message(sel->win, wm_atom[WMProtocols], wm_atom[WMDelete]);
203         else
204                 XKillClient(dpy, sel->win);
205 }
206
207 static void
208 resize_title(Client *c)
209 {
210         int i;
211
212         c->tw = 0;
213         for(i = 0; i < TLast; i++)
214                 if(c->tags[i])
215                         c->tw += textw(c->tags[i]) + dc.font.height;
216         c->tw += textw(c->name) + dc.font.height;
217         if(c->tw > c->w)
218                 c->tw = c->w + 2;
219         c->tx = c->x + c->w - c->tw + 2;
220         c->ty = c->y;
221         XMoveResizeWindow(dpy, c->title, c->tx, c->ty, c->tw, c->th);
222 }
223
224 void
225 update_name(Client *c)
226 {
227         XTextProperty name;
228         int n;
229         char **list = NULL;
230
231         name.nitems = 0;
232         c->name[0] = 0;
233         XGetTextProperty(dpy, c->win, &name, net_atom[NetWMName]);
234         if(!name.nitems)
235                 XGetWMName(dpy, c->win, &name);
236         if(!name.nitems)
237                 return;
238         if(name.encoding == XA_STRING)
239                 strncpy(c->name, (char *)name.value, sizeof(c->name));
240         else {
241                 if(XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success
242                                 && n > 0 && *list)
243                 {
244                         strncpy(c->name, *list, sizeof(c->name));
245                         XFreeStringList(list);
246                 }
247         }
248         XFree(name.value);
249         resize_title(c);
250 }
251
252 void
253 update_size(Client *c)
254 {
255         XSizeHints size;
256         long msize;
257         if(!XGetWMNormalHints(dpy, c->win, &size, &msize) || !size.flags)
258                 size.flags = PSize;
259         c->flags = size.flags;
260         if(c->flags & PBaseSize) {
261                 c->basew = size.base_width;
262                 c->baseh = size.base_height;
263         }
264         else
265                 c->basew = c->baseh = 0;
266         if(c->flags & PResizeInc) {
267                 c->incw = size.width_inc;
268                 c->inch = size.height_inc;
269         }
270         else
271                 c->incw = c->inch = 0;
272         if(c->flags & PMaxSize) {
273                 c->maxw = size.max_width;
274                 c->maxh = size.max_height;
275         }
276         else
277                 c->maxw = c->maxh = 0;
278         if(c->flags & PMinSize) {
279                 c->minw = size.min_width;
280                 c->minh = size.min_height;
281         }
282         else
283                 c->minw = c->minh = 0;
284         if(c->flags & PWinGravity)
285                 c->grav = size.win_gravity;
286         else
287                 c->grav = NorthWestGravity;
288 }
289
290 void
291 craise(Client *c)
292 {
293         XRaiseWindow(dpy, c->win);
294         XRaiseWindow(dpy, c->title);
295 }
296
297 void
298 lower(Client *c)
299 {
300         XLowerWindow(dpy, c->title);
301         XLowerWindow(dpy, c->win);
302 }
303
304 void
305 focus(Client *c)
306 {
307         if(sel && sel != c) {
308                 XSetWindowBorder(dpy, sel->win, dc.bg);
309                 XMapWindow(dpy, sel->title);
310                 draw_client(sel);
311         }
312         sel = c;
313         XUnmapWindow(dpy, c->title);
314         XSetWindowBorder(dpy, c->win, dc.fg);
315         draw_client(c);
316         XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime);
317         XFlush(dpy);
318         discard_events(EnterWindowMask);
319 }
320
321 static void
322 init_tags(Client *c)
323 {
324         XClassHint ch;
325         static unsigned int len = rule ? sizeof(rule) / sizeof(rule[0]) : 0;
326         unsigned int i, j;
327         Bool matched = False;
328
329         if(!len) {
330                 c->tags[tsel] = tags[tsel];
331                 return;
332         }
333
334         if(XGetClassHint(dpy, c->win, &ch)) {
335                 if(ch.res_class && ch.res_name) {
336                         fprintf(stderr, "%s:%s\n", ch.res_class, ch.res_name);
337                         for(i = 0; i < len; i++)
338                                 if(!strncmp(rule[i].class, ch.res_class, sizeof(rule[i].class))
339                                         && !strncmp(rule[i].instance, ch.res_name, sizeof(rule[i].instance)))
340                                 {
341                         fprintf(stderr, "->>>%s:%s\n", ch.res_class, ch.res_name);
342                                         for(j = 0; j < TLast; j++)
343                                                 c->tags[j] = rule[i].tags[j];
344                                         matched = True;
345                                         break;
346                                 }
347                 }
348                 if(ch.res_class)
349                         XFree(ch.res_class);
350                 if(ch.res_name)
351                         XFree(ch.res_name);
352         }
353
354         if(!matched)
355                 c->tags[tsel] = tags[tsel];
356 }
357
358 void
359 manage(Window w, XWindowAttributes *wa)
360 {
361         Client *c, **l;
362         XSetWindowAttributes twa;
363
364         c = emallocz(sizeof(Client));
365         c->win = w;
366         c->tx = c->x = wa->x;
367         c->ty = c->y = wa->y;
368         c->tw = c->w = wa->width;
369         c->h = wa->height;
370         c->th = th;
371         c->border = 1;
372         c->proto = win_proto(c->win);
373         update_size(c);
374         XSelectInput(dpy, c->win,
375                         StructureNotifyMask | PropertyChangeMask | EnterWindowMask);
376         XGetTransientForHint(dpy, c->win, &c->trans);
377         twa.override_redirect = 1;
378         twa.background_pixmap = ParentRelative;
379         twa.event_mask = ExposureMask;
380
381         c->title = XCreateWindow(dpy, root, c->tx, c->ty, c->tw, c->th,
382                         0, DefaultDepth(dpy, screen), CopyFromParent,
383                         DefaultVisual(dpy, screen),
384                         CWOverrideRedirect | CWBackPixmap | CWEventMask, &twa);
385
386         update_name(c);
387         init_tags(c);
388
389         for(l = &clients; *l; l = &(*l)->next);
390         c->next = *l; /* *l == nil */
391         *l = c;
392
393         XSetWindowBorderWidth(dpy, c->win, 1);
394         XMapRaised(dpy, c->win);
395         XMapRaised(dpy, c->title);
396         XGrabButton(dpy, Button1, Mod1Mask, c->win, False, ButtonPressMask,
397                         GrabModeAsync, GrabModeSync, None, None);
398         XGrabButton(dpy, Button2, Mod1Mask, c->win, False, ButtonPressMask,
399                         GrabModeAsync, GrabModeSync, None, None);
400         XGrabButton(dpy, Button3, Mod1Mask, c->win, False, ButtonPressMask,
401                         GrabModeAsync, GrabModeSync, None, None);
402         arrange(NULL);
403         if(c->tags[tsel])
404                 focus(c);
405         else
406                 ban_client(c);
407 }
408
409 void
410 gravitate(Client *c, Bool invert)
411 {
412         int dx = 0, dy = 0;
413
414         switch(c->grav) {
415         case StaticGravity:
416         case NorthWestGravity:
417         case NorthGravity:
418         case NorthEastGravity:
419                 dy = c->border;
420                 break;
421         case EastGravity:
422         case CenterGravity:
423         case WestGravity:
424                 dy = -(c->h / 2) + c->border;
425                 break;
426         case SouthEastGravity:
427         case SouthGravity:
428         case SouthWestGravity:
429                 dy = -c->h;
430                 break;
431         default:
432                 break;
433         }
434
435         switch (c->grav) {
436         case StaticGravity:
437         case NorthWestGravity:
438         case WestGravity:
439         case SouthWestGravity:
440                 dx = c->border;
441                 break;
442         case NorthGravity:
443         case CenterGravity:
444         case SouthGravity:
445                 dx = -(c->w / 2) + c->border;
446                 break;
447         case NorthEastGravity:
448         case EastGravity:
449         case SouthEastGravity:
450                 dx = -(c->w + c->border);
451                 break;
452         default:
453                 break;
454         }
455
456         if(invert) {
457                 dx = -dx;
458                 dy = -dy;
459         }
460         c->x += dx;
461         c->y += dy;
462 }
463
464
465 void
466 resize(Client *c)
467 {
468         XConfigureEvent e;
469
470         if(c->incw)
471                 c->w -= (c->w - c->basew) % c->incw;
472         if(c->inch)
473                 c->h -= (c->h - c->baseh) % c->inch;
474         if(c->minw && c->w < c->minw)
475                 c->w = c->minw;
476         if(c->minh && c->h < c->minh)
477                 c->h = c->minh;
478         if(c->maxw && c->w > c->maxw)
479                 c->w = c->maxw;
480         if(c->maxh && c->h > c->maxh)
481                 c->h = c->maxh;
482         resize_title(c);
483         XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
484         e.type = ConfigureNotify;
485         e.event = c->win;
486         e.window = c->win;
487         e.x = c->x;
488         e.y = c->y;
489         e.width = c->w;
490         e.height = c->h;
491         e.border_width = c->border;
492         e.above = None;
493         e.override_redirect = False;
494         XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&e);
495         XFlush(dpy);
496 }
497
498 static int
499 dummy_error_handler(Display *dsply, XErrorEvent *err)
500 {
501         return 0;
502 }
503
504 void
505 unmanage(Client *c)
506 {
507         Client **l;
508
509         XGrabServer(dpy);
510         XSetErrorHandler(dummy_error_handler);
511
512         XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
513         XDestroyWindow(dpy, c->title);
514
515         for(l = &clients; *l && *l != c; l = &(*l)->next);
516         *l = c->next;
517         for(l = &clients; *l; l = &(*l)->next)
518                 if((*l)->revert == c)
519                         (*l)->revert = NULL;
520         if(sel == c)
521                 sel = sel->revert ? sel->revert : clients;
522
523         free(c);
524
525         XFlush(dpy);
526         XSetErrorHandler(error_handler);
527         XUngrabServer(dpy);
528         arrange(NULL);
529         if(sel)
530                 focus(sel);
531 }
532
533 Client *
534 gettitle(Window w)
535 {
536         Client *c;
537         for(c = clients; c; c = c->next)
538                 if(c->title == w)
539                         return c;
540         return NULL;
541 }
542
543 Client *
544 getclient(Window w)
545 {
546         Client *c;
547         for(c = clients; c; c = c->next)
548                 if(c->win == w)
549                         return c;
550         return NULL;
551 }
552
553 void
554 draw_client(Client *c)
555 {
556         int i;
557         if(c == sel)
558                 return;
559
560         dc.x = dc.y = 0;
561         dc.h = c->th;
562
563         dc.w = 0;
564         for(i = 0; i < TLast; i++) {
565                 if(c->tags[i]) {
566                         dc.x += dc.w;
567                         dc.w = textw(c->tags[i]) + dc.font.height;
568                         draw(True, c->tags[i]);
569                 }
570         }
571         dc.x += dc.w;
572         dc.w = textw(c->name) + dc.font.height;
573         draw(True, c->name);
574         XCopyArea(dpy, dc.drawable, c->title, dc.gc,
575                         0, 0, c->tw, c->th, 0, 0);
576         XFlush(dpy);
577 }