tabbed

[fork] xembed tabbing program
git clone https://hhvn.uk/tabbed
git clone git://hhvn.uk/tabbed
Log | Files | Refs | README | LICENSE

tabbed.c (30615B)


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