Introduction
Introduction Statistics Contact Development Disclaimer Help
tabbed.c - tabbed - tab interface for application supporting Xembed
git clone git://git.suckless.org/tabbed
Log
Files
Refs
README
LICENSE
---
tabbed.c (30256B)
---
1 /*
2 * See LICENSE file for copyright and license details.
3 */
4
5 #include <sys/wait.h>
6 #include <locale.h>
7 #include <signal.h>
8 #include <stdarg.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <X11/Xatom.h>
14 #include <X11/keysym.h>
15 #include <X11/Xlib.h>
16 #include <X11/Xproto.h>
17 #include <X11/Xutil.h>
18 #include <X11/XKBlib.h>
19 #include <X11/Xft/Xft.h>
20
21 #include "arg.h"
22
23 /* XEMBED messages */
24 #define XEMBED_EMBEDDED_NOTIFY 0
25 #define XEMBED_WINDOW_ACTIVATE 1
26 #define XEMBED_WINDOW_DEACTIVATE 2
27 #define XEMBED_REQUEST_FOCUS 3
28 #define XEMBED_FOCUS_IN 4
29 #define XEMBED_FOCUS_OUT 5
30 #define XEMBED_FOCUS_NEXT 6
31 #define XEMBED_FOCUS_PREV 7
32 /* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */
33 #define XEMBED_MODALITY_ON 10
34 #define XEMBED_MODALITY_OFF 11
35 #define XEMBED_REGISTER_ACCELERATOR 12
36 #define XEMBED_UNREGISTER_ACCELERATOR 13
37 #define XEMBED_ACTIVATE_ACCELERATOR 14
38
39 /* Details for XEMBED_FOCUS_IN: */
40 #define XEMBED_FOCUS_CURRENT 0
41 #define XEMBED_FOCUS_FIRST 1
42 #define XEMBED_FOCUS_LAST 2
43
44 /* Macros */
45 #define MAX(a, b) ((a) > (b) ? (a) : (b))
46 #define MIN(a, b) ((a) < (b) ? (a) : (b))
47 #define LENGTH(x) (sizeof((x)) / sizeof(*(x)))
48 #define CLEANMASK(mask) (mask & ~(numlockmask | LockMask))
49 #define TEXTW(x) (textnw(x, strlen(x)) + dc.font.height)
50
51 enum { ColFG, ColBG, ColLast }; /* color */
52 enum { WMProtocols, WMDelete, WMName, WMState, WMFullscreen,
53 XEmbed, WMSelectTab, WMLast }; /* default atoms */
54
55 typedef union {
56 int i;
57 const void *v;
58 } Arg;
59
60 typedef struct {
61 unsigned int mod;
62 KeySym keysym;
63 void (*func)(const Arg *);
64 const Arg arg;
65 } Key;
66
67 typedef struct {
68 int x, y, w, h;
69 XftColor norm[ColLast];
70 XftColor sel[ColLast];
71 XftColor urg[ColLast];
72 Drawable drawable;
73 GC gc;
74 struct {
75 int ascent;
76 int descent;
77 int height;
78 XftFont *xfont;
79 } font;
80 } DC; /* draw context */
81
82 typedef struct {
83 char name[256];
84 Window win;
85 int tabx;
86 Bool urgent;
87 Bool closed;
88 } Client;
89
90 /* function declarations */
91 static void buttonpress(const XEvent *e);
92 static void cleanup(void);
93 static void clientmessage(const XEvent *e);
94 static void configurenotify(const XEvent *e);
95 static void configurerequest(const XEvent *e);
96 static void createnotify(const XEvent *e);
97 static void destroynotify(const XEvent *e);
98 static void die(const char *errstr, ...);
99 static void drawbar(void);
100 static void drawtext(const char *text, XftColor col[ColLast]);
101 static void *ecalloc(size_t n, size_t size);
102 static void *erealloc(void *o, size_t size);
103 static void expose(const XEvent *e);
104 static void focus(int c);
105 static void focusin(const XEvent *e);
106 static void focusonce(const Arg *arg);
107 static void focusurgent(const Arg *arg);
108 static void fullscreen(const Arg *arg);
109 static char *getatom(int a);
110 static int getclient(Window w);
111 static XftColor getcolor(const char *colstr);
112 static int getfirsttab(void);
113 static Bool gettextprop(Window w, Atom atom, char *text, unsigned int si…
114 static void initfont(const char *fontstr);
115 static Bool isprotodel(int c);
116 static void keypress(const XEvent *e);
117 static void killclient(const Arg *arg);
118 static void manage(Window win);
119 static void maprequest(const XEvent *e);
120 static void move(const Arg *arg);
121 static void movetab(const Arg *arg);
122 static void propertynotify(const XEvent *e);
123 static void resize(int c, int w, int h);
124 static void rotate(const Arg *arg);
125 static void run(void);
126 static void sendxembed(int c, long msg, long detail, long d1, long d2);
127 static void setcmd(int argc, char *argv[], int);
128 static void setup(void);
129 static void spawn(const Arg *arg);
130 static int textnw(const char *text, unsigned int len);
131 static void toggle(const Arg *arg);
132 static void unmanage(int c);
133 static void unmapnotify(const XEvent *e);
134 static void updatenumlockmask(void);
135 static void updatetitle(int c);
136 static int xerror(Display *dpy, XErrorEvent *ee);
137 static void xsettitle(Window w, const char *str);
138
139 /* variables */
140 static int screen;
141 static void (*handler[LASTEvent]) (const XEvent *) = {
142 [ButtonPress] = buttonpress,
143 [ClientMessage] = clientmessage,
144 [ConfigureNotify] = configurenotify,
145 [ConfigureRequest] = configurerequest,
146 [CreateNotify] = createnotify,
147 [UnmapNotify] = unmapnotify,
148 [DestroyNotify] = destroynotify,
149 [Expose] = expose,
150 [FocusIn] = focusin,
151 [KeyPress] = keypress,
152 [MapRequest] = maprequest,
153 [PropertyNotify] = propertynotify,
154 };
155 static int bh, obh, wx, wy, ww, wh;
156 static unsigned int numlockmask;
157 static Bool running = True, nextfocus, doinitspawn = True,
158 fillagain = False, closelastclient = False,
159 killclientsfirst = False;
160 static Display *dpy;
161 static DC dc;
162 static Atom wmatom[WMLast];
163 static Window root, win;
164 static Client **clients;
165 static int nclients, sel = -1, lastsel = -1;
166 static int (*xerrorxlib)(Display *, XErrorEvent *);
167 static int cmd_append_pos;
168 static char winid[64];
169 static char **cmd;
170 static char *wmname = "tabbed";
171 static const char *geometry;
172
173 char *argv0;
174
175 /* configuration, allows nested code to access above variables */
176 #include "config.h"
177
178 void
179 buttonpress(const XEvent *e)
180 {
181 const XButtonPressedEvent *ev = &e->xbutton;
182 int i, fc;
183 Arg arg;
184
185 if (ev->y < 0 || ev->y > bh)
186 return;
187
188 if (((fc = getfirsttab()) > 0 && ev->x < TEXTW(before)) || ev->x…
189 return;
190
191 for (i = fc; i < nclients; i++) {
192 if (clients[i]->tabx > ev->x) {
193 switch (ev->button) {
194 case Button1:
195 focus(i);
196 break;
197 case Button2:
198 focus(i);
199 killclient(NULL);
200 break;
201 case Button4: /* FALLTHROUGH */
202 case Button5:
203 arg.i = ev->button == Button4 ? -1 : 1;
204 rotate(&arg);
205 break;
206 }
207 break;
208 }
209 }
210 }
211
212 void
213 cleanup(void)
214 {
215 int i;
216
217 for (i = 0; i < nclients; i++) {
218 focus(i);
219 killclient(NULL);
220 XReparentWindow(dpy, clients[i]->win, root, 0, 0);
221 unmanage(i);
222 }
223 free(clients);
224 clients = NULL;
225
226 XFreePixmap(dpy, dc.drawable);
227 XFreeGC(dpy, dc.gc);
228 XDestroyWindow(dpy, win);
229 XSync(dpy, False);
230 free(cmd);
231 }
232
233 void
234 clientmessage(const XEvent *e)
235 {
236 const XClientMessageEvent *ev = &e->xclient;
237
238 if (ev->message_type == wmatom[WMProtocols] &&
239 ev->data.l[0] == wmatom[WMDelete]) {
240 if (nclients > 1 && killclientsfirst) {
241 killclient(0);
242 return;
243 }
244 running = False;
245 }
246 }
247
248 void
249 configurenotify(const XEvent *e)
250 {
251 const XConfigureEvent *ev = &e->xconfigure;
252
253 if (ev->window == win && (ev->width != ww || ev->height != wh)) {
254 ww = ev->width;
255 wh = ev->height;
256 XFreePixmap(dpy, dc.drawable);
257 dc.drawable = XCreatePixmap(dpy, root, ww, wh,
258 DefaultDepth(dpy, screen));
259
260 if (!obh && (wh <= bh)) {
261 obh = bh;
262 bh = 0;
263 } else if (!bh && (wh > obh)) {
264 bh = obh;
265 obh = 0;
266 }
267
268 if (sel > -1)
269 resize(sel, ww, wh - bh);
270 XSync(dpy, False);
271 }
272 }
273
274 void
275 configurerequest(const XEvent *e)
276 {
277 const XConfigureRequestEvent *ev = &e->xconfigurerequest;
278 XWindowChanges wc;
279 int c;
280
281 if ((c = getclient(ev->window)) > -1) {
282 wc.x = 0;
283 wc.y = bh;
284 wc.width = ww;
285 wc.height = wh - bh;
286 wc.border_width = 0;
287 wc.sibling = ev->above;
288 wc.stack_mode = ev->detail;
289 XConfigureWindow(dpy, clients[c]->win, ev->value_mask, &…
290 }
291 }
292
293 void
294 createnotify(const XEvent *e)
295 {
296 const XCreateWindowEvent *ev = &e->xcreatewindow;
297
298 if (ev->window != win && getclient(ev->window) < 0)
299 manage(ev->window);
300 }
301
302 void
303 destroynotify(const XEvent *e)
304 {
305 const XDestroyWindowEvent *ev = &e->xdestroywindow;
306 int c;
307
308 if ((c = getclient(ev->window)) > -1)
309 unmanage(c);
310 }
311
312 void
313 die(const char *errstr, ...)
314 {
315 va_list ap;
316
317 va_start(ap, errstr);
318 vfprintf(stderr, errstr, ap);
319 va_end(ap);
320 exit(EXIT_FAILURE);
321 }
322
323 void
324 drawbar(void)
325 {
326 XftColor *col;
327 int c, cc, fc, width;
328 char *name = NULL;
329
330 if (nclients == 0) {
331 dc.x = 0;
332 dc.w = ww;
333 XFetchName(dpy, win, &name);
334 drawtext(name ? name : "", dc.norm);
335 XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0,…
336 XSync(dpy, False);
337
338 return;
339 }
340
341 width = ww;
342 cc = ww / tabwidth;
343 if (nclients > cc)
344 cc = (ww - TEXTW(before) - TEXTW(after)) / tabwidth;
345
346 if ((fc = getfirsttab()) + cc < nclients) {
347 dc.w = TEXTW(after);
348 dc.x = width - dc.w;
349 drawtext(after, dc.sel);
350 width -= dc.w;
351 }
352 dc.x = 0;
353
354 if (fc > 0) {
355 dc.w = TEXTW(before);
356 drawtext(before, dc.sel);
357 dc.x += dc.w;
358 width -= dc.w;
359 }
360
361 cc = MIN(cc, nclients);
362 for (c = fc; c < fc + cc; c++) {
363 dc.w = width / cc;
364 if (c == sel) {
365 col = dc.sel;
366 dc.w += width % cc;
367 } else {
368 col = clients[c]->urgent ? dc.urg : dc.norm;
369 }
370 drawtext(clients[c]->name, col);
371 dc.x += dc.w;
372 clients[c]->tabx = dc.x;
373 }
374 XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, ww, bh, 0, 0);
375 XSync(dpy, False);
376 }
377
378 void
379 drawtext(const char *text, XftColor col[ColLast])
380 {
381 int i, j, x, y, h, len, olen;
382 char buf[256];
383 XftDraw *d;
384 XRectangle r = { dc.x, dc.y, dc.w, dc.h };
385
386 XSetForeground(dpy, dc.gc, col[ColBG].pixel);
387 XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
388 if (!text)
389 return;
390
391 olen = strlen(text);
392 h = dc.font.ascent + dc.font.descent;
393 y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent;
394 x = dc.x + (h / 2);
395
396 /* shorten text if necessary */
397 for (len = MIN(olen, sizeof(buf));
398 len && textnw(text, len) > dc.w - h; len--);
399
400 if (!len)
401 return;
402
403 memcpy(buf, text, len);
404 if (len < olen) {
405 for (i = len, j = strlen(titletrim); j && i;
406 buf[--i] = titletrim[--j])
407 ;
408 }
409
410 d = XftDrawCreate(dpy, dc.drawable, DefaultVisual(dpy, screen), …
411 XftDrawStringUtf8(d, &col[ColFG], dc.font.xfont, x, y, (XftChar8…
412 XftDrawDestroy(d);
413 }
414
415 void *
416 ecalloc(size_t n, size_t size)
417 {
418 void *p;
419
420 if (!(p = calloc(n, size)))
421 die("%s: cannot calloc\n", argv0);
422 return p;
423 }
424
425 void *
426 erealloc(void *o, size_t size)
427 {
428 void *p;
429
430 if (!(p = realloc(o, size)))
431 die("%s: cannot realloc\n", argv0);
432 return p;
433 }
434
435 void
436 expose(const XEvent *e)
437 {
438 const XExposeEvent *ev = &e->xexpose;
439
440 if (ev->count == 0 && win == ev->window)
441 drawbar();
442 }
443
444 void
445 focus(int c)
446 {
447 char buf[BUFSIZ] = "tabbed-"VERSION" ::";
448 size_t i, n;
449 XWMHints* wmh;
450 XWMHints* win_wmh;
451
452 /* If c, sel and clients are -1, raise tabbed-win itself */
453 if (nclients == 0) {
454 cmd[cmd_append_pos] = NULL;
455 for(i = 0, n = strlen(buf); cmd[i] && n < sizeof(buf); i…
456 n += snprintf(&buf[n], sizeof(buf) - n, " %s", c…
457
458 xsettitle(win, buf);
459 XRaiseWindow(dpy, win);
460
461 return;
462 }
463
464 if (c < 0 || c >= nclients)
465 return;
466
467 resize(c, ww, wh - bh);
468 XRaiseWindow(dpy, clients[c]->win);
469 XSetInputFocus(dpy, clients[c]->win, RevertToParent, CurrentTime…
470 sendxembed(c, XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0);
471 sendxembed(c, XEMBED_WINDOW_ACTIVATE, 0, 0, 0);
472 xsettitle(win, clients[c]->name);
473
474 if (sel != c) {
475 lastsel = sel;
476 sel = c;
477 }
478
479 if (clients[c]->urgent && (wmh = XGetWMHints(dpy, clients[c]->wi…
480 wmh->flags &= ~XUrgencyHint;
481 XSetWMHints(dpy, clients[c]->win, wmh);
482 clients[c]->urgent = False;
483 XFree(wmh);
484
485 /*
486 * gnome-shell will not stop notifying us about urgency,
487 * if we clear only the client hint and don't clear the
488 * hint from the main container window
489 */
490 if ((win_wmh = XGetWMHints(dpy, win))) {
491 win_wmh->flags &= ~XUrgencyHint;
492 XSetWMHints(dpy, win, win_wmh);
493 XFree(win_wmh);
494 }
495 }
496
497 drawbar();
498 XSync(dpy, False);
499 }
500
501 void
502 focusin(const XEvent *e)
503 {
504 const XFocusChangeEvent *ev = &e->xfocus;
505 int dummy;
506 Window focused;
507
508 if (ev->mode != NotifyUngrab) {
509 XGetInputFocus(dpy, &focused, &dummy);
510 if (focused == win)
511 focus(sel);
512 }
513 }
514
515 void
516 focusonce(const Arg *arg)
517 {
518 nextfocus = True;
519 }
520
521 void
522 focusurgent(const Arg *arg)
523 {
524 int c;
525
526 if (sel < 0)
527 return;
528
529 for (c = (sel + 1) % nclients; c != sel; c = (c + 1) % nclients)…
530 if (clients[c]->urgent) {
531 focus(c);
532 return;
533 }
534 }
535 }
536
537 void
538 fullscreen(const Arg *arg)
539 {
540 XEvent e;
541
542 e.type = ClientMessage;
543 e.xclient.window = win;
544 e.xclient.message_type = wmatom[WMState];
545 e.xclient.format = 32;
546 e.xclient.data.l[0] = 2;
547 e.xclient.data.l[1] = wmatom[WMFullscreen];
548 e.xclient.data.l[2] = 0;
549 XSendEvent(dpy, root, False, SubstructureNotifyMask, &e);
550 }
551
552 char *
553 getatom(int a)
554 {
555 static char buf[BUFSIZ];
556 Atom adummy;
557 int idummy;
558 unsigned long ldummy;
559 unsigned char *p = NULL;
560
561 XGetWindowProperty(dpy, win, wmatom[a], 0L, BUFSIZ, False, XA_ST…
562 &adummy, &idummy, &ldummy, &ldummy, &p);
563 if (p)
564 strncpy(buf, (char *)p, LENGTH(buf)-1);
565 else
566 buf[0] = '\0';
567 XFree(p);
568
569 return buf;
570 }
571
572 int
573 getclient(Window w)
574 {
575 int i;
576
577 for (i = 0; i < nclients; i++) {
578 if (clients[i]->win == w)
579 return i;
580 }
581
582 return -1;
583 }
584
585 XftColor
586 getcolor(const char *colstr)
587 {
588 XftColor color;
589
590 if (!XftColorAllocName(dpy, DefaultVisual(dpy, screen), DefaultC…
591 die("%s: cannot allocate color '%s'\n", argv0, colstr);
592
593 return color;
594 }
595
596 int
597 getfirsttab(void)
598 {
599 int cc, ret;
600
601 if (sel < 0)
602 return 0;
603
604 cc = ww / tabwidth;
605 if (nclients > cc)
606 cc = (ww - TEXTW(before) - TEXTW(after)) / tabwidth;
607
608 ret = sel - cc / 2 + (cc + 1) % 2;
609 return ret < 0 ? 0 :
610 ret + cc > nclients ? MAX(0, nclients - cc) :
611 ret;
612 }
613
614 Bool
615 gettextprop(Window w, Atom atom, char *text, unsigned int size)
616 {
617 char **list = NULL;
618 int n;
619 XTextProperty name;
620
621 if (!text || size == 0)
622 return False;
623
624 text[0] = '\0';
625 XGetTextProperty(dpy, w, &name, atom);
626 if (!name.nitems)
627 return False;
628
629 if (name.encoding == XA_STRING) {
630 strncpy(text, (char *)name.value, size - 1);
631 } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= S…
632 && n > 0 && *list) {
633 strncpy(text, *list, size - 1);
634 XFreeStringList(list);
635 }
636 text[size - 1] = '\0';
637 XFree(name.value);
638
639 return True;
640 }
641
642 void
643 initfont(const char *fontstr)
644 {
645 if (!(dc.font.xfont = XftFontOpenName(dpy, screen, fontstr))
646 && !(dc.font.xfont = XftFontOpenName(dpy, screen, "fixed")))
647 die("error, cannot load font: '%s'\n", fontstr);
648
649 dc.font.ascent = dc.font.xfont->ascent;
650 dc.font.descent = dc.font.xfont->descent;
651 dc.font.height = dc.font.ascent + dc.font.descent;
652 }
653
654 Bool
655 isprotodel(int c)
656 {
657 int i, n;
658 Atom *protocols;
659 Bool ret = False;
660
661 if (XGetWMProtocols(dpy, clients[c]->win, &protocols, &n)) {
662 for (i = 0; !ret && i < n; i++) {
663 if (protocols[i] == wmatom[WMDelete])
664 ret = True;
665 }
666 XFree(protocols);
667 }
668
669 return ret;
670 }
671
672 void
673 keypress(const XEvent *e)
674 {
675 const XKeyEvent *ev = &e->xkey;
676 unsigned int i;
677 KeySym keysym;
678
679 keysym = XkbKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0, 0);
680 for (i = 0; i < LENGTH(keys); i++) {
681 if (keysym == keys[i].keysym &&
682 CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) &&
683 keys[i].func)
684 keys[i].func(&(keys[i].arg));
685 }
686 }
687
688 void
689 killclient(const Arg *arg)
690 {
691 XEvent ev;
692
693 if (sel < 0)
694 return;
695
696 if (isprotodel(sel) && !clients[sel]->closed) {
697 ev.type = ClientMessage;
698 ev.xclient.window = clients[sel]->win;
699 ev.xclient.message_type = wmatom[WMProtocols];
700 ev.xclient.format = 32;
701 ev.xclient.data.l[0] = wmatom[WMDelete];
702 ev.xclient.data.l[1] = CurrentTime;
703 XSendEvent(dpy, clients[sel]->win, False, NoEventMask, &…
704 clients[sel]->closed = True;
705 } else {
706 XKillClient(dpy, clients[sel]->win);
707 }
708 }
709
710 void
711 manage(Window w)
712 {
713 updatenumlockmask();
714 {
715 int i, j, nextpos;
716 unsigned int modifiers[] = { 0, LockMask, numlockmask,
717 numlockmask | LockMask };
718 KeyCode code;
719 Client *c;
720 XEvent e;
721
722 XWithdrawWindow(dpy, w, 0);
723 XReparentWindow(dpy, w, win, 0, bh);
724 XSelectInput(dpy, w, PropertyChangeMask |
725 StructureNotifyMask | EnterWindowMask);
726 XSync(dpy, False);
727
728 for (i = 0; i < LENGTH(keys); i++) {
729 if ((code = XKeysymToKeycode(dpy, keys[i].keysym…
730 for (j = 0; j < LENGTH(modifiers); j++) {
731 XGrabKey(dpy, code, keys[i].mod |
732 modifiers[j], w, True,
733 GrabModeAsync, GrabMode…
734 }
735 }
736 }
737
738 c = ecalloc(1, sizeof *c);
739 c->win = w;
740
741 nclients++;
742 clients = erealloc(clients, sizeof(Client *) * nclients);
743
744 if(npisrelative) {
745 nextpos = sel + newposition;
746 } else {
747 if (newposition < 0)
748 nextpos = nclients - newposition;
749 else
750 nextpos = newposition;
751 }
752 if (nextpos >= nclients)
753 nextpos = nclients - 1;
754 if (nextpos < 0)
755 nextpos = 0;
756
757 if (nclients > 1 && nextpos < nclients - 1)
758 memmove(&clients[nextpos + 1], &clients[nextpos],
759 sizeof(Client *) * (nclients - nextpos -…
760
761 clients[nextpos] = c;
762 updatetitle(nextpos);
763
764 XLowerWindow(dpy, w);
765 XMapWindow(dpy, w);
766
767 e.xclient.window = w;
768 e.xclient.type = ClientMessage;
769 e.xclient.message_type = wmatom[XEmbed];
770 e.xclient.format = 32;
771 e.xclient.data.l[0] = CurrentTime;
772 e.xclient.data.l[1] = XEMBED_EMBEDDED_NOTIFY;
773 e.xclient.data.l[2] = 0;
774 e.xclient.data.l[3] = win;
775 e.xclient.data.l[4] = 0;
776 XSendEvent(dpy, root, False, NoEventMask, &e);
777
778 XSync(dpy, False);
779
780 /* Adjust sel before focus does set it to lastsel. */
781 if (sel >= nextpos)
782 sel++;
783 focus(nextfocus ? nextpos :
784 sel < 0 ? 0 :
785 sel);
786 nextfocus = foreground;
787 }
788 }
789
790 void
791 maprequest(const XEvent *e)
792 {
793 const XMapRequestEvent *ev = &e->xmaprequest;
794
795 if (getclient(ev->window) < 0)
796 manage(ev->window);
797 }
798
799 void
800 move(const Arg *arg)
801 {
802 if (arg->i >= 0 && arg->i < nclients)
803 focus(arg->i);
804 }
805
806 void
807 movetab(const Arg *arg)
808 {
809 int c;
810 Client *new;
811
812 if (sel < 0)
813 return;
814
815 c = (sel + arg->i) % nclients;
816 if (c < 0)
817 c += nclients;
818
819 if (c == sel)
820 return;
821
822 new = clients[sel];
823 if (sel < c)
824 memmove(&clients[sel], &clients[sel+1],
825 sizeof(Client *) * (c - sel));
826 else
827 memmove(&clients[c+1], &clients[c],
828 sizeof(Client *) * (sel - c));
829 clients[c] = new;
830 sel = c;
831
832 drawbar();
833 }
834
835 void
836 propertynotify(const XEvent *e)
837 {
838 const XPropertyEvent *ev = &e->xproperty;
839 XWMHints *wmh;
840 int c;
841 char* selection = NULL;
842 Arg arg;
843
844 if (ev->state == PropertyNewValue && ev->atom == wmatom[WMSelect…
845 selection = getatom(WMSelectTab);
846 if (!strncmp(selection, "0x", 2)) {
847 arg.i = getclient(strtoul(selection, NULL, 0));
848 move(&arg);
849 } else {
850 cmd[cmd_append_pos] = selection;
851 arg.v = cmd;
852 spawn(&arg);
853 }
854 } else if (ev->state == PropertyNewValue && ev->atom == XA_WM_HI…
855 (c = getclient(ev->window)) > -1 &&
856 (wmh = XGetWMHints(dpy, clients[c]->win))) {
857 if (wmh->flags & XUrgencyHint) {
858 XFree(wmh);
859 wmh = XGetWMHints(dpy, win);
860 if (c != sel) {
861 if (urgentswitch && wmh &&
862 !(wmh->flags & XUrgencyHint)) {
863 /* only switch, if tabbed was fo…
864 * since last urgency hint if WM…
865 * could not be received,
866 * default to no switch */
867 focus(c);
868 } else {
869 /* if no switch should be perfor…
870 * mark tab as urgent */
871 clients[c]->urgent = True;
872 drawbar();
873 }
874 }
875 if (wmh && !(wmh->flags & XUrgencyHint)) {
876 /* update tabbed urgency hint
877 * if not set already */
878 wmh->flags |= XUrgencyHint;
879 XSetWMHints(dpy, win, wmh);
880 }
881 }
882 XFree(wmh);
883 } else if (ev->state != PropertyDelete && ev->atom == XA_WM_NAME…
884 (c = getclient(ev->window)) > -1) {
885 updatetitle(c);
886 }
887 }
888
889 void
890 resize(int c, int w, int h)
891 {
892 XConfigureEvent ce;
893 XWindowChanges wc;
894
895 ce.x = 0;
896 ce.y = wc.y = bh;
897 ce.width = wc.width = w;
898 ce.height = wc.height = h;
899 ce.type = ConfigureNotify;
900 ce.display = dpy;
901 ce.event = clients[c]->win;
902 ce.window = clients[c]->win;
903 ce.above = None;
904 ce.override_redirect = False;
905 ce.border_width = 0;
906
907 XConfigureWindow(dpy, clients[c]->win, CWY | CWWidth | CWHeight,…
908 XSendEvent(dpy, clients[c]->win, False, StructureNotifyMask,
909 (XEvent *)&ce);
910 }
911
912 void
913 rotate(const Arg *arg)
914 {
915 int nsel = -1;
916
917 if (sel < 0)
918 return;
919
920 if (arg->i == 0) {
921 if (lastsel > -1)
922 focus(lastsel);
923 } else if (sel > -1) {
924 /* Rotating in an arg->i step around the clients. */
925 nsel = sel + arg->i;
926 while (nsel >= nclients)
927 nsel -= nclients;
928 while (nsel < 0)
929 nsel += nclients;
930 focus(nsel);
931 }
932 }
933
934 void
935 run(void)
936 {
937 XEvent ev;
938
939 /* main event loop */
940 XSync(dpy, False);
941 drawbar();
942 if (doinitspawn == True)
943 spawn(NULL);
944
945 while (running) {
946 XNextEvent(dpy, &ev);
947 if (handler[ev.type])
948 (handler[ev.type])(&ev); /* call handler */
949 }
950 }
951
952 void
953 sendxembed(int c, long msg, long detail, long d1, long d2)
954 {
955 XEvent e = { 0 };
956
957 e.xclient.window = clients[c]->win;
958 e.xclient.type = ClientMessage;
959 e.xclient.message_type = wmatom[XEmbed];
960 e.xclient.format = 32;
961 e.xclient.data.l[0] = CurrentTime;
962 e.xclient.data.l[1] = msg;
963 e.xclient.data.l[2] = detail;
964 e.xclient.data.l[3] = d1;
965 e.xclient.data.l[4] = d2;
966 XSendEvent(dpy, clients[c]->win, False, NoEventMask, &e);
967 }
968
969 void
970 setcmd(int argc, char *argv[], int replace)
971 {
972 int i;
973
974 cmd = ecalloc(argc + 3, sizeof(*cmd));
975 if (argc == 0)
976 return;
977 for (i = 0; i < argc; i++)
978 cmd[i] = argv[i];
979 cmd[replace > 0 ? replace : argc] = winid;
980 cmd_append_pos = argc + !replace;
981 cmd[cmd_append_pos] = cmd[cmd_append_pos + 1] = NULL;
982 }
983
984 void
985 setup(void)
986 {
987 int bitm, tx, ty, tw, th, dh, dw, isfixed;
988 XWMHints *wmh;
989 XClassHint class_hint;
990 XSizeHints *size_hint;
991 struct sigaction sa;
992
993 /* do not transform children into zombies when they terminate */
994 sigemptyset(&sa.sa_mask);
995 sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART;
996 sa.sa_handler = SIG_IGN;
997 sigaction(SIGCHLD, &sa, NULL);
998
999 /* clean up any zombies that might have been inherited */
1000 while (waitpid(-1, NULL, WNOHANG) > 0);
1001
1002 /* init screen */
1003 screen = DefaultScreen(dpy);
1004 root = RootWindow(dpy, screen);
1005 initfont(font);
1006 bh = dc.h = dc.font.height + 2;
1007
1008 /* init atoms */
1009 wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
1010 wmatom[WMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREE…
1011 False);
1012 wmatom[WMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
1013 wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False);
1014 wmatom[WMSelectTab] = XInternAtom(dpy, "_TABBED_SELECT_TAB", Fal…
1015 wmatom[WMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
1016 wmatom[XEmbed] = XInternAtom(dpy, "_XEMBED", False);
1017
1018 /* init appearance */
1019 wx = 0;
1020 wy = 0;
1021 ww = 800;
1022 wh = 600;
1023 isfixed = 0;
1024
1025 if (geometry) {
1026 tx = ty = tw = th = 0;
1027 bitm = XParseGeometry(geometry, &tx, &ty, (unsigned *)&t…
1028 (unsigned *)&th);
1029 if (bitm & XValue)
1030 wx = tx;
1031 if (bitm & YValue)
1032 wy = ty;
1033 if (bitm & WidthValue)
1034 ww = tw;
1035 if (bitm & HeightValue)
1036 wh = th;
1037 if (bitm & XNegative && wx == 0)
1038 wx = -1;
1039 if (bitm & YNegative && wy == 0)
1040 wy = -1;
1041 if (bitm & (HeightValue | WidthValue))
1042 isfixed = 1;
1043
1044 dw = DisplayWidth(dpy, screen);
1045 dh = DisplayHeight(dpy, screen);
1046 if (wx < 0)
1047 wx = dw + wx - ww - 1;
1048 if (wy < 0)
1049 wy = dh + wy - wh - 1;
1050 }
1051
1052 dc.norm[ColBG] = getcolor(normbgcolor);
1053 dc.norm[ColFG] = getcolor(normfgcolor);
1054 dc.sel[ColBG] = getcolor(selbgcolor);
1055 dc.sel[ColFG] = getcolor(selfgcolor);
1056 dc.urg[ColBG] = getcolor(urgbgcolor);
1057 dc.urg[ColFG] = getcolor(urgfgcolor);
1058 dc.drawable = XCreatePixmap(dpy, root, ww, wh,
1059 DefaultDepth(dpy, screen));
1060 dc.gc = XCreateGC(dpy, root, 0, 0);
1061
1062 win = XCreateSimpleWindow(dpy, root, wx, wy, ww, wh, 0,
1063 dc.norm[ColFG].pixel, dc.norm[ColBG].p…
1064 XMapRaised(dpy, win);
1065 XSelectInput(dpy, win, SubstructureNotifyMask | FocusChangeMask |
1066 ButtonPressMask | ExposureMask | KeyPressMask |
1067 PropertyChangeMask | StructureNotifyMask |
1068 SubstructureRedirectMask);
1069 xerrorxlib = XSetErrorHandler(xerror);
1070
1071 class_hint.res_name = wmname;
1072 class_hint.res_class = "tabbed";
1073 XSetClassHint(dpy, win, &class_hint);
1074
1075 size_hint = XAllocSizeHints();
1076 if (!isfixed) {
1077 size_hint->flags = PSize | PMinSize;
1078 size_hint->height = wh;
1079 size_hint->width = ww;
1080 size_hint->min_height = bh + 1;
1081 } else {
1082 size_hint->flags = PMaxSize | PMinSize;
1083 size_hint->min_width = size_hint->max_width = ww;
1084 size_hint->min_height = size_hint->max_height = wh;
1085 }
1086 wmh = XAllocWMHints();
1087 XSetWMProperties(dpy, win, NULL, NULL, NULL, 0, size_hint, wmh, …
1088 XFree(size_hint);
1089 XFree(wmh);
1090
1091 XSetWMProtocols(dpy, win, &wmatom[WMDelete], 1);
1092
1093 snprintf(winid, sizeof(winid), "%lu", win);
1094 setenv("XEMBED", winid, 1);
1095
1096 nextfocus = foreground;
1097 focus(-1);
1098 }
1099
1100 void
1101 spawn(const Arg *arg)
1102 {
1103 struct sigaction sa;
1104
1105 if (fork() == 0) {
1106 if(dpy)
1107 close(ConnectionNumber(dpy));
1108
1109 setsid();
1110
1111 sigemptyset(&sa.sa_mask);
1112 sa.sa_flags = 0;
1113 sa.sa_handler = SIG_DFL;
1114 sigaction(SIGCHLD, &sa, NULL);
1115
1116 if (arg && arg->v) {
1117 execvp(((char **)arg->v)[0], (char **)arg->v);
1118 fprintf(stderr, "%s: execvp %s", argv0,
1119 ((char **)arg->v)[0]);
1120 } else {
1121 cmd[cmd_append_pos] = NULL;
1122 execvp(cmd[0], cmd);
1123 fprintf(stderr, "%s: execvp %s", argv0, cmd[0]);
1124 }
1125 perror(" failed");
1126 exit(0);
1127 }
1128 }
1129
1130 int
1131 textnw(const char *text, unsigned int len)
1132 {
1133 XGlyphInfo ext;
1134 XftTextExtentsUtf8(dpy, dc.font.xfont, (XftChar8 *) text, len, &…
1135 return ext.xOff;
1136 }
1137
1138 void
1139 toggle(const Arg *arg)
1140 {
1141 *(Bool*) arg->v = !*(Bool*) arg->v;
1142 }
1143
1144 void
1145 unmanage(int c)
1146 {
1147 if (c < 0 || c >= nclients) {
1148 drawbar();
1149 XSync(dpy, False);
1150 return;
1151 }
1152
1153 if (!nclients)
1154 return;
1155
1156 if (c == 0) {
1157 /* First client. */
1158 nclients--;
1159 free(clients[0]);
1160 memmove(&clients[0], &clients[1], sizeof(Client *) * ncl…
1161 } else if (c == nclients - 1) {
1162 /* Last client. */
1163 nclients--;
1164 free(clients[c]);
1165 clients = erealloc(clients, sizeof(Client *) * nclients);
1166 } else {
1167 /* Somewhere inbetween. */
1168 free(clients[c]);
1169 memmove(&clients[c], &clients[c+1],
1170 sizeof(Client *) * (nclients - (c + 1)));
1171 nclients--;
1172 }
1173
1174 if (nclients <= 0) {
1175 lastsel = sel = -1;
1176
1177 if (closelastclient)
1178 running = False;
1179 else if (fillagain && running)
1180 spawn(NULL);
1181 } else {
1182 if (lastsel >= nclients)
1183 lastsel = nclients - 1;
1184 else if (lastsel > c)
1185 lastsel--;
1186
1187 if (c == sel && lastsel >= 0) {
1188 focus(lastsel);
1189 } else {
1190 if (sel > c)
1191 sel--;
1192 if (sel >= nclients)
1193 sel = nclients - 1;
1194
1195 focus(sel);
1196 }
1197 }
1198
1199 drawbar();
1200 XSync(dpy, False);
1201 }
1202
1203 void
1204 unmapnotify(const XEvent *e)
1205 {
1206 const XUnmapEvent *ev = &e->xunmap;
1207 int c;
1208
1209 if ((c = getclient(ev->window)) > -1)
1210 unmanage(c);
1211 }
1212
1213 void
1214 updatenumlockmask(void)
1215 {
1216 unsigned int i, j;
1217 XModifierKeymap *modmap;
1218
1219 numlockmask = 0;
1220 modmap = XGetModifierMapping(dpy);
1221 for (i = 0; i < 8; i++) {
1222 for (j = 0; j < modmap->max_keypermod; j++) {
1223 if (modmap->modifiermap[i * modmap->max_keypermo…
1224 == XKeysymToKeycode(dpy, XK_Num_Lock))
1225 numlockmask = (1 << i);
1226 }
1227 }
1228 XFreeModifiermap(modmap);
1229 }
1230
1231 void
1232 updatetitle(int c)
1233 {
1234 if (!gettextprop(clients[c]->win, wmatom[WMName], clients[c]->na…
1235 sizeof(clients[c]->name)))
1236 gettextprop(clients[c]->win, XA_WM_NAME, clients[c]->nam…
1237 sizeof(clients[c]->name));
1238 if (sel == c)
1239 xsettitle(win, clients[c]->name);
1240 drawbar();
1241 }
1242
1243 /* There's no way to check accesses to destroyed windows, thus those cas…
1244 * ignored (especially on UnmapNotify's). Other types of errors call Xl…
1245 * default error handler, which may call exit. */
1246 int
1247 xerror(Display *dpy, XErrorEvent *ee)
1248 {
1249 if (ee->error_code == BadWindow
1250 || (ee->request_code == X_SetInputFocus &&
1251 ee->error_code == BadMatch)
1252 || (ee->request_code == X_PolyText8 &&
1253 ee->error_code == BadDrawable)
1254 || (ee->request_code == X_PolyFillRectangle &&
1255 ee->error_code == BadDrawable)
1256 || (ee->request_code == X_PolySegment &&
1257 ee->error_code == BadDrawable)
1258 || (ee->request_code == X_ConfigureWindow &&
1259 ee->error_code == BadMatch)
1260 || (ee->request_code == X_GrabButton &&
1261 ee->error_code == BadAccess)
1262 || (ee->request_code == X_GrabKey &&
1263 ee->error_code == BadAccess)
1264 || (ee->request_code == X_CopyArea &&
1265 ee->error_code == BadDrawable))
1266 return 0;
1267
1268 fprintf(stderr, "%s: fatal error: request code=%d, error code=%d…
1269 argv0, ee->request_code, ee->error_code);
1270 return xerrorxlib(dpy, ee); /* may call exit */
1271 }
1272
1273 void
1274 xsettitle(Window w, const char *str)
1275 {
1276 XTextProperty xtp;
1277
1278 if (XmbTextListToTextProperty(dpy, (char **)&str, 1,
1279 XUTF8StringStyle, &xtp) == Success) {
1280 XSetTextProperty(dpy, w, &xtp, wmatom[WMName]);
1281 XSetTextProperty(dpy, w, &xtp, XA_WM_NAME);
1282 XFree(xtp.value);
1283 }
1284 }
1285
1286 void
1287 usage(void)
1288 {
1289 die("usage: %s [-dfksv] [-g geometry] [-n name] [-p [s+/-]pos]\n"
1290 " [-r narg] [-o color] [-O color] [-t color] [-T color…
1291 " [-u color] [-U color] command...\n", argv0);
1292 }
1293
1294 int
1295 main(int argc, char *argv[])
1296 {
1297 Bool detach = False;
1298 int replace = 0;
1299 char *pstr;
1300
1301 ARGBEGIN {
1302 case 'c':
1303 closelastclient = True;
1304 fillagain = False;
1305 break;
1306 case 'd':
1307 detach = True;
1308 break;
1309 case 'f':
1310 fillagain = True;
1311 break;
1312 case 'g':
1313 geometry = EARGF(usage());
1314 break;
1315 case 'k':
1316 killclientsfirst = True;
1317 break;
1318 case 'n':
1319 wmname = EARGF(usage());
1320 break;
1321 case 'O':
1322 normfgcolor = EARGF(usage());
1323 break;
1324 case 'o':
1325 normbgcolor = EARGF(usage());
1326 break;
1327 case 'p':
1328 pstr = EARGF(usage());
1329 if (pstr[0] == 's') {
1330 npisrelative = True;
1331 newposition = atoi(&pstr[1]);
1332 } else {
1333 newposition = atoi(pstr);
1334 }
1335 break;
1336 case 'r':
1337 replace = atoi(EARGF(usage()));
1338 break;
1339 case 's':
1340 doinitspawn = False;
1341 break;
1342 case 'T':
1343 selfgcolor = EARGF(usage());
1344 break;
1345 case 't':
1346 selbgcolor = EARGF(usage());
1347 break;
1348 case 'U':
1349 urgfgcolor = EARGF(usage());
1350 break;
1351 case 'u':
1352 urgbgcolor = EARGF(usage());
1353 break;
1354 case 'v':
1355 die("tabbed-"VERSION", © 2009-2016 tabbed engineers, "
1356 "see LICENSE for details.\n");
1357 break;
1358 default:
1359 usage();
1360 break;
1361 } ARGEND;
1362
1363 if (argc < 1) {
1364 doinitspawn = False;
1365 fillagain = False;
1366 }
1367
1368 setcmd(argc, argv, replace);
1369
1370 if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
1371 fprintf(stderr, "%s: no locale support\n", argv0);
1372 if (!(dpy = XOpenDisplay(NULL)))
1373 die("%s: cannot open display\n", argv0);
1374
1375 setup();
1376 printf("0x%lx\n", win);
1377 fflush(NULL);
1378
1379 if (detach) {
1380 if (fork() == 0) {
1381 fclose(stdout);
1382 } else {
1383 if (dpy)
1384 close(ConnectionNumber(dpy));
1385 return EXIT_SUCCESS;
1386 }
1387 }
1388
1389 run();
1390 cleanup();
1391 XCloseDisplay(dpy);
1392
1393 return EXIT_SUCCESS;
1394 }
You are viewing proxied material from suckless.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.