[hackers] [wmii] Add wimenu || Kris Maglione

From: <hg_AT_suckless.org>
Date: Tue, 14 Oct 2008 01:38:06 +0000 (UTC)

changeset: 2353:54a6aab54ce8
tag: tip
user: Kris Maglione <jg_AT_suckless.org>
date: Mon Oct 13 21:38:03 2008 -0400
files: cmd/menu/Makefile cmd/menu/dat.h cmd/menu/event.c cmd/menu/fns.h cmd/menu/main.c cmd/menu/menu.c
description:
Add wimenu

diff -r 53fc8cdc900c -r 54a6aab54ce8 cmd/menu/Makefile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cmd/menu/Makefile Mon Oct 13 21:38:03 2008 -0400
@@ -0,0 +1,26 @@
+ROOT= ../..
+include $(ROOT)/mk/hdr.mk
+include $(ROOT)/mk/wmii.mk
+
+main.c: $(ROOT)/mk/wmii.mk
+
+TARG = menu
+HFILES= dat.h fns.h
+
+LIB = $(LIBIXP)
+LDFLAGS += -lm $(LIBX11) -lXext -lXrandr -lXinerama \
+ -lregexp9 -lbio -lfmt -lutf
+CFLAGS += $(INCX11) -DVERSION=\"$(VERSION)\" \
+ -DIXP_NEEDAPI=86
+OBJ = main \
+ event \
+ menu \
+ ../wmii/geom \
+ ../wmii/map \
+ ../wmii/printevent \
+ ../wmii/x11 \
+ ../wmii/xext \
+ ../util
+
+include $(ROOT)/mk/one.mk
+
diff -r 53fc8cdc900c -r 54a6aab54ce8 cmd/menu/dat.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cmd/menu/dat.h Mon Oct 13 21:38:03 2008 -0400
@@ -0,0 +1,63 @@
+#define IXP_P9_STRUCTS
+#define IXP_NO_P9_
+#include <fmt.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <util.h>
+#include <ixp.h>
+#include <x11.h>
+
+#define BLOCK(x) do { x; }while(0)
+
+#ifndef EXTERN
+# define EXTERN extern
+#endif
+
+typedef struct Item Item;
+
+struct Item {
+ char* string;
+ char* retstring;
+ Item* next_link;
+ Item* next;
+ Item* prev;
+ int len;
+ int width;
+};
+
+EXTERN long xtime;
+EXTERN Image* ibuf;
+EXTERN Font* font;
+EXTERN CTuple cnorm, csel;
+EXTERN bool ontop;
+
+EXTERN Cursor cursor[1];
+EXTERN Visual* render_visual;
+
+EXTERN IxpServer srv;
+
+EXTERN Item* items;
+EXTERN Item* matchfirst;
+EXTERN Item* matchstart;
+EXTERN Item* matchend;
+EXTERN Item* matchidx;
+
+EXTERN Item* histidx;
+
+EXTERN char filter[1024];
+
+EXTERN int maxwidth;
+EXTERN int result;
+
+EXTERN char buffer[8092];
+EXTERN char* _buffer;
+
+static char* const _buf_end = buffer + sizeof buffer;
+
+#define bufclear() \
+ BLOCK( _buffer = buffer; _buffer[0] = '\0' )
+#define bufprint(...) \
+ _buffer = seprint(_buffer, _buf_end, __VA_ARGS__)
+
diff -r 53fc8cdc900c -r 54a6aab54ce8 cmd/menu/event.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cmd/menu/event.c Mon Oct 13 21:38:03 2008 -0400
@@ -0,0 +1,334 @@
+/* Copyright ©2006-2008 Kris Maglione <fbsdaemon_AT_gmail.com>
+ * See LICENSE file for license details.
+ */
+#include "dat.h"
+#include "fns.h"
+
+typedef void (*EvHandler)(XEvent*);
+static EvHandler handler[LASTEvent];
+
+void
+dispatch_event(XEvent *e) {
+ if(e->type < nelem(handler)) {
+ if(handler[e->type])
+ handler[e->type](e);
+ }else
+ xext_event(e);
+}
+
+#define handle(w, fn, ev) \
+ BLOCK(if((w)->handler->fn) (w)->handler->fn((w), ev))
+
+static int
+findtime(Display *d, XEvent *e, XPointer v) {
+ Window *w;
+
+ w = (Window*)v;
+ if(e->type == PropertyNotify && e->xproperty.window == w->w) {
+ xtime = e->xproperty.time;
+ return true;
+ }
+ return false;
+}
+
+void
+xtime_kludge(void) {
+ /* Round trip. */
+ static Window *w;
+ WinAttr wa;
+ XEvent e;
+ long l;
+
+ if(w == nil) {
+ w = createwindow(&scr.root, Rect(0, 0, 1, 1), 0, InputOnly, &wa, 0);
+ selectinput(w, PropertyChangeMask);
+ }
+ changeprop_long(w, "ATOM", "ATOM", &l, 0);
+ sync();
+ XIfEvent(display, &e, findtime, (void*)w);
+}
+
+uint
+flushevents(long event_mask, bool dispatch) {
+ XEvent ev;
+ uint n = 0;
+
+ while(XCheckMaskEvent(display, event_mask, &ev)) {
+ if(dispatch)
+ dispatch_event(&ev);
+ n++;
+ }
+ return n;
+}
+
+static int
+findenter(Display *d, XEvent *e, XPointer v) {
+ long *l;
+
+ USED(d);
+ l = (long*)v;
+ if(*l)
+ return false;
+ if(e->type == EnterNotify)
+ return true;
+ if(e->type == MotionNotify)
+ (*l)++;
+ return false;
+}
+
+/* This isn't perfect. If there were motion events in the queue
+ * before this was called, then it flushes nothing. If we don't
+ * check for them, we might lose a legitamate enter event.
+ */
+uint
+flushenterevents(void) {
+ XEvent e;
+ long l;
+ int n;
+
+ l = 0;
+ n = 0;
+ while(XCheckIfEvent(display, &e, findenter, (void*)&l))
+ n++;
+ return n;
+}
+
+static void
+buttonrelease(XButtonPressedEvent *ev) {
+ Window *w;
+
+ if((w = findwin(ev->window)))
+ handle(w, bup, ev);
+}
+
+static void
+buttonpress(XButtonPressedEvent *ev) {
+ Window *w;
+
+ if((w = findwin(ev->window)))
+ handle(w, bdown, ev);
+ else
+ XAllowEvents(display, ReplayPointer, ev->time);
+}
+
+static void
+configurerequest(XConfigureRequestEvent *ev) {
+ XWindowChanges wc;
+ Window *w;
+
+ if((w = findwin(ev->window)))
+ handle(w, configreq, ev);
+ else{
+ wc.x = ev->x;
+ wc.y = ev->y;
+ wc.width = ev->width;
+ wc.height = ev->height;
+ wc.border_width = ev->border_width;
+ wc.sibling = ev->above;
+ wc.stack_mode = ev->detail;
+ XConfigureWindow(display, ev->window, ev->value_mask, &wc);
+ }
+}
+
+static void
+configurenotify(XConfigureEvent *ev) {
+ Window *w;
+
+ USED(ev);
+ if((w = findwin(ev->window)))
+ handle(w, config, ev);
+}
+
+static void
+clientmessage(XClientMessageEvent *ev) {
+
+ USED(ev);
+}
+
+static void
+destroynotify(XDestroyWindowEvent *ev) {
+ Window *w;
+
+ if((w = findwin(ev->window)))
+ handle(w, destroy, ev);
+}
+
+static void
+enternotify(XCrossingEvent *ev) {
+ Window *w;
+ static int sel_screen;
+
+ xtime = ev->time;
+ if(ev->mode != NotifyNormal)
+ return;
+
+ if((w = findwin(ev->window)))
+ handle(w, enter, ev);
+ else if(ev->window == scr.root.w)
+ sel_screen = true;
+}
+
+static void
+leavenotify(XCrossingEvent *ev) {
+
+ xtime = ev->time;
+#if 0
+ if((ev->window == scr.root.w) && !ev->same_screen)
+ sel_screen = true;
+#endif
+}
+
+static void
+focusin(XFocusChangeEvent *ev) {
+ Window *w;
+
+ /* Yes, we're focusing in on nothing, here. */
+ if(ev->detail == NotifyDetailNone) {
+ /* FIXME: Do something. */
+ return;
+ }
+
+ if(!((ev->detail == NotifyNonlinear)
+ ||(ev->detail == NotifyNonlinearVirtual)
+ ||(ev->detail == NotifyVirtual)
+ ||(ev->detail == NotifyInferior)
+ ||(ev->detail == NotifyAncestor)))
+ return;
+ if((ev->mode == NotifyWhileGrabbed)) /* && (screen->hasgrab != &c_root)) */
+ return;
+
+ if((w = findwin(ev->window)))
+ handle(w, focusin, ev);
+#if 0
+ else if(ev->mode == NotifyGrab) {
+ if(ev->window == scr.root.w)
+ screen->hasgrab = &c_root;
+ /* Some unmanaged window has grabbed focus */
+ else if((c = screen->focus)) {
+ print_focus("focusin", &c_magic, "<magic>");
+ screen->focus = &c_magic;
+ if(c->sel)
+ frame_draw(c->sel);
+ }
+ }
+#endif
+}
+
+static void
+focusout(XFocusChangeEvent *ev) {
+ Window *w;
+
+ if(!((ev->detail == NotifyNonlinear)
+ ||(ev->detail == NotifyNonlinearVirtual)
+ ||(ev->detail == NotifyVirtual)
+ ||(ev->detail == NotifyInferior)
+ ||(ev->detail == NotifyAncestor)))
+ return;
+#if 0
+ if(ev->mode == NotifyUngrab)
+ screen->hasgrab = nil;
+#endif
+
+ if((w = findwin(ev->window)))
+ handle(w, focusout, ev);
+}
+
+static void
+expose(XExposeEvent *ev) {
+ Window *w;
+
+ if(ev->count == 0) {
+ if((w = findwin(ev->window)))
+ handle(w, expose, ev);
+ }
+}
+
+static void
+keypress(XKeyEvent *ev) {
+ Window *w;
+
+ xtime = ev->time;
+ if((w = findwin(ev->window)))
+ handle(w, kdown, ev);
+}
+
+static void
+mappingnotify(XMappingEvent *ev) {
+
+ /* Why do you need me to tell you this? */
+ XRefreshKeyboardMapping(ev);
+}
+
+static void
+maprequest(XMapRequestEvent *ev) {
+
+ USED(ev);
+}
+
+static void
+motionnotify(XMotionEvent *ev) {
+ Window *w;
+
+ xtime = ev->time;
+ if((w = findwin(ev->window)))
+ handle(w, motion, ev);
+}
+
+static void
+propertynotify(XPropertyEvent *ev) {
+ Window *w;
+
+ xtime = ev->time;
+ if((w = findwin(ev->window)))
+ handle(w, property, ev);
+}
+
+static void
+mapnotify(XMapEvent *ev) {
+ Window *w;
+
+ if((w = findwin(ev->window)))
+ handle(w, map, ev);
+}
+
+static void
+unmapnotify(XUnmapEvent *ev) {
+ Window *w;
+
+ if((w = findwin(ev->window)) && (ev->event == w->parent->w)) {
+ w->mapped = false;
+ if(ev->send_event || w->unmapped-- == 0)
+ handle(w, unmap, ev);
+ }
+}
+
+static EvHandler handler[LASTEvent] = {
+ [ButtonPress] = (EvHandler)buttonpress,
+ [ButtonRelease] = (EvHandler)buttonrelease,
+ [ConfigureRequest] = (EvHandler)configurerequest,
+ [ConfigureNotify] = (EvHandler)configurenotify,
+ [ClientMessage] = (EvHandler)clientmessage,
+ [DestroyNotify] = (EvHandler)destroynotify,
+ [EnterNotify] = (EvHandler)enternotify,
+ [Expose] = (EvHandler)expose,
+ [FocusIn] = (EvHandler)focusin,
+ [FocusOut] = (EvHandler)focusout,
+ [KeyPress] = (EvHandler)keypress,
+ [LeaveNotify] = (EvHandler)leavenotify,
+ [MapNotify] = (EvHandler)mapnotify,
+ [MapRequest] = (EvHandler)maprequest,
+ [MappingNotify] = (EvHandler)mappingnotify,
+ [MotionNotify] = (EvHandler)motionnotify,
+ [PropertyNotify] = (EvHandler)propertynotify,
+ [UnmapNotify] = (EvHandler)unmapnotify,
+};
+
+void
+check_x_event(IxpConn *c) {
+ XEvent ev;
+
+ USED(c);
+ while(XCheckMaskEvent(display, ~0, &ev))
+ dispatch_event(&ev);
+}
+
diff -r 53fc8cdc900c -r 54a6aab54ce8 cmd/menu/fns.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cmd/menu/fns.h Mon Oct 13 21:38:03 2008 -0400
@@ -0,0 +1,29 @@
+
+void check_x_event(IxpConn*);
+void debug(int, const char*, ...);
+void dispatch_event(XEvent*);
+Item* filter_list(Item*, char*);
+uint flushenterevents(void);
+uint flushevents(long, bool);
+void init_screens(void);
+void menu_init(void);
+void menu_show(void);
+void xtime_kludge(void);
+void update_filter(void);
+
+/* geom.c */
+Align get_sticky(Rectangle src, Rectangle dst);
+Cursor quad_cursor(Align);
+Align quadrant(Rectangle, Point);
+bool rect_contains_p(Rectangle, Rectangle);
+bool rect_haspoint_p(Point, Rectangle);
+bool rect_intersect_p(Rectangle, Rectangle);
+Rectangle rect_intersection(Rectangle, Rectangle);
+
+/* xext.c */
+void randr_event(XEvent*);
+bool render_argb_p(Visual*);
+void xext_event(XEvent*);
+void xext_init(void);
+Rectangle* xinerama_screens(int*);
+
diff -r 53fc8cdc900c -r 54a6aab54ce8 cmd/menu/main.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cmd/menu/main.c Mon Oct 13 21:38:03 2008 -0400
@@ -0,0 +1,292 @@
+/* Copyright ©2004-2006 Anselm R. Garbe <garbeam at gmail dot com>
+ * Copyright ©2006-2008 Kris Maglione <fbsdaemon_AT_gmail.com>
+ * See LICENSE file for license details.
+ */
+#define IXP_NO_P9_
+#define IXP_P9_STRUCTS
+#define EXTERN
+#include "dat.h"
+#include <X11/Xproto.h>
+#include <locale.h>
+#include <string.h>
+#include <strings.h>
+#include <bio.h>
+#include "fns.h"
+#define link _link
+
+static const char version[] = "wimenu-"VERSION", ©2008 Kris Maglione\n";
+static IxpClient* client;
+static IxpCFid* ctlfid;
+static Biobuf* inbuf;
+static char ctl[1024];
+static char* ectl;
+
+static char* (*find)(const char*, const char*);
+
+static void
+usage(void) {
+ fatal("usage: wimenu ...\n");
+}
+
+static int
+errfmt(Fmt *f) {
+ return fmtstrcpy(f, ixp_errbuf());
+}
+
+/* Stubs. */
+void
+debug(int flag, const char *fmt, ...) {
+ va_list ap;
+
+ USED(flag);
+ va_start(ap, fmt);
+ vfprint(2, fmt, ap);
+ va_end(ap);
+}
+
+void dprint(long, char*, ...);
+void dprint(long mask, char *fmt, ...) {
+ va_list ap;
+
+ USED(mask);
+ va_start(ap, fmt);
+ vfprint(2, fmt, ap);
+ va_end(ap);
+}
+
+static char*
+readctl(char *key) {
+ char *s, *p;
+ int nkey, n;
+
+ nkey = strlen(key);
+ p = ctl - 1;
+ do {
+ p++;
+ if(!strncmp(p, key, nkey)) {
+ p += nkey;
+ s = strchr(p, '\n');
+ n = (s ? s : ectl) - p;
+ s = emalloc(n + 1);
+ s[n] = '\0';
+ return strncpy(s, p, n);
+ }
+ } while((p = strchr(p, '\n')));
+ return "";
+}
+
+static void
+splice(Item *i) {
+ i->next->prev = i->prev;
+ i->prev->next = i->next;
+}
+static void
+link(Item *i, Item *j) {
+ i->next = j;
+ j->prev = i;
+}
+
+static Item*
+populate_list(Biobuf *buf, bool hist) {
+ Item ret;
+ Item *i;
+ char *p;
+
+ i = &ret;
+ while((p = Brdstr(buf, '\n', true))) {
+ link(i, emallocz(sizeof *i));
+ i->next_link = i->next;
+ i = i->next;
+ i->string = p;
+ i->retstring = p;
+ if(!hist) {
+ i->len = strlen(p);
+ i->width = textwidth_l(font, p, i->len);
+ if(i->width > maxwidth)
+ maxwidth = i->width;
+ }
+ }
+
+ link(i, &ret);
+ splice(&ret);
+ return ret.next != &ret ? ret.next : nil;
+}
+
+Item*
+filter_list(Item *i, char *filter) {
+ static Item exact;
+ Item start, substr;
+ Item *exactp, *startp, *substrp;
+ Item **ip;
+ char *p;
+ int len;
+
+ len = strlen(filter);
+ exactp = &exact;
+ startp = &start;
+ substrp = &substr;
+ for(; i; i=i->next_link)
+ if((p = find(i->string, filter))) {
+ ip = &substrp;
+ if(p == i->string)
+ if(strlen(p) == len)
+ ip = &exactp;
+ else
+ ip = &startp;
+ link(*ip, i);
+ *ip = i;
+ }
+
+ link(substrp, &exact);
+ link(startp, &substr);
+ link(exactp, &start);
+ splice(&substr);
+ splice(&start);
+ splice(&exact);
+ return exact.next;
+}
+
+void
+update_filter(void) {
+ /* TODO: Perhaps filter only previous matches unless filter
+ * has been truncated.
+ */
+ matchfirst = matchstart = matchidx = filter_list(items, filter);
+}
+
+/*
+ * There's no way to check accesses to destroyed windows, thus
+ * those cases are ignored (especially on UnmapNotifies).
+ * Other types of errors call Xlib's default error handler, which
+ * calls exit().
+ */
+ErrorCode ignored_xerrors[] = {
+ { 0, BadWindow },
+ { X_SetInputFocus, BadMatch },
+ { X_PolyText8, BadDrawable },
+ { X_PolyFillRectangle, BadDrawable },
+ { X_PolySegment, BadDrawable },
+ { X_ConfigureWindow, BadMatch },
+ { X_GrabKey, BadAccess },
+ { X_GetAtomName, BadAtom },
+};
+
+static void
+end(IxpConn *c) {
+
+ USED(c);
+ srv.running = 0;
+}
+
+static void
+preselect(IxpServer *s) {
+
+ USED(s);
+ check_x_event(nil);
+}
+
+void
+init_screens(void) {
+ Rectangle *rects;
+ Point p;
+ int i, n;
+
+ /* Pick the screen with the pointer, for now. Later,
+ * try for the screen with the focused window first.
+ */
+ p = querypointer(&scr.root);
+ rects = xinerama_screens(&n);
+ for(i=0; i < n; i++)
+ if(rect_haspoint_p(p, rects[i]))
+ break;
+ if(i == n)
+ i = 0;
+ /* Probably not the best route. */
+ scr.rect = rects[i];
+ menu_show();
+}
+
+int
+main(int argc, char *argv[]) {
+ Item hist = { .string = "", };
+ Item *item;
+ char *address;
+ char *histfile;
+ int i;
+
+ quotefmtinstall();
+ fmtinstall('r', errfmt);
+ address = getenv("WMII_ADDRESS");
+ histfile = nil;
+ find = strstr;
+
+ ARGBEGIN{
+ case 'a':
+ address = EARGF(usage());
+ break;
+ case 'h':
+ histfile = EARGF(usage());
+ break;
+ case 'i':
+ find = strcasestr;
+ break;
+ default:
+ usage();
+ }ARGEND;
+
+ if(argc)
+ usage();
+
+ setlocale(LC_CTYPE, "");
+
+ initdisplay();
+
+ if(address && *address)
+ client = ixp_mount(address);
+ else
+ client = ixp_nsmount("wmii");
+ if(client == nil)
+ fatal("can't mount: %r\n");
+
+ ctlfid = ixp_open(client, "ctl", OREAD);
+ i = ixp_read(ctlfid, ctl, 1023);
+ ectl = ctl + i;
+
+ srv.preselect = preselect;
+ ixp_listen(&srv, ConnectionNumber(display), nil, check_x_event, end);
+
+ loadcolor(&cnorm, readctl("normcolors "));
+ loadcolor(&csel, readctl("focuscolors "));
+ font = loadfont(readctl("font "));
+ if(!font)
+ fatal("Can't load font %q", readctl("font "));
+
+ inbuf = Bfdopen(0, OREAD);
+ items = populate_list(inbuf, false);
+ update_filter();
+
+ Bterm(inbuf);
+ histidx = &hist;
+ if(histfile) {
+ inbuf = Bopen(histfile, OREAD);
+ if(!inbuf)
+ fatal("Can't open histfile %q: %r", histfile);
+ item = populate_list(inbuf, true);
+ if(item) {
+ link(item->prev, &hist);
+ item->prev = nil;
+ }
+ Bterm(inbuf);
+ }
+
+ xext_init();
+ menu_init();
+ init_screens();
+
+ i = ixp_serverloop(&srv);
+ if(i)
+ fprint(2, "%s: error: %r\n", argv0);
+ XCloseDisplay(display);
+ return result;
+}
+
diff -r 53fc8cdc900c -r 54a6aab54ce8 cmd/menu/menu.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cmd/menu/menu.c Mon Oct 13 21:38:03 2008 -0400
@@ -0,0 +1,354 @@
+#include "dat.h"
+#include <ctype.h>
+#include <string.h>
+#include "fns.h"
+
+static Window* barwin;
+static Handlers handlers;
+
+static int ltwidth;
+static int numlock;
+
+static void menu_draw(void);
+
+enum {
+ ACCEPT,
+ REJECT,
+ HIST_NEXT,
+ HIST_PREV,
+ KILL_CHAR,
+ KILL_WORD,
+ KILL_LINE,
+ CMPL_NEXT,
+ CMPL_PREV,
+ CMPL_FIRST,
+ CMPL_LAST,
+ CMPL_NEXT_PAGE,
+ CMPL_PREV_PAGE,
+};
+
+void
+menu_init(void) {
+ WinAttr wa;
+
+ ltwidth = textwidth(font, "<");
+
+ wa.override_redirect = 1;
+ wa.background_pixmap = ParentRelative;
+ wa.event_mask = ExposureMask | KeyPressMask;
+ barwin = createwindow(&scr.root, Rect(0, 0, 1, 1), scr.depth, InputOutput,
+ &wa, CWOverrideRedirect
+ | CWBackPixmap
+ | CWEventMask);
+ sethandler(barwin, &handlers);
+}
+
+static void
+menu_unmap(long id, void *p) {
+
+ USED(id, p);
+ unmapwin(barwin);
+ XFlush(display);
+}
+
+static void
+menu_cmd(int op) {
+ bool res;
+ int i;
+
+ i = strlen(filter);
+ switch(op) {
+ case ACCEPT:
+ srv.running = false;
+ if(matchidx)
+ print("%s", matchidx->retstring);
+ else
+ result = 1;
+ break;
+ case REJECT:
+ srv.running = false;
+ result = 1;
+ break;
+ case HIST_NEXT:
+ if(histidx->next) {
+ histidx = histidx->next;
+ strncpy(filter, histidx->string, sizeof filter);
+ }
+ break;
+ case HIST_PREV:
+ if(histidx->prev) {
+ histidx = histidx->prev;
+ strncpy(filter, histidx->string, sizeof filter);
+ }
+ break;
+ case KILL_CHAR:
+ if(i > 0)
+ filter[i-1] = '\0';
+ break;
+ case KILL_WORD:
+ if(i == 0)
+ break;
+ for(i--; i >= 0 && isspace(filter[i]); i--)
+ filter[i] = '\0';
+ if(i >= 0)
+ res = !isalnum(filter[i]);
+ for(; i >= 0 && !isalnum(filter[i]) == res && !isspace(filter[i]); i--)
+ filter[i] = '\0';
+ break;
+ case KILL_LINE:
+ /* TODO: Add a caret. */
+ filter[0] = '\0';
+ break;
+ case CMPL_NEXT:
+ matchidx = matchidx->next;
+ break;
+ case CMPL_PREV:
+ matchidx = matchidx->prev;
+ break;
+ case CMPL_FIRST:
+ matchidx = matchfirst;
+ break;
+ case CMPL_LAST:
+ matchidx = matchfirst->prev;
+ break;
+ case CMPL_NEXT_PAGE:
+ matchstart = matchend->next;
+ break;
+ case CMPL_PREV_PAGE:
+ matchend = matchstart->prev;
+ break;
+ }
+ update_filter();
+ menu_draw();
+}
+
+static void
+menu_draw(void) {
+ Rectangle r, r2;
+ CTuple *c;
+ Item *i;
+ int inputw, itemoff, end, pad;
+
+ r = barwin->r;
+ r = rectsetorigin(r, ZP);
+ r2 = r;
+
+ inputw = min(Dx(r) / 3, maxwidth) + pad;
+ itemoff = inputw + 2 * ltwidth;
+ end = Dx(r) - ltwidth;
+ pad = (font->height & ~1);
+
+ fill(ibuf, r, cnorm.bg);
+
+ for(i=matchstart; i->string; i=i->next) {
+ r2.min.x = itemoff;
+ itemoff = itemoff + i->width + pad;
+ r2.max.x = min(itemoff, end);
+ if(i != matchstart && itemoff > end)
+ break;
+
+ c = (i == matchidx) ? &csel : &cnorm;
+ fill(ibuf, r2, c->bg);
+ drawstring(ibuf, font, r2, Center, i->string, c->fg);
+ matchend = i;
+ if(i->next == matchfirst)
+ break;
+ }
+
+ r2 = r;
+ r2.min.x = inputw;
+ if(matchstart != matchfirst)
+ drawstring(ibuf, font, r2, West, "<", cnorm.fg);
+ if(matchend->next != matchfirst)
+ drawstring(ibuf, font, r2, East, ">", cnorm.fg);
+ r2 = r;
+ r2.max.x = inputw;
+ drawstring(ibuf, font, r2, West, filter, cnorm.fg);
+
+ r2.min.x = textwidth(font, filter) + pad/2 + 1;
+ r2.max.x = r2.min.x + 2;
+ r2.min.y++;
+ r2.max.y--;
+ border(ibuf, r2, 1, cnorm.border);
+
+ border(ibuf, r, 1, cnorm.border);
+ copyimage(barwin, r, ibuf, ZP);
+}
+
+void
+menu_show(void) {
+ Rectangle r;
+ int height, pad;
+
+ USED(menu_unmap);
+
+ pad = (font->height & ~1)/2;
+ height = font->height + 2;
+
+ r = scr.rect;
+ if(ontop)
+ r.max.y = r.min.y + height;
+ else
+ r.min.y = r.max.y - height;
+ reshapewin(barwin, r);
+
+ freeimage(ibuf);
+ ibuf = allocimage(Dx(r), Dy(r), scr.depth);
+
+ mapwin(barwin);
+ raisewin(barwin);
+ menu_draw();
+ setfocus(barwin, RevertToPointerRoot);
+}
+
+static void
+kdown_event(Window *w, XKeyEvent *e) {
+ char buf[32];
+ int num, i;
+ KeySym ksym;
+
+ buf[0] = 0;
+ num = XLookupString(e, buf, sizeof buf, &ksym, 0);
+ if(IsKeypadKey(ksym))
+ if(ksym == XK_KP_Enter)
+ ksym = XK_Return;
+ else if(ksym >= XK_KP_0 && ksym <= XK_KP_9)
+ ksym = (ksym - XK_KP_0) + XK_0;
+
+ if(IsFunctionKey(ksym)
+ || IsKeypadKey(ksym)
+ || IsMiscFunctionKey(ksym)
+ || IsPFKey(ksym)
+ || IsPrivateKeypadKey(ksym))
+ return;
+
+ /* first check if a control mask is omitted */
+ if(e->state & ControlMask) {
+ switch (ksym) {
+ default:
+ return;
+ case XK_bracketleft: /* Esc */
+ menu_cmd(REJECT);
+ return;
+ case XK_j:
+ case XK_J:
+ case XK_m:
+ case XK_M:
+ menu_cmd(ACCEPT);
+ return;
+ case XK_n:
+ case XK_N:
+ menu_cmd(HIST_NEXT);
+ return;
+ case XK_p:
+ case XK_P:
+ menu_cmd(HIST_PREV);
+ return;
+ case XK_i: /* Tab */
+ case XK_I:
+ menu_cmd(CMPL_NEXT);
+ return;
+ case XK_h:
+ case XK_H:
+ menu_cmd(KILL_CHAR);
+ return;
+ case XK_w:
+ case XK_W:
+ menu_cmd(KILL_WORD);
+ return;
+ case XK_u:
+ case XK_U:
+ menu_cmd(KILL_LINE);
+ return;
+ }
+ }
+ /* Alt-<Key> - Vim */
+ if((e->state & ~(numlock | LockMask)) & Mod1Mask) {
+ switch(ksym) {
+ default:
+ return;
+ case XK_h:
+ menu_cmd(CMPL_PREV);
+ return;
+ case XK_l:
+ menu_cmd(CMPL_NEXT);
+ return;
+ case XK_j:
+ menu_cmd(CMPL_NEXT_PAGE);
+ return;
+ case XK_k:
+ menu_cmd(CMPL_PREV_PAGE);
+ return;
+ case XK_g:
+ menu_cmd(CMPL_FIRST);
+ return;
+ case XK_G:
+ menu_cmd(CMPL_LAST);
+ return;
+ }
+ }
+ switch(ksym) {
+ default:
+ if(num && !iscntrl(buf[0])) {
+ i = strlen(filter);
+ if(i < sizeof filter - 1) {
+ filter[i] = buf[0];
+ filter[i+1] = '\0';
+ }
+ update_filter();
+ menu_draw();
+ }
+ break;
+ case XK_Escape:
+ menu_cmd(REJECT);
+ return;
+ case XK_Return:
+ menu_cmd(ACCEPT);
+ return;
+ case XK_BackSpace:
+ menu_cmd(KILL_CHAR);
+ return;
+ case XK_Up:
+ menu_cmd(HIST_PREV);
+ return;
+ case XK_Down:
+ menu_cmd(HIST_NEXT);
+ return;
+ case XK_Home:
+ /* TODO: Caret. */
+ menu_cmd(CMPL_FIRST);
+ return;
+ case XK_End:
+ /* TODO: Caret. */
+ menu_cmd(CMPL_LAST);
+ return;
+ case XK_Left:
+ menu_cmd(CMPL_PREV);
+ return;
+ case XK_Right:
+ menu_cmd(CMPL_NEXT);
+ return;
+ case XK_Next:
+ menu_cmd(CMPL_NEXT_PAGE);
+ return;
+ case XK_Prior:
+ menu_cmd(CMPL_PREV_PAGE);
+ return;
+ case XK_Tab:
+ menu_cmd(CMPL_NEXT);
+ return;
+ }
+}
+
+static void
+expose_event(Window *w, XExposeEvent *e) {
+
+ USED(w);
+ menu_draw();
+}
+
+static Handlers handlers = {
+ .expose = expose_event,
+ .kdown = kdown_event,
+};
+
Received on Tue Oct 14 2008 - 01:38:06 UTC

This archive was generated by hypermail 2.2.0 : Tue Oct 14 2008 - 01:48:05 UTC