ref: 92964c4ffc4d2cb245749401b5d15614318e45b4
parent: 235f692a93b73fff063ec4c47ccccc361dab1fb5
author: glenda <glenda@9front.local>
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