#include #include #include #include #include #include #include typedef struct Player Player; typedef struct Evh Evh; enum { KF= 0xF000, /* Rune: beginning of private Unicode space */ Spec= 0xF800, Kup= KF|0x0E, Kdown= Spec|0x00, }; struct Player{ char* dir; char** songs; int nsongs; int active; int pause; int pid; // of player process Panel* gtag; Panel* gsongcol; Panel* gsongs; Panel* gpause; }; struct Evh { char* name; void (*f)(Player*, Oev*); }; Player p; static char playbuf[4*1024]; static Channel* plumbc; void update(Player*); void omerogone(void) { sysfatal("terminated"); } static void playproc(void *a) { int fd, afd; int n, wn; int playing; Player* p = a; threadsetname("playproc"); again: if (p->active < 0 || p->active >= p->nsongs) threadexits("bug"); fd = open(p->songs[p->active], OREAD); afd= open("/devs/audio/audio", OWRITE); if (fd < 0 || afd < 0){ close(fd); close(afd); threadexits("open"); } playing = p->active; do{ if (p->pause){ while(p->pause) sleep(100); if (playing != p->active){ wn = -1; n = 0; break; } } wn = n = read(fd, playbuf, sizeof(playbuf)); if (n > 0) wn = write(afd, playbuf, n); } while(n > 0 && wn == n && playing == p->active); close(fd); close(afd); if (n < 0 || n != wn) threadexits("io"); if (playing == p->active){ p->active++; if (p->active == p->nsongs) p->active = 0; update(p); goto again; } threadexits(nil); } void play(Player* p) { proccreate(playproc, p, 16*1024); } void stop(Player* p) { p->active = -1; } static void plumbimg(char* dir, char* arg) { Plumbmsg*m; static int plumbsendfd = -1; if (plumbsendfd < 0) plumbsendfd = open("/mnt/plumb/send", OWRITE|OCEXEC); if (plumbsendfd < 0) return; m = malloc(sizeof(Plumbmsg)); if (m == nil) return; m->src = strdup(argv0); m->dst = strdup("poster"); m->wdir= strdup(dir); m->type = strdup("text"); m->attr = nil; m->data = smprint("%s/%s", dir, arg); m->ndata= -1; assert(m->wdir && m->src && m->data); plumbsend(plumbsendfd, m); plumbfree(m); } static int mayplumbimg(char* dir, char* f) { int n; n = strlen(f); if (n < 5) return 0; n -= 4; if (!cistrcmp(f+n, ".jpg") || !strcmp(f+n, ".gif") || !strcmp(f+n, ".img") || !strcmp(f+n, ".png")){ plumbimg(dir, f); return 1; } return 0; } int readsongs(Player* p, char* dir) { Dir* ents; int fd; int i; char buf[512]; int hasfiles; if (chdir(dir) < 0) return -1; getwd(buf, sizeof(buf)); fd = open(".", OREAD); if (fd < 0) return -1; free(p->dir); p->dir = estrdup(buf); for (i = 0; i < p->nsongs; i++) free(p->songs[i]); free(p->songs); p->songs = 0; p->nsongs = dirreadall(fd, &ents); close(fd); if (p->nsongs < 0){ return -1; } p->songs = emalloc(p->nsongs * sizeof(char*)); hasfiles = 0; for (i = 0; i < p->nsongs; i++) if (ents[i].qid.type&QTDIR) p->songs[i] = smprint("%s/", ents[i].name); else { if (!mayplumbimg(p->dir, ents[i].name)){ hasfiles++; p->songs[i] = estrdup(ents[i].name); }; } free(ents); p->active = -1; p->pause = 0; if (hasfiles) evhistory("oplayer", "look", p->dir); return hasfiles; } void controls(Panel* w) { Panel* c; createsubpanel(w, "button:Play"); createsubpanel(w, "button:Stop"); createsubpanel(w, "button:Next"); createsubpanel(w, "button:Done"); p.gpause = createsubpanel(w, "button:Pause"); c = createsubpanel(w, "slider:volume"); openpanel(c, OWRITE); writepanel(c, "75", 2); closepanel(c); openpanelctl(c); panelctl(c, "tag"); closepanelctl(c); } void showlist(Player* p) { int i; char* s; char* txt; char* mrk; char* ap; txt = emalloc(32*1024); s = ap = txt; *txt = 0; for (i = 0; i < p->nsongs; i++){ if (i != p->active) mrk = ""; else { ap = s; mrk = "->"; } s = seprint(s, txt+(32*1024), "%s\t%s\n", mrk, p->songs[i]); } if (!txt[0]) strcpy(txt, "no songs."); openpanel(p->gsongs, OWRITE|OTRUNC); writepanel(p->gsongs, txt, strlen(txt)); closepanel(p->gsongs); openpanelctl(p->gsongs); panelctl(p->gsongs, "sel %d %d\n", ap - txt, ap - txt); closepanelctl(p->gsongs); free(txt); } void ui(Panel* w) { Panel* c; p.gtag = createsubpanel(w, "tag:player"); c = createsubpanel(w, "row:controls"); controls(c); p.gsongs = createsubpanel(w, "text:songs"); } void update(Player* p) { char* tag; tag = smprint("player — %s ..", p->dir ? p->dir : "no dir"); openpanel(p->gtag, OWRITE|OTRUNC); writepanel(p->gtag, tag, strlen(tag)); closepanel(p->gtag); free(tag); showlist(p); } int isdir(char* p) { Dir* d; int r; d = dirstat(p); if (d == nil) return 0; r = (d->qid.type&QTDIR); free(d); return r; } static int findsong(Player* p, char* s) { int i; for (i = 0; i < p->nsongs; i++) if (!strcmp(p->songs[i], s)) return i; // no exact match, try substring for (i = 0; i < p->nsongs; i++) if (strstr(p->songs[i], s)) return i; return 0; } void playfile(Player* p, char* file) { char* d; if (isdir(file)){ if (readsongs(p, file) > 0){ p->active = p->pause = 0; play(p); } update(p); } else if (access(file, AREAD) != -1){ d = utfrrune(file, '/'); if (d) *d++ = 0; readsongs(p, d ? file : "."); if (d){ p->active = findsong(p, d); p->pause = 0; play(p); } update(p); } } void elook(Player* p, Oev* e) { char str[40]; int vol; if (strstr(e->path, "/tag:")){ if (readsongs(p, e->arg + 11 + 1) != -1) update(p); return; } if (strstr(e->path, "/text:songs")){ if (isdir(e->arg + 11 + 1)) readsongs(p, e->arg + 11 + 1); else { p->active = findsong(p, e->arg + 11 + 1); play(p); } update(p); } if (strstr(e->path, "/button:Play")){ p->active = 0; play(p); update(p); return; } if (strstr(e->path, "/button:Stop")){ stop(p); update(p); return; } if (strstr(e->path, "/button:Done")){ stop(p); omeroterm(); threadexitsall(nil); } if (strstr(e->path, "/button:Pause")){ openpanel(p->gpause, OWRITE|OTRUNC); if (p->pause){ writepanel(p->gpause,"Pause", 5); p->pause = 0; } else { writepanel(p->gpause,"Resume", 6); p->pause++; } closepanel(p->gpause); return; } if (strstr(e->path, "/button:Next")){ p->active++; if (p->active == p->nsongs) p->active = 0; update(p); play(p); return; } if (strstr(e->path, "/slider:volume")){ vol = atoi(e->arg); seprint(str, str+sizeof(str), "audio out %d", vol); writefstr("/devs/audio/volume", str); } } void eexit(Player* , Oev* ) { omeroterm(); threadexitsall(nil); } Evh evsh[] = { { "Look", elook }, { "look", elook }, { "exit", eexit }, { "exec", elook }, // BUG { "data", elook }, // BUG }; void threadmain(int argc, char* argv[]) { Panel* w; Channel* ec; Oev e; Plumbmsg* m; int i, ai; Alt a[] = { { nil, &e, CHANRCV }, { nil, &m, CHANRCV }, { nil, nil,CHANEND }}; ARGBEGIN{ case 'd': omerodebug++; break; default: fprint(2, "usage: %s [-d] [dir]\n", argv0); sysfatal("usage"); }ARGEND; if (argc > 1){ fprint(2, "usage: %s [-d] [dir]\n", argv0); sysfatal("usage"); } ec = omeroeventchan(nil); a[0].c = ec; w = createpanel("oplayer", "col", nil); if (w == nil) sysfatal("createpanel: %r\n"); plumbc = createportproc("song"); a[1].c = plumbc; readsongs(&p, argc ? argv[0] : "/n/music"); ui(w); update(&p); closepanelctl(w); while((ai = alt(a)) != -1){ switch(ai){ case 0: for (i = 0; i < nelem(evsh); i++) if (!strcmp(evsh[i].name, e.ev)){ evsh[i].f(&p, &e); break; } clearoev(&e); break; case 1: playfile(&p, m->data); plumbfree(m); break; } } eexit(&p, nil); }