code: plan9front

Download patch

ref: 6bf304a797175ec69f28e8f590e9c8affa42b7a3
parent: 8dcf65f21e079f471eef6d3b3d0360c7beac4cd6
author: Sigrid Solveig Haflínudóttir <sigrid@ftrv.se>
date: Tue Aug 23 15:17:09 EDT 2022

audio/zuke: rewrite rendering logic

--- a/sys/src/cmd/audio/zuke/zuke.c
+++ b/sys/src/cmd/audio/zuke/zuke.c
@@ -82,7 +82,6 @@
 static Image *cover;
 static Channel *playc;
 static Channel *redrawc;
-static Image *back;
 static Mousectl *mctl;
 static Keyboardctl *kctl;
 static int colwidth[10];
@@ -115,6 +114,7 @@
 
 static int Scrollwidth;
 static int Scrollheight;
+static int Seekthicc;
 static int Coversz;
 
 static char *
@@ -224,63 +224,28 @@
 getcol(Meta *m, int c)
 {
 	static char tmp[32];
+	char *s;
 
+	s = nil;
 	switch(c){
-	case Palbum: return m->album;
-	case Partist: return m->artist[0];
-	case Pdate: return m->date;
-	case Ptitle: return (!colspath && *m->title == 0) ? m->basename : m->title;
-	case Ptrack: snprint(tmp, sizeof(tmp), "%4s", m->track); return m->track ? tmp : nil;
-	case Ppath: return m->path;
+	case Palbum: s = m->album; break;
+	case Partist: s = m->artist[0]; break;
+	case Pdate: s = m->date; break;
+	case Ptitle: s = (!colspath && (m->title == nil || *m->title == 0)) ? m->basename : m->title; break;
+	case Ptrack: snprint(tmp, sizeof(tmp), "%4s", m->track); s = m->track ? tmp : nil; break;
+	case Ppath: s = m->path; break;
 	case Pduration:
 		tmp[0] = 0;
 		if(m->duration > 0)
 			snprint(tmp, sizeof(tmp), "%8P", m->duration/1000);
-		return tmp;
+		s = tmp;
+		break;
 	default: sysfatal("invalid column '%c'", c);
 	}
 
-	return nil;
+	return s ? s : "";
 }
 
