main.c (5723B)
1 /* Copyright ©2006-2010 Kris Maglione <maglione.k at Gmail> 2 * See LICENSE file for license details. 3 */ 4 #define EXTERN 5 #include "dat.h" 6 #include <X11/Xproto.h> 7 #include <locale.h> 8 #include <strings.h> 9 #include <unistd.h> 10 #include <bio.h> 11 #include <stuff/clientutil.h> 12 #include "fns.h" 13 #define link _link 14 15 static const char version[] = "wimenu-"VERSION", "COPYRIGHT"\n"; 16 static Biobuf* cmplbuf; 17 static Biobuf* inbuf; 18 static bool alwaysprint; 19 static char* cmdsep; 20 static int screen_hint; 21 22 static void 23 usage(void) { 24 fprint(2, "usage: %s -i [-a <address>] [-h <history>] [-p <prompt>] [-r <rows>] [-s <screen>]\n", argv0); 25 fprint(2, " See manual page for full usage details.\n"); 26 exit(1); 27 } 28 29 static int 30 errfmt(Fmt *f) { 31 return fmtstrcpy(f, ixp_errbuf()); 32 } 33 34 static inline void 35 splice(Item *i) { 36 i->next->prev = i->prev; 37 i->prev->next = i->next; 38 } 39 static inline void 40 link(Item *i, Item *j) { 41 i->next = j; 42 j->prev = i; 43 } 44 45 static Item* 46 populate_list(Biobuf *buf, bool hist) { 47 Item ret; 48 Item *i; 49 char *p; 50 bool stop; 51 52 stop = !hist && !isatty(buf->fid); 53 ret.next_link = nil; 54 i = &ret; 55 while((p = Brdstr(buf, '\n', true))) { 56 if(stop && p[0] == '\0') 57 break; 58 i->next_link = emallocz(sizeof *i); 59 i = i->next_link; 60 i->string = p; 61 i->retstring = p; 62 if(cmdsep && (p = strstr(p, cmdsep))) { 63 *p = '\0'; 64 i->retstring = p + strlen(cmdsep); 65 } 66 if(!hist) { 67 i->len = strlen(i->string); 68 i->width = textwidth_l(font, i->string, i->len) + itempad; 69 match.maxwidth = max(i->width, match.maxwidth); 70 } 71 } 72 73 return ret.next_link; 74 } 75 76 static void 77 check_competions(IxpConn *c) { 78 char *s; 79 80 s = Brdstr(cmplbuf, '\n', true); 81 if(!s) { 82 ixp_hangup(c); 83 return; 84 } 85 input.filter_start = strtol(s, nil, 10); 86 match.all = populate_list(cmplbuf, false); 87 update_filter(false); 88 menu_draw(); 89 } 90 91 Item* 92 filter_list(Item *i, char *filter) { 93 static Item exact; 94 Item start, substr; 95 Item *exactp, *startp, *substrp; 96 Item **ip; 97 char *p; 98 int len; 99 100 len = strlen(filter); 101 exactp = &exact; 102 startp = &start; 103 substrp = &substr; 104 for(; i; i=i->next_link) 105 if((p = find(i->string, filter))) { 106 ip = &substrp; 107 if(p == i->string) 108 if(strlen(p) == len) 109 ip = &exactp; 110 else 111 ip = &startp; 112 link(*ip, i); 113 *ip = i; 114 } 115 116 link(substrp, &exact); 117 link(startp, &substr); 118 link(exactp, &start); 119 splice(&substr); 120 splice(&start); 121 splice(&exact); 122 return exact.next; 123 } 124 125 void 126 update_input(void) { 127 if(alwaysprint) { 128 write(1, input.string, input.pos - input.string); 129 write(1, "\n", 1); 130 write(1, input.pos, input.end - input.pos); 131 write(1, "\n", 1); 132 } 133 } 134 135 void 136 update_filter(bool print) { 137 char *filter; 138 139 filter = input.string + min(input.filter_start, input.pos - input.string); 140 if(input.pos < input.end) 141 filter = freelater(estrndup(filter, input.pos - filter)); 142 143 match.sel = nil; 144 match.first = match.start = filter_list(match.all, filter); 145 if(print) 146 update_input(); 147 } 148 149 enum { PointerScreen = -1 }; 150 151 void 152 init_screens(void) { 153 Rectangle *rects; 154 Point p; 155 int i, n; 156 157 rects = xinerama_screens(&n); 158 if(screen_hint >= 0 && screen_hint < n) 159 i = screen_hint; 160 else { 161 /* Pick the screen with the pointer, for now. Later, 162 * try for the screen with the focused window first. 163 */ 164 p = querypointer(&scr.root); 165 for(i=0; i < n; i++) 166 if(rect_haspoint_p(rects[i], p)) 167 break; 168 if(i == n) 169 i = 0; 170 } 171 scr.rect = rects[i]; 172 menu_show(); 173 } 174 175 ErrorCode ignored_xerrors[] = { 176 { 0, BadWindow }, 177 { X_GetAtomName, BadAtom }, 178 }; 179 180 int 181 main(int argc, char *argv[]) { 182 static char *address; 183 static char *histfile; 184 static char *keyfile; 185 static bool nokeys; 186 Item *item; 187 int i; 188 long ndump; 189 190 setlocale(LC_ALL, ""); 191 fmtinstall('r', errfmt); 192 quotefmtinstall(); 193 194 screen_hint = PointerScreen; 195 196 find = strstr; 197 compare = strncmp; 198 199 ndump = -1; 200 201 ARGBEGIN{ 202 case 'a': 203 address = EARGF(usage()); 204 break; 205 case 'c': 206 alwaysprint = true; 207 break; 208 case 'h': 209 histfile = EARGF(usage()); 210 break; 211 case 'i': 212 find = strcasestr; 213 compare = strncasecmp; 214 break; 215 case 'K': 216 nokeys = true; 217 case 'k': 218 keyfile = EARGF(usage()); 219 break; 220 case 'n': 221 ndump = strtol(EARGF(usage()), nil, 10); 222 break; 223 case 'p': 224 menu.prompt = EARGF(usage()); 225 break; 226 case 'r': 227 menu.rows = strtol(EARGF(usage()), nil, 10); 228 break; 229 case 's': 230 screen_hint = strtol(EARGF(usage()), nil, 10); 231 break; 232 case 'S': 233 cmdsep = EARGF(usage()); 234 break; 235 case 'v': 236 lprint(1, "%s", version); 237 return 0; 238 default: 239 usage(); 240 }ARGEND; 241 242 if(argc) 243 usage(); 244 245 initdisplay(); 246 247 xext_init(); 248 if(!isatty(0)) 249 menu_init(); 250 251 client_init(address); 252 253 srv.preselect = event_preselect; 254 ixp_listen(&srv, ConnectionNumber(display), nil, event_fdready, event_fdclosed); 255 256 menu.ontop = !strcmp(readctl("/ctl", "bar "), "on top"); 257 client_readconfig(&cnorm, &csel, &font); 258 259 itempad = (font->height & ~1) + font->pad.min.x + font->pad.max.x; 260 261 cmplbuf = Bfdopen(0, OREAD); 262 match.all = populate_list(cmplbuf, false); 263 if(!isatty(cmplbuf->fid)) 264 ixp_listen(&srv, cmplbuf->fid, inbuf, check_competions, nil); 265 266 caret_insert("", true); 267 update_filter(false); 268 269 if(!nokeys) 270 parse_keys(binding_spec); 271 if(keyfile) { 272 i = open(keyfile, O_RDONLY); 273 if(read(i, buffer, sizeof(buffer)) > 0) 274 parse_keys(buffer); 275 } 276 277 histsel = &hist; 278 link(&hist, &hist); 279 if(histfile && (inbuf = Bopen(histfile, OREAD))) { 280 item = filter_list(populate_list(inbuf, true), ""); 281 if(item->string) { 282 link(item->prev, &hist); 283 link(&hist, item); 284 } 285 Bterm(inbuf); 286 } 287 288 if(menu.win == nil) 289 menu_init(); 290 291 init_screens(); 292 293 i = ixp_serverloop(&srv); 294 if(i) 295 fprint(2, "%s: error: %r\n", argv0); 296 XCloseDisplay(display); 297 298 if(ndump >= 0 && histfile && result == 0) 299 history_dump(histfile, ndump); 300 301 return result; 302 } 303