shithub: zuke

ref: 6a761aa78791cdb90ccbd0babcf538043882c9b3
dir: /zuke.c/

View raw version
#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);
}