home *** CD-ROM | disk | FTP | other *** search
/ ftp.freefriends.org / ftp.freefriends.org.tar / ftp.freefriends.org / arnold / Source / 9menu-1.8.shar.gz / 9menu-1.8.shar / 9menu.c < prev    next >
C/C++ Source or Header  |  2004-06-01  |  20KB  |  812 lines

  1. /*
  2.  * 9menu.c
  3.  *
  4.  * This program puts up a window that is just a menu, and executes
  5.  * commands that correspond to the items selected.
  6.  *
  7.  * Initial idea: Arnold Robbins
  8.  * Version using libXg: Matty Farrow (some ideas borrowed)
  9.  * This code by: David Hogan and Arnold Robbins
  10.  *
  11.  * Copyright (c), Arnold Robbins and David Hogan
  12.  *
  13.  * Arnold Robbins
  14.  * arnold@skeeve.com
  15.  * October, 1994
  16.  *
  17.  * Code added to cause pop-up (unIconify) to move menu to mouse.
  18.  * Christopher Platt
  19.  * platt@coos.dartmouth.edu
  20.  * May, 1995
  21.  *
  22.  * Said code moved to -teleport option, and -warp option added.
  23.  * Arnold Robbins
  24.  * June, 1995
  25.  *
  26.  * Code added to allow -fg and -bg colors.
  27.  * John M. O'Donnell
  28.  * odonnell@stpaul.lampf.lanl.gov
  29.  * April, 1997
  30.  *
  31.  * Code added for -file and -path optioins.
  32.  * Peter Seebach
  33.  * seebs@plethora.net
  34.  * October, 2001
  35.  *
  36.  * Code added to allow up and down arrow keys to go up
  37.  * and down menu and RETURN to select an item.
  38.  * Matthias Bauer
  39.  * bauerm@immd1.informatik.uni-erlangen.de
  40.  * June, 2003
  41.  *
  42.  * spawn() changed to do exec directly if -popup, based on
  43.  * suggestion from
  44.  * Andrew Stribblehill
  45.  * a.d.stribblehill@durham.ac.uk
  46.  * June, 2004
  47.  */
  48.  
  49. #include <stdio.h>
  50. #include <fcntl.h>
  51. #include <signal.h>
  52. #include <stdlib.h>
  53. #include <string.h>
  54. #include <unistd.h>
  55. #include <sys/param.h>
  56. #include <sys/types.h>
  57. #include <sys/wait.h>
  58. #include <X11/X.h>
  59. #include <X11/Xatom.h>
  60. #include <X11/Xlib.h>
  61. #include <X11/Xproto.h>
  62. #include <X11/Xutil.h>
  63. #include <X11/keysymdef.h>
  64. #include <X11/keysym.h>
  65.  
  66. char version[] = "@(#) 9menu version 1.8";
  67.  
  68. Display *dpy;        /* lovely X stuff */
  69. int screen;
  70. Window root;
  71. Window menuwin;
  72. GC gc;
  73. unsigned long black;
  74. unsigned long white;
  75. char *fgcname = NULL;
  76. char *bgcname = NULL;
  77. Colormap defcmap;
  78. XColor color;
  79. XFontStruct *font;
  80. Atom wm_protocols;
  81. Atom wm_delete_window;
  82. int g_argc;            /* for XSetWMProperties to use */
  83. char **g_argv;
  84. int f_argc;            /* for labels read from files */
  85. char **f_argv;
  86. char *geometry = "";
  87. int savex, savey;
  88. Window savewindow;
  89.  
  90. char *fontlist[] = {    /* default font list if no -font */
  91.     "pelm.latin1.9",
  92.     "lucm.latin1.9",
  93.     "blit",
  94.     "9x15bold",
  95.     "9x15",
  96.     "lucidasanstypewriter-12",
  97.     "fixed",
  98.     NULL
  99. };
  100.  
  101. /* the 9menu icon, for garish window managers */
  102. #define nine_menu_width 40
  103. #define nine_menu_height 40
  104. static unsigned char nine_menu_bits[] = {
  105.    0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x04, 0x00,
  106.    0x80, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00,
  107.    0x00, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x04,
  108.    0x00, 0x80, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00, 0x00, 0xfc, 0xff, 0xff,
  109.    0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00,
  110.    0xfc, 0xff, 0xff, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x04, 0x00,
  111.    0x80, 0xe0, 0x01, 0x04, 0x00, 0x80, 0xe0, 0x00, 0xfc, 0xff, 0xff, 0xe0,
  112.    0x01, 0xfc, 0xff, 0xff, 0x20, 0x03, 0x04, 0x00, 0x80, 0x00, 0x06, 0x04,
  113.    0x00, 0x80, 0x00, 0x0c, 0xfc, 0xff, 0xff, 0x00, 0x08, 0xfc, 0xff, 0xff,
  114.    0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00,
  115.    0xfc, 0xff, 0xff, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x04, 0x00,
  116.    0x80, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00,
  117.    0x00, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x04,
  118.    0x00, 0x80, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00, 0x00, 0xfc, 0xff, 0xff,
  119.    0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00,
  120.    0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  121.    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  122.  
  123. /* Modify this to your liking */
  124. #define CONFIG_MENU_UP_KEY  XK_Up
  125. #define CONFIG_MENU_DOWN_KEY    XK_Down
  126. #define CONFIG_MENU_SELECT_KEY  XK_Return
  127.  
  128. char *progname;        /* my name */
  129. char *displayname;    /* X display */
  130. char *fontname;        /* font */
  131. char *labelname;    /* window and icon name */
  132. char *filename;        /* file to read options or labels from */
  133. int popup;        /* true if we're a popup window */
  134. int popdown;        /* autohide after running a command */
  135. int iconic;        /* start iconified */
  136. int teleport;        /* teleport the menu */
  137. int warp;        /* warp the mouse */
  138.  
  139. char **labels;        /* list of labels and commands */
  140. char **commands;
  141. int numitems;
  142.  
  143. char *shell = "/bin/sh";    /* default shell */
  144.  
  145. extern void usage(), run_menu(), spawn(), ask_wm_for_delete();
  146. extern void reap(), set_wm_hints();
  147. extern void redraw(), teleportmenu(), warpmouse(), restoremouse();
  148. extern void memory();
  149. extern int args();
  150.  
  151. /* memory --- print the out of memory message and die */
  152.  
  153. void
  154. memory(s)
  155. char *s;
  156. {
  157.     fprintf(stderr, "%s: couldn't allocate memory for %s\n", progname, s);
  158.     exit(1);
  159. }
  160.  
  161. /* args --- go through the argument list, set options */
  162.  
  163. int
  164. args(argc, argv)
  165. int argc;
  166. char **argv;
  167. {
  168.     int i;
  169.     if (argc == 0 || argv == NULL || argv[0] == '\0')
  170.         return -1;
  171.     for (i = 0; i < argc && argv[i] != NULL; i++) {
  172.         if (strcmp(argv[i], "-display") == 0) {
  173.             displayname = argv[i+1];
  174.             i++;
  175.         } else if (strcmp(argv[i], "-file") == 0) {
  176.             filename = argv[i+1];
  177.             i++;
  178.         } else if (strcmp(argv[i], "-font") == 0) {
  179.             fontname = argv[i+1];
  180.             i++;
  181.         } else if (strcmp(argv[i], "-geometry") == 0) {
  182.             geometry = argv[i+1];
  183.             i++;
  184.         } else if (strcmp(argv[i], "-label") == 0) {
  185.             labelname = argv[i+1];
  186.             i++;
  187.         } else if (strcmp(argv[i], "-shell") == 0) {
  188.             shell = argv[i+1];
  189.             i++;
  190.         } else if (strcmp(argv[i], "-popup") == 0)
  191.             popup++;
  192.         else if (strcmp(argv[i], "-popdown") == 0)
  193.             popdown++;
  194.         else if (strcmp(argv[i], "-fg") == 0)
  195.             fgcname = argv[++i];
  196.         else if (strcmp(argv[i], "-bg") == 0)
  197.             bgcname = argv[++i];
  198.         else if (strcmp(argv[i], "-iconic") == 0)
  199.             iconic++;
  200.         else if (strcmp(argv[i], "-path") == 0) {
  201.             char pathbuf[MAXPATHLEN];
  202.             char *s, *t;
  203.  
  204.             s = getenv("PATH");
  205.             if (s != NULL) {
  206.                 /* append current dir to PATH */
  207.                 getcwd(pathbuf, MAXPATHLEN);
  208.                 t = malloc(strlen(s) + strlen(pathbuf) + 7);
  209.                 sprintf(t, "PATH=%s:%s", pathbuf, s);
  210.                 putenv(t);
  211.             }
  212.         } else if (strcmp(argv[i], "-teleport") == 0)
  213.             teleport++;
  214.         else if (strcmp(argv[i], "-warp") == 0)
  215.             warp++;
  216.         else if (strcmp(argv[i], "-version") == 0) {
  217.             printf("%s\n", version);
  218.             exit(0);
  219.         } else if (argv[i][0] == '-')
  220.             usage();
  221.         else
  222.             break;
  223.     }
  224.     return i;
  225. }
  226.  
  227. /* main --- crack arguments, set up X stuff, run the main menu loop */
  228.  
  229. int
  230. main(argc, argv)
  231. int argc;
  232. char **argv;
  233. {
  234.     int i, j;
  235.     char *cp;
  236.     XGCValues gv;
  237.     unsigned long mask;
  238.     int nlabels = 0;
  239.  
  240.     g_argc = argc;
  241.     g_argv = argv;
  242.  
  243.     /* set default label name */
  244.     if ((cp = strrchr(argv[0], '/')) == NULL)
  245.         labelname = argv[0];
  246.     else
  247.         labelname = ++cp;
  248.  
  249.     ++argv;
  250.     --argc;
  251.  
  252.     /* and program name for diagnostics */
  253.     progname = labelname;
  254.  
  255.     i = args(argc, argv);
  256.  
  257.     numitems = argc - i;
  258.  
  259.     if (numitems <= 0 && filename == NULL)
  260.         usage();
  261.  
  262.     if (filename) {
  263.         /* Read options and labels from file */
  264.         char fbuf[1024];
  265.         FILE *fp;
  266.  
  267.         fp = fopen(filename, "r");
  268.         if (fp == NULL) {
  269.             fprintf(stderr, "%s: couldn't open '%s'\n", progname,
  270.                 filename);
  271.             exit(1);
  272.         }
  273.         while (fgets(fbuf, sizeof fbuf, fp)) {
  274.             char *s = fbuf;
  275.             strtok(s, "\n");
  276.             if (s[0] == '-') {
  277.                 char *temp[3];
  278.                 temp[0] = s;
  279.                 temp[1] = strchr(s, ' ');
  280.                 if (temp[1]) {
  281.                     *(temp[1]++) = '\0';
  282.                     s = malloc(strlen(temp[1]) + 1);
  283.                     if (s == NULL)
  284.                         memory("temporary argument");
  285.                     strcpy(s, temp[1]);
  286.                     temp[1] = s;
  287.                 }
  288.                 temp[2] = 0;
  289.                 args(temp[1] ? 2 : 1, temp);
  290.                 continue;
  291.             }
  292.             if (s[0] == '#')
  293.                 continue;
  294.             /* allow - in menu items to be escaped */
  295.             if (s[0] == '\\')
  296.                 ++s;
  297.             /* allocate space */
  298.             if (f_argc < nlabels + 1) {
  299.                 int k;
  300.                 char **temp = malloc(sizeof(char *) * (f_argc + 5));
  301.                 if (temp == 0)
  302.                     memory("temporary item");
  303.  
  304.                 for (k = 0; k < nlabels; k++)
  305.                     temp[k] = f_argv[k];
  306.  
  307.                 free(f_argv);
  308.                 f_argv = temp;
  309.                 f_argc += 5;
  310.             }
  311.             f_argv[nlabels] = malloc(strlen(s) + 1);
  312.             if (f_argv[nlabels] == NULL)
  313.                 memory("temporary text");
  314.             strcpy(f_argv[nlabels], s);
  315.             ++nlabels;
  316.         }
  317.     }
  318.  
  319.     labels = (char **) malloc((numitems + nlabels) * sizeof(char *));
  320.     commands = (char **) malloc((numitems + nlabels) * sizeof(char *));
  321.     if (commands == NULL || labels == NULL)
  322.         memory("command and label arrays");
  323.  
  324.     for (j = 0; j < numitems; j++) {
  325.         labels[j] = argv[i + j];
  326.         if ((cp = strchr(labels[j], ':')) != NULL) {
  327.             *cp++ = '\0';
  328.             commands[j] = cp;
  329.         } else
  330.             commands[j] = labels[j];
  331.     }
  332.  
  333.     /*
  334.      * Now we no longer need i (our offset into argv) so we recycle it,
  335.      * while keeping the old value of j!
  336.      */
  337.     for (i = 0; i < nlabels; i++) {
  338.         labels[j] = f_argv[i];
  339.         if ((cp = strchr(labels[j], ':')) != NULL) {
  340.             *cp++ = '\0';
  341.             commands[j] = cp;
  342.         } else
  343.             commands[j] = labels[j];
  344.         ++j;
  345.     }
  346.  
  347.     /* And now we merge the totals */
  348.     numitems += nlabels;
  349.  
  350.     dpy = XOpenDisplay(displayname);
  351.     if (dpy == NULL) {
  352.         fprintf(stderr, "%s: cannot open display", progname);
  353.         if (displayname != NULL)
  354.             fprintf(stderr, " %s", displayname);
  355.         fprintf(stderr, "\n");
  356.         exit(1);
  357.     }
  358.     screen = DefaultScreen(dpy);
  359.     root = RootWindow(dpy, screen);
  360.     /*
  361.      * This used to be
  362.      * black = BlackPixel(dpy, screen);
  363.      * white = WhitePixel(dpy, screen);
  364.      */
  365.     defcmap = DefaultColormap(dpy, screen);
  366.     if (fgcname == NULL
  367.         || XParseColor(dpy, defcmap, fgcname, &color) == 0
  368.         || XAllocColor(dpy, defcmap, &color) == 0)
  369.         black = BlackPixel(dpy, screen);
  370.     else
  371.         black = color.pixel;
  372.  
  373.     if (bgcname == NULL
  374.         || XParseColor(dpy, defcmap, bgcname, &color) == 0
  375.         || XAllocColor(dpy, defcmap, &color) == 0)
  376.         white = WhitePixel(dpy, screen);
  377.     else
  378.         white = color.pixel;
  379.  
  380.     /* try user's font first */
  381.     if (fontname != NULL) {
  382.         font = XLoadQueryFont(dpy, fontname);
  383.         if (font == NULL)
  384.             fprintf(stderr, "%s: warning: can't load font %s\n",
  385.                 progname, fontname);
  386.     }
  387.  
  388.     /* if no user font, try one of our default fonts */
  389.     if (font == NULL) {
  390.         for (i = 0; fontlist[i] != NULL; i++) {
  391.             font = XLoadQueryFont(dpy, fontlist[i]);
  392.             if (font != NULL)
  393.                 break;
  394.         }
  395.     }
  396.  
  397.     if (font == NULL) {
  398.         fprintf(stderr, "%s: fatal: cannot load a font\n", progname);
  399.         exit(1);
  400.     }
  401.  
  402.     gv.foreground = black^white;
  403.     gv.background = white;
  404.     gv.font = font->fid;
  405.     gv.function = GXxor;
  406.     gv.line_width = 0;
  407.     mask = GCForeground | GCBackground | GCFunction | GCFont | GCLineWidth;
  408.     gc = XCreateGC(dpy, root, mask, &gv);
  409.  
  410.     signal(SIGCHLD, reap);
  411.  
  412.     run_menu();
  413.  
  414.     XCloseDisplay(dpy);
  415.     exit(0);
  416. }
  417.  
  418. /* spawn --- run a command */
  419.  
  420. void
  421. spawn(com)
  422. char *com;
  423. {
  424.     int pid;
  425.     static char *sh_base = NULL;
  426.  
  427.     if (sh_base == NULL) {
  428.         sh_base = strrchr(shell, '/');
  429.         if (sh_base != NULL)
  430.             sh_base++;
  431.         else
  432.             sh_base = shell;
  433.     }
  434.  
  435.     /*
  436.      * Since -popup means run command and exit, just
  437.      * fall straight into exec code.  Thus only fork
  438.      * if not popup.
  439.      */
  440.     if (! popup) {
  441.         if (strncmp(com, "exec ", 5) != 0) {
  442.             pid = fork();
  443.             if (pid < 0) {
  444.                 fprintf(stderr, "%s: can't fork\n", progname);
  445.                 return;
  446.             } else if (pid > 0)
  447.                 return;
  448.         } else {
  449.             com += 5;
  450.         }
  451.     }
  452.  
  453.     close(ConnectionNumber(dpy));
  454.     execl(shell, sh_base, "-c", com, NULL);
  455.     execl("/bin/sh", "sh", "-c", com, NULL);
  456.     _exit(1);
  457. }
  458.  
  459. /* reap --- collect dead children */
  460.  
  461. void
  462. reap(s)
  463. int s;
  464. {
  465.     (void) wait((int *) NULL);
  466.     signal(s, reap);
  467. }
  468.  
  469. /* usage --- print a usage message and die */
  470.  
  471. void
  472. usage()
  473. {
  474.     fprintf(stderr, "usage: %s [-display displayname] [-font fname] ", progname);
  475.     fprintf(stderr, "[-file filename] [-path]");
  476.     fprintf(stderr, "[-geometry geom] [-shell shell]  [-label name] ");
  477.     fprintf(stderr, "[-popup] [-popdown] [-iconic]  [-teleport] ");
  478.     fprintf(stderr, "[-warp]  [-version] menitem:command ...\n");
  479.     exit(0);
  480. }
  481.  
  482. /* run_menu --- put up the window, execute selected commands */
  483.  
  484. void
  485. run_menu()
  486. {
  487.     XEvent ev;
  488.     XClientMessageEvent *cmsg;
  489.     KeySym key;
  490.     int i, cur, old, wide, high, ico, dx, dy;
  491.  
  492.     dx = 0;
  493.     for (i = 0; i < numitems; i++) {
  494.         wide = XTextWidth(font, labels[i], strlen(labels[i])) + 4;
  495.         if (wide > dx)
  496.             dx = wide;
  497.     }
  498.     wide = dx;
  499.  
  500.     old = cur = -1;
  501.  
  502.     high = font->ascent + font->descent + 1;
  503.     dy = numitems * high;
  504.  
  505.     set_wm_hints(wide, dy);
  506.  
  507.     ask_wm_for_delete();
  508.  
  509. #define    MenuMask (ButtonPressMask|ButtonReleaseMask\
  510.     |LeaveWindowMask|PointerMotionMask|ButtonMotionMask\
  511.     |ExposureMask|StructureNotifyMask|KeyPressMask)
  512.  
  513.     XSelectInput(dpy, menuwin, MenuMask);
  514.  
  515.     XMapWindow(dpy, menuwin);
  516.  
  517.     ico = 1;    /* warp to first item */
  518.     i = 0;        /* save menu Item position */
  519.  
  520.     for (;;) {
  521.         XNextEvent(dpy, &ev);
  522.         switch (ev.type) {
  523.         default:
  524.             fprintf(stderr, "%s: unknown ev.type %d\n",
  525.                 progname, ev.type);
  526.             break;
  527.         case ButtonRelease:
  528.             /* allow button 1 or button 3 */
  529.             if (ev.xbutton.button == Button2)
  530.                 break;
  531.             i = ev.xbutton.y/high;
  532.             if (ev.xbutton.x < 0 || ev.xbutton.x > wide)
  533.                 break;
  534.             else if (i < 0 || i >= numitems)
  535.                 break;
  536.             if (warp)
  537.                 restoremouse();
  538.             if (strcmp(labels[i], "exit") == 0) {
  539.                 if (commands[i] != labels[i]) {
  540.                     spawn(commands[i]);
  541.                 }
  542.                 return;
  543.             }
  544.             spawn(commands[i]);
  545.             if (popup)
  546.                 return;
  547.             if (cur >= 0 && cur < numitems)
  548.                 XFillRectangle(dpy, menuwin, gc, 0, cur*high, wide, high);
  549.             if (popdown)
  550.                 XIconifyWindow(dpy, menuwin, screen);
  551.             cur = -1;
  552.             break;
  553.         case ButtonPress:
  554.         case MotionNotify:
  555.             old = cur;
  556.             cur = ev.xbutton.y/high;
  557.             if (ev.xbutton.x < 0 || ev.xbutton.x > wide)
  558.                 cur = -1;
  559.             else if (cur < 0 || cur >= numitems)
  560.                 cur = -1;
  561.             if (cur == old)
  562.                 break;
  563.             if (old >= 0 && old < numitems)
  564.                 XFillRectangle(dpy, menuwin, gc, 0, old*high, wide, high);
  565.             if (cur >= 0 && cur < numitems)
  566.                 XFillRectangle(dpy, menuwin, gc, 0, cur*high, wide, high);
  567.             break;
  568.         case KeyPress:
  569.             key = XKeycodeToKeysym(dpy, ev.xkey.keycode, 0);    
  570.             if (key != CONFIG_MENU_UP_KEY
  571.                 && key != CONFIG_MENU_DOWN_KEY
  572.                 && key != CONFIG_MENU_SELECT_KEY)
  573.                 break;
  574.  
  575.             if (key == CONFIG_MENU_UP_KEY) {
  576.                 old = cur;
  577.                 cur--;
  578.             } else if (key == CONFIG_MENU_DOWN_KEY) {
  579.                 old = cur;
  580.                 cur++;
  581.             }
  582.             
  583.             while (cur < 0)
  584.                 cur += numitems;
  585.         
  586.             cur %= numitems;
  587.  
  588.             if (key == CONFIG_MENU_UP_KEY || key == CONFIG_MENU_DOWN_KEY) {
  589.                 if (cur == old)
  590.                     break;
  591.                 if (old >= 0 && old < numitems && cur != -1)
  592.                     XFillRectangle(dpy, menuwin, gc, 0, old*high, wide, high);
  593.                 if (cur >= 0 && cur < numitems && cur != -1)
  594.                     XFillRectangle(dpy, menuwin, gc, 0, cur*high, wide, high);
  595.                 break;
  596.             }
  597.  
  598.             if (warp)
  599.                 restoremouse();
  600.             if (key == CONFIG_MENU_SELECT_KEY) {
  601.                 if (strcmp(labels[cur], "exit") == 0) {
  602.                     if (commands[cur] != labels[cur]) {
  603.                         spawn(commands[cur]);
  604.                     }
  605.                     return;
  606.                 }
  607.                 spawn(commands[cur]);
  608.             }
  609.  
  610.             if (popup)
  611.                 return;
  612.             if (popdown)
  613.                 XIconifyWindow(dpy, menuwin, screen);
  614.             break;
  615.         case LeaveNotify:
  616.             cur = old = -1;
  617.             XClearWindow(dpy, menuwin);
  618.             redraw(cur, high, wide);
  619.             break;
  620.         case ReparentNotify:
  621.         case ConfigureNotify:
  622.             /*
  623.              * ignore these, they come from XMoveWindow
  624.              * and are enabled by Struct..
  625.              */
  626.             break;
  627.         case UnmapNotify:
  628.             ico = 1;
  629.             XClearWindow(dpy, menuwin);
  630.             break;
  631.         case MapNotify:
  632.             if (ico) {
  633.                 if (teleport)
  634.                     teleportmenu(i, wide, high);
  635.                 else if (warp)
  636.                     warpmouse(i, wide, high);
  637.             }
  638.             XClearWindow(dpy, menuwin);
  639.             redraw(cur = i, high, wide);
  640.             ico = 0;
  641.             break;
  642.         case Expose:
  643.             XClearWindow(dpy, menuwin);
  644.             redraw(cur, high, wide);
  645.             break;
  646.         case ClientMessage:
  647.             cmsg = &ev.xclient;
  648.             if (cmsg->message_type == wm_protocols
  649.                 && cmsg->data.l[0] == wm_delete_window)
  650.                 return;
  651.         case MappingNotify:    /* why do we get this? */
  652.             break;
  653.         }
  654.     }
  655. }
  656.  
  657. /* set_wm_hints --- set all the window manager hints */
  658.  
  659. void
  660. set_wm_hints(wide, high)
  661. int wide, high;
  662. {
  663.     Pixmap iconpixmap;
  664.     XWMHints *wmhints;
  665.     XSizeHints *sizehints;
  666.     XClassHint *classhints;
  667.     XTextProperty wname, iname;
  668.  
  669.     if ((sizehints = XAllocSizeHints()) == NULL)
  670.         memory("size hints");
  671.  
  672.     if ((wmhints = XAllocWMHints()) == NULL)
  673.         memory("window manager hints");
  674.  
  675.     if ((classhints = XAllocClassHint()) == NULL)
  676.         memory("class hints");
  677.  
  678.     /* fill in hints in order to parse geometry spec */
  679.     sizehints->width = sizehints->min_width = sizehints->max_width = wide;
  680.     sizehints->height = sizehints->min_height = sizehints->max_height = high;
  681.     sizehints->flags = USSize|PSize|PMinSize|PMaxSize;
  682.     if (XWMGeometry(dpy, screen, geometry, "", 1, sizehints,
  683.             &sizehints->x, &sizehints->y,
  684.             &sizehints->width, &sizehints->height,
  685.             &sizehints->win_gravity) & (XValue|YValue))
  686.         sizehints->flags |= USPosition;
  687.  
  688.     /* override -geometry for size of window */
  689.     sizehints->width = sizehints->min_width = sizehints->max_width = wide;
  690.     sizehints->height = sizehints->min_height = sizehints->max_height = high;
  691.  
  692.     if (XStringListToTextProperty(& labelname, 1, & wname) == 0)
  693.         memory("window name structure");
  694.  
  695.     if (XStringListToTextProperty(& labelname, 1, & iname) == 0)
  696.         memory("icon name structure");
  697.  
  698.     menuwin = XCreateSimpleWindow(dpy, root, sizehints->x, sizehints->y,
  699.                 sizehints->width, sizehints->height, 1, black, white);
  700.  
  701.     iconpixmap = XCreateBitmapFromData(dpy, menuwin,
  702.                        nine_menu_bits,
  703.                        nine_menu_width,
  704.                        nine_menu_height);
  705.  
  706.     wmhints->icon_pixmap = iconpixmap;
  707.     wmhints->input = False;        /* no keyboard input */
  708.     if (iconic)
  709.         wmhints->initial_state = IconicState;
  710.     else
  711.         wmhints->initial_state = NormalState;
  712.  
  713.     wmhints->flags = IconPixmapHint | StateHint | InputHint;
  714.  
  715.     classhints->res_name = progname;
  716.     classhints->res_class = "9menu";
  717.  
  718. #ifdef SET_PROPERTIES_MANUALLY
  719.     /*
  720.      * For some reason, XSetWMProperties (see below) is failing
  721.      * John O'Donnell replaces it with the following commands
  722.      * (this leaves out XSetWMClientMachine,
  723.      * and also environment variable checking from ClassHint)
  724.      */
  725.     XSetWMName(dpy, menuwin, &wname);
  726.     XSetWMIconName(dpy, menuwin, &iname);
  727.     XSetCommand(dpy, menuwin, g_argv, g_argc);
  728.     XSetWMHints(dpy, menuwin, wmhints);
  729.     XSetClassHint(dpy, menuwin, classhints);
  730.     XSetWMNormalHints(dpy, menuwin, sizehints);
  731. #else
  732.     XSetWMProperties(dpy, menuwin, & wname, & iname,
  733.         g_argv, g_argc, sizehints, wmhints, classhints);
  734. #endif
  735. }
  736.  
  737. /* ask_wm_for_delete --- jump through hoops to ask WM to delete us */
  738.  
  739. void
  740. ask_wm_for_delete()
  741. {
  742.     int status;
  743.  
  744.     wm_protocols = XInternAtom(dpy, "WM_PROTOCOLS", False);
  745.     wm_delete_window = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
  746.     status = XSetWMProtocols(dpy, menuwin, & wm_delete_window, 1);
  747.  
  748.     if (status != True)
  749.         fprintf(stderr, "%s: could not ask for clean delete\n",
  750.             progname);
  751. }
  752.  
  753. /* redraw --- actually redraw the menu */
  754.  
  755. void
  756. redraw(cur, high, wide)
  757. int cur, high, wide;
  758. {
  759.     int tx, ty, i;
  760.  
  761.     for (i = 0; i < numitems; i++) {
  762.         tx = (wide - XTextWidth(font, labels[i], strlen(labels[i]))) / 2;
  763.         ty = i*high + font->ascent + 1;
  764.         XDrawString(dpy, menuwin, gc, tx, ty, labels[i], strlen(labels[i]));
  765.     }
  766.     if (cur >= 0 && cur < numitems)
  767.         XFillRectangle(dpy, menuwin, gc, 0, cur*high, wide, high);
  768. }
  769.  
  770. /* teleportmenu --- move the menu to the right place */
  771.  
  772. void
  773. teleportmenu(cur, wide, high)
  774. int cur, wide, high;
  775. {
  776.     int x, y, dummy;
  777.     Window wdummy;
  778.  
  779.     if (XQueryPointer(dpy, menuwin, &wdummy, &wdummy, &x, &y,
  780.                    &dummy, &dummy, &dummy))
  781.         XMoveWindow(dpy, menuwin, x-wide/2, y-cur*high-high/2);
  782. }
  783.  
  784. /* warpmouse --- bring the mouse to the menu */
  785.  
  786. void
  787. warpmouse(cur, wide, high)
  788. int cur, wide, high;
  789. {
  790.     int dummy;
  791.     Window wdummy;
  792.     int offset;
  793.  
  794.     /* move tip of pointer into middle of menu item */
  795.     offset = (font->ascent + font->descent + 1) / 2;
  796.     offset += 6;    /* fudge factor */
  797.  
  798.     if (XQueryPointer(dpy, menuwin, &wdummy, &wdummy, &savex, &savey,
  799.                    &dummy, &dummy, &dummy))
  800.         XWarpPointer(dpy, None, menuwin, 0, 0, 0, 0,
  801.                 wide/2, cur*high-high/2+offset);
  802. }
  803.  
  804. /* restoremouse --- put the mouse back where it was */
  805.  
  806. void
  807. restoremouse()
  808. {
  809.     XWarpPointer(dpy, menuwin, root, 0, 0, 0, 0,
  810.                 savex, savey);
  811. }
  812.