tabbed

Suckless' tabbed with my personal patches
git clone https://git.instinctive.eu/tabbed.git
Log | Files | Refs | README | LICENSE

tabbed.c (31701B)


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