ref: 3cbd8d28f0b43229ada32916236208d38bca116a
dir: /zuke.c/
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>
#include <keyboard.h>
#include <bio.h>
#include <thread.h>
static int plen;
static struct {
char *file;
char *name;
}*plist;
static int pid, pcur, pcurplaying;
static int scroll, scrollsz, pause;
static Image *cola, *colb;
static Font *f;
static Channel ctl;
static int audio;
int mainstacksize = 32768;
static void
redraw(Image *screen, int new)
{
Point p, sp;
int i;
sp.x = sp.y = 0;
if(new)
draw(screen, screen->r, cola, nil, ZP);
p.x = screen->r.min.x + 2;
p.y = screen->r.min.y + 2;
scrollsz = Dy(screen->r) / f->height - 1;
for(i = scroll; i < plen; i++){
if(p.y > screen->r.max.y)
break;
if(pcurplaying == i){
Point right, left;
left.y = right.y = p.y - 1;
left.x = p.x;
right.x = screen->r.max.x;
line(screen, left, right, 0, 0, 0, colb, sp);
left.y = right.y = p.y + f->height;
line(screen, left, right, 0, 0, 0, colb, sp);
}
if(pcur == i){
Rectangle sel;
sel = screen->r;
sel.min.y = p.y;
sel.max.y = p.y + f->height;
draw(screen, sel, colb, nil, ZP);
string(screen, p, cola, sp, f, plist[i].name);
}
else
string(screen, p, colb, sp, f, plist[i].name);
p.y += f->height;
}
flushimage(display, 1);
}
typedef struct Relayctx Relayctx;
struct Relayctx {
int in, out;
Channel *ev;
};
typedef struct Decctx Decctx;
struct Decctx {
int in, out;
Channel *quit, *wait;
};
static void
relay(void *v)
{
int n;
char *buf;
Ioproc *io;
Relayctx *ctx;
ctx = v;
io = ioproc();
buf = malloc(65536);
sendp(ctx->ev, nil);
while((n = ioread(io, ctx->in, buf, 65536)) > 0){
if(iowrite(io, ctx->out, buf, n) != n)
break;
}
free(buf);
closeioproc(io);
sendp(ctx->ev, nil);
}
typedef struct Playctx Playctx;
struct Playctx {
Channel *quit;
Channel *wait;
int p[4];
};
static void
player(void *dec_)
{
Decctx *dec;
static Relayctx rel[2];
Alt a[3];
dec = dec_;
threadsetname("player");
if(rel[0].ev == nil){
rel[0].ev = chancreate(sizeof(void*), 0);
rel[1].ev = chancreate(sizeof(void*), 0);
}
audio = open("/dev/audio", OWRITE);
pcurplaying = pcur;
again:
rel[0].in = open(plist[pcurplaying].file, OREAD);
rel[0].out = dec->in;
rel[1].in = dec->out;
rel[1].out = audio;
if(audio < 0 || rel[0].in < 0){
close(audio);
close(rel[0].in);
fprint(2, "%r\n");
threadexits(nil);
}
threadcreate(relay, &rel[0], 4096); recvp(rel[0].ev);
threadcreate(relay, &rel[1], 4096); recvp(rel[1].ev);
memset(a, 0, sizeof(a));
a[0].op = a[1].op = CHANRCV;
a[0].c = dec->quit;
a[1].c = rel[0].ev;
a[2].op = CHANEND;
while(1){
int r;
r = alt(a);
close(rel[0].in);
if(r == 1){
if(++pcurplaying >= plen)
pcurplaying = 0;
redraw(screen, 1);
goto again;
}
else{
close(rel[1].in);
close(rel[0].out);
recvp(rel[0].ev);
recvp(rel[1].ev);
close(audio);
sendp(dec->wait, nil);
break;
}
}
threadexits(nil);
}
static void
play(void)
{
static Decctx ctx;
int p[4];
if(ctx.quit == nil){
ctx.quit = chancreate(sizeof(void*), 0);
ctx.wait = chancreate(sizeof(void*), 0);
}
if(pcur == pcurplaying)
return;
if(pid != 0){
sendp(ctx.quit, nil);
recvp(ctx.wait);
}
pid = 0;
pcurplaying = -1;
if(pcur < 0)
return;
if(ctx.quit == nil)
ctx.quit = chancreate(sizeof(void*), 0);
pipe(&p[0]);
pipe(&p[2]);
if((pid = rfork(RFFDG|RFREND|RFPROC)) == 0){
close(p[1]); dup(p[0], 0);
close(p[2]); dup(p[3], 1);
close(2); open("/dev/null", OWRITE);
execl("/bin/rc", "-c", "play", "-o", "/fd/1", "/fd/0", nil);
}
if(pid < 0)
sysfatal("%r");
close(p[0]);
close(p[3]);
ctx.in = p[1];
ctx.out = p[2];
pid = proccreate(player, &ctx, 4096);
pcurplaying = pcur;
}
static void
readplist(void)
{
Biobuf b;
Binit(&b, 0, OREAD);
for(plen = 0;; plen++){
char *s[2];
int n;
s[0] = Brdstr(&b, '\n', 1);
if(s[0] == nil)
break;
plist = realloc(plist, sizeof(*plist)*(plen+1));
n = getfields(s[0], s, 2, 1, "\t");
if(n < 1)
break;
plist[plen].file = plist[plen].name = s[0];
if(n > 1)
plist[plen].name = s[1];
else if((plist[plen].name = strrchr(s[0], '/')) != nil)
plist[plen].name++;
}
}
static void
usage(void)
{
fprint(2, "usage: zuke [-b]\n");
exits("usage");
}
void
eresized(int new)
{
if(getwindow(display, Refnone) < 0)
sysfatal("can't reattach to window: %r");
redraw(screen, new);
}
void
threadmain(int argc, char **argv)
{
int inv;
inv = 0;
ARGBEGIN{
case 'b':
inv = 1;
break;
default:
usage();
}ARGEND
readplist();
if(plen < 1){
fprint(2, "empty playlist\n");
exits("empty");
}
if(initdraw(0, 0, "zuke") < 0)
sysfatal("initdraw failed");
f = display->defaultfont;
cola = inv ? display->black : display->white;
colb = inv ? display->white : display->black;
einit(Emouse | Ekeyboard);
srand(time(0));
pcurplaying = -1;
redraw(screen, 1);
for(;;){
int oldpcur, oldpcurplaying, key;
Event e;
oldpcurplaying = pcurplaying;
oldpcur = pcur;
key = event(&e);
if(key == Emouse){
if(e.mouse.buttons > 0){
pcur = scroll + (e.mouse.xy.y - screen->r.min.y)/f->height;
if(e.mouse.buttons == 4)
play();
}
}
else if(key == Ekeyboard){
if(e.kbdc == Kup)
pcur--;
else if(e.kbdc == Kpgup)
pcur -= scrollsz;
else if(e.kbdc == Kdown)
pcur++;
else if(e.kbdc == Kpgdown)
pcur += scrollsz;
else if(e.kbdc == Kend)
pcur = plen-1;
else if(e.kbdc == Khome)
pcur = 0;
else if(e.kbdc == 0x0a)
play();
else if(e.kbdc == 'q' || e.kbdc == Kesc)
break;
else if(e.kbdc == 'o')
pcur = pcurplaying;
else if(e.kbdc == '>' && pcurplaying >= 0){
pcur = pcurplaying;
if(++pcur >= plen)
pcur = 0;
play();
}
else if(e.kbdc == '<' && pcurplaying >= 0){
pcur = pcurplaying;
if(--pcur < 0)
pcur = plen-1;
play();
}
else if(e.kbdc == 's' && pid > 0){
pcur = -1;
play();
}
}
if(pcur != oldpcur || oldpcurplaying != pcurplaying){
if(pcur < 0)
pcur = 0;
else if(pcur >= plen)
pcur = plen - 1;
if(pcur < scroll)
scroll = pcur;
else if(pcur > scroll + scrollsz)
scroll = pcur - scrollsz;
if(scroll > plen - scrollsz)
scroll = plen - scrollsz;
else if(scroll < 0)
scroll = 0;
if(pcur != oldpcur || oldpcurplaying != pcurplaying)
redraw(screen, 1);
}
}
pcur = -1;
play();
threadexitsall(nil);
}