1 /*
2 * cda - Command-line CD Audio Player/Ripper
3 *
4 * Copyright (C) 1993-2004 Ti Kan
5 * E-mail: xmcd@amb.org
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 *
21 */
22
23 /*
24 * Visual mode support
25 *
26 * Contributing author: Philip Le Riche
27 * E-Mail: pleriche@uk03.bull.co.uk
28 */
29
30 #ifndef lint
31 static char *_visual_c_ident_ = "@(#)visual.c 6.125 04/04/05";
32 #endif
33
34 #include "common_d/appenv.h"
35
36 #ifndef NOVISUAL
37
38 #include "common_d/util.h"
39 #include "common_d/version.h"
40 #include "libdi_d/libdi.h"
41 #include "cdinfo_d/cdinfo.h"
42
43 /* TRUE and FALSE is redefined in curses.h. */
44 #undef TRUE
45 #undef FALSE
46
47 /* curses.h redefines SYSV - handle with care */
48 #ifdef SYSV
49 #undef SYSV
50 #include <curses.h>
51 #define SYSV
52 #else
53 #if defined(_ULTRIX)
54 #include <cursesX.h>
55 #else
56 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__bsdi__)
57 #include <ncurses.h>
58 #else
59 #include <curses.h>
60 #endif /* __FreeBSD__ __NetBSD__ __bsdi__ */
61 #endif /* ultrix */
62 #endif /* SYSV */
63
64 #ifdef _SCO_SVR3
65 #include <sys/stream.h>
66 #include <sys/ptem.h>
67 #endif
68
69 #ifndef __QNX__
70 #include <term.h>
71 #endif
72
73 #include "cda_d/cda.h"
74 #include "cda_d/visual.h"
75
76 extern appdata_t app_data;
77 extern cdinfo_incore_t *dbp;
78 extern char *emsgp,
79 **cda_devlist,
80 errmsg[],
81 spipe[],
82 rpipe[];
83 extern int cda_sfd[],
84 cda_rfd[],
85 exit_status;
86 extern pid_t daemon_pid;
87
88 STATIC int scr_lines = 0, /* number of screen lines */
89 scr_cols = 0, /* number of screen cols */
90 scroll_line, /* 1st line of info window */
91 scroll_length, /* # of scrollable lines */
92 route, /* Stereo, Mono ... */
93 old_route = 1,
94 track = -2, /* Current track no. */
95 savch; /* Saved ch for cda_ungetch */
96 STATIC unsigned int jc_alarm = 0; /* Saved alarm for job ctl */
97 STATIC bool_t isvisual = FALSE, /* Currently in visual mode */
98 stat_on = FALSE, /* Visual: cda is "on" */
99 ostat_on = TRUE, /* Previous value */
100 refresh_fkeys = TRUE, /* Refresh funct key labels */
101 help = FALSE, /* Display help in info_win? */
102 old_help = TRUE, /* Previous value */
103 refresh_sts = FALSE, /* Refresh status line */
104 win_resize = FALSE, /* Window resize */
105 echflag = FALSE, /* Own echo flag */
106 savch_echo = FALSE, /* Control echo for savch */
107 loaddb = TRUE; /* Load CD information */
108 STATIC word32_t oastat0 = (word32_t)-1, /* Previous status value */
109 oastat1 = (word32_t)-1;
110 STATIC WINDOW *info_win, /* Scrolling window for info */
111 *status_win; /* Window for status */
112
113
114 /* Fatal error macro for visual mode */
115 #define CDA_V_FATAL(msg, code) { \
116 emsgp = (msg); \
117 exit_status = (code); \
118 cda_quit(curstat_addr()); \
119 }
120
121 /* Keyboard input to command function mapping table */
122 typedef struct {
123 int key;
124 void (*keyfunc)(int, word32_t[]);
125 } keytbl_t;
126
127
128 /***********************
129 * internal routines *
130 ***********************/
131
132
133 #if defined(SIGTSTP) && defined(SIGCONT)
134 /*
135 * ontstp
136 * Handler for job control stop signal
137 *
138 * Args:
139 * signo - The signal number
140 *
141 * Return:
142 * Nothing.
143 */
144 /*ARGSUSED*/
145 STATIC void
ontstp(int signo)146 ontstp(int signo)
147 {
148 /* Cancel alarms and save state */
149 jc_alarm = alarm(0);
150
151 /* Put screen in sane state */
152 (void) keypad(stdscr, FALSE);
153 (void) putp(cursor_normal);
154 (void) endwin();
155
156 /* Now stop the process */
157 (void) util_signal(SIGTSTP, SIG_DFL);
158 (void) kill((pid_t) getpid(), SIGTSTP);
159 }
160
161
162 /*
163 * oncont
164 * Handler for job control continue signal
165 *
166 * Args:
167 * signo - The signal number
168 *
169 * Return:
170 * Nothing.
171 */
172 /*ARGSUSED*/
173 STATIC void
oncont(int signo)174 oncont(int signo)
175 {
176 /* Use the window resize routine to restore visual attributes */
177 win_resize = TRUE;
178
179 if (jc_alarm != 0) {
180 /* Get back into the loop */
181 (void) kill(getpid(), SIGALRM);
182 }
183 }
184 #endif /* SIGTSTP SIGCONT */
185
186
187 #ifdef SIGWINCH
188 /*
189 * onwinch
190 * Handler for window size change signal
191 *
192 * Args:
193 * signo - The signal number
194 *
195 * Return:
196 * Nothing.
197 */
198 /*ARGSUSED*/
199 STATIC void
onwinch(int signo)200 onwinch(int signo)
201 {
202 win_resize = TRUE;
203 }
204 #endif
205
206
207 /*
208 * cda_makewins
209 * Create the main window areas on the display.
210 *
211 * Args:
212 * None.
213 *
214 * Return:
215 * Nothing. Program exits on error.
216 */
217 STATIC void
cda_makewins(void)218 cda_makewins(void)
219 {
220 WINDOW *new_info_win,
221 *new_status_win;
222 #ifdef TIOCGWINSZ
223 int fd;
224 struct winsize ws;
225 #endif
226
227 if (!isvisual) {
228 /* Startup */
229 if (initscr() == NULL)
230 CDA_FATAL("cda visual Error: Cannot initialize curses");
231
232 isvisual = TRUE;
233 }
234 else {
235 /* Re-initialize due to resize */
236 (void) endwin();
237 #ifdef __hpux
238 if (initscr() == NULL)
239 #else
240 if (newterm(NULL, stdout, stdin) == NULL)
241 #endif
242 {
243 CDA_FATAL("cda visual Error: Cannot re-initialize curses");
244 }
245 }
246
247 scr_cols = scr_lines = 0;
248
249 #ifdef TIOCGWINSZ
250 fd = fileno(stdout);
251 if (isatty(fd) && ioctl(fd, TIOCGWINSZ, &ws) >= 0) {
252 scr_lines = (int) ws.ws_row;
253 scr_cols = (int) ws.ws_col;
254 }
255 #endif /* TIOCGWINSZ */
256
257 if (scr_cols == 0 || scr_lines == 0) {
258 #ifdef getmaxyx
259 getmaxyx(stdscr, scr_lines, scr_cols);
260 #else
261 scr_lines = LINES;
262 scr_cols = COLS;
263 #endif
264 }
265
266 if (scr_lines < 9 || scr_cols < 40) {
267 CDA_V_FATAL(
268 "cda visual Error: "
269 "The terminal screen size is too small.\n"
270 "It must be at least 80 columns x 9 lines\n",
271 6
272 );
273 }
274
275 LINES = scr_lines;
276 COLS = scr_cols;
277
278 #if defined(NCURSES_VERSION_MAJOR) && (NCURSES_VERSION_MAJOR >= 4)
279 (void) resizeterm(LINES, COLS);
280 #endif
281
282 #ifdef SIGWINCH
283 /* Handle terminal screen size change */
284 (void) util_signal(SIGWINCH, onwinch);
285 #endif
286
287 #if defined(SIGTSTP) && defined(SIGCONT)
288 /* Handle job control */
289 (void) util_signal(SIGTSTP, ontstp);
290 (void) util_signal(SIGCONT, oncont);
291 #endif
292
293 (void) noecho();
294 (void) cbreak();
295 (void) clear();
296 (void) putp(cursor_invisible);
297 (void) scrollok(stdscr, FALSE);
298
299 new_info_win = newpad(132, scr_cols);
300 if (new_info_win == NULL) {
301 CDA_V_FATAL(
302 "cda visual Error: Cannot create information window.",
303 6
304 );
305 }
306
307 new_status_win = newwin(7, scr_cols, scr_lines-8, 0);
308 if (new_status_win == NULL) {
309 CDA_V_FATAL(
310 "cda visual Error: Cannot create status window.",
311 6
312 );
313 }
314
315 if (info_win != NULL)
316 (void) delwin(info_win);
317 if (status_win != NULL)
318 (void) delwin(status_win);
319
320 info_win = new_info_win;
321 status_win = new_status_win;
322
323 (void) keypad(info_win, TRUE);
324 (void) keypad(status_win, TRUE);
325
326 (void) wclear(info_win);
327 (void) wclear(status_win);
328 (void) wmove(status_win, 3, 0);
329 (void) waddstr(status_win, STATUS_LINE0);
330 (void) wmove(status_win, 4, 0);
331 (void) waddstr(status_win, STATUS_LINE1);
332 (void) wmove(status_win, 5, 0);
333 (void) waddstr(status_win, STATUS_LINE2);
334 (void) box(status_win, 0, 0);
335
336 (void) refresh();
337 }
338
339
340 /*
341 * cda_wgetch
342 * Own version of curses wgetch. This interworks with cda_ungetch.
343 *
344 * Args:
345 * None.
346 *
347 * Return:
348 * Input character or function key token.
349 */
350 STATIC int
cda_wgetch(WINDOW * win)351 cda_wgetch(WINDOW *win)
352 {
353 int ch;
354
355 if (savch) {
356 /* Echo character now if echo on but not yet echoed */
357 if (!savch_echo && echflag &&
358 isprint(savch) && !iscntrl(savch)) {
359 (void) waddch(win, savch);
360 (void) wrefresh(win);
361 }
362 ch = savch;
363 savch = 0;
364 return (ch);
365 }
366
367 ch = wgetch(win);
368
369 /* Do our own echoing because switching it on and off doesn't
370 * seem to work on some platforms.
371 */
372 if (echflag && isprint(ch) && !iscntrl(ch)) {
373 (void) waddch(win, ch);
374 (void) wrefresh(win);
375 }
376
377 return (ch);
378 }
379
380
381 /*
382 * cda_ungetch
383 * Own version of ungetch, because some systems don't have it.
384 * Also, we need to remember the echo status of the ungotten
385 * character.
386 *
387 * Args:
388 * Char or function key token to return.
389 *
390 * Return:
391 * Nothing.
392 */
393 STATIC void
cda_ungetch(int ch)394 cda_ungetch(int ch)
395 {
396 savch = ch;
397 /* Remember whether the character has been echoed */
398 savch_echo = echflag;
399 }
400
401
402 /*
403 * cda_wgetstr
404 * Own version of wgetstr, using cda_wgetch and cda_ungetch.
405 *
406 * Args:
407 * Buffer to be filled with input string.
408 *
409 * Return:
410 * Nothing.
411 */
412 STATIC void
cda_wgetstr(WINDOW * win,char * inbuf,int max)413 cda_wgetstr(WINDOW *win, char *inbuf, int max)
414 {
415 int ch,
416 n,
417 x,
418 y;
419 char *p;
420 bool_t eos = FALSE;
421
422 p = inbuf;
423 n = 0;
424
425 while (!eos) {
426 if (n > max) {
427 beep();
428 break;
429 }
430
431 ch = cda_wgetch(win);
432
433 switch (ch) {
434 case KEY_BACKSPACE:
435 case KEY_LEFT:
436 case '\010':
437 if (n > 0) {
438 p--;
439 n--;
440 /* Echo the effect of backspace */
441 getyx(win, y, x);
442 (void) wmove(win, y, x-1);
443 (void) waddch(win, ' ');
444 (void) wmove(win, y, x-1);
445 }
446 break;
447
448 case KEY_UP:
449 case '\036': /* ctrl-^ */
450 case KEY_DOWN:
451 case '\026': /* ctrl-v */
452 case KEY_PPAGE:
453 case '\002': /* ctrl-b */
454 case '\025': /* ctrl-u */
455 case KEY_NPAGE:
456 case '\006': /* ctrl-f */
457 case '\004': /* ctrl-d */
458 if (n > 0) {
459 n--;
460 getyx(win, y, x);
461 (void) wmove(win, y, x-1);
462 (void) waddch(win, ' ');
463 (void) wmove(win, y, x-1);
464 }
465
466 switch (ch) {
467 case KEY_UP:
468 case '\036':
469 scroll_line--;
470 break;
471 case KEY_DOWN:
472 case '\026':
473 scroll_line++;
474 break;
475 case KEY_PPAGE:
476 case '\002':
477 scroll_line -= (scr_lines - 9);
478 break;
479 case '\025':
480 scroll_line -= ((scr_lines - 9) / 2);
481 break;
482 case KEY_NPAGE:
483 case '\006':
484 scroll_line += (scr_lines - 9);
485 break;
486 case '\004':
487 scroll_line += ((scr_lines - 9) / 2);
488 break;
489 }
490
491 if (scroll_line < 0)
492 scroll_line = 0;
493 else if (scroll_line > (scroll_length - 1))
494 scroll_line = scroll_length - 1;
495
496 (void) prefresh(info_win, scroll_line, 0, 0, 0,
497 scr_lines - 9, scr_cols - 1);
498 break;
499
500 case '\n':
501 case '\r':
502 /* End-of-string */
503 eos = TRUE;
504 break;
505
506 default:
507 if (!isprint(ch) || iscntrl(ch))
508 beep();
509 else {
510 *p++ = (char) ch;
511 n++;
512 }
513
514 break;
515 }
516
517 (void) wrefresh(win);
518 }
519
520 *p = '\0';
521 }
522
523
524 /*
525 * cda_v_echo
526 * Own versions of curses echo function.
527 *
528 * Args:
529 * doecho - Whether to enable echo
530 *
531 * Return:
532 * Nothing.
533 *
534 */
535 STATIC void
cda_v_echo(bool_t doecho)536 cda_v_echo(bool_t doecho)
537 {
538 echflag = doecho;
539 }
540
541
542 /*
543 * cda_clrstatus
544 * Clear the status line and position cursor at the beginning.
545 *
546 * Args:
547 * None.
548 *
549 * Return:
550 * Nothing.
551 */
552 STATIC void
cda_clrstatus(void)553 cda_clrstatus(void)
554 {
555 int i;
556
557 (void) wmove(status_win, 1, 1);
558 for (i = 1; i < scr_cols-1; i++)
559 (void) waddch(status_win, ' ');
560 (void) wmove(status_win, 1, 1);
561 }
562
563
564 /*
565 * cda_v_errhandler
566 * Handler function used when the daemon has exited.
567 *
568 * Args:
569 * code - status code from cda_sendcmd
570 *
571 * Return:
572 * Nothing.
573 */
574 STATIC void
cda_v_errhandler(int code)575 cda_v_errhandler(int code)
576 {
577 if (code == CDA_OK || code == CDA_PARMERR)
578 /* Ignore these codes */
579 return;
580
581 /* Daemon is exiting or has exited */
582 cda_clrstatus();
583
584 if (code < 0 || code == CDA_DAEMON_EXIT) {
585 (void) wprintw(status_win,
586 "CD audio daemon pid=%d dev=%s exited.",
587 daemon_pid, app_data.device);
588
589 ostat_on = stat_on;
590 stat_on = FALSE;
591
592 if (cda_sfd[1] >= 0) {
593 (void) close(cda_sfd[1]);
594 (void) close(cda_rfd[1]);
595 (void) close(cda_rfd[0]);
596 cda_sfd[1] = cda_rfd[1] = cda_rfd[0] = -1;
597 }
598
599 loaddb = TRUE; /* Force reload of CDDB */
600 track = -2; /* Force redisplay of version info */
601 scroll_line = 0;
602 }
603 else
604 (void) wprintw(status_win, "%s", emsgp);
605
606 (void) wrefresh(status_win);
607 }
608
609
610 /*
611 * cda_v_match_select
612 * Ask the user to select from a list of fuzzy CDDB matches.
613 *
614 * Args:
615 * matchlist - List of fuzzy matches
616 *
617 * Return:
618 * User selection number, or 0 for no selection.
619 */
620 STATIC int
cda_v_match_select(cdinfo_match_t * matchlist)621 cda_v_match_select(cdinfo_match_t *matchlist)
622 {
623 int i,
624 n,
625 x;
626 cdinfo_match_t *p;
627 chset_conv_t *up;
628 char *p1,
629 *p2,
630 *q1,
631 *q2,
632 input[64];
633
634 (void) wclear(info_win);
635 (void) wmove(info_win, 0, 0);
636
637 for (p = matchlist, i = 0; p != NULL; p = p->next, i++)
638 ;
639
640 if (i == 1 && app_data.single_fuzzy) {
641 (void) wprintw(info_win, "\n\n%s\n%s\n\n",
642 "CDDB has found an inexact match. If this is not the",
643 "correct album, please submit corrections using xmcd.");
644 return 1;
645 }
646
647 /* Convert CD info from UTF-8 to local charset if possible */
648 if ((up = util_chset_open(CHSET_UTF8_TO_LOCALE)) == NULL)
649 return 0;
650
651 (void) wprintw(info_win, "\n%s\n%s\n\n",
652 "CDDB has found the following potential matches for the album",
653 "that you have. Please choose an appropriate entry:");
654
655 for (p = matchlist, i = 1; p != NULL; p = p->next, i++) {
656 q1 = (p->artist == NULL) ? "(unknown artist)" : p->artist;
657 q2 = (p->title == NULL) ? "(unknown title)" : p->title;
658 p1 = p2 = NULL;
659 if (!util_chset_conv(up, q1, &p1, FALSE) &&
660 !util_newstr(&p1, q1)) {
661 util_chset_close(up);
662 return 0;
663 }
664 if (!util_chset_conv(up, q2, &p2, FALSE) &&
665 !util_newstr(&p2, q2)) {
666 util_chset_close(up);
667 return 0;
668 }
669 (void) wprintw(info_win, " %2d. %.30s / %-40s\n", i,
670 p1 == NULL ? "" : p1,
671 p2 == NULL ? "" : p2);
672 if (p1 != NULL)
673 MEM_FREE(p1);
674 if (p2 != NULL)
675 MEM_FREE(p2);
676 }
677
678 (void) wprintw(info_win, " %2d. None of the above\n\n", i);
679
680 util_chset_close(up);
681
682 scroll_line = 0;
683 (void) prefresh(info_win, scroll_line, 0, 0, 0,
684 scr_lines - 9, scr_cols - 1);
685 getyx(info_win, scroll_length, x);
686 #ifdef lint
687 x = x; /* Get rid of "set but not used" lint warning */
688 #endif
689 --scroll_length;
690
691 for (;;) {
692 cda_clrstatus();
693 (void) wprintw(status_win, "Choose one (1-%d): ", i);
694 cda_v_echo(TRUE);
695 (void) putp(cursor_normal);
696 (void) wrefresh(status_win);
697 cda_wgetstr(status_win, input, 60);
698
699 n = atoi(input);
700 if (n > 0 && n <= i)
701 break;
702
703 beep();
704 }
705
706 cda_v_echo(FALSE);
707 (void) putp(cursor_invisible);
708
709 if (n == i)
710 return 0;
711
712 cda_clrstatus();
713 (void) wprintw(status_win, "Accessing CDDB...");
714 (void) wrefresh(status_win);
715 util_delayms(2000);
716 return (n);
717 }
718
719
720 /*
721 * cda_v_auth
722 * Ask the user to enter name and password for proxy authorization.
723 *
724 * Args:
725 * retrycnt - Number of times the user tried
726 *
727 * Return:
728 * 0 - failure
729 * 1 - success
730 */
731 STATIC int
cda_v_auth(int retrycnt)732 cda_v_auth(int retrycnt)
733 {
734 word32_t arg[CDA_NARGS];
735 char input[64];
736 size_t maxsz = (CDA_NARGS - 1) * sizeof(word32_t);
737 int x,
738 retcode;
739
740 (void) wclear(info_win);
741 (void) wmove(info_win, 0, 0);
742
743 if (retrycnt == 0)
744 (void) wprintw(info_win, "Proxy Authorization is required.\n");
745 else {
746 (void) wprintw(info_win, "Proxy Authorization failed.");
747
748 if (retrycnt < 3)
749 (void) wprintw(info_win, " Try again.\n");
750 else {
751 (void) wprintw(info_win, " Too many tries.\n");
752 scroll_line = 0;
753 (void) prefresh(info_win, scroll_line, 0, 0, 0,
754 scr_lines - 9, scr_cols - 1);
755 getyx(info_win, scroll_length, x);
756 #ifdef lint
757 x = x; /* Get rid of "set but not used" lint warning */
758 #endif
759 --scroll_length;
760 util_delayms(2000);
761 return 0;
762 }
763 }
764
765 (void) wprintw(info_win, "%s\n\n",
766 "Please enter your proxy user name and password.");
767
768 scroll_line = 0;
769 (void) prefresh(info_win, scroll_line, 0, 0, 0,
770 scr_lines - 9, scr_cols - 1);
771 getyx(info_win, scroll_length, x);
772 #ifdef lint
773 x = x; /* Get rid of "set but not used" lint warning */
774 #endif
775 --scroll_length;
776
777 /* Get user name */
778 input[0] = '\0';
779 cda_clrstatus();
780 (void) wprintw(status_win, "Username: ");
781 cda_v_echo(TRUE);
782 (void) putp(cursor_normal);
783 (void) wrefresh(status_win);
784 cda_wgetstr(status_win, input, 60);
785 if (input[0] == '\0')
786 return 0;
787
788 if (!util_newstr(&dbp->proxy_user, input))
789 CDA_V_FATAL(app_data.str_nomemory, 6);
790
791 /* Get password */
792 input[0] = '\0';
793 cda_clrstatus();
794 (void) wprintw(status_win, "Password: ");
795 cda_v_echo(FALSE);
796 (void) wrefresh(status_win);
797 cda_wgetstr(status_win, input, 60);
798
799 if (!util_newstr(&dbp->proxy_passwd, input))
800 CDA_V_FATAL(app_data.str_nomemory, 6);
801
802 /* Pass the proxy user name and password to the daemon */
803
804 (void) memset(arg, 0, CDA_NARGS * sizeof(word32_t));
805 (void) strcpy((char *) &arg[0], "user");
806 (void) strncpy((char *) &arg[1], dbp->proxy_user, maxsz - 1);
807 if (!cda_sendcmd(CDA_AUTHUSER, arg, &retcode)) {
808 (void) wprintw(status_win, "%s:\n%s\n"
809 "Failed sending proxy user name to the cda daemon.",
810 emsgp
811 );
812 util_delayms(2000);
813 }
814
815 (void) memset(arg, 0, CDA_NARGS * sizeof(word32_t));
816 (void) strcpy((char *) &arg[0], "pass");
817 (void) strncpy((char *) &arg[1], dbp->proxy_passwd, maxsz - 1);
818 if (!cda_sendcmd(CDA_AUTHPASS, arg, &retcode)) {
819 (void) wprintw(status_win, "%s:\n%s\n"
820 "Failed sending proxy password to the cda daemon.",
821 emsgp
822 );
823 util_delayms(2000);
824 }
825
826 return 1;
827 }
828
829
830 /*
831 * cda_screen
832 * Paints the screen in visual mode, geting status and extinfo
833 * as required.
834 *
835 * Args:
836 * None.
837 *
838 * Return:
839 * Nothing.
840 */
841 /*ARGSUSED*/
842 STATIC void
cda_screen(int signo)843 cda_screen(int signo)
844 {
845 word32_t arg[CDA_NARGS],
846 discid,
847 astat0 = 0,
848 astat1 = 0;
849 int x,
850 y,
851 i,
852 retcode,
853 vol,
854 bal,
855 rptcnt;
856 unsigned int discnum = 0,
857 trk,
858 idx,
859 min,
860 sec;
861 char *p,
862 *p1,
863 *p2,
864 *q1,
865 *q2,
866 *ctrlver;
867 curstat_t *s = curstat_addr();
868 bool_t playing;
869 struct stat stbuf;
870 static unsigned int odiscnum;
871 static chset_conv_t *up = NULL;
872
873 /* Convert CD info from UTF-8 to local charset if possible */
874 if (up == NULL && (up = util_chset_open(CHSET_UTF8_TO_LOCALE)) == NULL)
875 return;
876
877 /* Window resize event? */
878 if (win_resize) {
879 win_resize = FALSE;
880
881 /* Re-init curses */
882 cda_makewins();
883
884 /* Force a repaint */
885 wclear(info_win);
886 wclear(status_win);
887 oastat0 = oastat1 = (word32_t) -1;
888 ostat_on = !stat_on;
889 old_help = !help;
890 old_route = !route;
891 refresh_fkeys = TRUE;
892 }
893
894 /* Need to refresh function key labels? */
895 if (refresh_fkeys) {
896 refresh_fkeys = FALSE;
897 (void) wmove(status_win, 3, 0);
898 (void) waddstr(status_win, STATUS_LINE0);
899 (void) wmove(status_win, 4, 0);
900 (void) waddstr(status_win, STATUS_LINE1);
901 (void) wmove(status_win, 5, 0);
902 (void) waddstr(status_win, STATUS_LINE2);
903 (void) wrefresh(status_win);
904
905 (void) box(status_win, 0, 0);
906 }
907
908 /* If daemon running, get status and update display */
909 switch ((int) stat_on) {
910 case FALSE:
911 /* Daemon not running - just update display to "off" */
912 if (stat_on != ostat_on) {
913 (void) wmove(status_win, ON_Y, ON_X);
914 (void) waddstr(status_win, "On");
915
916 (void) wmove(status_win, OFF_Y, OFF_X);
917 (void) wattron(status_win, A_STANDOUT);
918 (void) waddstr(status_win, "Off");
919 (void) wattroff(status_win, A_STANDOUT);
920
921 (void) wmove(status_win, LOAD_Y, LOAD_X);
922 (void) waddstr(status_win, "Load");
923
924 (void) wmove(status_win, EJECT_Y, EJECT_X);
925 (void) waddstr(status_win, "Eject");
926
927 (void) wmove(status_win, PLAY_Y, PLAY_X);
928 (void) waddstr(status_win, "Play");
929
930 (void) wmove(status_win, PAUSE_Y, PAUSE_X);
931 (void) waddstr(status_win, "Pause");
932
933 (void) wmove(status_win, STOP_Y, STOP_X);
934 (void) waddstr(status_win, "Stop");
935
936 (void) wmove(status_win, LOCK_Y, LOCK_X);
937 (void) waddstr(status_win, "Lock");
938
939 (void) wmove(status_win, UNLOCK_Y, UNLOCK_X);
940 (void) waddstr(status_win, "Unlock");
941
942 (void) wmove(status_win, SHUFFLE_Y, SHUFFLE_X);
943 (void) waddstr(status_win, "Shuffle");
944
945 (void) wmove(status_win, PROGRAM_Y, PROGRAM_X);
946 (void) waddstr(status_win, "Program");
947
948 (void) wmove(status_win, REPEAT_ON_Y, REPEAT_ON_X);
949 (void) waddstr(status_win, "On");
950
951 (void) wmove(status_win, REPEAT_OFF_Y, REPEAT_OFF_X);
952 (void) waddstr(status_win, "Off");
953 }
954
955 /* Check to see if daemon started */
956 if (cda_daemon_alive()) {
957 /* Let the mkfifo complete */
958 util_delayms(1000);
959
960 /* Daemon is alive: open FIFOs - command side */
961 if (cda_sfd[1] < 0)
962 cda_sfd[1] = open(spipe, O_WRONLY
963 #ifdef O_NOFOLLOW
964 | O_NOFOLLOW
965 #endif
966 );
967
968 if (cda_sfd[1] >= 0) {
969 stat_on = TRUE;
970
971 cda_rfd[1] = open(rpipe, O_RDONLY
972 #ifdef O_NOFOLLOW
973 | O_NOFOLLOW
974 #endif
975 );
976 if (cda_rfd[1] < 0) {
977 CDA_V_FATAL(
978 "Cannot open recv pipe for reading", 6
979 );
980 }
981 cda_rfd[0] = open(rpipe, O_WRONLY
982 #ifdef O_NOFOLLOW
983 | O_NOFOLLOW
984 #endif
985 );
986 if (cda_rfd[0] < 0) {
987 CDA_V_FATAL(
988 "Cannot open recv pipe for writing", 6
989 );
990 }
991 }
992 else {
993 CDA_V_FATAL(
994 "Cannot open send pipe for writing", 6
995 );
996 }
997
998 /* Check FIFOs */
999 if (fstat(cda_sfd[1], &stbuf) < 0 ||
1000 !S_ISFIFO(stbuf.st_mode))
1001 CDA_V_FATAL("Send pipe error: Not a FIFO", 6);
1002
1003 if (fstat(cda_rfd[1], &stbuf) < 0 ||
1004 !S_ISFIFO(stbuf.st_mode))
1005 CDA_V_FATAL("Recv pipe error: Not a FIFO", 6);
1006
1007 if (fstat(cda_rfd[0], &stbuf) < 0 ||
1008 !S_ISFIFO(stbuf.st_mode))
1009 CDA_V_FATAL("Recv pipe error: Not a FIFO", 6);
1010 }
1011
1012 if (!stat_on)
1013 break;
1014
1015 arg[0] = 0;
1016 if (!cda_sendcmd(CDA_ON, arg, &retcode))
1017 cda_v_errhandler(retcode);
1018 else {
1019 daemon_pid = (pid_t) arg[0];
1020 cda_clrstatus();
1021 (void) wprintw(status_win,
1022 "CD audio daemon pid=%d dev=%s started.",
1023 daemon_pid, app_data.device);
1024 (void) wrefresh(status_win);
1025 }
1026
1027 /*FALLTHROUGH*/
1028
1029 case TRUE:
1030 /* Daemon running - get status and update display */
1031 (void) memset(arg, 0, CDA_NARGS * sizeof(word32_t));
1032 if (!cda_sendcmd(CDA_STATUS, arg, &retcode)) {
1033 cda_v_errhandler(retcode);
1034
1035 oastat0 = astat0;
1036 oastat1 = astat1;
1037
1038 /* Come back in 1 sec */
1039 (void) util_signal(SIGALRM, cda_screen);
1040 (void) alarm(1);
1041 return;
1042 }
1043
1044 (void) wmove(status_win, ON_Y, ON_X);
1045 (void) wattron(status_win, A_STANDOUT);
1046 (void) waddstr(status_win, "On");
1047 (void) wattroff(status_win, A_STANDOUT);
1048
1049 (void) wmove(status_win, OFF_Y, OFF_X);
1050 (void) waddstr(status_win, "Off");
1051
1052 astat0 = arg[0];
1053 astat1 = arg[1];
1054 rptcnt = (int) arg[2];
1055 vol = (int) arg[3];
1056 bal = (int) arg[4];
1057 route = (int) arg[5];
1058 discid = arg[6];
1059 discnum = (unsigned int) arg[7];
1060 s->cur_disc = arg[7];
1061
1062 switch (app_data.chg_method) {
1063 case CHG_SCSI_LUN:
1064 s->curdev = cda_devlist[s->cur_disc - 1];
1065 break;
1066 case CHG_SCSI_MEDCHG:
1067 case CHG_OS_IOCTL:
1068 case CHG_NONE:
1069 default:
1070 s->curdev = cda_devlist[0];
1071 break;
1072 }
1073
1074 if (RD_ARG_MODE(astat0) == MOD_NODISC || odiscnum != discnum)
1075 loaddb = TRUE;
1076
1077 if (discid != 0 && loaddb) {
1078 /* Load CD information */
1079 loaddb = FALSE;
1080 cda_clrstatus();
1081 (void) wprintw(status_win, "Accessing CDDB...");
1082 (void) wrefresh(status_win);
1083 util_delayms(2000);
1084
1085 cda_dbclear(s, TRUE);
1086 dbp->discid = discid;
1087 if (cda_dbload(dbp->discid, cda_v_match_select,
1088 cda_v_auth, 0))
1089 s->qmode = QMODE_MATCH;
1090 else
1091 s->qmode = QMODE_NONE;
1092
1093 cda_clrstatus();
1094 }
1095
1096 if (astat0 != oastat0 || astat1 != oastat1) {
1097 if (RD_ARG_MODE(astat0) == MOD_STOP &&
1098 RD_ARG_MODE(oastat0) != MOD_NODISC) {
1099 /* CD no longer busy or playing:
1100 * clear status line.
1101 */
1102 cda_clrstatus();
1103 refresh_sts = TRUE;
1104 }
1105
1106 switch (RD_ARG_MODE(astat0)) {
1107 case MOD_BUSY:
1108 case MOD_NODISC:
1109 (void) wmove(status_win, LOAD_Y, LOAD_X);
1110 (void) waddstr(status_win, "Load");
1111
1112 (void) wmove(status_win, EJECT_Y, EJECT_X);
1113 (void) wattron(status_win, A_STANDOUT);
1114 (void) waddstr(status_win, "Eject");
1115 (void) wattroff(status_win, A_STANDOUT);
1116
1117 (void) wmove(status_win, PLAY_Y, PLAY_X);
1118 (void) waddstr(status_win, "Play");
1119
1120 (void) wmove(status_win, PAUSE_Y, PAUSE_X);
1121 (void) waddstr(status_win, "Pause");
1122
1123 (void) wmove(status_win, STOP_Y, STOP_X);
1124 (void) wattron(status_win, A_STANDOUT);
1125 (void) waddstr(status_win, "Stop");
1126 (void) wattroff(status_win, A_STANDOUT);
1127
1128 break;
1129
1130 case MOD_STOP:
1131 (void) wmove(status_win, LOAD_Y, LOAD_X);
1132 (void) wattron(status_win, A_STANDOUT);
1133 (void) waddstr(status_win, "Load");
1134 (void) wattroff(status_win, A_STANDOUT);
1135
1136 (void) wmove(status_win, EJECT_Y, EJECT_X);
1137 (void) waddstr(status_win, "Eject");
1138
1139 (void) wmove(status_win, PLAY_Y, PLAY_X);
1140 (void) waddstr(status_win, "Play");
1141
1142 (void) wmove(status_win, PAUSE_Y, PAUSE_X);
1143 (void) waddstr(status_win, "Pause");
1144
1145 (void) wmove(status_win, STOP_Y, STOP_X);
1146 (void) wattron(status_win, A_STANDOUT);
1147 (void) waddstr(status_win, "Stop");
1148 (void) wattroff(status_win, A_STANDOUT);
1149
1150 break;
1151
1152 case MOD_PLAY:
1153 (void) wmove(status_win, LOAD_Y, LOAD_X);
1154 (void) wattron(status_win, A_STANDOUT);
1155 (void) waddstr(status_win, "Load");
1156 (void) wattroff(status_win, A_STANDOUT);
1157
1158 (void) wmove(status_win, EJECT_Y, EJECT_X);
1159 (void) waddstr(status_win, "Eject");
1160
1161 (void) wmove(status_win, PLAY_Y, PLAY_X);
1162 (void) wattron(status_win, A_STANDOUT);
1163 (void) waddstr(status_win, "Play");
1164 (void) wattroff(status_win, A_STANDOUT);
1165
1166 (void) wmove(status_win, PAUSE_Y, PAUSE_X);
1167 (void) waddstr(status_win, "Pause");
1168
1169 (void) wmove(status_win, STOP_Y, STOP_X);
1170 (void) waddstr(status_win, "Stop");
1171
1172 break;
1173
1174 case MOD_PAUSE:
1175 (void) wmove(status_win, LOAD_Y, LOAD_X);
1176 (void) wattron(status_win, A_STANDOUT);
1177 (void) waddstr(status_win, "Load");
1178 (void) wattroff(status_win, A_STANDOUT);
1179
1180 (void) wmove(status_win, EJECT_Y, EJECT_X);
1181 (void) waddstr(status_win, "Eject");
1182
1183 (void) wmove(status_win, PLAY_Y, PLAY_X);
1184 (void) waddstr(status_win, "Play");
1185
1186 (void) wmove(status_win, PAUSE_Y, PAUSE_X);
1187 (void) wattron(status_win, A_STANDOUT);
1188 (void) waddstr(status_win, "Pause");
1189 (void) wattroff(status_win, A_STANDOUT);
1190
1191 (void) wmove(status_win, STOP_Y, STOP_X);
1192 (void) waddstr(status_win, "Stop");
1193
1194 break;
1195 }
1196
1197 (void) wmove(status_win, LOCK_Y, LOCK_X);
1198 if (RD_ARG_LOCK(astat0))
1199 (void) wattron(status_win, A_STANDOUT);
1200 (void) waddstr(status_win, "Lock");
1201 (void) wattroff(status_win, A_STANDOUT);
1202
1203 (void) wmove(status_win, UNLOCK_Y, UNLOCK_X);
1204 if (!RD_ARG_LOCK(astat0))
1205 (void) wattron(status_win, A_STANDOUT);
1206 (void) waddstr(status_win, "Unlock");
1207 (void) wattroff(status_win, A_STANDOUT);
1208
1209 (void) wmove(status_win, SHUFFLE_Y, SHUFFLE_X);
1210 if (RD_ARG_SHUF(astat0))
1211 (void) wattron(status_win, A_STANDOUT);
1212 (void) waddstr(status_win, "Shuffle");
1213 (void) wattroff(status_win, A_STANDOUT);
1214
1215 (void) wmove(status_win, PROGRAM_Y, PROGRAM_X);
1216 if (stat_on &&
1217 RD_ARG_MODE(astat0) != MOD_BUSY &&
1218 RD_ARG_MODE(astat0) != MOD_NODISC &&
1219 !RD_ARG_SHUF(astat0)) {
1220 arg[0] = 1;
1221 if (!cda_sendcmd(CDA_PROGRAM, arg, &retcode)) {
1222 cda_v_errhandler(retcode);
1223
1224 oastat0 = astat0;
1225 oastat1 = astat1;
1226
1227 /* Come back in 1 sec */
1228 (void) util_signal(SIGALRM,
1229 cda_screen);
1230 (void) alarm(1);
1231 return;
1232 }
1233
1234 if (arg[0] != 0)
1235 wattron(status_win, A_STANDOUT);
1236 }
1237 (void) waddstr(status_win, "Program");
1238 (void) wattroff(status_win, A_STANDOUT);
1239
1240 (void) wmove(status_win, REPEAT_ON_Y, REPEAT_ON_X);
1241 if (RD_ARG_REPT(astat0))
1242 (void) wattron(status_win, A_STANDOUT);
1243 (void) waddstr(status_win, "On");
1244 (void) wattroff(status_win, A_STANDOUT);
1245
1246 (void) wmove(status_win, REPEAT_OFF_Y, REPEAT_OFF_X);
1247 if (!RD_ARG_REPT(astat0))
1248 (void) wattron(status_win, A_STANDOUT);
1249 (void) waddstr(status_win, "Off");
1250 (void) wattroff(status_win, A_STANDOUT);
1251 }
1252
1253 wmove(status_win, 1, 1);
1254 if (RD_ARG_MODE(astat0) == MOD_BUSY) {
1255 cda_clrstatus();
1256 (void) wprintw(status_win, "CD Busy");
1257 }
1258 else if (RD_ARG_MODE(astat0) == MOD_PLAY ||
1259 RD_ARG_MODE(astat0) == MOD_PAUSE ||
1260 RD_ARG_MODE(astat0) == MOD_NODISC) {
1261
1262 if (RD_ARG_MODE(astat0) == MOD_NODISC) {
1263 track = -2; /* Force redisplay of
1264 * version/device
1265 */
1266 (void) wprintw(status_win, "No Disc ");
1267 }
1268 else {
1269 trk = (unsigned int) RD_ARG_TRK(astat1);
1270 idx = (unsigned int) RD_ARG_IDX(astat1);
1271 min = (unsigned int) RD_ARG_MIN(astat1);
1272 sec = (unsigned int) RD_ARG_SEC(astat1);
1273
1274 if (idx == 0 && app_data.subq_lba) {
1275 /* In LBA mode the time information
1276 * isn't meaningful when in the
1277 * lead-in, so just set to zero.
1278 */
1279 min = sec = 0;
1280 }
1281
1282 (void) wprintw(status_win,
1283 "Disc %u Track %02u Index %02u "
1284 "%s%02u:%02u ",
1285 discnum, trk, idx,
1286 (idx == 0) ? "-" : "+", min, sec);
1287 }
1288
1289 (void) wprintw(status_win,
1290 "Volume %3u%% Balance %3u%% ", vol, bal);
1291
1292 switch (route) {
1293 case 0:
1294 (void) wprintw(status_win, "Stereo ");
1295 break;
1296 case 1:
1297 (void) wprintw(status_win, "Reverse ");
1298 break;
1299 case 2:
1300 (void) wprintw(status_win, "Mono-L ");
1301 break;
1302 case 3:
1303 (void) wprintw(status_win, "Mono-R ");
1304 break;
1305 case 4:
1306 (void) wprintw(status_win, "Mono-L+R ");
1307 break;
1308 }
1309
1310 if (rptcnt >= 0)
1311 (void) wprintw(status_win, "Count %u", rptcnt);
1312
1313 getyx(status_win, y, x);
1314 #ifdef lint
1315 y = y; /* Get rid of "set but not used" lint warning */
1316 #endif
1317 for (i = x; i < scr_cols-1; i++)
1318 (void) waddch(status_win, ' ');
1319
1320 (void) wmove(status_win, 1, 1);
1321 }
1322 else if (refresh_sts) {
1323 refresh_sts = FALSE;
1324 cda_clrstatus();
1325
1326 if (stat_on &&
1327 RD_ARG_MODE(astat0) != MOD_BUSY &&
1328 RD_ARG_MODE(astat0) != MOD_NODISC &&
1329 !RD_ARG_SHUF(astat0)) {
1330 arg[0] = 1;
1331 if (!cda_sendcmd(CDA_PROGRAM, arg, &retcode)) {
1332 cda_v_errhandler(retcode);
1333
1334 oastat0 = astat0;
1335 oastat1 = astat1;
1336
1337 /* Come back in 1 sec */
1338 (void) util_signal(SIGALRM,
1339 cda_screen);
1340 (void) alarm(1);
1341 return;
1342 }
1343
1344 if (arg[0] != 0) {
1345 (void) wprintw(status_win,
1346 "Program: ");
1347 for (i = 1; i <= arg[0]; i++) {
1348 (void) wprintw(status_win,
1349 " %02u", arg[i+1]);
1350 }
1351 }
1352 }
1353 }
1354 break;
1355 }
1356
1357 (void) wrefresh(status_win);
1358
1359 /* See if we want to display help info */
1360 if (help) {
1361 if (!old_help) {
1362 (void) wclear(info_win);
1363 (void) wmove(info_win, 0, 0);
1364 (void) wprintw(info_win, HELP_INFO);
1365 scroll_line = 0;
1366 (void) prefresh(info_win, scroll_line, 0, 0, 0,
1367 scr_lines - 9, scr_cols - 1);
1368 old_help = help;
1369 }
1370 odiscnum = discnum;
1371 (void) util_signal(SIGALRM, cda_screen);
1372 (void) alarm(1);
1373 return;
1374 }
1375 else if (old_help) {
1376 (void) wclear(info_win);
1377 scroll_line = 0;
1378 track = -2; /* Force display of version/device */
1379 }
1380
1381 /* Disc changed: redisplay */
1382 if (odiscnum != discnum) {
1383 (void) wclear(info_win);
1384 scroll_line = 0;
1385 track = -2;
1386 }
1387
1388 /* If state is unchanged since last time, no more to do */
1389 if (stat_on == ostat_on && old_help == help && old_route == route &&
1390 RD_ARG_MODE(astat0) == RD_ARG_MODE(oastat0) &&
1391 RD_ARG_TRK(astat1) == RD_ARG_TRK(oastat1) &&
1392 RD_ARG_IDX(astat1) == RD_ARG_IDX(oastat1) &&
1393 odiscnum == discnum) {
1394 oastat0 = astat0;
1395 oastat1 = astat1;
1396
1397 /* Call us again - nothing is too much trouble! */
1398 (void) util_signal(SIGALRM, cda_screen);
1399 (void) alarm(1);
1400 return;
1401 }
1402
1403 old_help = help;
1404 old_route = route;
1405 odiscnum = discnum;
1406 ostat_on = stat_on;
1407 oastat0 = astat0;
1408 oastat1 = astat1;
1409
1410 /* Now display data, according to our state: */
1411
1412 /* Off, busy or no disc, display version and device */
1413 if (!stat_on ||
1414 RD_ARG_MODE(astat0) == MOD_BUSY ||
1415 RD_ARG_MODE(astat0) == MOD_NODISC) {
1416 if (track != -1) {
1417 track = -1;
1418 (void) wclear(info_win);
1419 (void) wmove(info_win, 0,0);
1420 (void) wprintw(info_win,
1421 "CDA - Command Line CD Audio Player/Ripper");
1422 (void) wmove(info_win, 0, scr_cols-18);
1423 (void) wprintw(info_win, "Press ");
1424 (void) wattron(info_win, A_STANDOUT);
1425 (void) wprintw(info_win, "?");
1426 (void) wattroff(info_win, A_STANDOUT);
1427 (void) wprintw(info_win, " for help.\n\n");
1428
1429 (void) wprintw(info_win, "CD audio %s.%s.%s\n",
1430 VERSION_MAJ, VERSION_MIN, VERSION_TEENY);
1431
1432 switch ((int) stat_on) {
1433 case TRUE:
1434 if (cda_sendcmd(CDA_VERSION, arg, &retcode)) {
1435 (void) wprintw(info_win,
1436 "CD audio daemon %s\n",
1437 (char *) arg);
1438 break;
1439 }
1440 cda_v_errhandler(retcode);
1441 /*FALLTHROUGH*/
1442
1443 case FALSE:
1444 (void) sprintf((char *) arg, "%s.%s.%s\n%s\n",
1445 VERSION_MAJ, VERSION_MIN,
1446 VERSION_TEENY, di_methodstr());
1447 break;
1448 }
1449 (void) wprintw(info_win, "\nDevice: %s\n",
1450 app_data.device);
1451 if (stat_on) {
1452 if (!cda_sendcmd(CDA_DEVICE, arg, &retcode)) {
1453 cda_v_errhandler(retcode);
1454
1455 oastat0 = astat0;
1456 oastat1 = astat1;
1457
1458 /* Come back in 1 sec */
1459 (void) util_signal(SIGALRM,
1460 cda_screen);
1461 (void) alarm(1);
1462 return;
1463 }
1464 (void) wprintw(info_win, "%s", (char *) arg);
1465 }
1466 (void) wprintw(info_win,
1467 "\n%s\nURL: %s\nE-mail: %s\n\n%s\n\n",
1468 COPYRIGHT, XMCD_URL, EMAIL, GNU_BANNER);
1469
1470 ctrlver = cdinfo_cddbctrl_ver();
1471 (void) wprintw(info_win, "CDDB%s service%s%s\n%s\n",
1472 (cdinfo_cddb_ver() == 2) ?
1473 "\262" : " \"classic\"",
1474 (ctrlver[0] == '\0') ? "" : ": ",
1475 (ctrlver[0] == '\0') ? "\n" : ctrlver,
1476 CDDB_BANNER);
1477
1478 (void) prefresh(info_win, scroll_line, 0, 0, 0,
1479 scr_lines - 9, scr_cols - 1);
1480 getyx(info_win, scroll_length, i);
1481 --scroll_length;
1482 }
1483 }
1484 else if (track != RD_ARG_TRK(astat1)) {
1485 /* If disc loaded, display extinfo */
1486
1487 (void) wclear(info_win);
1488 (void) wmove(info_win, 0, 0);
1489
1490 /* Get CD information */
1491 if (RD_ARG_MODE(astat0) == MOD_PLAY ||
1492 RD_ARG_MODE(astat0) == MOD_PAUSE) {
1493 track = RD_ARG_TRK(astat1);
1494 }
1495 else
1496 track = -1;
1497
1498 arg[0] = 0;
1499 arg[1] = track;
1500 if (!cda_sendcmd(CDA_EXTINFO, arg, &retcode)) {
1501 cda_v_errhandler(retcode);
1502
1503 oastat0 = astat0;
1504 oastat1 = astat1;
1505
1506 /* Come back in 1 sec */
1507 (void) util_signal(SIGALRM, cda_screen);
1508 (void) alarm(1);
1509 return;
1510 }
1511
1512 if (s->qmode != QMODE_MATCH ||
1513 RD_ARG_MODE(astat0) == MOD_STOP) {
1514 int ntrks;
1515
1516 /* No CD information */
1517 arg[0] = 0;
1518 if (!cda_sendcmd(CDA_TOC, arg, &retcode)) {
1519 cda_v_errhandler(retcode);
1520
1521 oastat0 = astat0;
1522 oastat1 = astat1;
1523
1524 /* Come back in 1 sec */
1525 (void) util_signal(SIGALRM, cda_screen);
1526 (void) alarm(1);
1527 return;
1528 }
1529
1530 dbp->discid = arg[0];
1531 ntrks = (int) (arg[0] & 0xff);
1532
1533 q1 = (dbp->disc.genre == NULL) ?
1534 "(unknown genre)" :
1535 cdinfo_genre_name(dbp->disc.genre);
1536 p1 = NULL;
1537 if (!util_chset_conv(up, q1, &p1, FALSE) &&
1538 !util_newstr(&p1, q1)) {
1539 util_chset_close(up);
1540 return;
1541 }
1542 (void) wprintw(info_win, "Disc %u Genre: %s %s",
1543 discnum,
1544 p1 == NULL ? "" : p1,
1545 (dbp->disc.notes != NULL ||
1546 dbp->disc.credit_list != NULL) ?
1547 "*" : "");
1548 if (p1 != NULL)
1549 MEM_FREE(p1);
1550
1551 (void) wmove(info_win, 0, scr_cols-18);
1552 (void) wprintw(info_win, "Press ");
1553 (void) wattron(info_win, A_STANDOUT);
1554 (void) wprintw(info_win, "?");
1555 (void) wattroff(info_win, A_STANDOUT);
1556 (void) wprintw(info_win, " for help.\n\n");
1557
1558 q1 = (dbp->disc.artist == NULL) ?
1559 "" : dbp->disc.artist;
1560 q2 = (dbp->disc.title == NULL) ?
1561 "(unknown title)" : dbp->disc.title;
1562 p1 = p2 = NULL;
1563 if (!util_chset_conv(up, q1, &p1, FALSE) &&
1564 !util_newstr(&p1, q1)) {
1565 util_chset_close(up);
1566 return;
1567 }
1568 if (!util_chset_conv(up, q2, &p2, FALSE) &&
1569 !util_newstr(&p1, q1)) {
1570 util_chset_close(up);
1571 return;
1572 }
1573 (void) wprintw(info_win, "%s%s%s\n\n",
1574 p1 == NULL ? "" : p1,
1575 (dbp->disc.artist != NULL &&
1576 dbp->disc.title != NULL) ? " / " : "",
1577 p2 == NULL ? "" : p2);
1578 if (p1 != NULL)
1579 MEM_FREE(p1);
1580 MEM_FREE(p2);
1581
1582 for (i = 0; i < (int) ntrks; i++) {
1583 RD_ARG_TOC(arg[i+1], trk, playing, min, sec);
1584 (void) wprintw(info_win, "%s%02u %02u:%02u ",
1585 playing ? ">" : " ",
1586 trk, min, sec);
1587
1588 if (dbp->track[i].title != NULL) {
1589 q1 = dbp->track[i].title;
1590 p1 = NULL;
1591 if (!util_chset_conv(up, q1, &p1, FALSE) &&
1592 !util_newstr(&p1, q1)) {
1593 util_chset_close(up);
1594 return;
1595 }
1596 (void) wprintw(info_win, "%s%s\n",
1597 p1 == NULL ? "" : p1,
1598 (dbp->track[i].notes != NULL ||
1599 dbp->track[i].credit_list != NULL) ?
1600 "*" : "");
1601 if (p1 != NULL)
1602 MEM_FREE(p1);
1603 }
1604 else {
1605 (void) wprintw(info_win, "??%s\n",
1606 (dbp->track[i].notes != NULL ||
1607 dbp->track[i].credit_list != NULL) ?
1608 "*" : "");
1609 }
1610 }
1611
1612 RD_ARG_TOC(arg[i+1], trk, playing, min, sec);
1613 (void) wprintw(info_win, "\nTotal Time: %02u:%02u\n",
1614 min, sec);
1615 }
1616 else {
1617 /* Got CD information */
1618 if (dbp->disc.notes == NULL) {
1619 (void) wprintw(info_win,
1620 "No Disc Notes for this CD.\n");
1621 }
1622 else {
1623 q1 = (dbp->disc.artist == NULL) ?
1624 "" : dbp->disc.artist;
1625 q2 = (dbp->disc.title == NULL) ?
1626 "(unknown title)" : dbp->disc.title;
1627 p1 = p2 = NULL;
1628 if (!util_chset_conv(up, q1, &p1, FALSE) &&
1629 !util_newstr(&p1, q1)) {
1630 util_chset_close(up);
1631 return;
1632 }
1633 if (!util_chset_conv(up, q2, &p2, FALSE) &&
1634 !util_newstr(&p2, q2)) {
1635 util_chset_close(up);
1636 return;
1637 }
1638 (void) wprintw(info_win, "%s%s%s\n\n",
1639 p1 == NULL ? "" : p1,
1640 (dbp->disc.artist != NULL &&
1641 dbp->disc.title != NULL) ? " / " : "",
1642 p2 == NULL ? "" : p2);
1643
1644 q1 = dbp->disc.notes;
1645 p1 = NULL;
1646 if (!util_chset_conv(up, q1, &p1, FALSE) &&
1647 !util_newstr(&p1, q1)) {
1648 util_chset_close(up);
1649 return;
1650 }
1651 /* Not using wprintw here to avoid a bug
1652 * with very long strings
1653 */
1654 for (p = p1; p != NULL && *p != '\0'; p++)
1655 (void) waddch(info_win, *p);
1656 (void) waddch(info_win, '\n');
1657 if (p1 != NULL)
1658 MEM_FREE(p1);
1659 }
1660
1661 for (i = 0; i < scr_cols-1; i++)
1662 (void) waddch(info_win, ACS_HLINE);
1663 (void) waddch(info_win, '\n');
1664
1665 /* If Play or Pause, display track info */
1666 if (RD_ARG_MODE(astat0) == MOD_PLAY ||
1667 RD_ARG_MODE(astat0) == MOD_PAUSE) {
1668 if (dbp->track[arg[2]].title == NULL) {
1669 (void) wprintw(info_win,
1670 "(No title for track %02u.)\n",
1671 arg[1]);
1672 }
1673 else {
1674 q1 = dbp->track[arg[2]].title;
1675 p1 = NULL;
1676 if (!util_chset_conv(up, q1,
1677 &p1, FALSE) &&
1678 !util_newstr(&p1, q1)) {
1679 util_chset_close(up);
1680 return;
1681 }
1682 (void) wprintw(info_win, "%s\n",
1683 p1 == NULL ? "" : p1);
1684 if (p1 != NULL)
1685 MEM_FREE(p1);
1686
1687 if (dbp->track[arg[2]].notes != NULL) {
1688 q1 = dbp->track[arg[2]].notes;
1689 p1 = NULL;
1690 if (!util_chset_conv(up,
1691 q1, &p1, FALSE) &&
1692 !util_newstr(&p1, q1)) {
1693 util_chset_close(up);
1694 return;
1695 }
1696 /* Not using wprintw here
1697 * to avoid a bug with very
1698 * long strings
1699 */
1700 (void) waddch(info_win, '\n');
1701 for (p = p1;
1702 p != NULL && *p != '\0';
1703 p++)
1704 (void) waddch(info_win,
1705 *p);
1706 (void) waddch(info_win, '\n');
1707 if (p1 != NULL)
1708 MEM_FREE(p1);
1709 }
1710 }
1711 }
1712 }
1713 scroll_line = 0;
1714 getyx(info_win, scroll_length, i);
1715 (void) prefresh(info_win, scroll_line, 0, 0, 0,
1716 scr_lines - 9, scr_cols - 1);
1717 }
1718 oastat0 = astat0;
1719 oastat1 = astat1;
1720
1721 /* Come back in 1 sec */
1722 (void) util_signal(SIGALRM, cda_screen);
1723 (void) alarm(1);
1724 }
1725
1726
1727 /*
1728 * cda_v_on_off
1729 * Handler function for the visual mode on/off control.
1730 *
1731 * Args:
1732 * inp - command character
1733 * arg - command arguments
1734 *
1735 * Return:
1736 * Nothing.
1737 */
1738 /*ARGSUSED*/
1739 STATIC void
cda_v_on_off(int inp,word32_t arg[])1740 cda_v_on_off(int inp, word32_t arg[])
1741 {
1742 int retcode;
1743 curstat_t *s = curstat_addr();
1744
1745 cda_clrstatus();
1746
1747 if (!stat_on) {
1748 if (cda_daemon_alive()) {
1749 (void) wprintw(status_win,
1750 "CD audio daemon already running.");
1751 (void) wrefresh(status_win);
1752 beep();
1753 }
1754 else {
1755 /* Start CDA daemon */
1756 loaddb = TRUE;
1757 (void) wprintw(status_win, "Initializing...");
1758 (void) wrefresh(status_win);
1759
1760 if (!cda_daemon(s)) {
1761 cda_clrstatus();
1762 (void) wprintw(status_win,
1763 "Cannot start CD audio daemon.");
1764 (void) wrefresh(status_win);
1765 }
1766 }
1767 }
1768 else {
1769 if (cda_sendcmd(CDA_OFF, arg, &retcode)) {
1770 (void) wprintw(status_win,
1771 "Stopping CD audio daemon...");
1772 (void) wrefresh(status_win);
1773
1774 /* Wait for daemon to die */
1775 do {
1776 util_delayms(1000);
1777 } while (cda_daemon_alive());
1778 }
1779
1780 cda_v_errhandler(retcode);
1781 }
1782 }
1783
1784
1785 /*
1786 * cda_v_load_eject
1787 * Handler function for the visual mode load/eject control.
1788 *
1789 * Args:
1790 * inp - command character
1791 * arg - command arguments
1792 *
1793 * Return:
1794 * Nothing.
1795 */
1796 /*ARGSUSED*/
1797 STATIC void
cda_v_load_eject(int inp,word32_t arg[])1798 cda_v_load_eject(int inp, word32_t arg[])
1799 {
1800 int retcode;
1801
1802 if (!stat_on || RD_ARG_MODE(arg[0]) == MOD_BUSY) {
1803 beep();
1804 return;
1805 }
1806
1807 arg[0] = (RD_ARG_MODE(arg[0]) == MOD_NODISC) ? 0 : 1;
1808
1809 if (!cda_sendcmd(CDA_DISC, arg, &retcode))
1810 cda_v_errhandler(retcode);
1811
1812 loaddb = TRUE; /* Force reload of CDDB */
1813 refresh_sts = TRUE;
1814 }
1815
1816
1817 /*
1818 * cda_v_play_pause
1819 * Handler function for the visual mode play/pause control.
1820 *
1821 * Args:
1822 * inp - command character
1823 * arg - command arguments
1824 *
1825 * Return:
1826 * Nothing.
1827 */
1828 /*ARGSUSED*/
1829 STATIC void
cda_v_play_pause(int inp,word32_t arg[])1830 cda_v_play_pause(int inp, word32_t arg[])
1831 {
1832 word32_t cmd;
1833 int retcode;
1834
1835 if (!stat_on ||
1836 RD_ARG_MODE(arg[0]) == MOD_BUSY ||
1837 RD_ARG_MODE(arg[0]) == MOD_NODISC) {
1838 beep();
1839 return;
1840 }
1841
1842 if (RD_ARG_MODE(arg[0]) == MOD_PLAY)
1843 cmd = CDA_PAUSE;
1844 else
1845 cmd = CDA_PLAY;
1846
1847 arg[0] = 0;
1848 if (!cda_sendcmd(cmd, arg, &retcode))
1849 cda_v_errhandler(retcode);
1850 }
1851
1852
1853 /*
1854 * cda_v_stop
1855 * Handler function for the visual mode stop control.
1856 *
1857 * Args:
1858 * inp - command character
1859 * arg - command arguments
1860 *
1861 * Return:
1862 * Nothing.
1863 */
1864 /*ARGSUSED*/
1865 STATIC void
cda_v_stop(int inp,word32_t arg[])1866 cda_v_stop(int inp, word32_t arg[])
1867 {
1868 int retcode;
1869
1870 if (!stat_on ||
1871 RD_ARG_MODE(arg[0]) == MOD_BUSY ||
1872 RD_ARG_MODE(arg[0]) == MOD_NODISC) {
1873 beep();
1874 return;
1875 }
1876
1877 if (RD_ARG_MODE(arg[0]) != MOD_PLAY &&
1878 RD_ARG_MODE(arg[0]) != MOD_PAUSE) {
1879 beep();
1880 return;
1881 }
1882
1883 if (!cda_sendcmd(CDA_STOP, arg, &retcode))
1884 cda_v_errhandler(retcode);
1885
1886 refresh_sts = TRUE;
1887 }
1888
1889
1890 /*
1891 * cda_v_lock
1892 * Handler function for the visual mode lock/unlock control.
1893 *
1894 * Args:
1895 * inp - command character
1896 * arg - command arguments
1897 *
1898 * Return:
1899 * Nothing.
1900 */
1901 /*ARGSUSED*/
1902 STATIC void
cda_v_lock(int inp,word32_t arg[])1903 cda_v_lock(int inp, word32_t arg[])
1904 {
1905 int retcode;
1906
1907 if (!stat_on ||
1908 RD_ARG_MODE(arg[0]) == MOD_BUSY ||
1909 RD_ARG_MODE(arg[0]) == MOD_NODISC) {
1910 beep();
1911 return;
1912 }
1913
1914 arg[0] = RD_ARG_LOCK(arg[0]) ? 0 : 1;
1915
1916 if (!cda_sendcmd(CDA_LOCK, arg, &retcode))
1917 cda_v_errhandler(retcode);
1918 }
1919
1920
1921 /*
1922 * cda_v_shufprog
1923 * Handler function for the visual mode shuffle/program control.
1924 *
1925 * Args:
1926 * inp - command character
1927 * arg - command arguments
1928 *
1929 * Return:
1930 * Nothing.
1931 */
1932 /*ARGSUSED*/
1933 STATIC void
cda_v_shufprog(int inp,word32_t arg[])1934 cda_v_shufprog(int inp, word32_t arg[])
1935 {
1936 word32_t astat0 = arg[0];
1937 int i,
1938 j,
1939 retcode;
1940 char inbuf[80],
1941 *p;
1942
1943 if (!stat_on ||
1944 RD_ARG_MODE(astat0) == MOD_BUSY ||
1945 RD_ARG_MODE(astat0) == MOD_NODISC) {
1946 beep();
1947 return;
1948 }
1949
1950 /* Not allowed if play or pause */
1951 if (RD_ARG_MODE(astat0) == MOD_PLAY ||
1952 RD_ARG_MODE(astat0) == MOD_PAUSE) {
1953 beep();
1954 return;
1955 }
1956
1957 /* See if program on */
1958 arg[0] = 1;
1959 if (!cda_sendcmd(CDA_PROGRAM, arg, &retcode)) {
1960 cda_v_errhandler(retcode);
1961 return;
1962 }
1963
1964 /* If neither program nor shuffle, set shuffle */
1965 if (!RD_ARG_SHUF(astat0) && arg[0] == 0) {
1966 arg[0] = 1;
1967 if (!cda_sendcmd(CDA_SHUFFLE, arg, &retcode)) {
1968 cda_v_errhandler(retcode);
1969 return;
1970 }
1971 }
1972 else if (RD_ARG_SHUF(astat0)) {
1973 /* If shuffle, turn it off and prompt for program */
1974 arg[0] = 0;
1975 if (!cda_sendcmd(CDA_SHUFFLE, arg, &retcode)) {
1976 cda_v_errhandler(retcode);
1977 return;
1978 }
1979
1980 (void) wmove(status_win, SHUFFLE_Y, SHUFFLE_X);
1981 (void) waddstr(status_win, "Shuffle");
1982
1983 (void) wmove(status_win, PROGRAM_Y, PROGRAM_X);
1984 (void) wattron(status_win, A_STANDOUT);
1985 (void) waddstr(status_win, "Program");
1986 (void) wattroff(status_win, A_STANDOUT);
1987
1988 cda_clrstatus();
1989 (void) wprintw(status_win, "Program: ");
1990 cda_v_echo(TRUE);
1991 (void) putp(cursor_normal);
1992 (void) wrefresh(status_win);
1993
1994 /* Before reading the program list, check for
1995 * F6 or "u", and dismiss prog mode if found
1996 */
1997 i = cda_wgetch(status_win);
1998 if (i != KEY_F(6) && i != 'u') {
1999 cda_ungetch(i);
2000
2001 cda_wgetstr(status_win, inbuf, scr_cols-12);
2002
2003 /* Is the string just read was
2004 * terminated by F6, it will
2005 * have been ungotten, but must be
2006 * thrown away or it will cause
2007 * return to shuffle mode.
2008 */
2009 if (savch == KEY_F(6))
2010 savch = 0;
2011
2012 j = 0;
2013 p = inbuf;
2014 while ((i = strtol(p, &p, 10)) != 0) {
2015 arg[++j] = i;
2016
2017 if (p == NULL)
2018 break;
2019
2020 while (*p != '\0' && !isdigit((int) *p))
2021 p++;
2022 }
2023
2024 arg[0] = (word32_t) -j;
2025 if (!cda_sendcmd(CDA_PROGRAM, arg, &retcode)) {
2026 emsgp = NULL;
2027 beep();
2028 }
2029 }
2030
2031 cda_v_echo(FALSE);
2032 (void) putp(cursor_invisible);
2033
2034 refresh_sts = TRUE;
2035 }
2036 else {
2037 /* Program is on - reset it */
2038 arg[0] = 0;
2039 if (!cda_sendcmd(CDA_PROGRAM, arg, &retcode)) {
2040 cda_v_errhandler(retcode);
2041 return;
2042 }
2043 refresh_sts = TRUE;
2044 }
2045 }
2046
2047
2048 /*
2049 * cda_v_repeat
2050 * Handler function for the visual mode repeat control.
2051 *
2052 * Args:
2053 * inp - command character
2054 * arg - command arguments
2055 *
2056 * Return:
2057 * Nothing.
2058 */
2059 /*ARGSUSED*/
2060 STATIC void
cda_v_repeat(int inp,word32_t arg[])2061 cda_v_repeat(int inp, word32_t arg[])
2062 {
2063 int retcode;
2064
2065 if (!stat_on) {
2066 beep();
2067 return;
2068 }
2069
2070 arg[0] = RD_ARG_REPT(arg[0]) ? 0 : 1;
2071 if (!cda_sendcmd(CDA_REPEAT, arg, &retcode))
2072 cda_v_errhandler(retcode);
2073 }
2074
2075
2076 /*
2077 * cda_v_disc
2078 * Handler function for the visual mode disc change control.
2079 *
2080 * Args:
2081 * inp - command character
2082 * arg - command arguments
2083 *
2084 * Return:
2085 * Nothing.
2086 */
2087 STATIC void
cda_v_disc(int inp,word32_t arg[])2088 cda_v_disc(int inp, word32_t arg[])
2089 {
2090 int retcode;
2091
2092 if (!stat_on) {
2093 beep();
2094 return;
2095 }
2096
2097 arg[0] = (inp == 'd') ? 2 : 3;
2098 if (!cda_sendcmd(CDA_DISC, arg, &retcode))
2099 cda_v_errhandler(retcode);
2100
2101 loaddb = TRUE;
2102 }
2103
2104
2105 /*
2106 * cda_v_track
2107 * Handler function for the visual mode track change control.
2108 *
2109 * Args:
2110 * inp - command character
2111 * arg - command arguments
2112 *
2113 * Return:
2114 * Nothing.
2115 */
2116 STATIC void
cda_v_track(int inp,word32_t arg[])2117 cda_v_track(int inp, word32_t arg[])
2118 {
2119 int retcode;
2120
2121 if (!stat_on ||
2122 RD_ARG_MODE(arg[0]) == MOD_BUSY ||
2123 RD_ARG_MODE(arg[0]) == MOD_NODISC ||
2124 RD_ARG_MODE(arg[0]) != MOD_PLAY) {
2125 beep();
2126 return;
2127 }
2128
2129 arg[0] = (inp == KEY_LEFT || inp == 'C') ? 0 : 1;
2130 if (!cda_sendcmd(CDA_TRACK, arg, &retcode))
2131 cda_v_errhandler(retcode);
2132 }
2133
2134
2135 /*
2136 * cda_v_index
2137 * Handler function for the visual mode index change control.
2138 *
2139 * Args:
2140 * inp - command character
2141 * arg - command arguments
2142 *
2143 * Return:
2144 * Nothing.
2145 */
2146 STATIC void
cda_v_index(int inp,word32_t arg[])2147 cda_v_index(int inp, word32_t arg[])
2148 {
2149 int retcode;
2150
2151 if (!stat_on ||
2152 RD_ARG_MODE(arg[0]) == MOD_BUSY ||
2153 RD_ARG_MODE(arg[0]) == MOD_NODISC) {
2154 beep();
2155 return;
2156 }
2157
2158 arg[0] = (inp == '<') ? 0 : 1;
2159 if (!cda_sendcmd(CDA_INDEX, arg, &retcode))
2160 cda_v_errhandler(retcode);
2161 }
2162
2163
2164 /*
2165 * cda_v_volume
2166 * Handler function for the visual mode volume control.
2167 *
2168 * Args:
2169 * inp - command character
2170 * arg - command arguments
2171 *
2172 * Return:
2173 * Nothing.
2174 */
2175 STATIC void
cda_v_volume(int inp,word32_t arg[])2176 cda_v_volume(int inp, word32_t arg[])
2177 {
2178 int retcode;
2179
2180 if (!stat_on) {
2181 beep();
2182 return;
2183 }
2184
2185 arg[0] = 0;
2186 if (!cda_sendcmd(CDA_VOLUME, arg, &retcode)) {
2187 cda_v_errhandler(retcode);
2188 return;
2189 }
2190
2191 if (inp == '+') {
2192 if (arg[1] <= 95)
2193 arg[1] += 5;
2194 else
2195 arg[1] = 100;
2196 }
2197 else {
2198 if (arg[1] >= 5)
2199 arg[1] -= 5;
2200 else
2201 arg[1] = 0;
2202 }
2203
2204 arg[0] = 1;
2205 if (!cda_sendcmd(CDA_VOLUME, arg, &retcode)) {
2206 cda_v_errhandler(retcode);
2207 return;
2208 }
2209 }
2210
2211
2212 /*
2213 * cda_v_balance
2214 * Handler function for the visual mode balance control.
2215 *
2216 * Args:
2217 * inp - command character
2218 * arg - command arguments
2219 *
2220 * Return:
2221 * Nothing.
2222 */
2223 STATIC void
cda_v_balance(int inp,word32_t arg[])2224 cda_v_balance(int inp, word32_t arg[])
2225 {
2226 int retcode;
2227
2228 if (!stat_on) {
2229 beep();
2230 return;
2231 }
2232
2233 arg[0] = 0;
2234 if (!cda_sendcmd(CDA_BALANCE, arg, &retcode)) {
2235 cda_v_errhandler(retcode);
2236 return;
2237 }
2238
2239 if (inp == 'r' || inp == 'R') {
2240 if (arg[1] <= 95)
2241 arg[1] += 5;
2242 else
2243 arg[1] = 100;
2244 }
2245 else {
2246 if (arg[1] >= 5)
2247 arg[1] -= 5;
2248 else
2249 arg[1] = 0;
2250 }
2251
2252 arg[0] = 1;
2253 if (!cda_sendcmd(CDA_BALANCE, arg, &retcode))
2254 cda_v_errhandler(retcode);
2255 }
2256
2257
2258 /*
2259 * cda_v_route
2260 * Handler function for the visual mode channel routing control.
2261 *
2262 * Args:
2263 * inp - command character
2264 * arg - command arguments
2265 *
2266 * Return:
2267 * Nothing.
2268 */
2269 /*ARGSUSED*/
2270 STATIC void
cda_v_route(int inp,word32_t arg[])2271 cda_v_route(int inp, word32_t arg[])
2272 {
2273 int retcode;
2274
2275 if (!stat_on) {
2276 beep();
2277 return;
2278 }
2279
2280 if (--route < 0)
2281 route = 4;
2282
2283 arg[0] = 1;
2284 arg[1] = route;
2285 if (!cda_sendcmd(CDA_ROUTE, arg, &retcode))
2286 cda_v_errhandler(retcode);
2287 }
2288
2289
2290 /*
2291 * cda_v_help
2292 * Handler function for the visual mode help control.
2293 *
2294 * Args:
2295 * inp - command character
2296 * arg - command arguments
2297 *
2298 * Return:
2299 * Nothing.
2300 */
2301 /*ARGSUSED*/
2302 STATIC void
cda_v_help(int inp,word32_t arg[])2303 cda_v_help(int inp, word32_t arg[])
2304 {
2305 if (inp == '?') {
2306 help = TRUE;
2307 scroll_length = HELP_SCROLL_LENGTH;
2308 }
2309 else
2310 help = FALSE;
2311 }
2312
2313
2314 /*
2315 * cda_v_repaint
2316 * Handler function for the visual mode repaint control.
2317 *
2318 * Args:
2319 * inp - command character
2320 * arg - command arguments
2321 *
2322 * Return:
2323 * Nothing.
2324 */
2325 /*ARGSUSED*/
2326 STATIC void
cda_v_repaint(int inp,word32_t arg[])2327 cda_v_repaint(int inp, word32_t arg[])
2328 {
2329 wclear(info_win);
2330 wclear(status_win);
2331 oastat0 = oastat1 = (word32_t) -1;
2332 ostat_on = !stat_on;
2333 old_help = !help;
2334 old_route = !route;
2335 refresh_fkeys = TRUE;
2336 }
2337
2338
2339 /*
2340 * cda_v_scroll
2341 * Handler function for the visual mode scroll control.
2342 *
2343 * Args:
2344 * inp - command character
2345 * arg - command arguments
2346 *
2347 * Return:
2348 * Nothing.
2349 */
2350 /*ARGSUSED*/
2351 STATIC void
cda_v_scroll(int inp,word32_t arg[])2352 cda_v_scroll(int inp, word32_t arg[])
2353 {
2354 switch (inp) {
2355 case KEY_UP:
2356 case '^':
2357 case '\036': /* ctrl-^ */
2358 scroll_line--;
2359 break;
2360 case KEY_DOWN:
2361 case 'v':
2362 case 'V':
2363 case '\026': /* ctrl-v */
2364 scroll_line++;
2365 break;
2366 case KEY_PPAGE:
2367 case '\002': /* ctrl-b */
2368 scroll_line -= (scr_lines - 9);
2369 break;
2370 case '\025': /* ctrl-u */
2371 scroll_line -= ((scr_lines - 9) / 2);
2372 break;
2373 case KEY_NPAGE:
2374 case '\006': /* ctrl-f */
2375 scroll_line += (scr_lines - 9);
2376 break;
2377 case '\004': /* ctrl-d */
2378 scroll_line += ((scr_lines - 9) / 2);
2379 break;
2380 default:
2381 return;
2382 }
2383
2384 if (scroll_line < 0)
2385 scroll_line = 0;
2386 else if (scroll_line > (scroll_length - 1))
2387 scroll_line = scroll_length - 1;
2388
2389 (void) prefresh(info_win, scroll_line, 0, 0, 0,
2390 scr_lines - 9, scr_cols - 1);
2391 }
2392
2393
2394 /*
2395 * cda_v_debug
2396 * Handler function for the debug command - toggle debug mode.
2397 *
2398 * Args:
2399 * inp - command character
2400 * arg - command arguments
2401 *
2402 * Return:
2403 * Nothing.
2404 */
2405 /*ARGSUSED*/
2406 STATIC void
cda_v_debug(int inp,word32_t arg[])2407 cda_v_debug(int inp, word32_t arg[])
2408 {
2409 int retcode;
2410
2411 if (!stat_on) {
2412 beep();
2413 return;
2414 }
2415
2416 /* Query current debug mode */
2417 arg[0] = 0;
2418 if (!cda_sendcmd(CDA_DEBUG, arg, &retcode)) {
2419 beep();
2420 cda_v_errhandler(retcode);
2421 return;
2422 }
2423
2424 /* Toggle debug mode */
2425 arg[0] = 1;
2426 if (arg[1] == 0)
2427 arg[1] = 1;
2428 else
2429 arg[1] = 0;
2430
2431 if (!cda_sendcmd(CDA_DEBUG, arg, &retcode)) {
2432 beep();
2433 cda_v_errhandler(retcode);
2434 return;
2435 }
2436
2437 cda_clrstatus();
2438 (void) wprintw(status_win, "Debug mode is now %s.",
2439 (arg[1] == 0) ? "off" : "on");
2440 (void) wrefresh(status_win);
2441 }
2442
2443
2444 /*
2445 * cda_v_dirtrk
2446 * Handler function for the visual mode direct track access.
2447 *
2448 * Args:
2449 * inp - command character
2450 * arg - command arguments
2451 *
2452 * Return:
2453 * Nothing.
2454 */
2455 STATIC void
cda_v_dirtrk(int inp,word32_t arg[])2456 cda_v_dirtrk(int inp, word32_t arg[])
2457 {
2458 int i,
2459 mins,
2460 secs,
2461 retcode;
2462 char inbuf[80],
2463 *p;
2464
2465 if (!isdigit(inp))
2466 return;
2467
2468 if (!stat_on) {
2469 beep();
2470 return;
2471 }
2472
2473 cda_ungetch(inp);
2474
2475 cda_clrstatus();
2476 (void) wprintw(status_win, "Track n [mins secs] : ");
2477 cda_v_echo(TRUE);
2478 (void) putp(cursor_normal);
2479 (void) wrefresh(status_win);
2480
2481 cda_wgetstr(status_win, inbuf, 20);
2482 if ((i = strtol(inbuf, &p, 10)) == 0)
2483 beep();
2484 else {
2485 mins = secs = 0;
2486 if (p != NULL) {
2487 while (*p != '\0' && !isdigit((int) *p))
2488 p++;
2489
2490 mins = strtol(p, &p, 10);
2491
2492 if (p != NULL) {
2493 while (*p != '\0' && !isdigit((int) *p))
2494 p++;
2495
2496 secs = strtol(p, &p, 10);
2497 }
2498 }
2499
2500 arg[0] = i;
2501 arg[1] = mins;
2502 arg[2] = secs;
2503 if (!cda_sendcmd(CDA_PLAY, arg, &retcode))
2504 cda_v_errhandler(retcode);
2505 }
2506
2507 cda_v_echo(FALSE);
2508 (void) putp(cursor_invisible);
2509 refresh_sts = TRUE;
2510 }
2511
2512
2513 #ifdef KEY_RESIZE
2514 /*
2515 * cda_v_resize
2516 * Handler function when KEY_RESIZE is received. This is to support
2517 * ncurses library configured with its own SIGWINCH handler.
2518 *
2519 * Args:
2520 * inp - command character
2521 * arg - command arguments
2522 *
2523 * Return:
2524 * Nothing.
2525 */
2526 /*ARGSUSED*/
2527 STATIC void
cda_v_resize(int inp,word32_t arg[])2528 cda_v_resize(int inp, word32_t arg[])
2529 {
2530 win_resize = TRUE;
2531 }
2532 #endif
2533
2534
2535 /***********************
2536 * public routines *
2537 ***********************/
2538
2539
2540 /*
2541 * cda_vtidy
2542 * Tidy up and go home for visual mode.
2543 *
2544 * Args:
2545 * None.
2546 *
2547 * Return:
2548 * Nothing.
2549 */
2550 void
cda_vtidy(void)2551 cda_vtidy(void)
2552 {
2553 if (isvisual) {
2554 (void) keypad(stdscr, FALSE);
2555 (void) putp(cursor_normal);
2556 (void) endwin();
2557 isvisual = FALSE;
2558 }
2559
2560 (void) printf("%s\n", emsgp != NULL ? emsgp : "Goodbye!");
2561 }
2562
2563
2564 /* Keyboard input to command function mapping table */
2565 STATIC keytbl_t ktbl[] = {
2566 { KEY_F(1), cda_v_on_off },
2567 { 'o', cda_v_on_off },
2568 { 'O', cda_v_on_off },
2569 { KEY_F(2), cda_v_load_eject },
2570 { 'j', cda_v_load_eject },
2571 { 'J', cda_v_load_eject },
2572 { KEY_F(3), cda_v_play_pause },
2573 { 'p', cda_v_play_pause },
2574 { 'P', cda_v_play_pause },
2575 { KEY_F(4), cda_v_stop },
2576 { 's', cda_v_stop },
2577 { 'S', cda_v_stop },
2578 { KEY_F(5), cda_v_lock },
2579 { 'k', cda_v_lock },
2580 { 'K', cda_v_lock },
2581 { KEY_F(6), cda_v_shufprog },
2582 { 'u', cda_v_shufprog },
2583 { 'U', cda_v_shufprog },
2584 { KEY_F(7), cda_v_repeat },
2585 { 'e', cda_v_repeat },
2586 { 'E', cda_v_repeat },
2587 { 'd', cda_v_disc },
2588 { 'D', cda_v_disc },
2589 { KEY_LEFT, cda_v_track },
2590 { KEY_RIGHT, cda_v_track },
2591 { 'C', cda_v_track },
2592 { 'c', cda_v_track },
2593 { '<', cda_v_index },
2594 { '>', cda_v_index },
2595 { '+', cda_v_volume },
2596 { '-', cda_v_volume },
2597 { 'l', cda_v_balance },
2598 { 'L', cda_v_balance },
2599 { 'r', cda_v_balance },
2600 { 'R', cda_v_balance },
2601 { '\011', cda_v_route },
2602 { '?', cda_v_help },
2603 { ' ', cda_v_help },
2604 { '\014', cda_v_repaint },
2605 { '\022', cda_v_repaint },
2606 { KEY_UP, cda_v_scroll },
2607 { KEY_DOWN, cda_v_scroll },
2608 { '^', cda_v_scroll },
2609 { 'v', cda_v_scroll },
2610 { '\036', cda_v_scroll },
2611 { '\026', cda_v_scroll },
2612 { 'V', cda_v_scroll },
2613 { KEY_PPAGE, cda_v_scroll },
2614 { KEY_NPAGE, cda_v_scroll },
2615 { '\002', cda_v_scroll },
2616 { '\025', cda_v_scroll },
2617 { '\006', cda_v_scroll },
2618 { '\004', cda_v_scroll },
2619 { 'd', cda_v_debug },
2620 { '\0', cda_v_dirtrk },
2621 #ifdef KEY_RESIZE
2622 { KEY_RESIZE, cda_v_resize },
2623 #endif
2624 { '\0', NULL }
2625 };
2626
2627
2628 /*
2629 * cda_visual
2630 * Visual (curses mode) interface.
2631 *
2632 * Args:
2633 * None.
2634 *
2635 * Return:
2636 * Nothing.
2637 */
2638 void
cda_visual(void)2639 cda_visual(void)
2640 {
2641 word32_t arg[CDA_NARGS];
2642 int inp,
2643 i,
2644 retcode;
2645 curstat_t *s = curstat_addr();
2646
2647 /* Set up device list */
2648 cda_parse_devlist(s);
2649
2650 /* Initialize curses */
2651 cda_makewins();
2652
2653 /* Paint the screen every second */
2654 cda_screen(0);
2655
2656 /* Main processing loop */
2657 for (;;) {
2658 inp = cda_wgetch(status_win);
2659
2660 if (inp == KEY_F(8) || inp == 'q' || inp == 'Q')
2661 break;
2662
2663 /* Cancel alarm so we don't nest */
2664 (void) alarm(0);
2665
2666 /* Get current status */
2667 if (stat_on && !cda_sendcmd(CDA_STATUS, arg, &retcode))
2668 cda_v_errhandler(retcode);
2669
2670 /* Execute command function */
2671 for (i = 0; ktbl[i].keyfunc != NULL; i++) {
2672 if (ktbl[i].key == inp || ktbl[i].key == '\0') {
2673 (*ktbl[i].keyfunc)(inp, arg);
2674 break;
2675 }
2676 }
2677
2678 /* Repaint screen */
2679 cda_screen(0);
2680 }
2681
2682 /* Tidy up and go home */
2683 (void) alarm(0);
2684 emsgp = NULL;
2685
2686 if (cda_daemon_alive()) {
2687 (void) sprintf(errmsg, "CD audio daemon pid=%d still running.",
2688 (int) daemon_pid);
2689 emsgp = errmsg;
2690 }
2691
2692 exit_status = 0;
2693 cda_quit(s);
2694 /*NOTREACHED*/
2695 }
2696
2697
2698 #endif /* NOVISUAL */
2699
2700