-static void
-adjustcolumns(void)
-{
-	int i, n, x, total, width;
-
-	if(mincolwidth[0] == 0){
-		for(i = 0; cols[i] != 0; i++)
-			mincolwidth[i] = 1;
-		for(n = 0; n < pl->n; n++){
-			for(i = 0; cols[i] != 0; i++){
-				if((x = stringwidth(f, getcol(pl->m+n, cols[i]))) > mincolwidth[i])
-					mincolwidth[i] = x;
-			}
-		}
-	}
-
-	total = 0;
-	n = 0;
-	width = Dx(screen->r);
-	for(i = 0; cols[i] != 0; i++){
-		if(cols[i] == Pduration || cols[i] == Pdate || cols[i] == Ptrack)
-			width -= mincolwidth[i] + 8;
-		else{
-			total += mincolwidth[i];
-			n++;
-		}
-	}
-	colspath = 0;
-	for(i = 0; cols[i] != 0; i++){
-		if(cols[i] == Ppath || cols[i] == Pbasename)
-			colspath = 1;
-		if(cols[i] == Pduration || cols[i] == Pdate || cols[i] == Ptrack)
-			colwidth[i] = mincolwidth[i];
-		else
-			colwidth[i] = (width - Scrollwidth - n*8) * mincolwidth[i] / total;
-	}
-}
-
 static Meta *
 getmeta(int i)
 {
@@ -296,47 +261,123 @@
 static void
 redraw_(int full)
 {
-	Image *col;
-	Point p, sp;
-	Rectangle sel, r;
-	int i, j, left, right, scrollcenter, w;
+	static Image *back, *ocover;
+	static int oscrollcenter, opcur, opcurplaying;
+
+	int x, i, j, scrollcenter, w;
 	uvlong dur, msec;
+	Rectangle sel, r;
 	char tmp[32];
+	Point p, sp, p₀, p₁;
+	Image *col;
 
+	/* seekbar playback/duration text */
+	msec = 0;
+	dur = getmeta(pcurplaying)->duration;
+	if(pcurplaying >= 0){
+		msec = byteswritten*1000/Bps;
+		if(dur > 0){
+			snprint(tmp, sizeof(tmp), "%s%P/%P 100%%",
+				shuffle != nil ? "∫ " : "",
+				dur/1000, dur/1000);
+			w = stringwidth(f, tmp);
+			msec = MIN(msec, dur);
+			snprint(tmp, sizeof(tmp), "%s%P/%P %d%%",
+				shuffle != nil ? "∫ " : "",
+				(uvlong)(newseekmx >= 0 ? seekoff : msec)/1000,
+				dur/1000, volume);
+		}else{
+			snprint(tmp, sizeof(tmp), "%s%P %d%%",
+				shuffle != nil ? "∫ " : "",
+				msec/1000, 100);
+			w = stringwidth(f, tmp);
+			snprint(tmp, sizeof(tmp), "%s%P %d%%",
+				shuffle != nil ? "∫ " : "",
+				msec/1000, volume);
+		}
+	}else{
+		snprint(tmp, sizeof(tmp), "%s%d%%", shuffle != nil ? "∫ " : "", 100);
+		w = stringwidth(f, tmp);
+		snprint(tmp, sizeof(tmp), "%s%d%%", shuffle != nil ? "∫ " : "", volume);
+	}
+
 	lockdisplay(display);
 
 	if(back == nil || Dx(screen->r) != Dx(back->r) || Dy(screen->r) != Dy(back->r)){
 		freeimage(back);
 		back = allocimage(display, Rpt(ZP,subpt(screen->r.max, screen->r.min)), XRGB32, 0, DNofill);
+		full = 1;
 	}
 
-	left = back->r.min.x;
-	if(scrollsz < pl->n) /* adjust for scrollbar */
-		left += Scrollwidth + 1;
+	r = screen->r;
 
-	if(full){
-		draw(back, back->r, colors[Dback].im, nil, ZP);
+	/* scrollbar */
+	p₀ = Pt(r.min.x, r.min.y);
+	p₁ = Pt(r.min.x+Scrollwidth, r.max.y-Seekthicc);
+	if(scroll < 1)
+		scrollcenter = 0;
+	else
+		scrollcenter = (p₁.y-p₀.y-Scrollheight/2 - Seekthicc)*scroll / (pl->n - scrollsz);
+	if(full || oscrollcenter != scrollcenter){
+		draw(screen, Rpt(p₀, Pt(p₁.x, p₁.y)), colors[Dback].im, nil, ZP);
+		line(screen, Pt(p₁.x, p₀.y), p₁, Endsquare, Endsquare, 0, colors[Dflow].im, ZP);
+		r = Rpt(
+			Pt(p₀.x+1, p₀.y + scrollcenter + Scrollheight/4),
+			Pt(p₁.x-1, p₀.y + scrollcenter + Scrollheight/4 + Scrollheight)
+		);
+		/* scrollbar handle */
+		draw(screen, r, colors[Dblow].im, nil, ZP);
+	}
 
-		adjustcolumns();
-		if(scrollsz < pl->n){ /* scrollbar */
-			p.x = sp.x = back->r.min.x + Scrollwidth;
-			p.y = back->r.min.y;
-			sp.y = back->r.max.y;
-			line(back, p, sp, Endsquare, Endsquare, 0, colors[Dflow].im, ZP);
+	/* seek bar rectangle */
+	r = Rpt(Pt(p₀.x, p₁.y), Pt(screen->r.max.x-w-4, screen->r.max.y));
 
-			r = back->r;
-			r.max.x = r.min.x + Scrollwidth - 1;
-			r.min.x += 1;
-			if(scroll < 1)
-				scrollcenter = 0;
-			else
-				scrollcenter = (Dy(back->r)-Scrollheight*5/4)*scroll / (pl->n - scrollsz);
-			r.min.y += scrollcenter + Scrollheight/4;
-			r.max.y = r.min.y + Scrollheight;
-			draw(back, r, colors[Dblow].im, nil, ZP);
+	/* playback/duration text */
+	draw(screen, Rpt(Pt(r.max.x, p₁.y), screen->r.max), colors[Dblow].im, nil, ZP);
+	p = addpt(Pt(screen->r.max.x - stringwidth(f, tmp) - 4, p₁.y), Pt(2, 2));
+	string(screen, p, colors[Dfhigh].im, ZP, f, tmp);
+
+	/* seek control */
+	if(pcurplaying >= 0 && dur > 0){
+		border(screen, r, 3, colors[Dblow].im, ZP);
+		r = insetrect(r, 3);
+		seekbar = r;
+		p = r.min;
+		x = p.x + Dx(r) * (double)msec / (double)dur;
+		r.min.x = x;
+		draw(screen, r, colors[Dback].im, nil, ZP);
+		r.min.x = p.x;
+		r.max.x = x;
+		draw(screen, r, colors[Dbmed].im, nil, ZP);
+	}else
+		draw(screen, r, colors[Dblow].im, nil, ZP);
+
+	Rectangle bp[2] = {
+		Rpt(addpt(screen->r.min, Pt(Scrollwidth+1, 0)), subpt(screen->r.max, Pt(0, Seekthicc))), 
+		ZR,
+	};
+
+	if(cover != nil){
+		r.min.x = screen->r.max.x - Dx(cover->r) - 8;
+		r.min.y = p₁.y - Dy(cover->r) - 6;
+		r.max.x = screen->r.max.x;
+		r.max.y = p₁.y + 2;
+		if(full || cover != ocover){
+			border(screen, r, 4, colors[Dblow].im, ZP);
+			draw(screen, insetrect(r, 4), cover, nil, ZP);
 		}
+		bp[1] = bp[0];
+		bp[0].max.y = r.min.y;
+		bp[1].max.x = r.min.x;
+		bp[1].min.y = r.min.y;
+	}else if(ocover != nil)
+		full = 1;
 
-		p.x = sp.x = left;
+	/* playlist */
+	if(full || oscrollcenter != scrollcenter || pcur != opcur || pcurplaying != opcurplaying){
+		draw(back, back->r, colors[Dback].im, nil, ZP);
+
+		p.x = sp.x = Scrollwidth;
 		p.y = 0;
 		sp.y = back->r.max.y;
 		for(i = 0; cols[i+1] != 0; i++){
@@ -347,7 +388,7 @@
 		}
 
 		sp.x = sp.y = 0;
-		p.x = left + 2;
+		p.x = Scrollwidth + 2;
 		p.y = back->r.min.y + 2;
 
 		for(i = scroll; i < pl->n; i++, p.y += f->height){
@@ -357,7 +398,7 @@
 				break;
 
 			if(pcur == i){
-				sel.min.x = left;
+				sel.min.x = Scrollwidth;
 				sel.min.y = p.y;
 				sel.max.x = back->r.max.x;
 				sel.max.y = p.y + f->height;
@@ -369,7 +410,7 @@
 
 			sel = back->r;
 
-			p.x = left + 2 + 3;
+			p.x = Scrollwidth + 2 + 3;
 			for(j = 0; cols[j] != 0; j++){
 				sel.max.x = p.x + colwidth[j];
 				replclipr(back, 0, sel);
@@ -381,7 +422,7 @@
 			if(pcurplaying == i){
 				Point rightp, leftp;
 				leftp.y = rightp.y = p.y - 1;
-				leftp.x = left;
+				leftp.x = Scrollwidth;
 				rightp.x = back->r.max.x;
 				line(back, leftp, rightp, 0, 0, 0, colors[Dflow].im, sp);
 				leftp.y = rightp.y = p.y + f->height;
@@ -388,75 +429,15 @@
 				line(back, leftp, rightp, 0, 0, 0, colors[Dflow].im, sp);
 			}
 		}
-	}
 
-	msec = 0;
-	dur = getmeta(pcurplaying)->duration;
-	if(pcurplaying >= 0){
-		msec = byteswritten*1000/Bps;
-		if(dur > 0){
-			snprint(tmp, sizeof(tmp), "%s%P/%P 100%%",
-				shuffle != nil ? "∫ " : "",
-				dur/1000, dur/1000);
-			w = stringwidth(f, tmp);
-			msec = MIN(msec, dur);
-			snprint(tmp, sizeof(tmp), "%s%P/%P %d%%",
-				shuffle != nil ? "∫ " : "",
-				(uvlong)(newseekmx >= 0 ? seekoff : msec)/1000,
-				dur/1000, volume);
-		}else{
-			snprint(tmp, sizeof(tmp), "%s%P %d%%",
-				shuffle != nil ? "∫ " : "",
-				msec/1000, 100);
-			w = stringwidth(f, tmp);
-			snprint(tmp, sizeof(tmp), "%s%P %d%%",
-				shuffle != nil ? "∫ " : "",
-				msec/1000, volume);
-		}
-	}else{
-		snprint(tmp, sizeof(tmp), "%s%d%%", shuffle != nil ? "∫ " : "", 100);
-		w = stringwidth(f, tmp);
-		snprint(tmp, sizeof(tmp), "%s%d%%", shuffle != nil ? "∫ " : "", volume);
+		for(i = 0; bp[i].max.x ; i++)
+			draw(screen, bp[i], back, nil, subpt(bp[i].min, screen->r.min));
 	}
+	oscrollcenter = scrollcenter;
+	opcurplaying = pcurplaying;
+	ocover = cover;
+	opcur = pcur;
 
-	r = back->r;
-	right = r.max.x - w - 4;
-	r.min.x = left;
-	r.min.y = r.max.y - f->height - 4;
-	if(pcurplaying < 0 || dur == 0)
-		r.min.x = right;
-	draw(back, r, colors[Dblow].im, nil, ZP);
-	p = addpt(Pt(r.max.x-stringwidth(f, tmp)-4, r.min.y), Pt(2, 2));
-	r.max.x = right;
-	string(back, p, colors[Dfhigh].im, sp, f, tmp);
-	sel = r;
-
-	if(cover != nil && full){
-		r.max.x = r.min.x;
-		r.min.x = back->r.max.x - cover->r.max.x - 8;
-		draw(back, r, colors[Dblow].im, nil, ZP);
-		r = back->r;
-		r.min.x = r.max.x - cover->r.max.x - 8;
-		r.min.y = r.max.y - cover->r.max.y - 8 - f->height - 4;
-		r.max.y = r.min.y + cover->r.max.y + 8;
-		draw(back, r, colors[Dblow].im, nil, ZP);
-		draw(back, insetrect(r, 4), cover, nil, ZP);
-	}
-
-	/* seek bar */
-	seekbar = ZR;
-	if(pcurplaying >= 0 && dur > 0){
-		r = insetrect(sel, 3);
-		draw(back, r, colors[Dback].im, nil, ZP);
-		seekbar = Rpt(addpt(screen->r.min, r.min), addpt(screen->r.min, r.max));
-		r.max.x = r.min.x + Dx(r) * (double)msec / (double)dur;
-		draw(back, r, colors[Dbmed].im, nil, ZP);
-	}
-
-	if(!full)
-		replclipr(screen, 0, Rpt(addpt(screen->r.min, sel.min), screen->r.max));
-	draw(screen, screen->r, back, nil, ZP);
-	replclipr(screen, 0, screen->r);
 	flushimage(display, 1);
 	unlockdisplay(display);
 }
@@ -464,15 +445,18 @@
 static void
 redrawproc(void *)
 {
-	ulong full, nbfull;
+	ulong full, nbfull, another;
 
 	threadsetname("redraw");
 	while(recv(redrawc, &full) == 1){
 		redraw_(full);
-		nbfull = 0;
-		while(nbrecv(redrawc, &nbfull) > 0);
-		/* full redraw was requested after a partial one */
-		if(nbfull >= full)
+		another = 0;
+		full = 0;
+		while(nbrecv(redrawc, &nbfull) > 0){
+			full |= nbfull;
+			another = 1;
+		}
+		if(another)
 			redraw_(nbfull);
 	}
 
@@ -575,7 +559,6 @@
 pnotify(Player *p)
 {
 	Meta *m;
-	char *s;
 	int i;
 
 	if(!pnotifies)
@@ -584,7 +567,7 @@
 	if(p != nil){
 		m = getmeta(p->pcur);
 		for(i = 0; cols[i] != 0; i++)
-			Bprint(&out, "%s\t", (s = getcol(m, cols[i])) ? s : "");
+			Bprint(&out, "%s\t", getcol(m, cols[i]));
 	}
 	Bprint(&out, "\n");
 	Bflush(&out);
@@ -739,7 +722,7 @@
 	byteswritten = boffset;
 	pcurplaying = player->pcur;
 	if(c != Cseekrel)
-		redraw(1);
+		redraw(0);
 
 	while(1){
 		n = ioread(io, p[1], buf, Relbufsz);
@@ -750,7 +733,7 @@
 		if(player->img != nil && nbrecv(player->img, &thiscover) != 0){
 			freeimage(cover);
 			cover = thiscover;
-			redraw(1);
+			redraw(0);
 			player->img = nil;
 		}
 		r = nbrecv(player->ctl, &c);
@@ -884,8 +867,8 @@
 readplist(int fd)
 {
 	char *raw, *s, *e, *a[5], *b;
+	int plsz, i, x;
 	Playlist *pl;
-	int plsz;
 	Meta *m;
 
 	if((raw = readall(fd)) == nil)
@@ -903,15 +886,25 @@
 		return nil;
 	}
 
+	for(i = 0; cols[i] != 0; i++)
+		mincolwidth[i] = 1;
+
 	pl->raw = raw;
-	for(s = pl->raw, m = pl->m;; s = e){
+	for(s = pl->raw, m = pl->m, e = s; e != nil; s = e){
 		if((e = strchr(s, '\n')) == nil)
-			break;
+			goto addit;
 		s += 2;
 		*e++ = 0;
 		switch(s[-2]){
 		case 0:
 			if(m->path != nil){
+addit:
+				for(i = 0; cols[i] != 0; i++){
+					if((x = stringwidth(f, getcol(m, cols[i]))) > mincolwidth[i])
+						mincolwidth[i] = x;
+				}
+				if(m->filefmt == nil)
+					m->filefmt = "";
 				pl->n++;
 				m++;
 			}
@@ -942,8 +935,6 @@
 			break;
 		}
 	}
-	if(m != nil && m->path != nil)
-		pl->n++;
 
 	return pl;
 }
@@ -966,9 +957,10 @@
 	inc = (d == '/' || d == 'n') ? 1 : -1;
 	if(d == '/' || d == '?')
 		sz = enter(inc > 0 ? "forward:" : "backward:", buf, sizeof(buf), mctl, kctl, screen->screen);
-	redraw(1);
-	if(sz < 1)
+	if(sz < 1){
+		redraw(1);
 		return;
+	}
 
 	cycle = 1;
 	for(i = pcur+inc; i >= 0 && i < pl->n;){
@@ -991,7 +983,7 @@
 	if(i >= 0 && i < pl->n){
 		pcur = i;
 		recenter();
-		redraw(1);
+		redraw(0);
 	}else if(cycle && i+inc < 0){
 		cycle = 0;
 		i = pl->n;
@@ -1108,6 +1100,33 @@
 }
 
 static void
+adjustcolumns(void)
+{
+	int i, n, total, width;
+
+	total = 0;
+	n = 0;
+	width = Dx(screen->r);
+	for(i = 0; cols[i] != 0; i++){
+		if(cols[i] == Pduration || cols[i] == Pdate || cols[i] == Ptrack)
+			width -= mincolwidth[i] + 8;
+		else{
+			total += mincolwidth[i];
+			n++;
+		}
+	}
+	colspath = 0;
+	for(i = 0; cols[i] != 0; i++){
+		if(cols[i] == Ppath || cols[i] == Pbasename)
+			colspath = 1;
+		if(cols[i] == Pduration || cols[i] == Pdate || cols[i] == Ptrack)
+			colwidth[i] = mincolwidth[i];
+		else
+			colwidth[i] = (width - Scrollwidth - n*8) * mincolwidth[i] / total;
+	}
+}
+
+static void
 usage(void)
 {
 	fprint(2, "usage: %s [-s] [-c aAdDtTp]\n", argv0);
@@ -1133,7 +1152,8 @@
 		{ nil, &ind, CHANRCV },
 		{ nil, nil, CHANEND },
 	};
-	int n, scrolling, oldpcur, oldbuttons, pnew, shuffled, oscroll;
+	int n, scrolling, oldpcur, oldbuttons, pnew, shuffled;
+	int seekmx, full;
 	char buf[64];
 
 	shuffled = 0;
@@ -1154,12 +1174,6 @@
 		break;
 	}ARGEND;
 
-	if((pl = readplist(0)) == nil){
-		fprint(2, "playlist: %r\n");
-		sysfatal("playlist error");
-	}
-	close(0);
-
 	Binit(&out, 1, OWRITE);
 	pnotifies = fd2path(1, buf, sizeof(buf)) == 0 && strcmp(buf, "/dev/cons") != 0;
 
@@ -1170,6 +1184,7 @@
 	f = display->defaultfont;
 	Scrollwidth = MAX(14, stringwidth(f, "#"));
 	Scrollheight = MAX(16, f->height);
+	Seekthicc = Scrollheight + 2;
 	Coversz = MAX(64, stringwidth(f, "∫ 00:00:00/00:00:00 100%"));
 	if((mctl = initmouse(nil, screen)) == nil)
 		sysfatal("initmouse: %r");
@@ -1194,6 +1209,13 @@
 	fmtinstall('P', positionfmt);
 	threadsetname("zuke");
 
+	if((pl = readplist(0)) == nil){
+		fprint(2, "playlist: %r\n");
+		sysfatal("playlist error");
+	}
+	close(0);
+	adjustcolumns();
+
 	if(shuffled){
 		pcur = nrand(pl->n);
 		toggleshuffle();
@@ -1203,12 +1225,13 @@
 	redraw(1);
 	m.buttons = 0;
 	scrolling = 0;
+	seekmx = 0;
 
 	proccreate(plumbaudio, kctl->c, 4096);
 
 	for(;;){
-		oscroll = scroll;
 		oldpcur = pcur;
+		full = 0;
 		if(seekmx != newseekmx){
 			seekmx = newseekmx;
 			redraw(0);
@@ -1242,7 +1265,7 @@
 
 			n = (m.xy.y - screen->r.min.y)/f->height;
 
-			if(m.xy.x <= screen->r.min.x+Scrollwidth){
+			if(m.xy.x <= screen->r.min.x+Scrollwidth && m.xy.y <= screen->r.max.y-Seekthicc){
 				if(m.buttons == 1){
 					scroll -= n+1;
 					break;
@@ -1263,7 +1286,7 @@
 			if(scrolling){
 				if(scrollsz >= pl->n)
 					break;
-				scroll = (m.xy.y - screen->r.min.y - Scrollheight/4)*(pl->n-scrollsz) / (Dy(screen->r)-Scrollheight/2);
+				scroll = (m.xy.y - screen->r.min.y)*(pl->n-scrollsz) / (Dy(screen->r)-Seekthicc);
 			}else if(m.buttons == 1 || m.buttons == 2){
 				n += scroll;
 				if(n < pl->n){
@@ -1279,6 +1302,7 @@
 		case Eresize: /* resize */
 			if(getwindow(display, Refnone) < 0)
 				sysfatal("getwindow: %r");
+			adjustcolumns();
 			redraw(1);
 			break;
 		case Ekey:
@@ -1368,10 +1392,12 @@
 				pcurplaying = -1;
 				freeimage(cover);
 				cover = nil;
+				full = 1;
 				break;
 			case 's':
 				toggleshuffle();
 				recenter();
+				full = 1;
 				break;
 			case 'c':
 			case 'p':
@@ -1405,9 +1431,7 @@
 
 		updatescrollsz();
 		scroll = CLAMP(scroll, 0, pl->n - scrollsz);
-
-		if(scroll != oscroll || pcur != oldpcur)
-			redraw(1);
+		redraw(full);
 	}
 
 end: