tabbed

Simple tabbing application for X11.
git clone git://r-36.net/tabbed
Log | Files | Refs | README | LICENSE

tabbed.c (30581B)


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