Warning: Attempt to read property "date" on null in /usr/local/www/websvn.planix.org/blame.php on line 247

Warning: Attempt to read property "msg" on null in /usr/local/www/websvn.planix.org/blame.php on line 247
WebSVN – planix.SVN – Blame – /os/branches/feature_unix/sys/src/games/music/jukebox/music.c – Rev 2

Subversion Repositories planix.SVN

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

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