music.c 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <thread.h>
  4. #include <draw.h>
  5. #include <keyboard.h>
  6. #include <mouse.h>
  7. #include <control.h>
  8. #include "colors.h"
  9. #include "client.h"
  10. #include "playlist.h"
  11. #include "../debug.h"
  12. int debug = 0; //DBGSERVER|DBGPUMP|DBGSTATE|DBGPICKLE|DBGPLAY;
  13. char usage[] = "Usage: %s [-d mask] [-t] [-w]\n";
  14. typedef struct But {
  15. char *name;
  16. Control *ctl;
  17. } But;
  18. typedef struct Simpleitem {
  19. char *address;
  20. char *data;
  21. } Simpleitem;
  22. typedef struct Multiitem {
  23. char *address;
  24. int ndata;
  25. char **data;
  26. } Multiitem;
  27. enum {
  28. WinBrowse,
  29. WinPlay,
  30. WinPlaylist,
  31. WinError,
  32. Topselect = 0x7fffffff,
  33. Browsedepth = 63,
  34. };
  35. typedef enum {
  36. PlayIdle,
  37. PlayStart,
  38. Playing,
  39. PlayPause,
  40. } Playstate;
  41. typedef enum {
  42. User,
  43. Troot,
  44. Rroot,
  45. Tchildren,
  46. Rchildren,
  47. Tparent,
  48. Rparent,
  49. Tinfo,
  50. Rinfo,
  51. Tparentage,
  52. Rparentage,
  53. Tplay,
  54. Rplay,
  55. } Srvstate;
  56. enum {
  57. Exitbutton,
  58. Pausebutton,
  59. Playbutton,
  60. Stopbutton,
  61. Prevbutton,
  62. Nextbutton,
  63. Rootbutton,
  64. Deletebutton,
  65. Helpbutton,
  66. Volume,
  67. Browsetopwin,
  68. Browsebotwin,
  69. Browsebotscr,
  70. Playevent,
  71. Playlistwin,
  72. Nalt,
  73. };
  74. But buts[] = {
  75. [Exitbutton] = {"skull", nil},
  76. [Pausebutton] = {"pause", nil},
  77. [Playbutton] = {"play", nil},
  78. [Stopbutton] = {"stop", nil},
  79. [Prevbutton] = {"prev", nil},
  80. [Nextbutton] = {"next", nil},
  81. [Rootbutton] = {"root", nil},
  82. [Deletebutton] = {"trash", nil},
  83. [Helpbutton] = {"question", nil},
  84. };
  85. struct tab {
  86. char *tabname;
  87. char *winname;
  88. Control *tab;
  89. Control *win;
  90. } tabs[4] = {
  91. [WinBrowse] = {"Browse", "browsewin", nil, nil},
  92. [WinPlay] = {"Playing", "playwin", nil, nil},
  93. [WinPlaylist] = {"Playlist", "listwin", nil, nil},
  94. [WinError] = {"Errors", "errorwin", nil, nil},
  95. };
  96. char *helptext[] = {
  97. "Buttons, left to right:",
  98. " Exit: exit jukebox",
  99. " Pause: pause/resume playback",
  100. " Play: play selection in Playlist",
  101. " Stop: stop playback",
  102. " Prev: play previous item in Playlist",
  103. " Next: play next item in Playlist",
  104. " Root: browse to root of database tree",
  105. " Delete: empty Playlist, reread database",
  106. " Help: show this window",
  107. "",
  108. "Browse window: (click tab to bring forward)",
  109. " Top window displays current item",
  110. " Bottom window displays selectable subitems",
  111. " Mouse commands:",
  112. " Left: selected subitem becomes current",
  113. " Right: parent of current item becomes current",
  114. " Middle: add item or subitem to Playlist",
  115. "",
  116. "Playing window",
  117. " Displays item currently playing",
  118. "",
  119. "Playlist window",
  120. " Displays contents of Playlist",
  121. " Mouse commands:",
  122. " Left: select item",
  123. " (then click the play button)",
  124. "",
  125. "Error window",
  126. " Displays error messages received from player",
  127. " (e.g., can't open file)",
  128. nil,
  129. };
  130. struct Browsestack {
  131. char *onum;
  132. int scrollpos;
  133. } browsestack[Browsedepth];
  134. int browsesp; /* browse stack pointer */
  135. int browseline; /* current browse position */
  136. Control *vol;
  137. Control *browsetopwin;
  138. Control *browsebotwin;
  139. Control *playlistwin;
  140. Control *errortext;
  141. Control *browsetopscr;
  142. Control *browsebotscr;
  143. Playstate playstate;
  144. ulong playingbuts = 1<<Pausebutton | 1<<Stopbutton | 1<<Prevbutton | 1<<Nextbutton;
  145. ulong activebuts;
  146. int tabht;
  147. Image *vol1img;
  148. Image *vol2img;
  149. int resizeready;
  150. int borderwidth = 1;
  151. int butht, butwid;
  152. int errorlines;
  153. int tflag;
  154. int pflag;
  155. Controlset *cs;
  156. char *root;
  157. Multiitem parent;
  158. Simpleitem children[2048];
  159. int nchildren;
  160. int selected;
  161. Channel *playevent;
  162. void
  163. readbuts(void)
  164. {
  165. static char str[32], file[64];
  166. But *b;
  167. int fd;
  168. Image *img, *mask;
  169. for(b = buts; b < &buts[nelem(buts)]; b++){
  170. sprint(file, "%s/%s.bit", ICONPATH, b->name);
  171. if((fd = open(file, OREAD)) < 0)
  172. sysfatal("open: %s: %r", file);
  173. mask = readimage(display, fd, 0);
  174. close(fd);
  175. butwid = Dx(mask->r);
  176. butht = Dy(mask->r);
  177. b->ctl = createbutton(cs, b->name);
  178. chanprint(cs->ctl, "%q align center", b->name);
  179. chanprint(cs->ctl, "%q border 0", b->name);
  180. img = allocimage(display, mask->r, screen->chan, 0, 0xe0e0ffff);
  181. draw(img, img->r, darkgreen, mask, mask->r.min);
  182. sprint(str, "%s.active", b->name);
  183. namectlimage(img, str);
  184. img = allocimage(display, mask->r, screen->chan, 0, 0xe0e0ffff);
  185. draw(img, img->r, lightblue, mask, mask->r.min);
  186. sprint(str, "%s.passive", b->name);
  187. namectlimage(img, str);
  188. chanprint(cs->ctl, "%q image %q", b->name, str);
  189. sprint(str, "%s.mask", b->name);
  190. namectlimage(mask, str);
  191. chanprint(cs->ctl, "%q mask %q", b->name, str);
  192. chanprint(cs->ctl, "%q light red", b->name);
  193. chanprint(cs->ctl, "%q size %d %d %d %d", b->name, butwid, butht, butwid, butht);
  194. }
  195. }
  196. void
  197. activatebuttons(ulong mask)
  198. { // mask bit i corresponds to buts[i];
  199. ulong bit;
  200. But *b;
  201. static char str[40];
  202. int i;
  203. for(i = 0; i < nelem(buts); i++){
  204. b = &buts[i];
  205. bit = 1 << i;
  206. if((mask & bit) && (activebuts & bit) == 0){
  207. // button was `deactive'
  208. activate(b->ctl);
  209. activebuts |= bit;
  210. sprint(str, "%s.active", b->name);
  211. chanprint(cs->ctl, "%q image %q", b->name, str);
  212. chanprint(cs->ctl, "%q show", b->name);
  213. }
  214. }
  215. }
  216. void
  217. deactivatebuttons(ulong mask)
  218. { // mask bit i corresponds with buts[i];
  219. ulong bit;
  220. But *b;
  221. static char str[40];
  222. int i;
  223. for(i = 0; i < nelem(buts); i++){
  224. b = &buts[i];
  225. bit = 1 << i;
  226. if((mask & bit) && (activebuts & bit)){
  227. // button was active
  228. deactivate(b->ctl);
  229. activebuts &= ~bit;
  230. sprint(str, "%s.passive", b->name);
  231. chanprint(cs->ctl, "%q image %q", b->name, str);
  232. chanprint(cs->ctl, "%q show", b->name);
  233. }
  234. }
  235. }
  236. void
  237. resizecontrolset(Controlset *){
  238. static Point pol[3];
  239. char *p;
  240. if(getwindow(display, Refbackup) < 0)
  241. sysfatal("getwindow");
  242. draw(screen, screen->r, bordercolor, nil, screen->r.min);
  243. if(!resizeready)
  244. return;
  245. #ifndef REPLACESEMANTICS
  246. if(vol1img)
  247. chanprint(cs->ctl, "volume image darkgreen");
  248. if(vol2img)
  249. chanprint(cs->ctl, "volume indicatorcolor red");
  250. chanprint(cs->ctl, "wholewin size");
  251. chanprint(cs->ctl, "wholewin rect %R", screen->r);
  252. chanprint(cs->ctl, "sync");
  253. p = recvp(cs->data);
  254. if(strcmp(p, "sync"))
  255. sysfatal("huh?");
  256. free(p);
  257. if(vol1img){
  258. freectlimage("volume.img");
  259. freeimage(vol1img);
  260. }
  261. if(vol2img){
  262. freectlimage("indicator.img");
  263. freeimage(vol2img);
  264. }
  265. vol1img = allocimage(display, vol->rect, screen->chan, 0, 0xe0e0ffff);
  266. vol2img = allocimage(display, vol->rect, screen->chan, 0, 0xe0e0ffff);
  267. pol[0] = Pt(vol->rect.min.x, vol->rect.max.y);
  268. pol[1] = Pt(vol->rect.max.x, vol->rect.min.y);
  269. pol[2] = vol->rect.max;
  270. fillpoly(vol1img, pol, 3, 0, darkgreen, ZP);
  271. fillpoly(vol2img, pol, 3, 0, red, ZP);
  272. namectlimage(vol1img, "volume.img");
  273. namectlimage(vol2img, "indicator.img");
  274. chanprint(cs->ctl, "volume image volume.img");
  275. chanprint(cs->ctl, "volume indicatorcolor indicator.img");
  276. #else
  277. chanprint(cs->ctl, "wholewin size");
  278. chanprint(cs->ctl, "wholewin rect %R", screen->r);
  279. chanprint(cs->ctl, "sync");
  280. p = recvp(cs->data);
  281. if(strcmp(p, "sync"))
  282. sysfatal("huh?");
  283. free(p);
  284. new1img = allocimage(display, vol->rect, screen->chan, 0, 0xe0e0ffff);
  285. new2img = allocimage(display, vol->rect, screen->chan, 0, 0xe0e0ffff);
  286. pol[0] = Pt(vol->rect.min.x, vol->rect.max.y);
  287. pol[1] = Pt(vol->rect.max.x, vol->rect.min.y);
  288. pol[2] = vol->rect.max;
  289. fillpoly(new1img, pol, 3, 0, darkgreen, ZP);
  290. fillpoly(new2img, pol, 3, 0, red, ZP);
  291. namectlimage(new1img, "volume.img");
  292. namectlimage(new2img, "indicator.img");
  293. if(vol1img)
  294. freeimage(vol1img);
  295. else
  296. chanprint(cs->ctl, "volume image volume.img");
  297. if(vol2img)
  298. freeimage(vol2img);
  299. else
  300. chanprint(cs->ctl, "volume indicatorcolor indicator.img");
  301. vol1img = new1img;
  302. vol2img = new2img;
  303. #endif
  304. chanprint(cs->ctl, "browsetopscr vis '%d'",
  305. Dy(controlcalled("browsetopscr")->rect)/romanfont->height);
  306. chanprint(cs->ctl, "browsebotscr vis '%d'",
  307. Dy(controlcalled("browsebotscr")->rect)/romanfont->height);
  308. chanprint(cs->ctl, "playscr vis '%d'",
  309. Dy(controlcalled("playscr")->rect)/romanfont->height);
  310. chanprint(cs->ctl, "playlistscr vis '%d'",
  311. Dy(controlcalled("playlistscr")->rect)/romanfont->height);
  312. chanprint(cs->ctl, "wholewin show");
  313. }
  314. void
  315. maketab(void)
  316. {
  317. int i;
  318. tabht = boldfont->height + 1 + borderwidth;
  319. createtab(cs, "tabs");
  320. for(i = 0; i < nelem(tabs); i++){
  321. tabs[i].tab = createtextbutton(cs, tabs[i].tabname);
  322. chanprint(cs->ctl, "%q size %d %d %d %d", tabs[i].tab->name,
  323. stringwidth(boldfont, tabs[i].tabname), tabht, 1024, tabht);
  324. chanprint(cs->ctl, "%q align uppercenter", tabs[i].tab->name);
  325. chanprint(cs->ctl, "%q font boldfont", tabs[i].tab->name);
  326. chanprint(cs->ctl, "%q image background", tabs[i].tab->name);
  327. chanprint(cs->ctl, "%q light background", tabs[i].tab->name);
  328. chanprint(cs->ctl, "%q pressedtextcolor red", tabs[i].tab->name);
  329. chanprint(cs->ctl, "%q textcolor darkgreen", tabs[i].tab->name);
  330. chanprint(cs->ctl, "%q mask transparent", tabs[i].tab->name);
  331. chanprint(cs->ctl, "%q text %q", tabs[i].tab->name, tabs[i].tabname);
  332. chanprint(cs->ctl, "tabs add %s %s", tabs[i].tabname, tabs[i].winname);
  333. }
  334. chanprint(cs->ctl, "tabs separation %d", 2);
  335. chanprint(cs->ctl, "tabs image background");
  336. chanprint(cs->ctl, "tabs value 0");
  337. }
  338. void
  339. makeplaycontrols(void)
  340. {
  341. int w;
  342. Control *playscr;
  343. w = stringwidth(romanfont, "Roll over Beethoven");
  344. playscr = createslider(cs, "playscr");
  345. chanprint(cs->ctl, "playscr size 12, 24, 12, 1024");
  346. createtext(cs, "playtext");
  347. chanprint(cs->ctl, "playtext size %d %d %d %d",
  348. w, 5*romanfont->height, 2048, 1024);
  349. chanprint(cs->ctl, "playscr format '%%s: playtext topline %%d'");
  350. controlwire(playscr, "event", cs->ctl);
  351. tabs[WinPlay].win = createrow(cs, tabs[WinPlay].winname);
  352. chanprint(cs->ctl, "%q add playscr playtext", tabs[WinPlay].win->name);
  353. }
  354. void
  355. makebrowsecontrols(void)
  356. {
  357. int w;
  358. w = stringwidth(romanfont, "Roll over Beethoven");
  359. browsetopscr = createslider(cs, "browsetopscr");
  360. chanprint(cs->ctl, "browsetopscr size 12, 24, 12, %d", 12*romanfont->height);
  361. browsetopwin = createtext(cs, "browsetopwin");
  362. chanprint(cs->ctl, "browsetopwin size %d %d %d %d",
  363. w, 3*romanfont->height, 2048, 12*romanfont->height);
  364. createrow(cs, "browsetop");
  365. chanprint(cs->ctl, "browsetop add browsetopscr browsetopwin");
  366. browsebotscr = createslider(cs, "browsebotscr");
  367. chanprint(cs->ctl, "browsebotscr size 12, 24, 12, 1024");
  368. browsebotwin = createtext(cs, "browsebotwin");
  369. chanprint(cs->ctl, "browsebotwin size %d %d %d %d",
  370. w, 3*romanfont->height, 2048, 1024);
  371. createrow(cs, "browsebot");
  372. chanprint(cs->ctl, "browsebot add browsebotscr browsebotwin");
  373. chanprint(cs->ctl, "browsetopscr format '%%s: browsetopwin topline %%d'");
  374. controlwire(browsetopscr, "event", cs->ctl);
  375. // chanprint(cs->ctl, "browsebotscr format '%%s: browsebotwin topline %%d'");
  376. // controlwire(browsebotscr, "event", cs->ctl);
  377. tabs[WinBrowse].win = createcolumn(cs, tabs[WinBrowse].winname);
  378. chanprint(cs->ctl, "%q add browsetop browsebot", tabs[WinBrowse].win->name);
  379. }
  380. void
  381. makeplaylistcontrols(void)
  382. {
  383. int w;
  384. Control *playlistscr;
  385. w = stringwidth(romanfont, "Roll over Beethoven");
  386. playlistscr = createslider(cs, "playlistscr");
  387. chanprint(cs->ctl, "playlistscr size 12, 24, 12, 1024");
  388. playlistwin = createtext(cs, "playlistwin");
  389. chanprint(cs->ctl, "playlistwin size %d %d %d %d",
  390. w, 5*romanfont->height, 2048, 1024);
  391. // chanprint(cs->ctl, "playlistwin selectmode multi");
  392. chanprint(cs->ctl, "playlistscr format '%%s: playlistwin topline %%d'");
  393. controlwire(playlistscr, "event", cs->ctl);
  394. tabs[WinPlaylist].win = createrow(cs, tabs[WinPlaylist].winname);
  395. chanprint(cs->ctl, "%q add playlistscr playlistwin", tabs[WinPlaylist].win->name);
  396. }
  397. void
  398. makeerrorcontrols(void)
  399. {
  400. int w;
  401. Control *errorscr;
  402. w = stringwidth(romanfont, "Roll over Beethoven");
  403. errorscr = createslider(cs, "errorscr");
  404. chanprint(cs->ctl, "errorscr size 12, 24, 12, 1024");
  405. errortext = createtext(cs, "errortext");
  406. chanprint(cs->ctl, "errortext size %d %d %d %d",
  407. w, 5*romanfont->height, 2048, 1024);
  408. chanprint(cs->ctl, "errortext selectmode multi");
  409. chanprint(cs->ctl, "errorscr format '%%s: errortext topline %%d'");
  410. controlwire(errorscr, "event", cs->ctl);
  411. tabs[WinError].win = createrow(cs, tabs[WinError].winname);
  412. chanprint(cs->ctl, "%q add errorscr errortext", tabs[WinError].win->name);
  413. }
  414. void
  415. makecontrols(void)
  416. {
  417. int i;
  418. cs = newcontrolset(screen, nil, nil, nil);
  419. // make shared buttons
  420. readbuts();
  421. vol = createslider(cs, "volume");
  422. chanprint(cs->ctl, "volume size %d %d %d %d", 2*butwid, butht, 2048, butht);
  423. chanprint(cs->ctl, "volume absolute 1");
  424. chanprint(cs->ctl, "volume indicatorcolor red");
  425. chanprint(cs->ctl, "volume max 100");
  426. chanprint(cs->ctl, "volume orient hor");
  427. chanprint(cs->ctl, "volume clamp low 1");
  428. chanprint(cs->ctl, "volume clamp high 0");
  429. chanprint(cs->ctl, "volume format '%%s volume %%d'");
  430. createrow(cs, "buttonrow");
  431. for(i = 0; i < nelem(buts); i++)
  432. chanprint(cs->ctl, "buttonrow add %s", buts[i].name);
  433. chanprint(cs->ctl, "buttonrow add volume");
  434. chanprint(cs->ctl, "buttonrow separation %d", borderwidth);
  435. chanprint(cs->ctl, "buttonrow image darkgreen");
  436. makebrowsecontrols();
  437. makeplaycontrols();
  438. makeplaylistcontrols();
  439. makeerrorcontrols();
  440. maketab();
  441. chanprint(cs->ctl, "%q image background", "text slider");
  442. chanprint(cs->ctl, "text font romanfont");
  443. chanprint(cs->ctl, "slider indicatorcolor darkgreen");
  444. chanprint(cs->ctl, "row separation %d", borderwidth);
  445. chanprint(cs->ctl, "row image darkgreen");
  446. chanprint(cs->ctl, "column separation %d", 2);
  447. chanprint(cs->ctl, "column image darkgreen");
  448. createcolumn(cs, "wholewin");
  449. chanprint(cs->ctl, "wholewin separation %d", borderwidth);
  450. chanprint(cs->ctl, "wholewin add buttonrow tabs");
  451. chanprint(cs->ctl, "wholewin image darkgreen");
  452. chanprint(cs->ctl, "%q image darkgreen", "column row");
  453. }
  454. void
  455. makewindow(int dx, int dy, int wflag){
  456. int mountfd, fd, n;
  457. static char aname[128];
  458. static char rio[128] = "/mnt/term";
  459. char *args[6];
  460. if(wflag){
  461. /* find out screen size */
  462. fd = open("/mnt/wsys/screen", OREAD);
  463. if(fd >= 0 && read(fd, aname, 60) == 60){
  464. aname[60] = '\0';
  465. n = tokenize(aname, args, nelem(args));
  466. if(n != 5)
  467. fprint(2, "Not an image: /mnt/wsys/screen\n");
  468. else{
  469. n = atoi(args[3]) - atoi(args[1]);
  470. if(n <= 0 || n > 2048)
  471. fprint(2, "/mnt/wsys/screen very wide: %d\n", n);
  472. else
  473. if(n < dx) dx = n-1;
  474. n = atoi(args[4]) - atoi(args[2]);
  475. if(n <= 0 || n > 2048)
  476. fprint(2, "/mnt/wsys/screen very high: %d\n", n);
  477. else
  478. if(n < dy) dy = n-1;
  479. }
  480. close(fd);
  481. }
  482. n = 0;
  483. if((fd = open("/env/wsys", OREAD)) < 0){
  484. n = strlen(rio);
  485. fd = open("/mnt/term/env/wsys", OREAD);
  486. if(fd < 0)
  487. sysfatal("/env/wsys");
  488. }
  489. if(read(fd, rio+n, sizeof(rio)-n-1) <= 0)
  490. sysfatal("/env/wsys");
  491. mountfd = open(rio, ORDWR);
  492. if(mountfd < 0)
  493. sysfatal("open %s: %r", rio);
  494. snprint(aname, sizeof aname, "new -dx %d -dy %d", dx, dy);
  495. rfork(RFNAMEG);
  496. if(mount(mountfd, -1, "/mnt/wsys", MREPL, aname) < 0)
  497. sysfatal("mount: %r");
  498. if(bind("/mnt/wsys", "/dev", MBEFORE) < 0)
  499. sysfatal("mount: %r");
  500. }
  501. if(initdraw(nil, nil, "music") < 0)
  502. sysfatal("initdraw: %r");
  503. initcontrols();
  504. if(dx <= 320)
  505. colorinit("/lib/font/bit/lucidasans/unicode.6.font",
  506. "/lib/font/bit/lucidasans/boldunicode.8.font");
  507. else
  508. colorinit("/lib/font/bit/lucidasans/unicode.8.font",
  509. "/lib/font/bit/lucidasans/boldunicode.10.font");
  510. makecontrols();
  511. resizeready = 1;
  512. resizecontrolset(cs);
  513. if(debug & DBGCONTROL)
  514. fprint(2, "resize done\n");
  515. }
  516. void
  517. setparent(char *addr)
  518. {
  519. int i;
  520. if(parent.address)
  521. free(parent.address);
  522. parent.address = strdup(addr);
  523. for(i = 0; i < parent.ndata; i++)
  524. if(parent.data[i])
  525. free(parent.data[i]);
  526. parent.ndata = 0;
  527. if(parent.data){
  528. free(parent.data);
  529. parent.data = nil;
  530. }
  531. chanprint(cs->ctl, "browsetopwin clear");
  532. chanprint(cs->ctl, "browsetopscr max 0");
  533. chanprint(cs->ctl, "browsetopscr value 0");
  534. }
  535. void
  536. addparent(char *str)
  537. {
  538. parent.data = realloc(parent.data, (parent.ndata+1)*sizeof(char*));
  539. parent.data[parent.ndata] = strdup(str);
  540. parent.ndata++;
  541. chanprint(cs->ctl, "browsetopwin accumulate %q", str);
  542. chanprint(cs->ctl, "browsetopscr max %d", parent.ndata);
  543. }
  544. void
  545. clearchildren(void)
  546. {
  547. int i;
  548. for(i = 0; i < nchildren; i++){
  549. if(children[i].address)
  550. free(children[i].address);
  551. if(children[i].data)
  552. free(children[i].data);
  553. }
  554. nchildren= 0;
  555. chanprint(cs->ctl, "browsebotwin clear");
  556. chanprint(cs->ctl, "browsebotwin topline 0");
  557. chanprint(cs->ctl, "browsebotscr max 0");
  558. chanprint(cs->ctl, "browsebotscr value 0");
  559. selected = -1;
  560. }
  561. void
  562. addchild(char *addr, char *data)
  563. {
  564. children[nchildren].address = addr;
  565. children[nchildren].data = data;
  566. nchildren++;
  567. chanprint(cs->ctl, "browsebotwin accumulate %q", data);
  568. chanprint(cs->ctl, "browsebotscr max %d", nchildren);
  569. }
  570. static void
  571. playlistselect(int n)
  572. {
  573. if(playlist.selected >= 0 && playlist.selected < playlist.nentries){
  574. chanprint(cs->ctl, "playlistwin select %d 0", playlist.selected);
  575. deactivatebuttons(1<<Playbutton);
  576. }
  577. playlist.selected = n;
  578. if(playlist.selected < 0 || playlist.selected >= playlist.nentries)
  579. return;
  580. activatebuttons(1<<Playbutton);
  581. chanprint(cs->ctl, "playlistwin select %d 1", n);
  582. if(n >= 0 && n <= playlist.nentries - Dy(playlistwin->rect)/romanfont->height)
  583. n--;
  584. else
  585. n = playlist.nentries - Dy(playlistwin->rect)/romanfont->height + 1;
  586. if(n < 0) n = 0;
  587. if(n < playlist.nentries){
  588. chanprint(cs->ctl, "playlistwin topline %d", n);
  589. chanprint(cs->ctl, "playlistscr value %d", n);
  590. }
  591. chanprint(cs->ctl, "playlist show");
  592. }
  593. void
  594. updateplaylist(int trunc)
  595. {
  596. char *s;
  597. int fd;
  598. while(cs->ctl->s - cs->ctl->n < cs->ctl->s/4)
  599. sleep(100);
  600. if(trunc){
  601. playlistselect(-1);
  602. chanprint(cs->ctl, "playlistwin clear");
  603. chanprint(cs->ctl, "playlistwin topline 0");
  604. chanprint(cs->ctl, "playlistscr max 0");
  605. chanprint(cs->ctl, "playlistscr value 0");
  606. deactivatebuttons(1<<Playbutton | 1<<Deletebutton);
  607. chanprint(cs->ctl, "playlistwin show");
  608. chanprint(cs->ctl, "playlistscr show");
  609. s = smprint("%s/ctl", srvmount);
  610. if((fd = open(s, OWRITE)) >= 0){
  611. fprint(fd, "reread");
  612. close(fd);
  613. }
  614. free(s);
  615. return;
  616. }
  617. if(playlist.entry[playlist.nentries].onum){
  618. s = getoneliner(playlist.entry[playlist.nentries].onum);
  619. chanprint(cs->ctl, "playlistwin accumulate %q", s);
  620. free(s);
  621. }else
  622. chanprint(cs->ctl, "playlistwin accumulate %q", playlist.entry[playlist.nentries].file);
  623. playlist.nentries++;
  624. chanprint(cs->ctl, "playlistscr max %d", playlist.nentries);
  625. if(playlist.selected == playlist.nentries - 1)
  626. playlistselect(playlist.selected);
  627. activatebuttons(1<<Playbutton|1<<Deletebutton);
  628. chanprint(cs->ctl, "playlistscr show");
  629. chanprint(cs->ctl, "playlistwin show");
  630. }
  631. void
  632. browseto(char *onum, int line)
  633. {
  634. onum = strdup(onum);
  635. setparent(onum);
  636. clearchildren();
  637. fillbrowsetop(onum);
  638. chanprint(cs->ctl, "browsetop show");
  639. fillbrowsebot(onum);
  640. if(line){
  641. chanprint(cs->ctl, "browsebotscr value %d", line);
  642. chanprint(cs->ctl, "browsebotwin topline %d", line);
  643. }
  644. chanprint(cs->ctl, "browsebot show");
  645. free(onum);
  646. }
  647. void
  648. browsedown(char *onum)
  649. {
  650. if(browsesp == 0){
  651. /* Make room for an entry by deleting the last */
  652. free(browsestack[Browsedepth-1].onum);
  653. memmove(browsestack + 1, browsestack, (Browsedepth-1) * sizeof(browsestack[0]));
  654. browsesp++;
  655. }
  656. /* Store current position in current stack frame */
  657. assert(browsesp > 0 && browsesp < Browsedepth);
  658. browsestack[browsesp].onum = strdup(parent.address);
  659. browsestack[browsesp].scrollpos = browseline;
  660. browsesp--;
  661. browseline = 0;
  662. if(browsestack[browsesp].onum && strcmp(browsestack[browsesp].onum, onum) == 0)
  663. browseline = browsestack[browsesp].scrollpos;
  664. browseto(onum, browseline);
  665. }
  666. void
  667. browseup(char *onum)
  668. {
  669. if(browsesp == Browsedepth){
  670. /* Make room for an entry by deleting the first */
  671. free(browsestack[0].onum);
  672. memmove(browsestack, browsestack + 1, browsesp * sizeof(browsestack[0]));
  673. browsesp--;
  674. }
  675. /* Store current position in current stack frame */
  676. assert(browsesp >= 0 && browsesp < Browsedepth);
  677. browsestack[browsesp].onum = strdup(parent.address);
  678. browsestack[browsesp].scrollpos = browseline;
  679. browsesp++;
  680. browseline = 0;
  681. if(browsestack[browsesp].onum && strcmp(browsestack[browsesp].onum, onum) == 0)
  682. browseline = browsestack[browsesp].scrollpos;
  683. browseto(onum, browseline);
  684. }
  685. void
  686. addplaytext(char *s)
  687. {
  688. chanprint(cs->ctl, "playtext accumulate %q", s);
  689. }
  690. void
  691. work(void)
  692. {
  693. static char *eventstr, *args[64], *s;
  694. static char buf[4096];
  695. int a, n, i;
  696. Alt alts[] = {
  697. [Exitbutton] = {buts[Exitbutton].ctl->event, &eventstr, CHANRCV},
  698. [Pausebutton] = {buts[Pausebutton].ctl->event, &eventstr, CHANRCV},
  699. [Playbutton] = {buts[Playbutton].ctl->event, &eventstr, CHANRCV},
  700. [Stopbutton] = {buts[Stopbutton].ctl->event, &eventstr, CHANRCV},
  701. [Prevbutton] = {buts[Prevbutton].ctl->event, &eventstr, CHANRCV},
  702. [Nextbutton] = {buts[Nextbutton].ctl->event, &eventstr, CHANRCV},
  703. [Rootbutton] = {buts[Rootbutton].ctl->event, &eventstr, CHANRCV},
  704. [Deletebutton] = {buts[Deletebutton].ctl->event, &eventstr, CHANRCV},
  705. [Helpbutton] = {buts[Helpbutton].ctl->event, &eventstr, CHANRCV},
  706. [Volume] = {vol->event, &eventstr, CHANRCV},
  707. [Browsetopwin] = {browsetopwin->event, &eventstr, CHANRCV},
  708. [Browsebotwin] = {browsebotwin->event, &eventstr, CHANRCV},
  709. [Browsebotscr] = {browsebotscr->event, &eventstr, CHANRCV},
  710. [Playevent] = {playevent, &eventstr, CHANRCV},
  711. [Playlistwin] = {playlistwin->event, &eventstr, CHANRCV},
  712. [Nalt] = {nil, nil, CHANEND}
  713. };
  714. activate(vol);
  715. activate(controlcalled("tabs"));
  716. activatebuttons(1 << Exitbutton | 1 << Rootbutton | 1 << Helpbutton);
  717. root = getroot();
  718. setparent(root);
  719. clearchildren();
  720. addparent("Root");
  721. chanprint(cs->ctl, "browsetop show");
  722. fillbrowsebot(root);
  723. chanprint(cs->ctl, "browsebot show");
  724. eventstr = nil;
  725. selected = -1;
  726. for(;;){
  727. a = alt(alts);
  728. if(debug & DBGCONTROL)
  729. fprint(2, "Event: %s\n", eventstr);
  730. n = tokenize(eventstr, args, nelem(args));
  731. switch(a){
  732. default:
  733. sysfatal("Illegal event %d in work", a);
  734. case Volume:
  735. if(n != 3 || strcmp(args[0], "volume") || strcmp(args[1], "volume"))
  736. sysfatal("Bad Volume event[%d]: %s %s", n, args[0], args[1]);
  737. setvolume(args[2]);
  738. break;
  739. case Exitbutton:
  740. return;
  741. case Pausebutton:
  742. if(n != 3 || strcmp(args[0], "pause:") || strcmp(args[1], "value"))
  743. sysfatal("Bad Pausebutton event[%d]: %s %s", n, args[0], args[1]);
  744. if(strcmp(args[2], "0") == 0)
  745. fprint(playctlfd, "resume");
  746. else
  747. fprint(playctlfd, "pause");
  748. break;
  749. case Playbutton:
  750. if(n != 3 || strcmp(args[0], "play:") || strcmp(args[1], "value"))
  751. sysfatal("Bad Playbutton event[%d]: %s %s", n, args[0], args[1]);
  752. if(playlist.selected >= 0){
  753. fprint(playctlfd, "play %d", playlist.selected);
  754. }else
  755. fprint(playctlfd, "play");
  756. break;
  757. case Stopbutton:
  758. if(n != 3 || strcmp(args[0], "stop:") || strcmp(args[1], "value"))
  759. sysfatal("Bad Stopbutton event[%d]: %s %s", n, args[0], args[1]);
  760. if(strcmp(args[2], "0") == 0)
  761. chanprint(cs->ctl, "%q value 1", buts[Stopbutton].ctl->name);
  762. fprint(playctlfd, "stop");
  763. break;
  764. case Prevbutton:
  765. if(n != 3 || strcmp(args[0], "prev:") || strcmp(args[1], "value"))
  766. sysfatal("Bad Prevbutton event[%d]: %s %s", n, args[0], args[1]);
  767. if(strcmp(args[2], "0") == 0)
  768. break;
  769. chanprint(cs->ctl, "%q value 0", buts[Prevbutton].ctl->name);
  770. fprint(playctlfd, "skip -1");
  771. break;
  772. case Nextbutton:
  773. if(n != 3 || strcmp(args[0], "next:") || strcmp(args[1], "value"))
  774. sysfatal("Bad Nextbutton event[%d]: %s %s", n, args[0], args[1]);
  775. if(strcmp(args[2], "0") == 0)
  776. break;
  777. chanprint(cs->ctl, "%q value 0", buts[Nextbutton].ctl->name);
  778. fprint(playctlfd, "skip 1");
  779. break;
  780. case Playlistwin:
  781. if(debug & (DBGCONTROL|DBGPLAY))
  782. fprint(2, "Playlistevent: %s %s\n", args[0], args[1]);
  783. if(n != 4 || strcmp(args[0], "playlistwin:") || strcmp(args[1], "select"))
  784. sysfatal("Bad Playlistwin event[%d]: %s %s", n, args[0], args[1]);
  785. n = atoi(args[2]);
  786. if(n < 0 || n >= playlist.nentries)
  787. sysfatal("Selecting line %d of %d", n, playlist.nentries);
  788. if(playlist.selected >= 0 && playlist.selected < playlist.nentries){
  789. chanprint(cs->ctl, "playlistwin select %d 0", playlist.selected);
  790. chanprint(cs->ctl, "playlistwin show");
  791. }
  792. playlist.selected = -1;
  793. deactivatebuttons(1<<Playbutton);
  794. if(strcmp(args[3], "1") == 0)
  795. playlistselect(n);
  796. break;
  797. case Rootbutton:
  798. chanprint(cs->ctl, "%q value 0", buts[Rootbutton].ctl->name);
  799. setparent(root);
  800. clearchildren();
  801. addparent("Root");
  802. chanprint(cs->ctl, "browsetop show");
  803. fillbrowsebot(root);
  804. chanprint(cs->ctl, "browsebot show");
  805. break;
  806. case Deletebutton:
  807. if(n != 3 || strcmp(args[0], "trash:") || strcmp(args[1], "value"))
  808. sysfatal("Bad Deletebutton event[%d]: %s %s", n, args[0], args[1]);
  809. if(strcmp(args[2], "0") == 0)
  810. break;
  811. chanprint(cs->ctl, "%q value 0", buts[Deletebutton].ctl->name);
  812. sendplaylist(nil, nil);
  813. break;
  814. case Helpbutton:
  815. chanprint(cs->ctl, "%q value 0", buts[Helpbutton].ctl->name);
  816. if(errorlines > 0){
  817. chanprint(cs->ctl, "errortext clear");
  818. chanprint(cs->ctl, "errortext topline 0");
  819. chanprint(cs->ctl, "errorscr max 0");
  820. chanprint(cs->ctl, "errorscr value 0");
  821. }
  822. if(errorlines >= 0){
  823. for(i = 0; helptext[i]; i++)
  824. chanprint(cs->ctl, "errortext accumulate %q", helptext[i]);
  825. chanprint(cs->ctl, "errorscr max %d", i);
  826. }
  827. chanprint(cs->ctl, "errortext topline 0");
  828. chanprint(cs->ctl, "errorscr value 0");
  829. errorlines = -1;
  830. chanprint(cs->ctl, "tabs value %d", WinError);
  831. break;
  832. case Browsetopwin:
  833. if(n != 4 || strcmp(args[0], "browsetopwin:") || strcmp(args[1], "select"))
  834. sysfatal("Bad Browsetopwin event[%d]: %s %s", n, args[0], args[1]);
  835. if(strcmp(args[3], "0") == 0)
  836. break;
  837. chanprint(cs->ctl, "browsetopwin select %s 0", args[2]);
  838. selected = -1;
  839. if(strcmp(args[3], "2") == 0)
  840. doplay(parent.address);
  841. else if(strcmp(args[3], "4") == 0){
  842. s = getparent(parent.address);
  843. browsedown(s);
  844. }
  845. break;
  846. case Browsebotwin:
  847. if(n != 4 || strcmp(args[0], "browsebotwin:") || strcmp(args[1], "select"))
  848. sysfatal("Bad Browsebotwin event[%d]: %s %s", n, args[0], args[1]);
  849. n = atoi(args[2]);
  850. if(n < 0 || n >= nchildren)
  851. sysfatal("Selection out of range: %d [%d]", n, nchildren);
  852. if(strcmp(args[3], "0") == 0){
  853. selected = -1;
  854. break;
  855. }
  856. if(n < 0)
  857. break;
  858. chanprint(cs->ctl, "browsebotwin select %d 0", n);
  859. selected = n;
  860. if(selected >= nchildren)
  861. sysfatal("Select out of range: %d [0⋯%d)", selected, nchildren);
  862. if(strcmp(args[3], "1") == 0){
  863. browseup(children[selected].address);
  864. }else if(strcmp(args[3], "2") == 0)
  865. doplay(children[selected].address);
  866. else if(strcmp(args[3], "4") == 0)
  867. browsedown(getparent(parent.address));
  868. break;
  869. case Browsebotscr:
  870. browseline = atoi(args[2]);
  871. chanprint(cs->ctl, "browsebotwin topline %d", browseline);
  872. break;
  873. case Playevent:
  874. if(n < 3 || strcmp(args[0], "playctlproc:"))
  875. sysfatal("Bad Playevent event[%d]: %s", n, args[0]);
  876. if(debug & (DBGCONTROL|DBGPLAY))
  877. fprint(2, "Playevent: %s %s\n", args[1], args[2]);
  878. if(strcmp(args[1], "error") ==0){
  879. if(n != 4){
  880. fprint(2, "Playevent: %s: arg count: %d\n", args[1], n);
  881. break;
  882. }
  883. if(errorlines < 0){
  884. chanprint(cs->ctl, "errortext clear");
  885. chanprint(cs->ctl, "errortext topline 0");
  886. chanprint(cs->ctl, "errorscr max 0");
  887. chanprint(cs->ctl, "errorscr value 0");
  888. errorlines = 0;
  889. }
  890. n = errorlines;
  891. chanprint(cs->ctl, "errortext accumulate %q", args[3]);
  892. chanprint(cs->ctl, "errorscr max %d", ++errorlines);
  893. if(n >= 0 && n <= errorlines - Dy(errortext->rect)/romanfont->height)
  894. n--;
  895. else
  896. n = errorlines - Dy(errortext->rect)/romanfont->height + 1;
  897. if(n < 0) n = 0;
  898. if(n < errorlines){
  899. chanprint(cs->ctl, "errortext topline %d", n);
  900. chanprint(cs->ctl, "errorscr value %d", n);
  901. }
  902. chanprint(cs->ctl, "tabs value %d", WinError);
  903. }else if(strcmp(args[1], "play") ==0){
  904. chanprint(cs->ctl, "%q value 1", buts[Playbutton].ctl->name);
  905. chanprint(cs->ctl, "%q value 0", buts[Stopbutton].ctl->name);
  906. chanprint(cs->ctl, "%q value 0", buts[Pausebutton].ctl->name);
  907. playlistselect(strtoul(args[2], nil, 0));
  908. chanprint(cs->ctl, "playtext clear");
  909. chanprint(cs->ctl, "playtext topline 0");
  910. chanprint(cs->ctl, "playscr max 0");
  911. chanprint(cs->ctl, "playscr value 0");
  912. playstate = Playing;
  913. activatebuttons(playingbuts);
  914. qlock(&playlist);
  915. if(playlist.selected < playlist.nentries){
  916. fillplaytext(playlist.entry[playlist.selected].onum);
  917. chanprint(cs->ctl, "playscr max %d", n);
  918. }
  919. qunlock(&playlist);
  920. chanprint(cs->ctl, "playwin show");
  921. }else if(strcmp(args[1], "stop") ==0){
  922. chanprint(cs->ctl, "%q value 0", buts[Playbutton].ctl->name);
  923. chanprint(cs->ctl, "%q value 1", buts[Stopbutton].ctl->name);
  924. chanprint(cs->ctl, "%q value 0", buts[Pausebutton].ctl->name);
  925. playlistselect(strtoul(args[2], nil, 0));
  926. chanprint(cs->ctl, "%q show", tabs[WinPlaylist].winname);
  927. playstate = PlayIdle;
  928. deactivatebuttons(playingbuts);
  929. }else if(strcmp(args[1], "pause") ==0){
  930. activatebuttons(playingbuts);
  931. chanprint(cs->ctl, "%q value 1", buts[Playbutton].ctl->name);
  932. chanprint(cs->ctl, "%q value 0", buts[Stopbutton].ctl->name);
  933. if(playstate == PlayPause){
  934. chanprint(cs->ctl, "%q value 0", buts[Pausebutton].ctl->name);
  935. playstate = Playing;
  936. }else{
  937. chanprint(cs->ctl, "%q value 1", buts[Pausebutton].ctl->name);
  938. playstate = PlayPause;
  939. }
  940. }else if(strcmp(args[1], "exits") ==0){
  941. threadexits("exitevent");
  942. }else{
  943. fprint(2, "Unknown play event:");
  944. for(i=0; i<n; i++)
  945. fprint(2, " %s", args[i]);
  946. fprint(2, "\n");
  947. }
  948. break;
  949. }
  950. if(eventstr){
  951. free(eventstr);
  952. eventstr = nil;
  953. }
  954. }
  955. }
  956. void
  957. threadmain(int argc, char *argv[]){
  958. int wflag;
  959. wflag = 0;
  960. ARGBEGIN{
  961. case 'd':
  962. debug = strtol(ARGF(), nil, 0);
  963. break;
  964. case 't':
  965. tflag = 1;
  966. break;
  967. case 'w':
  968. wflag = 1;
  969. break;
  970. default:
  971. sysfatal(usage, argv0);
  972. }ARGEND
  973. quotefmtinstall();
  974. if(tflag)
  975. makewindow(320, 320, wflag);
  976. else
  977. makewindow(480, 480, wflag);
  978. playlist.selected = -1;
  979. playctlfd = open(playctlfile, OWRITE);
  980. if(playctlfd < 0)
  981. sysfatal("%s: %r", playctlfile);
  982. proccreate(playlistproc, nil, 8192);
  983. playevent = chancreate(sizeof(char *), 1);
  984. proccreate(playctlproc, playevent, 8192);
  985. proccreate(playvolproc, cs->ctl, 8192);
  986. work();
  987. closecontrolset(cs);
  988. threadexitsall(nil);
  989. }