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