1 /*
2  * Copyright (C) 1990 Regents of the University of California.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and
5  * its documentation for any purpose is hereby granted without fee,
6  * provided that the above copyright notice appear in all copies and that
7  * both that copyright notice and this permission notice appear in
8  * supporting documentation, and that the name of the University of
9  * California not be used in advertising or publicity pertaining to
10  * distribution of the software without specific, written prior
11  * permission.  the University of California makes no representations
12  * about the suitability of this software for any purpose.  It is provided
13  * "as is" without express or implied warranty.
14  */
15 
16 # include <X11/Intrinsic.h>
17 # include <X11/StringDefs.h>
18 # include <X11/Xaw/Form.h>
19 # include <X11/Xaw/Toggle.h>
20 
21 # include <stdio.h>
22 
23 # include "debug.h"
24 # include "cdrom_globs.h"
25 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
26 # include "cdrom_freebsd.h"
27 #endif
28 #ifdef sun
29 # include "cdrom_sun.h"
30 #endif
31 #ifdef sgi
32 # include "cdrom_sgi.h"
33 #endif
34 
35 static void     scan_update();
36 static XtIntervalId	ivid			= -1;
37 static XtIntervalId	scanivid		= -1;
38 static XtIntervalId	stativid		= -1;
39 static unsigned int	timer_mod		= 1000 / TIMER_PERIOD;
40 unsigned int		timer_fsecs;
41 
42 extern AppData app_data;
43 
44 int
cdrom_get_curtime()45 cdrom_get_curtime() {
46 
47 	int curtime;
48 	int curtrack;
49 	struct msf curmsf, track_start;
50 
51 	if (cdrom_open() == -1) {
52 		debug_printf(1, "cdrom_get_curtime: error from cdrom_open\n");
53 		return 0;
54 	}
55 
56 	switch (cdrom_status()) {
57 	case CDROM_PAUSED:
58 	case CDROM_PLAYING:
59 	    if (cdrom_get_curmsf(&curmsf) == -1) {
60 		debug_printf(1, "get_curtime: error reading location\n");
61 		return 0;
62 	    }
63 
64 	    if (((curtrack = cdrom_get_curtrack()) == -1) ||
65 		(curtrack != cdi.curtrack))
66 	    {
67 		return(0);
68 	    }
69 	    else
70 	    {
71 	        track_start = cdi.addrs[cdi.curtrack - 1];
72 	        curtime = (curmsf.minute - track_start.minute) * 60 +
73 			(curmsf.second - track_start.second) + 1;
74 
75 	        return (curtime);
76 	    }
77 	    break;
78 	default:
79 	    return(0);
80 	}
81 }
82 
83 static void	update_track();
84 
85 /*
86  * we poll the cd-rom drive every TIMER_PERIOD milliseconds to see where
87  * it is and if it's on a new track, and update the label widget.
88  */
89 void
cdrom_timer_on()90 cdrom_timer_on() {
91 
92 	if (cdi.state & CDROM_STATE_PLAY)
93 		ivid = XtAppAddTimeOut(appc, TIMER_PERIOD, update_track, NULL);
94 }
95 
96 
97 /*
98  * cdrom_timer_off turns off the X timeout.
99  */
100 void
cdrom_timer_off()101 cdrom_timer_off() {
102 	if (ivid != -1) {
103 		XtRemoveTimeOut(ivid);
104 		ivid = -1;
105 	}
106 }
107 
108 /*
109  * activates player by track/index:
110  */
111 int
cdrom_play()112 cdrom_play() {
113 	int	ret;
114 
115 	debug_printf(1, "cdrom_play: starting track %d\n", (int) cdi.curtrack);
116 
117 	track_button_update();
118 
119 	if ((cdi.state & CDROM_STATE_SHUFFLE) ||
120 	    (cdi.state & CDROM_STATE_PROGRAM))
121 		ret = cdrom_play_track(cdi.curtrack, cdi.curtrack);
122 	else
123 		ret = cdrom_play_track(cdi.curtrack, cdi.maxtrack);
124 
125 	cdrom_timer_on();
126 
127 	return(ret);
128 }
129 
130 
131 /*
132  * resets player to disc origin and turns off all modes:
133  */
134 void
cdrom_reset()135 cdrom_reset() {
136 
137 	cdrom_timer_off();
138 
139 	if (cdi.curtrack != 0)
140 	{
141 	    cdi.curtrack = 1;
142 	    (void) cdrom_play(); /* force disk to beginning */
143 
144 	    /*
145 	     * "pause disk" immediately; doesn't appear that you can truly
146 	     * stop the disk at origin; just stop at current location...
147 	     */
148 	    cdrom_stop();
149 	    cdi.state |= CDROM_STATE_STOP;
150 	    debug_printf(1, "cdrom_reset: forcing immediate stop\n");
151 	    leds_stop();
152 	    cdrom_timer_off();
153 	    cdi.curtrack = 0;
154 	}
155 
156 	cdi.state &= ~(CDROM_STATE_PLAY | CDROM_STATE_PAUSE |
157 				CDROM_STATE_EJECTED);
158 
159 	update_title();
160 
161 	play_button_reset();
162 	pause_button_reset();
163 	timer_button_reset();
164 
165 	track_button_update();
166 	timer_button_update();
167 }
168 
169 /*
170  * rewinds the player through minute/second/frames:
171  */
172 void
cdrom_rewind()173 cdrom_rewind () {
174 	struct msf	track_start;
175         struct msf	start_addr, end_addr;
176 	int		curtrack;
177 
178 	if (cdrom_get_curmsf(&start_addr) == -1) {
179 		debug_printf(1, "rew: error reading location\n");
180 		return;
181 	}
182 
183 	curtrack = cdrom_get_curtrack();
184 
185 	if (curtrack != cdi.curtrack)
186 		track_button_update();
187 
188 	/* find start of current track: */
189 	track_start = cdi.addrs[curtrack - 1];
190 
191 	/*
192 	 * deal with cases where we can't back up beyond the current track:
193 	 */
194 	if ((curtrack == cdi.mintrack)  ||
195 	    (cdi.state & CDROM_STATE_SHUFFLE) ||
196 	    (cdi.state & CDROM_STATE_PROGRAM))
197 	{
198 	    /* Ugh, this is ugly... (drich) */
199 	    if (((cdi.state & CDROM_STATE_PLAY) &&
200 		 ((track_start.minute * 60) + track_start.second) >=
201 		 ((start_addr.minute * 60) + start_addr.second -
202 		  app_data.scanSkipInterval)) ||
203 		((cdi.state & CDROM_STATE_PAUSE) &&
204 		 ((track_start.minute * 60) + track_start.second) >=
205 		 ((start_addr.minute * 60) + start_addr.second -
206 		  app_data.pauseSkipInterval)))
207 	    {
208 	        start_addr = track_start;
209 	        start_addr.second++; /* guarantee we never back up too far */
210 	    }
211 	    else
212 	    {
213 		if (cdi.state & CDROM_STATE_PAUSE) {
214 			start_addr.second -= app_data.pauseSkipInterval;
215 		} else if (cdi.state & CDROM_STATE_PLAY) {
216 			start_addr.second -= app_data.scanSkipInterval;
217 		}
218 	        if ((char) start_addr.second < 0)
219 	        {
220 		    start_addr.minute--;
221 		    start_addr.second = 60 + (char) start_addr.second;
222 	        }
223 	    }
224 
225 	}
226 	else /* normal case */
227 	{
228 		if (cdi.state & CDROM_STATE_PAUSE) {
229 			start_addr.second -= app_data.pauseSkipInterval;
230 		} else if (cdi.state & CDROM_STATE_PLAY) {
231 			start_addr.second -= app_data.scanSkipInterval;
232 		}
233 		if ((char) start_addr.second < 0)
234 		{
235 			start_addr.minute--;
236 			start_addr.second = 60 + (char) start_addr.second;
237 		}
238 	}
239 
240 	if ((cdi.state & CDROM_STATE_PROGRAM) ||
241 	    (cdi.state & CDROM_STATE_SHUFFLE))
242 	{
243 
244 	    /* then to end of current selection (start of next track - 1 sec) */
245 	    end_addr = cdi.addrs[cdi.curtrack];
246 	    end_addr.second--;
247 	    if ((char) end_addr.second < 0)
248 	    {
249 		end_addr.minute--;
250 		end_addr.second = 59;
251 	    }
252 	}
253 	else
254 	{
255 
256 	    /*
257 	     * to end of last track; array 0-based, so really index for
258 	     * leadout addr:
259 	     */
260 	    end_addr = cdi.addrs[cdi.maxtrack];
261 	    end_addr.second--; /* go to last second */
262 	    if ((char) end_addr.second < 0)
263 	    {
264 		end_addr.minute--;
265 		end_addr.second = 59;
266 	    }
267 	}
268 
269 	if ((start_addr.minute == end_addr.minute) &&
270 	    (start_addr.second == end_addr.second))
271 	    end_addr.frame = start_addr.frame;
272 
273 	cdrom_play_msf (&start_addr, &end_addr);
274 
275 	timer_fsecs = 0;
276 	if (cdi.state & CDROM_STATE_PAUSE)
277 	{
278 	    if (scanivid == -1)
279 		scanivid = XtAppAddTimeOut(appc,
280 			(int)(app_data.pausePauseInterval * 1000.0),
281 			scan_update, NULL);
282 
283 	    cdi.state &= ~CDROM_STATE_PAUSE;	/* allow timer to change */
284 	    timer_button_update();
285 	    cdi.state |= CDROM_STATE_PAUSE;	/* restore true state */
286 
287 	    if (cdrom_pause() != -1)		/* re-pause */
288 	       cdi.state &= ~CDROM_STATE_STOP;
289 
290 	}
291 	else
292 	{
293 	    if (scanivid != -1) {
294 		XtRemoveTimeOut(scanivid);
295 		ivid = -1;
296 	    }
297 	    timer_button_update();
298 	}
299 
300 }
301 
302 /*
303  * fast-forwards the player through minute/second/frames:
304  */
305 void
cdrom_ff()306 cdrom_ff () {
307         struct msf	start_addr, end_addr,  next_start;
308 	char		t;
309 	int		curtrack;
310 
311 	if (cdrom_get_curmsf(&start_addr) == -1) {
312 		debug_printf(1, "ff: error reading location\n");
313 		return;
314 	}
315 
316 	curtrack = cdrom_get_curtrack();
317 
318 	if (curtrack != cdi.curtrack)
319 		track_button_update();
320 
321 	/* find start of next track */
322 	next_start = cdi.addrs[curtrack];
323 
324 	/*
325 	 * deal with cases where we can't fast forward beyond the current
326 	 * track:
327 	 */
328 	if ((curtrack == cdi.maxtrack)  ||
329 	    (cdi.state & CDROM_STATE_SHUFFLE) ||
330 	    (cdi.state & CDROM_STATE_PROGRAM))
331 	{
332 	    /* see if skipping ahead will go beyond the current track: */
333 	    /* Ugh, this is ugly... (drich) */
334 	    if (((cdi.state & CDROM_STATE_PLAY) &&
335 		 ((next_start.minute * 60) + next_start.second) <=
336 		 ((start_addr.minute * 60) + start_addr.second +
337 		  app_data.scanSkipInterval)) ||
338 		((cdi.state & CDROM_STATE_PAUSE) &&
339 		 ((next_start.minute * 60) + next_start.second) <=
340 		 ((start_addr.minute * 60) + start_addr.second +
341 		  app_data.pauseSkipInterval)))
342 	    {
343 
344 	        /* start at end of current track */
345 	        start_addr = next_start;
346 	        start_addr.second--;
347 	    }
348 	    else
349 	    {
350 		    if (cdi.state & CDROM_STATE_PAUSE) {
351 			    start_addr.second += app_data.pauseSkipInterval;
352 		    } else if (cdi.state & CDROM_STATE_PLAY) {
353 			    start_addr.second += app_data.scanSkipInterval;
354 		    }
355 		    if (start_addr.second >= 60)
356 		    {
357 			    start_addr.minute++;
358 			    start_addr.second = start_addr.second - 60;
359 		    }
360 	    }
361 	}
362 	else
363 	{
364 	    if (cdi.state & CDROM_STATE_PAUSE) {
365 		start_addr.second += app_data.pauseSkipInterval;
366 	    } else if (cdi.state & CDROM_STATE_PLAY) {
367 		start_addr.second += app_data.scanSkipInterval;
368 	    }
369 	    if (start_addr.second >= 60)
370 	    {
371 		start_addr.minute++;
372 		start_addr.second = start_addr.second - 60;
373 	    }
374 	}
375 
376 	if ((cdi.state & CDROM_STATE_PROGRAM) ||
377 	    (cdi.state & CDROM_STATE_SHUFFLE))
378 	{
379 
380 	    /* then to end of current selection */
381 	    end_addr = next_start;	/* use start of next */
382 	    end_addr.second--; 		/* and back off 1 second */
383 	    if ((char) end_addr.second < 0)
384 	    {
385 		end_addr.minute--;
386 		end_addr.second = 59;
387 	    }
388 	}
389 	else
390 	{
391 	    /*
392 	     * "to end of last track"; array 0-based, so really index for
393 	     * leadout addr
394 	     */
395 	    end_addr = cdi.addrs[cdi.maxtrack];
396 
397 	    end_addr.second--; /* (you can't play the leadout) */
398 	    if ((char) end_addr.second < 0)
399 	    {
400 		end_addr.minute--;
401 		end_addr.second = 59;
402 	    }
403 	}
404 
405 	if ((start_addr.minute == end_addr.minute) &&
406 	    (start_addr.second == end_addr.second))
407 	{
408 	    start_addr.frame = end_addr.frame = 0;
409 	}
410 
411 	cdrom_play_msf (&start_addr, &end_addr);
412 
413 	timer_fsecs = 0;
414 	if (cdi.state & CDROM_STATE_PAUSE)
415 	{
416 	    if (scanivid == -1)
417 		scanivid = XtAppAddTimeOut(appc,
418 			(int)(app_data.pausePauseInterval * 1000.0),
419 			scan_update, NULL);
420 
421 	    cdi.state &= ~CDROM_STATE_PAUSE;	/* allow timer to change */
422 	    timer_button_update();
423 	    cdi.state |= CDROM_STATE_PAUSE;	/* restore true state */
424 
425 	    if (cdrom_pause() != -1)		/* re-pause */
426 	       cdi.state &= ~CDROM_STATE_STOP;
427 	}
428 	else
429 	{
430 	    if (scanivid != -1) {
431 		XtRemoveTimeOut(scanivid);
432 		ivid = -1;
433 	    }
434 	    timer_button_update();
435 	}
436 }
437 
438 
439 /*
440  * called by update_track when the cd has hit
441  * the end of the track or the disc.
442  */
443 static void
cdrom_atend()444 cdrom_atend() {
445 	cdrom_timer_off();
446 	leds_stop();
447 	debug_printf(1, "cdrom_atend: at end\n");
448 
449 #ifdef sgi
450 	/* Force a stop to kill the child (if any).
451 	 * This is due to some sort of weirdness when the SGI runs off the
452 	 * disc during a CDreadda().
453 	 */
454 	if (cdi.scsi_audio) {
455 		cdrom_stop();
456 	}
457 #endif
458 	if (cdi.state & CDROM_STATE_SHUFFLE) {
459 
460 		if (cdi.currand == cdi.ntracks) {
461 			if ((cdi.state & CDROM_STATE_CYCLE) == 0) {
462 				debug_printf(1, "cdrom_atend: shuffle done\n");
463 
464 				cdrom_reset();
465 				return;
466 			}
467 
468 			debug_printf(1, "cdrom_atend: shuffle cycling\n");
469 			shuffle_setup();
470 		}
471 
472 		cdi.curtrack = shuffle_next_track();
473 	}
474 	else if (cdi.state & CDROM_STATE_PROGRAM) {
475 		if ((cdi.curtrack = program_goto_next_track()) == 0)
476 		{
477 		    if (cdi.state & CDROM_STATE_CYCLE)
478 		    {
479 		        debug_printf(1, "cdrom_atend: cycling program\n");
480 			cdi.curtrack = program_resume();
481 			timer_fsecs = 0;
482 			cdi.duration = 0;
483 			timer_button_update();
484 
485 			(void) cdrom_play();
486 		    }
487 		    else
488 		    {
489 		        debug_printf(1, "cdrom_atend: all done\n");
490 		    	cdrom_reset();
491 		    }
492 		    return;
493 		}
494 	}
495 
496 	else if ((cdi.curtrack < cdi.maxtrack) && (cdi.curtrack != 0)) {
497 		debug_printf(1, "cdrom_atend: continuing\n");
498 		cdi.curtrack++;
499 	}
500 	else if (cdi.state & CDROM_STATE_CYCLE) {
501 		debug_printf(1, "cdrom_atend: cycling\n");
502 		cdi.curtrack = cdi.mintrack;
503 	}
504 	else {
505 		debug_printf(1, "cdrom_atend: all done\n");
506 		buttons_reset();
507 		cdrom_reset();
508 		return;
509 	}
510 
511 	timer_fsecs = 0;
512 	cdi.duration = 0;
513 	timer_button_update();
514 
515 	(void) cdrom_play();
516 }
517 
518 /*
519  * scan_update is called when the scan timeout fires; it updates the timer
520  * and calls the label update routine.
521  */
522 /*ARGSUSED*/
523 static void
scan_update(data,id)524 scan_update(data, id)
525 	XtPointer	*data;
526 	XtIntervalId	*id;
527 {
528 	unsigned int	curtrack;
529 	Arg	args[1];
530 	Boolean	state;
531 
532 	extern Widget	rew_button_widget;
533 	extern Widget	ff_button_widget;
534 
535 	if ((curtrack = cdrom_get_curtrack()) != cdi.curtrack) {
536 		if (curtrack == 0) {
537 			cdrom_atend();
538 
539 			return;
540 		}
541 
542 		timer_fsecs = 0;
543 		cdi.duration = 0;
544 		timer_button_update();
545 
546 		cdi.curtrack = curtrack;
547 		track_button_update();
548 	}
549 
550 
551 	XtSetArg(args[0], XtNstate, &state);
552 	XtGetValues(rew_button_widget, args, 1);
553 	if (state == True)
554 	{
555 		cdrom_rewind();
556 		leds_update(BACKWARDS);
557 		if (cdi.state & CDROM_STATE_PAUSE) {
558 			if (cdrom_pause() != -1)
559 				 cdi.state &= ~CDROM_STATE_STOP;
560 		}
561 	        ivid = XtAppAddTimeOut(appc,
562 			(int)(app_data.scanPauseInterval * 1000.0),
563 			scan_update, NULL);
564 	}
565 	else
566 	{
567 		leds_update(FORWARDS);
568 		XtSetArg(args[0], XtNstate, &state);
569 		XtGetValues(ff_button_widget, args, 1);
570 		if (state == True)
571 		{
572 			cdrom_ff();
573 			if (cdi.state & CDROM_STATE_PAUSE) {
574 				if (cdrom_pause() != -1)
575 					 cdi.state &= ~CDROM_STATE_STOP;
576 			}
577 
578 			ivid = XtAppAddTimeOut(appc,
579 				(int)(app_data.scanPauseInterval * 1000.0),
580 				scan_update, NULL);
581 		}
582 		else if (scanivid != -1) {
583 			XtRemoveTimeOut(scanivid);
584 			scanivid = -1;
585 		}
586 	}
587 }
588 
589 
590 /*
591  * update_status is called when the status timeout fires;  it maintains
592  * the disc status, and will detect when a new disc has been inserted.
593  */
594 /*ARGSUSED*/
595 void
update_status(data,id)596 update_status(data, id)
597 	XtPointer	*data;
598 	XtIntervalId	*id;
599 {
600 	if (cdi.state & CDROM_STATE_EJECTED) {
601 		if (cdrom_open() == -1) {
602 			debug_printf(1, "cdrom_open: cdrom not ready\n");
603 		} else {
604 			cdi.state &= ~CDROM_STATE_EJECTED;
605 			buttons_reset();
606 		}
607 	}
608 
609 	/* Reset timer */
610 	stativid = XtAppAddTimeOut(appc, 1000, update_status, NULL);
611 }
612 
613 /*
614  * update_track is called when the timeout fires; it updates curtrack and
615  * calls the label update routine.
616  */
617 /*ARGSUSED*/
618 static void
update_track(data,id)619 update_track(data, id)
620 	XtPointer	*data;
621 	XtIntervalId	*id;
622 {
623 	unsigned int	curtrack;
624 	Arg	args[1];
625 	Boolean	state;
626 
627 	extern Widget	rew_button_widget;
628 	extern Widget	ff_button_widget;
629 #ifdef sgi
630 
631 	int	vol;
632 #endif
633 
634 	if ((curtrack = cdrom_get_curtrack()) != cdi.curtrack) {
635 		if (curtrack == 0) {
636 			cdrom_atend();
637 
638 			return;
639 		}
640 
641 		timer_fsecs = 0;
642 		cdi.duration = 0;
643 		timer_button_update();
644 
645 		cdi.curtrack = curtrack;
646 		track_button_update();
647 	}
648 
649 #ifdef sgi
650 	/* Update the volume control */
651 	if ((vol = cdrom_get_volume()) != 0) {
652 		set_volume(vol);
653 	}
654 #endif
655 
656 	XtSetArg(args[0], XtNstate, &state);
657 	XtGetValues(rew_button_widget, args, 1);
658 	if (state == True)
659 	{
660 		cdrom_rewind();
661 		leds_update(BACKWARDS);
662 		if (cdi.state & CDROM_STATE_PAUSE) {
663 			if (cdrom_pause() != -1)
664 				 cdi.state &= ~CDROM_STATE_STOP;
665 		}
666 	        ivid = XtAppAddTimeOut(appc,
667 			(int)(app_data.scanPauseInterval * 1000.0),
668 			update_track, NULL);
669 	}
670 	else
671 	{
672 		leds_update(FORWARDS);
673 		XtSetArg(args[0], XtNstate, &state);
674 		XtGetValues(ff_button_widget, args, 1);
675 		if (state == True)
676 		{
677 			cdrom_ff();
678 			if (cdi.state & CDROM_STATE_PAUSE) {
679 				if (cdrom_pause() != -1)
680 					 cdi.state &= ~CDROM_STATE_STOP;
681 			}
682 
683 			ivid = XtAppAddTimeOut(appc,
684 				(int)(app_data.scanPauseInterval * 1000.0),
685 				update_track, NULL);
686 		}
687 		else
688 		{
689 			if ((timer_fsecs++ % timer_mod) == 0) {
690 				timer_button_update();
691 			}
692 			ivid = XtAppAddTimeOut(appc, TIMER_PERIOD,
693 				update_track, NULL);
694 		}
695 	}
696 }
697 
698