shithub: dmenu

Download patch

ref: 92964c4ffc4d2cb245749401b5d15614318e45b4
parent: 235f692a93b73fff063ec4c47ccccc361dab1fb5
author: glenda <[email protected]>
date: Sun Sep 5 17:25:19 EDT 2021

line: only redraw what is needed

--- /dev/null
+++ b/line.c
@@ -1,0 +1,327 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <draw.h>
+#include <event.h>
+#include <keyboard.h>
+
+#define Ctl(c) ((c) - 64)
+#define PROMPT "  > "
+
+enum
+{
+	BACK,
+	HBACK,
+	TEXT,
+	HTEXT,
+	NCOLORS
+};
+
+Image *color[NCOLORS];
+char **lines, **matches, *buffer;
+usize nlines, nmatches;
+Rune kbinput[512];
+int selected;
+
+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");
+	memmove(matches, lines, nlines * sizeof *lines);
+	nmatches = nlines;
+}
+
+static Point
+linetopoint(int ln)
+{
+	return Pt(screen->r.min.x, screen->r.min.y + (ln + 2) * font->height);
+}
+
+static int
+pointtoline(Point pt)
+{
+	return (pt.y - screen->r.min.y) / font->height - 2;
+}
+
+static void
+drawbackground(Point pt, Image *color)
+{
+	draw(screen,
+	  Rect(screen->r.min.x, pt.y, screen->r.max.x, pt.y + font->height),
+	  color, nil, ZP);
+}
+
+static Point
+tabstring(Image *dst, Point dp, Image *src, Point sp, Font *f, char *s)
+{
+	Rune r[2] = {L'0', L'\0'};
+	int n, w0, x;
+	Point op;
+
+	op = dp;
+	w0 = stringwidth(f, "0");
+	for(; *s && (n = chartorune(r, s)) > 0; s += n){
+		if(r[0] == '\t'){
+			x = 8 * w0 - (dp.x - op.x) % (8 * w0);
+			sp.x += x;
+			dp.x += x;
+		}else{
+			dp = runestring(dst, dp, src, sp, f, r);
+		}
+	}
+	return dp;
+}
+
+static void
+drawprompt(void)
+{
+	char buf[512];
+
+	drawbackground(linetopoint(-2), color[BACK]);
+	drawbackground(linetopoint(-1), color[BACK]);
+	snprint(buf, sizeof buf, PROMPT"%S▏", kbinput);
+	tabstring(screen, linetopoint(-1), color[TEXT], ZP, font, buf);
+}
+
+static int
+drawline(int ln)
+{
+	Point pt = linetopoint(ln);
+	Image *bgcolor, *txcolor;
+
+	if(ln == selected){
+		bgcolor = color[HBACK];
+		txcolor = color[HTEXT];
+	}else{
+		bgcolor = color[BACK];
+		txcolor = color[TEXT];
+	}
+	pt.x += stringwidth(font, PROMPT);
+	drawbackground(pt, bgcolor);
+	if(ln < nmatches)
+		tabstring(screen, pt, txcolor, ZP, font, matches[ln]);
+	return pt.y < screen->r.max.y;
+}
+
+void
+eresized(int new)
+{
+	int i;
+
+	if(new && getwindow(display, Refnone) < 0)
+		sysfatal("resize failed: %r");
+
+	drawprompt();
+	for(i = 0; drawline(i); i++);
+}
+
+int
+match(char *s, char **words, int nwords)
+{
+	char **w;
+
+	if(nwords == 0)
+		return 1;
+	for(w = words; w < words + nwords; w++)
+		if(cistrstr(s, *w) == nil)
+			return 0;
+	return 1;
+}
+
+void
+filter(char **lines, int nlines)
+{
+	char *buf, *words[64], **l, **m;
+	int nwords;
+
+	if((buf = smprint("%S", kbinput)) == nil)
+		sysfatal("malloc");
+	nwords = tokenize(buf, words, sizeof words / sizeof *words);
+
+	nmatches = 0;
+	m = matches;
+	for(l = lines; l < lines + nlines; l++)
+		if(match(*l, words, nwords)){
+			*m++ = *l;
+			nmatches++;
+		}
+
+	selected = 0;
+	free(buf);
+	eresized(0); /* possibly different lines: redraw everything */
+}
+
+static void
+kbadd(Rune r)
+{
+	int len = runestrlen(kbinput);
+
+	if(len == sizeof kbinput / sizeof *kbinput)
+		return;
+	kbinput[len++] = r;
+	kbinput[len] = L'\0';
+
+	filter(matches, nmatches);
+}
+
+static void
+kbbackspace(void)
+{
+	usize len = runestrlen(kbinput);
+
+	if(len == 0)
+		return;
+	kbinput[len - 1] = L'\0';
+
+	filter(lines, nlines);
+}
+
+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';
+
+	filter(lines, nlines);
+}
+
+static void
+kbclear(void)
+{
+	kbinput[0] = L'\0';
+
+	filter(lines, nlines);
+}
+
+static void
+kbmove(int n)
+{
+	if(selected + n < 0)
+		selected = 0;
+	else if(selected + n >= nmatches)
+		selected = nmatches - 1;
+	else
+		selected += n;
+}
+
+static void
+mselect(Point pt)
+{
+	int old, sel = pointtoline(pt);;
+
+	if(sel >= 0 && sel != selected){
+		old = selected;
+		selected = sel;
+		drawline(old);
+		drawline(sel);
+	}
+}
+
+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();
+
+	if(initdraw(nil, nil, "linesel") < 0)
+		sysfatal("initdraw: %r");
+
+	if(bflag){
+		color[TEXT] = display->white;
+		color[BACK] = display->black;
+	}else{
+		color[TEXT] = display->black;
+		color[BACK] = display->white;
+	}
+	color[HTEXT] = display->black;
+	color[HBACK] = allocimage(display,
+	  Rect(0,0,1,1), screen->chan, 1, DPaleyellow);
+
+	eresized(0);
+
+	einit(Emouse|Ekeyboard);
+	for(;;){
+		switch(event(&e)){
+		case -1:
+			sysfatal("watching events: %r");
+
+		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;
+			case Kup:
+				kbmove(-1);
+				break;
+			case Kdown:
+				kbmove(+1);
+				break;
+			default:
+				kbadd(e.kbdc);
+				break;
+			}
+			break;
+
+		case Emouse:
+			if(e.mouse.buttons&1)
+				mselect(e.mouse.xy);
+			if(e.mouse.buttons&4)
+				goto End;
+			break;
+		}
+	}
+End:
+	if(nmatches > 0)
+		print("%s\n", matches[selected]);
+	exits(nil);
+}
--- a/mkfile
+++ b/mkfile
@@ -7,8 +7,10 @@
 
 LIB= /$objtype/lib/libdraw.a
 BIN= /$objtype/bin/sel
-MAN= /sys/man/1
 
 </sys/src/cmd/mkmany
 
-install:V: $MAN/$TARG
+install: $BIN
+
+$BIN:
+	mkdir -p $target