tabbed

Mahdi's build of tabbed
git clone git://mahdi.pw/tabbed.git
Log | Files | Refs | README | LICENSE

tabbed.c (33173B)


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