URI: 
       thingmenu.c - thingmenu - A simple graphical menu launcher for X11.
  HTML git clone git://bitreich.org/thingmenu
   DIR Log
   DIR Files
   DIR Refs
   DIR Tags
   DIR LICENSE
       ---
       thingmenu.c (15423B)
       ---
            1 /*
            2  * Copy me if you can.
            3  * by 20h
            4  */
            5 #include <unistd.h>
            6 #include <locale.h>
            7 #include <signal.h>
            8 #include <stdarg.h>
            9 #include <stdio.h>
           10 #include <string.h>
           11 #include <stdlib.h>
           12 #include <libgen.h>
           13 #include <sys/wait.h>
           14 #include <X11/keysym.h>
           15 #include <X11/Xatom.h>
           16 #include <X11/Xlib.h>
           17 #include <X11/Xutil.h>
           18 #include <X11/Xproto.h>
           19 #include <X11/extensions/XTest.h>
           20 
           21 /* macros */
           22 #define MAX(a, b)       ((a) > (b) ? (a) : (b))
           23 #define LENGTH(x)       (sizeof x / sizeof x[0])
           24 
           25 /* enums */
           26 enum { ColFG, ColBG, ColLast };
           27 enum { NetWMWindowType, NetLast };
           28 
           29 /* typedefs */
           30 typedef unsigned int uint;
           31 typedef unsigned long ulong;
           32 
           33 typedef struct {
           34         ulong norm[ColLast];
           35         ulong press[ColLast];
           36         ulong high[ColLast];
           37 
           38         Drawable drawable;
           39         GC gc;
           40         struct {
           41                 int ascent;
           42                 int descent;
           43                 int height;
           44                 XFontSet set;
           45                 XFontStruct *xfont;
           46         } font;
           47 } DC; /* draw context */
           48 
           49 typedef struct {
           50         char *label;
           51         char *cmd;
           52         uint width;
           53         int x, y, w, h;
           54         Bool highlighted;
           55         Bool pressed;
           56         Bool forceexit;
           57 } Entry;
           58 
           59 /* function declarations */
           60 static void motionnotify(XEvent *e);
           61 static void keyrelease(XEvent *e);
           62 static void buttonpress(XEvent *e);
           63 static void buttonrelease(XEvent *e);
           64 static void cleanup(void);
           65 static void configurenotify(XEvent *e);
           66 static void unmapnotify(XEvent *e);
           67 static void die(const char *errstr, ...);
           68 static void drawmenu(void);
           69 static void drawentry(Entry *e);
           70 static void expose(XEvent *e);
           71 static Entry *findentry(int x, int y);
           72 static ulong getcolor(const char *colstr);
           73 static void initfont(const char *fontstr);
           74 static void leavenotify(XEvent *e);
           75 static void press(Entry *e);
           76 static void run(void);
           77 static void setup(void);
           78 static void sigchld(int unused);
           79 static int textnw(const char *text, uint len);
           80 static void unpress(Entry *e);
           81 static void updateentries(void);
           82 
           83 /* variables */
           84 static int screen;
           85 static void (*handler[LASTEvent]) (XEvent *) = {
           86         [KeyRelease] = keyrelease,
           87         [ButtonPress] = buttonpress,
           88         [ButtonRelease] = buttonrelease,
           89         [ConfigureNotify] = configurenotify,
           90         [UnmapNotify] = unmapnotify,
           91         [Expose] = expose,
           92         [LeaveNotify] = leavenotify,
           93         [MotionNotify] = motionnotify
           94 };
           95 
           96 static Display *dpy;
           97 static DC dc;
           98 static Window root, win;
           99 static Bool running = True, horizontal = False;
          100 /*
          101  * ww = window width; www = wanted window width; wh = window height;
          102  * wx = window x position; wy = window y position;
          103  */
          104 static int ww = 0, www = 0, wh = 0, wx = 0, wy = 0;
          105 static char *name = "thingmenu";
          106 
          107 Entry **entries = NULL;
          108 int nentries = 0;
          109 int exitentry = -1;
          110 int oneshot = 1;
          111 Bool ispressing = 0;
          112 
          113 char *argv0;
          114 
          115 #include "arg.h"
          116 
          117 /* configuration, allows nested code to access above variables */
          118 #include "config.h"
          119 
          120 void
          121 motionnotify(XEvent *e)
          122 {
          123         XPointerMovedEvent *ev = &e->xmotion;
          124         int i;
          125 
          126         for(i = 0; i < nentries; i++) {
          127                 if(ev->x > entries[i]->x
          128                                 && ev->x < entries[i]->x + entries[i]->w
          129                                 && ev->y > entries[i]->y
          130                                 && ev->y < entries[i]->y + entries[i]->h) {
          131                         if (entries[i]->highlighted != True) {
          132                                 if (ispressing) {
          133                                         entries[i]->pressed = True;
          134                                 } else {
          135                                         entries[i]->highlighted = True;
          136                                 }
          137                                 drawentry(entries[i]);
          138                         }
          139                         continue;
          140                 }
          141                 if (entries[i]->pressed == True) {
          142                         entries[i]->pressed = False;
          143                         drawentry(entries[i]);
          144                 }
          145                 if (entries[i]->highlighted == True) {
          146                         entries[i]->highlighted = False;
          147                         drawentry(entries[i]);
          148                 }
          149         }
          150 }
          151 
          152 void
          153 keyrelease(XEvent *e)
          154 {
          155         int i;
          156         XKeyEvent *xkey = &e->xkey;
          157         KeySym key = XLookupKeysym(xkey, 0);
          158 
          159         for (i = 0; i < nentries && !entries[i]->highlighted; i++);
          160 
          161         if (key >= XK_0 && key <= XK_9) {
          162                 i = key - XK_0;
          163                 key = XK_Return;
          164         } else if (key >= XK_KP_0 && key <= XK_KP_9) {
          165                 i = key - XK_KP_0;
          166                 key = XK_Return;
          167         }
          168 
          169         switch (key) {
          170         case XK_KP_Insert:
          171                 i = 0;
          172                 key = XK_Return;
          173                 break;
          174         case XK_KP_End:
          175                 i = 1;
          176                 key = XK_Return;
          177                 break;
          178         case XK_KP_Down:
          179                 i = 2;
          180                 key = XK_Return;
          181                 break;
          182         case XK_KP_Page_Down:
          183                 i = 3;
          184                 key = XK_Return;
          185                 break;
          186         case XK_KP_Left:
          187                 i = 4;
          188                 key = XK_Return;
          189                 break;
          190         case XK_KP_Begin:
          191                 i = 5;
          192                 key = XK_Return;
          193                 break;
          194         case XK_KP_Right:
          195                 i = 6;
          196                 key = XK_Return;
          197                 break;
          198         case XK_KP_Home:
          199                 i = 7;
          200                 key = XK_Return;
          201                 break;
          202         case XK_KP_Up:
          203                 i = 8;
          204                 key = XK_Return;
          205                 break;
          206         case XK_KP_Page_Up:
          207                 i = 9;
          208                 key = XK_Return;
          209                 break;
          210         }
          211 
          212         switch (key) {
          213         case XK_k:
          214                 key = XK_Up;
          215         case XK_j:
          216                 if(key == XK_j)
          217                         key = XK_Down;
          218         case XK_Up:
          219         case XK_Down:
          220                 if (i < nentries) {
          221                         entries[i]->highlighted = False;
          222                         drawentry(entries[i]);
          223                 }
          224 
          225                 if (key == XK_Up) {
          226                         i = ((i - 1) + nentries) % nentries;
          227                 } else if(key == XK_Down) {
          228                         if (i < nentries) {
          229                                 i = (i + 1) % nentries;
          230                         } else {
          231                                 i = 0;
          232                         }
          233                 }
          234 
          235                 entries[i]->highlighted = True;
          236                 drawentry(entries[i]);
          237                 break;
          238         case XK_period:
          239         case XK_KP_Decimal:
          240         case XK_KP_Delete:
          241                 i = exitentry;
          242         case XK_Return:
          243         case XK_space:
          244                 if (i < nentries) {
          245                         press(entries[i]);
          246                         unpress(entries[i]);
          247                 }
          248                 break;
          249         case XK_Escape:
          250                 running = False;
          251                 break;
          252         }
          253 }
          254 
          255 void
          256 buttonpress(XEvent *e)
          257 {
          258         XButtonPressedEvent *ev = &e->xbutton;
          259         Entry *en;
          260 
          261         if(ev->button != Button1)
          262                 return;
          263 
          264         ispressing = True;
          265 
          266         if((en = findentry(ev->x, ev->y)))
          267                 press(en);
          268 }
          269 
          270 void
          271 buttonrelease(XEvent *e)
          272 {
          273         XButtonPressedEvent *ev = &e->xbutton;
          274         Entry *en;
          275 
          276         if(ev->button != Button1)
          277                 return;
          278 
          279         ispressing = False;
          280 
          281         if((en = findentry(ev->x, ev->y)))
          282                 unpress(en);
          283 }
          284 
          285 void
          286 cleanup(void)
          287 {
          288         if(dc.font.set)
          289                 XFreeFontSet(dpy, dc.font.set);
          290         else
          291                 XFreeFont(dpy, dc.font.xfont);
          292         XFreePixmap(dpy, dc.drawable);
          293         XFreeGC(dpy, dc.gc);
          294         XDestroyWindow(dpy, win);
          295         XSync(dpy, False);
          296         XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime);
          297 }
          298 
          299 void
          300 configurenotify(XEvent *e)
          301 {
          302         XConfigureEvent *ev = &e->xconfigure;
          303 
          304         if(ev->window == win && (ev->width != ww || ev->height != wh)) {
          305                 ww = ev->width;
          306                 wh = ev->height;
          307                 XFreePixmap(dpy, dc.drawable);
          308                 dc.drawable = XCreatePixmap(dpy, root, ww, wh,
          309                                 DefaultDepth(dpy, screen));
          310                 updateentries();
          311         }
          312 }
          313 
          314 void
          315 die(const char *errstr, ...)
          316 {
          317         va_list ap;
          318 
          319         va_start(ap, errstr);
          320         vfprintf(stderr, errstr, ap);
          321         va_end(ap);
          322         exit(EXIT_FAILURE);
          323 }
          324 
          325 void
          326 drawmenu(void)
          327 {
          328         int i;
          329 
          330         for(i = 0; i < nentries; i++)
          331                 drawentry(entries[i]);
          332         XSync(dpy, False);
          333 }
          334 
          335 void
          336 drawentry(Entry *e)
          337 {
          338         int x, y, h, len;
          339         XRectangle r = { e->x, e->y, e->w, e->h };
          340         const char *l;
          341         ulong *col;
          342 
          343         if(e->pressed)
          344                 col = dc.press;
          345         else if(e->highlighted)
          346                 col = dc.high;
          347         else
          348                 col = dc.norm;
          349 
          350         XSetForeground(dpy, dc.gc, col[ColBG]);
          351         XFillRectangles(dpy, dc.drawable, dc.gc, &r, 1);
          352         XSetForeground(dpy, dc.gc, dc.norm[ColFG]);
          353         r.height -= 1;
          354         r.width -= 1;
          355         XDrawRectangles(dpy, dc.drawable, dc.gc, &r, 1);
          356         XSetForeground(dpy, dc.gc, col[ColFG]);
          357 
          358         l = e->label;
          359         len = strlen(l);
          360         h = dc.font.height;
          361         y = e->y + (e->h / 2) - (h / 2) + dc.font.ascent;
          362         x = e->x + (e->w / 2) - (textnw(l, len) / 2);
          363         if(dc.font.set) {
          364                 XmbDrawString(dpy, dc.drawable, dc.font.set, dc.gc, x, y, l,
          365                                 len);
          366         } else
          367                 XDrawString(dpy, dc.drawable, dc.gc, x, y, l, len);
          368         XCopyArea(dpy, dc.drawable, win, dc.gc, e->x, e->y, e->w, e->h,
          369                         e->x, e->y);
          370 }
          371 
          372 void
          373 unmapnotify(XEvent *e)
          374 {
          375         running = False;
          376 }
          377 
          378 void
          379 expose(XEvent *e)
          380 {
          381         XExposeEvent *ev = &e->xexpose;
          382 
          383         if(ev->count == 0 && (ev->window == win))
          384                 drawmenu();
          385 }
          386 
          387 Entry *
          388 findentry(int x, int y)
          389 {
          390         int i;
          391 
          392         for(i = 0; i < nentries; i++) {
          393                 if(x > entries[i]->x && x < entries[i]->x + entries[i]->w
          394                                 && y > entries[i]->y
          395                                 && y < entries[i]->y + entries[i]->h) {
          396                         return entries[i];
          397                 }
          398         }
          399         return NULL;
          400 }
          401 
          402 ulong
          403 getcolor(const char *colstr)
          404 {
          405         Colormap cmap = DefaultColormap(dpy, screen);
          406         XColor color;
          407 
          408         if(!XAllocNamedColor(dpy, cmap, colstr, &color, &color))
          409                 die("error, cannot allocate color '%s'\n", colstr);
          410         return color.pixel;
          411 }
          412 
          413 void
          414 initfont(const char *fontstr)
          415 {
          416         char *def, **missing;
          417         int i, n;
          418 
          419         missing = NULL;
          420         if(dc.font.set)
          421                 XFreeFontSet(dpy, dc.font.set);
          422         dc.font.set = XCreateFontSet(dpy, fontstr, &missing, &n, &def);
          423         if(missing) {
          424                 while(n--) {
          425                         fprintf(stderr, "thingmenu: missing fontset: %s\n",
          426                                         missing[n]);
          427                 }
          428                 XFreeStringList(missing);
          429         }
          430         if(dc.font.set) {
          431                 XFontStruct **xfonts;
          432                 char **font_names;
          433                 dc.font.ascent = dc.font.descent = 0;
          434                 n = XFontsOfFontSet(dc.font.set, &xfonts, &font_names);
          435                 for(i = 0, dc.font.ascent = 0, dc.font.descent = 0; i < n; i++) {
          436                         dc.font.ascent = MAX(dc.font.ascent, (*xfonts)->ascent);
          437                         dc.font.descent = MAX(dc.font.descent,(*xfonts)->descent);
          438                         xfonts++;
          439                 }
          440         }
          441         else {
          442                 if(dc.font.xfont)
          443                         XFreeFont(dpy, dc.font.xfont);
          444                 dc.font.xfont = NULL;
          445                 if(!(dc.font.xfont = XLoadQueryFont(dpy, fontstr))
          446                 && !(dc.font.xfont = XLoadQueryFont(dpy, "fixed")))
          447                         die("error, cannot load font: '%s'\n", fontstr);
          448                 dc.font.ascent = dc.font.xfont->ascent;
          449                 dc.font.descent = dc.font.xfont->descent;
          450         }
          451         dc.font.height = dc.font.ascent + dc.font.descent;
          452 }
          453 
          454 void
          455 leavenotify(XEvent *e)
          456 {
          457         unpress(NULL);
          458 }
          459 
          460 void
          461 run(void)
          462 {
          463         XEvent ev;
          464 
          465         /* main event loop */
          466         XSync(dpy, False);
          467         while(running) {
          468                 XNextEvent(dpy, &ev);
          469                 if(handler[ev.type])
          470                         (handler[ev.type])(&ev); /* call handler */
          471         }
          472 }
          473 
          474 void
          475 setup(void)
          476 {
          477         XSetWindowAttributes wa;
          478         XTextProperty str;
          479         XSizeHints *sizeh;
          480         XClassHint *ch;
          481         int i, sh, sw, ls;
          482 
          483         /* clean up any zombies immediately */
          484         sigchld(0);
          485 
          486         /* init screen */
          487         screen = DefaultScreen(dpy);
          488         root = RootWindow(dpy, screen);
          489         sw = DisplayWidth(dpy, screen) - 1;
          490         sh = DisplayHeight(dpy, screen) - 1;
          491         initfont(font);
          492 
          493         /* init atoms */
          494 
          495         /* init appearance */
          496 
          497         for (i = 0, www = 0; i < nentries; i++) {
          498                 ls = textnw(entries[i]->label,
          499                                 strlen(entries[i]->label));
          500                 if (ls > www)
          501                         www = ls;
          502         }
          503         www *= widthscaling;
          504 
          505         if (!ww) {
          506                 if (horizontal) {
          507                         ww = www * nentries;
          508                 } else {
          509                         ww = www;
          510                 }
          511         }
          512         if (!wh) {
          513                 if (horizontal) {
          514                         wh = dc.font.height * heightscaling;
          515                 } else {
          516                         wh = nentries * dc.font.height * heightscaling;
          517                 }
          518         }
          519         if (!wy)
          520                 wy = (sh - wh) / 2;
          521         if (wy < 0)
          522                 wy = sh + wy - wh;
          523         if (!wx)
          524                 wx = (sw - ww) / 2;
          525         if (wx < 0)
          526                 wx = sw + wx - ww;
          527 
          528         dc.norm[ColBG] = getcolor(normbgcolor);
          529         dc.norm[ColFG] = getcolor(normfgcolor);
          530         dc.press[ColBG] = getcolor(pressbgcolor);
          531         dc.press[ColFG] = getcolor(pressfgcolor);
          532         dc.high[ColBG] = getcolor(highlightbgcolor);
          533         dc.high[ColFG] = getcolor(highlightfgcolor);
          534 
          535         dc.drawable = XCreatePixmap(dpy, root, ww, wh, DefaultDepth(dpy, screen));
          536         dc.gc = XCreateGC(dpy, root, 0, 0);
          537         if(!dc.font.set)
          538                 XSetFont(dpy, dc.gc, dc.font.xfont->fid);
          539         for(i = 0; i < nentries; i++)
          540                 entries[i]->pressed = 0;
          541 
          542         wa.override_redirect = !wmborder;
          543         wa.border_pixel = dc.norm[ColFG];
          544         wa.background_pixel = dc.norm[ColBG];
          545         win = XCreateWindow(dpy, root, wx, wy, ww, wh, 0,
          546                             CopyFromParent, CopyFromParent, CopyFromParent,
          547                             CWOverrideRedirect | CWBorderPixel | CWBackingPixel, &wa);
          548         XSelectInput(dpy, win, StructureNotifyMask|KeyReleaseMask|
          549                                ButtonReleaseMask|ButtonPressMask|
          550                                ExposureMask|LeaveWindowMask|PointerMotionMask);
          551 
          552         sizeh = XAllocSizeHints();
          553         sizeh->flags = PMaxSize | PMinSize;
          554         sizeh->min_width = sizeh->max_width = ww;
          555         sizeh->min_height = sizeh->max_height = wh;
          556         XStringListToTextProperty(&name, 1, &str);
          557         ch = XAllocClassHint();
          558         ch->res_class = name;
          559         ch->res_name = name;
          560 
          561         XSetWMProperties(dpy, win, &str, &str, NULL, 0, sizeh, NULL,
          562                         ch);
          563 
          564         XFree(ch);
          565         XFree(str.value);
          566         XFree(sizeh);
          567 
          568         XMapRaised(dpy, win);
          569         updateentries();
          570         drawmenu();
          571 }
          572 
          573 void
          574 sigchld(int unused)
          575 {
          576         if (signal(SIGCHLD, sigchld) == SIG_ERR)
          577                 die("can't install SIGCHLD handler:");
          578         while (0 < waitpid(-1, NULL, WNOHANG));
          579 }
          580 
          581 int
          582 textnw(const char *text, uint len)
          583 {
          584         XRectangle r;
          585 
          586         if(dc.font.set) {
          587                 XmbTextExtents(dc.font.set, text, len, NULL, &r);
          588                 return r.width;
          589         }
          590         return XTextWidth(dc.font.xfont, text, len);
          591 }
          592 
          593 void
          594 runentry(Entry *e)
          595 {
          596         char *shell;
          597 
          598         if (oneshot || e->forceexit)
          599                 running = False;
          600 
          601         switch (fork()) {
          602         case -1:
          603                 break;
          604         case 0:
          605                 shell = getenv("SHELL");
          606                 if (!shell)
          607                         shell = "/bin/sh";
          608 
          609                 execlp(shell, basename(shell), "-c", e->cmd, (char *)NULL);
          610                 break;
          611         }
          612 }
          613 
          614 void
          615 press(Entry *e)
          616 {
          617         e->pressed = !e->pressed;
          618 
          619         drawentry(e);
          620 }
          621 
          622 void
          623 unpress(Entry *e)
          624 {
          625         int i;
          626 
          627         if (e != NULL) {
          628                 e->pressed = !e->pressed;
          629 
          630                 runentry(e);
          631                 drawentry(e);
          632         } else {
          633                 for(i = 0; i < nentries; i++) {
          634                         if(entries[i]->pressed) {
          635                                 entries[i]->pressed = 0;
          636                                 drawentry(entries[i]);
          637                         }
          638                 }
          639         }
          640 }
          641 
          642 void
          643 updateentries(void)
          644 {
          645         int i, x, y, h, w;
          646 
          647         x = 0;
          648         y = 0;
          649 
          650         if (horizontal) {
          651                 h = wh;
          652                 w = www;
          653         } else {
          654                 h = wh / nentries;
          655                 w = ww;
          656         }
          657         for(i = 0; i < nentries; i++) {
          658                 entries[i]->x = x;
          659                 entries[i]->y = y;
          660                 entries[i]->w = w;
          661                 entries[i]->h = h;
          662                 if (horizontal) {
          663                         x += w;
          664                 } else {
          665                         y += h;
          666                 }
          667         }
          668 }
          669 
          670 void
          671 usage(void)
          672 {
          673         fprintf(stderr, "usage: %s [-hnosx] [-g geometry] [-w widthscaling] "
          674                         "[-e heightscaling] [--] "
          675                         "label0 cmd0 [label1 cmd1 ...]\n", argv0);
          676         exit(1);
          677 }
          678 
          679 int
          680 main(int argc, char *argv[])
          681 {
          682         Bool addexit, usenumpad;
          683         char *label, *cmd;
          684         int i, xr, yr, bitm;
          685         unsigned int wr, hr;
          686 
          687         argv0 = argv[0];
          688 
          689         addexit = True;
          690         usenumpad = False;
          691 
          692         if (argc < 2)
          693                 usage();
          694 
          695         ARGBEGIN {
          696         case 'g':
          697                 bitm = XParseGeometry(EARGF(usage()), &xr, &yr, &wr, &hr);
          698                 if (bitm & XValue)
          699                         wx = xr;
          700                 if (bitm & YValue)
          701                         wy = yr;
          702                 if (bitm & WidthValue)
          703                         ww = (int)wr;
          704                 if (bitm & HeightValue)
          705                         wh = (int)hr;
          706                 if (bitm & XNegative && wx == 0)
          707                         wx = -1;
          708                 if (bitm & YNegative && wy == 0)
          709                         wy = -1;
          710                 break;
          711         case 'e':
          712                 heightscaling = atof(EARGF(usage()));
          713                 break;
          714         case 'n':
          715                 usenumpad = True;
          716                 break;
          717         case 'o':
          718                 horizontal = True;
          719                 break;
          720         case 's':
          721                 oneshot = 0;
          722                 break;
          723         case 'w':
          724                 widthscaling = atof(EARGF(usage()));
          725                 break;
          726         case 'x':
          727                 addexit = False;
          728                 break;
          729         default:
          730                 usage();
          731         } ARGEND;
          732 
          733         for (i = 0; argv[i]; i++) {
          734                 label = argv[i];
          735                 if (!argv[i+1])
          736                         break;
          737                 i++;
          738                 cmd = argv[i];
          739 
          740                 if (!(entries = realloc(entries, sizeof(entries[0])*(++nentries))))
          741                         die("realloc returned NULL");
          742                 if (!(entries[nentries-1] = calloc(1, sizeof(*entries[0]))))
          743                         die("calloc returned NULL");
          744                 if (usenumpad == True && nentries < 11) {
          745                         if (!(asprintf(&entries[nentries-1]->label,
          746                                         "%d:%s", nentries-1, strdup(label)))) {
          747                                 die("asprintf returned NULL\n");
          748                         }
          749                 } else {
          750                         if (!(entries[nentries-1]->label = strdup(label)))
          751                                 die("strdup returned NULL\n");
          752                 }
          753                 if (!(entries[nentries-1]->cmd = strdup(cmd)))
          754                         die("strdup returned NULL\n");
          755                 entries[nentries-1]->forceexit = False;
          756         }
          757         if (nentries < 1)
          758                 usage();
          759 
          760         if (addexit) {
          761                 if (!(entries = realloc(entries, sizeof(entries[0])*(++nentries))))
          762                         die("realloc returned NULL");
          763                 if (!(entries[nentries-1] = calloc(1, sizeof(*entries[0]))))
          764                         die("calloc returned NULL");
          765                 if (usenumpad == True) {
          766                         if (!(entries[nentries-1]->label = strdup(".:cancel")))
          767                                 die("strdup returned NULL\n");
          768                 } else {
          769                         if (!(entries[nentries-1]->label = strdup("cancel")))
          770                                 die("strdup returned NULL\n");
          771                 }
          772                 if (!(entries[nentries-1]->cmd = strdup("exit")))
          773                         die("strdup returned NULL\n");
          774                 entries[nentries-1]->forceexit = True;
          775                 exitentry = nentries - 1;
          776         }
          777 
          778         if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
          779                 fprintf(stderr, "warning: no locale support\n");
          780         if(!(dpy = XOpenDisplay(0)))
          781                 die("thingmenu: cannot open display\n");
          782 
          783         setup();
          784         run();
          785         cleanup();
          786         XCloseDisplay(dpy);
          787 
          788         for (i = 0; i < nentries; i++) {
          789                 free(entries[i]->label);
          790                 free(entries[i]->cmd);
          791                 free(entries[i]);
          792         }
          793         free(entries);
          794 
          795         return 0;
          796 }
          797