ref: 6bf304a797175ec69f28e8f590e9c8affa42b7a3
parent: 8dcf65f21e079f471eef6d3b3d0360c7beac4cd6
author: Sigrid Solveig Haflínudóttir <[email protected]>
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: