wmii

git clone git://oldgit.suckless.org/wmii/
Log | Files | Refs | README | LICENSE

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