Mahdi Mirzadeh

Home

About

Donate

Contact

Git

tabbed

Mahdi's build of tabbed
git clone https://github.com/MahdiMirzadeh/tabbed.git
Log | Files | Refs | README | LICENSE

tabbed.c (34312B)


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