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