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