shithub: dmenu

ref: 342d92a22a97b02235abe83f4ec420c8aa6622ef
dir: /linesel.c/

View raw version
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <draw.h>
#include <event.h>
#include <keyboard.h>

#define Ctl(c) ((c) - 64)

Image *fgcolor, *bgcolor;
char **lines, **matches, *buffer,  *selected;
usize nlines, nmatches;
Rune kbinput[512];

static void
readbuffer(void)
{
	Biobuf *bp;
	char *line, *s;

	if((bp = Bfdopen(0, OREAD)) == nil)
		sysfatal("setting buffering on fd0: %r");
	if ((buffer = Brdstr(bp, '\0', 1)) == nil)
		sysfatal("reading input lines: %r");

	for(line = s = buffer; s = strchr(s, '\n'); line = ++s){
		*s = '\0';
		if((lines = realloc(lines, ++nlines * sizeof *lines)) == nil)
			sysfatal("malloc: %r");
		lines[nlines-1] = line;
	}
	if((matches = malloc(nlines * sizeof *lines)) == nil)
		sysfatal("malloc: %r");
}

#define PROMPT "  > "

static void
redraw(Image *screen)
{
	short fh = font->height;
	Point pt = screen->r.min;
	usize i;
	char buf[512];

	draw(screen, screen->r, bgcolor, nil, ZP);

	pt = addpt(pt, Pt(0, fh));
	snprint(buf, sizeof buf, PROMPT"%S▏", kbinput);
	string(screen, pt, fgcolor, ZP, font, buf);	
	pt = addpt(pt, Pt(stringwidth(font, PROMPT), fh));

	for(i = 0; i < nmatches; i++){
		if (pt.y > screen->r.max.y)
			break;
		string(screen, pt, fgcolor, ZP, font, matches[i]);
		pt = addpt(pt, Pt(0, fh));
	}
}

static void
resetmatches(void)
{
	memmove(matches, lines, nlines * sizeof *lines);
	nmatches = nlines;
}

static void
kbadd(Rune r)
{
	usize len = runestrlen(kbinput);

	if (len == sizeof kbinput / sizeof *kbinput)
		return;
	kbinput[len++] = r;
	kbinput[len] = L'\0';
}

static void
kbbackspace(void)
{
	usize len = runestrlen(kbinput);

	if (len == 0)
		return;
	kbinput[len - 1] = L'\0';
}

static void
kbdelword(void)
{
	usize len = runestrlen(kbinput);

	if(len == 0)
		return;
	while(len > 0 && isspacerune(kbinput[len-1]))
		len--;
	while(len > 0 && !isspacerune(kbinput[len-1]))
		len--;
	kbinput[len] = L'\0';
}

static void
kbclear(void)
{
	kbinput[0] = L'\0';
}

void
eresized(int new)
{
	if(new && getwindow(display, Refnone) < 0)
		sysfatal("resize failed: %r");
	redraw(screen);
}

static void
usage(void)
{
	print("usage: %s [-b] <choices\n", argv0);
	exits("usage");
}

void
main(int argc, char **argv)
{
	Event e;
	int bflag = 0;

	ARGBEGIN{
	case 'b':
		bflag = 1;
		break;
	default:
		usage();
	}ARGEND;

	readbuffer();
	resetmatches();
	selected = matches[0];

	if(initdraw(nil, nil, "linesel") < 0)
		sysfatal("initdraw: %r");
	if(bflag){
		fgcolor = display->white;
		bgcolor = display->black;
	}else{
		fgcolor = display->black;
		bgcolor = display->white;
	}
	einit(Emouse|Ekeyboard);
	redraw(screen);

	for(;;){
		switch(event(&e)){
		case -1:
			sysfatal("watching channels: %r\n");

		case Ekeyboard:
			switch(e.kbdc){
			case Kdel:
				exits("interrupted with Del");
			case '\n':
				goto End;
			case Kbs:
				kbbackspace();
				break;
			case Ctl('W'):
				kbdelword();
				break;
			case Ctl('U'):
				kbclear();
				break;
			default:
				kbadd(e.kbdc);
				break;
			}
			redraw(screen);
			break;

		case Emouse:
			break;
		}
	}
End:
	print("%s\n", selected);
	exits(nil);
}