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