ref: cdd5704e4cf115186593e45491a64e2998ce5cb4
author: Sigrid Solveig Haflínudóttir <ftrvxmtrx@gmail.com>
date: Sun Jun 23 19:27:07 EDT 2013
first revision
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,9 @@
+</$objtype/mkfile
+MAN=/sys/man/1
+
+TARG=\
+ zuke\
+
+BIN=/$objtype/bin/audio
+
+</sys/src/cmd/mkmany
--- /dev/null
+++ b/zuke.c
@@ -1,0 +1,364 @@
+#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);
+}