/* Licence * ======= * * 9menu is free software, and is Copyright (c) 1994 by David Hogan and * Arnold Robbins. Permission is granted to all sentient beings to use * this software, to make copies of it, and to distribute those copies, * provided that: * * (1) the copyright and licence notices are left intact * (2) the recipients are aware that it is free software * (3) any unapproved changes in functionality are either * (i) only distributed as patches * or (ii) distributed as a new program which is not called 9menu * and whose documentation gives credit where it is due * (4) the authors are not held responsible for any defects * or shortcomings in the software, or damages caused by it. * * There is no warranty for this software. Have a nice day. * * -- * Arnold Robbins * arnold@skeeve.com * * 9menu.c * * This program puts up a window that is just a menu, and executes * commands that correspond to the items selected. * * Initial idea: Arnold Robbins * Version using libXg: Matty Farrow (some ideas borrowed) * This code by: David Hogan and Arnold Robbins */ /* * Heavily modified by Kris Maglione for use with wmii. */ #include #include #include #include #include char version[] = "@(#) wmii9menu version 1.8"; /* lovely X stuff */ Display *dpy; int screen; Window root; Window menuwin; Colormap defcmap; XColor color; XFontStruct *font; GC gc; ulong selfg, selbg; ulong normfg, normbg; ulong border; char *sfgname, *sbgname; char *nfgname, *nbgname; char *brcname; /* for XSetWMProperties to use */ int g_argc; char **g_argv; char *initial = ""; int cur; char *fontlist[] = { /* default font list if no -font */ "pelm.latin1.9", "lucm.latin1.9", "blit", "9x15bold", "9x15", "lucidasanstypewriter-12", "fixed", nil }; char *progname; /* my name */ char *displayname; /* X display */ char *fontname; /* font */ char **labels; /* list of labels and commands */ char **commands; int numitems; void usage(void); void run_menu(void); void create_window(int, int); void redraw(int, int); void warpmouse(int, int); void memory(void); int args(void); /* args --- go through the argument list, set options */ struct { char *name, **var; } argtab[] = { {"display", &displayname}, {"initial", &initial}, {"font", &fontname}, {"nb", &nbgname}, {"nf", &nfgname}, {"sb", &sbgname}, {"sf", &sfgname}, {"br", &brcname}, {0, }, }, *ap; static ulong getcolor(char *name, ulong def) { if((name != nil) && (XParseColor(dpy, defcmap, name, &color) != 0) && (XAllocColor(dpy, defcmap, &color) != 0)) return color.pixel; else return def; } /* main --- crack arguments, set up X stuff, run the main menu loop */ int main(int argc, char **argv) { int i, n; char *cp; XGCValues gv; ulong mask; g_argc = argc; g_argv = argv; /* set default label name */ if((cp = strrchr(argv[0], '/')) != nil) progname = ++cp; else progname = argv[0]; for(i = 1; i < argc && argv[i][0] == '-'; i++) { if(strcmp(argv[i], "-version") == 0) { printf("%s\n", version); exit(0); } SET(n); for(ap = argtab; ap->name; ap++) { n = strlen(ap->name); if(strncmp(ap->name, argv[i]+1, n) == 0) break; } if(ap->name == 0) usage(); if(argv[i][n+1] != '\0') *ap->var = &argv[i][n+1]; else { if(argc <= i+1) usage(); *ap->var = argv[++i]; } } argc -= i, argv += i; if(argc == 0) usage(); numitems = argc; labels = emalloc(numitems * sizeof(*labels)); commands = emalloc(numitems * sizeof(*labels)); for(i = 0; i < numitems; i++) { labels[i] = argv[i]; if((cp = strchr(labels[i], ':')) != nil) { *cp++ = '\0'; commands[i] = cp; } else commands[i] = labels[i]; if(strcmp(labels[i], initial) == 0) cur = i; } dpy = XOpenDisplay(displayname); if(dpy == nil) { fprintf(stderr, "%s: cannot open display", progname); if(displayname != nil) fprintf(stderr, " %s", displayname); fprintf(stderr, "\n"); exit(1); } screen = DefaultScreen(dpy); root = RootWindow(dpy, screen); defcmap = DefaultColormap(dpy, screen); selbg = getcolor(sbgname, BlackPixel(dpy, screen)); selfg = getcolor(sfgname, WhitePixel(dpy, screen)); normbg = getcolor(nbgname, selfg); normfg = getcolor(nfgname, selbg); border = getcolor(brcname, selbg); /* try user's font first */ if(fontname != nil) { font = XLoadQueryFont(dpy, fontname); if(font == nil) fprintf(stderr, "%s: warning: can't load font '%s'\n", progname, fontname); } /* if no user font, try one of our default fonts */ if(font == nil) { for(i = 0; fontlist[i] != nil; i++) { font = XLoadQueryFont(dpy, fontlist[i]); if(font != nil) break; } } if(font == nil) { fprintf(stderr, "%s: fatal: cannot load a font\n", progname); exit(1); } gv.foreground = normfg; gv.background = normbg; gv.font = font->fid; gv.line_width = 0; mask = GCForeground | GCBackground | GCFont | GCLineWidth; gc = XCreateGC(dpy, root, mask, &gv); run_menu(); XCloseDisplay(dpy); return 0; } /* usage --- print a usage message and die */ void usage(void) { fprintf(stderr, "usage: %s [-display ] [-font ] ", progname); fprintf(stderr, "[-{n,s}{f,b} ] [-br ] "); fprintf(stderr, "[-version] menitem[:command] ...\n"); exit(0); } /* run_menu --- put up the window, execute selected commands */ enum { MouseMask = ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | PointerMotionMask, MenuMask = MouseMask | StructureNotifyMask | ExposureMask }; void run_menu(void) { XEvent ev; int i, old, wide, high, dx; dx = 0; for(i = 0; i < numitems; i++) { wide = XTextWidth(font, labels[i], strlen(labels[i])) + 4; if(wide > dx) dx = wide; } wide = dx; high = font->ascent + font->descent + 1; create_window(wide, high); warpmouse(wide, high); XSelectInput(dpy, menuwin, MenuMask); XMapWindow(dpy, menuwin); for(;;) { XNextEvent(dpy, &ev); switch (ev.type) { default: fprintf(stderr, "%s: unknown ev.type %d\n", progname, ev.type); break; case ButtonRelease: i = ev.xbutton.y/high; if(ev.xbutton.x < 0 || ev.xbutton.x > wide) return; else if(i < 0 || i >= numitems) return; printf("%s\n", commands[i]); return; case ButtonPress: case MotionNotify: old = cur; cur = ev.xbutton.y/high; if(ev.xbutton.x < 0 || ev.xbutton.x > wide) cur = ~0; if(cur == old) break; redraw(high, wide); break; case MapNotify: redraw(high, wide); if(XGrabPointer(dpy, menuwin, False, MouseMask, GrabModeAsync, GrabModeAsync, 0, None, CurrentTime) != GrabSuccess) fprintf(stderr, "Failed to grab the mouse\n"); break; case Expose: redraw(high, wide); break; case MappingNotify: /* why do we get this? */ break; } } } /* set_wm_hints --- set all the window manager hints */ void create_window(int wide, int high) { XSetWindowAttributes wa = { 0 }; uint h; int x, y, dummy; Window wdummy; h = high * numitems; XQueryPointer(dpy, root, &wdummy, &wdummy, &x, &y, &dummy, &dummy, (uint*)&dummy); x -= wide / 2; if(x < 0) x = 0; else if(x + wide > DisplayWidth(dpy, screen)) x = DisplayWidth(dpy, screen) - wide; y -= cur * high + high / 2; if(y < 0) y = 0; else if(y + h > DisplayHeight(dpy, screen)) y = DisplayHeight(dpy, screen) - h; wa.override_redirect = True; wa.border_pixel = border; wa.background_pixel = normbg; menuwin = XCreateWindow(dpy, root, x, y, wide, h, 1, DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWEventMask, &wa); XSetCommand(dpy, menuwin, g_argv, g_argc); } /* redraw --- actually redraw the menu */ void redraw(int high, int wide) { int tx, ty, i; for(i = 0; i < numitems; i++) { tx = (wide - XTextWidth(font, labels[i], strlen(labels[i]))) / 2; ty = i*high + font->ascent + 1; if(cur == i) XSetForeground(dpy, gc, selbg); else XSetForeground(dpy, gc, normbg); XFillRectangle(dpy, menuwin, gc, 0, i*high, wide, high); if(cur == i) XSetForeground(dpy, gc, selfg); else XSetForeground(dpy, gc, normfg); XDrawString(dpy, menuwin, gc, tx, ty, labels[i], strlen(labels[i])); } } /* warpmouse --- bring the mouse to the menu */ void warpmouse(int wide, int high) { int offset; /* move tip of pointer into middle of menu item */ offset = (font->ascent + font->descent + 1) / 2; offset += 6; /* fudge factor */ XWarpPointer(dpy, None, menuwin, 0, 0, 0, 0, wide/2, cur*high-high/2+offset); }