/* Copyright ©2006-2010 Kris Maglione * See LICENSE file for license details. */ #define IXP_NO_P9_ #define IXP_P9_STRUCTS #define EXTERN #include "dat.h" #include #include #include #include #include #include #include "fns.h" #define link _link static const char version[] = "wimenu-"VERSION", "COPYRIGHT"\n"; static Biobuf* cmplbuf; static Biobuf* inbuf; static bool alwaysprint; static char* cmdsep; static int screen_hint; static void usage(void) { fatal("usage: wimenu -i [-h ] [-a
] [-p ] [-s ]\n"); } static int errfmt(Fmt *f) { return fmtstrcpy(f, ixp_errbuf()); } static inline void splice(Item *i) { i->next->prev = i->prev; i->prev->next = i->next; } static inline 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; bool stop; stop = !hist && !isatty(buf->fid); i = &ret; while((p = Brdstr(buf, '\n', true))) { if(stop && p[0] == '\0') break; link(i, emallocz(sizeof *i)); i->next_link = i->next; i = i->next; i->string = p; i->retstring = p; if(cmdsep && (p = strstr(p, cmdsep))) { *p = '\0'; i->retstring = p + strlen(cmdsep); } if(!hist) { i->len = strlen(i->string); i->width = textwidth_l(font, i->string, i->len); if(i->width > maxwidth) maxwidth = i->width; } } link(i, &ret); splice(&ret); return ret.next != &ret ? ret.next : nil; } static void check_competions(IxpConn *c) { char *s; s = Brdstr(cmplbuf, '\n', true); if(!s) { ixp_hangup(c); return; } input.filter_start = strtol(s, nil, 10); items = populate_list(cmplbuf, false); update_filter(false); menu_draw(); } 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_input(void) { if(alwaysprint) { write(1, input.string, input.pos - input.string); write(1, "\n", 1); write(1, input.pos, input.end - input.pos); write(1, "\n", 1); } } void update_filter(bool print) { char *filter; filter = input.string + min(input.filter_start, input.pos - input.string); if(input.pos < input.end) filter = freelater(estrndup(filter, input.pos - filter)); matchidx = nil; matchfirst = matchstart = filter_list(items, filter); if(print) update_input(); } enum { PointerScreen = -1 }; void init_screens(void) { Rectangle *rects; Point p; int i, n; rects = xinerama_screens(&n); if (screen_hint >= 0 && screen_hint < n) /* We were given a valid screen index, use that. */ i = screen_hint; else { /* Pick the screen with the pointer, for now. Later, * try for the screen with the focused window first. */ p = querypointer(&scr.root); for(i=0; i < n; i++) if(rect_haspoint_p(rects[i], p)) break; if(i == n) i = 0; } scr.rect = rects[i]; menu_show(); } int main(int argc, char *argv[]) { Item *item; static char *address; static char *histfile; static char *keyfile; static bool nokeys; int i; long ndump; setlocale(LC_CTYPE, ""); fmtinstall('r', errfmt); quotefmtinstall(); screen_hint = PointerScreen; find = strstr; compare = strncmp; ndump = -1; ARGBEGIN{ case 'a': address = EARGF(usage()); break; case 'c': alwaysprint = true; break; case 'h': histfile = EARGF(usage()); break; case 'i': find = strcasestr; compare = strncasecmp; break; case 'K': nokeys = true; case 'k': keyfile = EARGF(usage()); break; case 'n': ndump = strtol(EARGF(usage()), nil, 10); break; case 'p': prompt = EARGF(usage()); break; case 's': screen_hint = strtol(EARGF(usage()), nil, 10); break; case 'S': cmdsep = EARGF(usage()); break; case 'v': lprint(1, "%s", version); return 0; default: usage(); }ARGEND; if(argc) usage(); initdisplay(); xext_init(); if(!isatty(0)) menu_init(); client_init(address); srv.preselect = event_preselect; ixp_listen(&srv, ConnectionNumber(display), nil, event_fdready, event_fdclosed); ontop = !strcmp(readctl("bar on "), "top"); client_readconfig(&cnorm, &csel, &font); cmplbuf = Bfdopen(0, OREAD); items = populate_list(cmplbuf, false); if(!isatty(cmplbuf->fid)) ixp_listen(&srv, cmplbuf->fid, inbuf, check_competions, nil); caret_insert("", true); update_filter(false); if(!nokeys) parse_keys(binding_spec); if(keyfile) { i = open(keyfile, O_RDONLY); if(read(i, buffer, sizeof(buffer)) > 0) parse_keys(buffer); } histidx = &hist; link(&hist, &hist); if(histfile) { inbuf = Bopen(histfile, OREAD); if(inbuf) { item = populate_list(inbuf, true); if(item) { link(item->prev, &hist); link(&hist, item); } Bterm(inbuf); } } if(barwin == nil) menu_init(); init_screens(); i = ixp_serverloop(&srv); if(i) fprint(2, "%s: error: %r\n", argv0); XCloseDisplay(display); if(ndump >= 0 && histfile && result == 0) history_dump(histfile, ndump); return result; }