home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / xmcd-1.4 / cda.d / visual.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-10  |  29.7 KB  |  1,539 lines

  1. /*
  2.  *   cda - Command-line CD Audio Player
  3.  *
  4.  *   Copyright (C) 1995  Ti Kan
  5.  *   E-mail: ti@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    5.14 95/02/06";
  32. #endif
  33.  
  34. #ifndef NOVISUAL
  35.  
  36. #define _CDA_
  37.  
  38. #include "common.d/appenv.h"
  39. #include "common.d/util.h"
  40. #include "common.d/patchlevel.h"
  41. #include "libdi.d/libdi.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) || defined(__ultrix)
  54. #include <cursesX.h>
  55. #else
  56. #ifdef __FreeBSD__
  57. #include <ncurses.h>
  58. #else
  59. #include <curses.h>
  60. #endif    /* __FreeBSD__ */
  61. #endif    /* ultrix */
  62. #endif    /* SYSV */
  63.  
  64. #include <term.h>
  65.  
  66. #include "cda.d/cda.h"
  67. #include "cda.d/visual.h"
  68.  
  69. extern appdata_t    app_data;
  70. extern database_t    cur_db;
  71. extern curstat_t    status;
  72. extern char        *errmsg,
  73.             emsg[],
  74.             spipe[],
  75.             rpipe[];
  76. extern int        cda_sfd[],
  77.             cda_rfd[];
  78.  
  79. STATIC int        scroll_line = 0,    /* 1st line of info window */
  80.             scroll_length,        /* Number of scrollable lines */
  81.             route = 0,        /* Stereo, Mono ... */
  82.             old_route = 1,
  83.             track = -2,        /* Current track no. */
  84.             savch = 0;        /* Saved char for cda_ungetch */
  85. STATIC bool_t        isvisual = FALSE,    /* Currently in visual mode */
  86.             stat_on = FALSE,    /* Visual: cda is "on" */
  87.             ostat_on = TRUE,    /* Previous value */
  88.             refresh_fkeys = TRUE,    /* Refresh funct key labels */
  89.             help = FALSE,        /* Display help in info_win? */
  90.             old_help = TRUE,    /* Previous value */
  91.             refresh_sts = TRUE,    /* Refresh status line */
  92.             echflag = FALSE,    /* Own echo flag */
  93.             savch_echo;        /* Control echo for savch */
  94. STATIC word32_t        oastat0 = (word32_t)-1,    /* Previous status value */
  95.             oastat1 = (word32_t)-1;
  96. STATIC WINDOW        *info_win,        /* Scrolling window for info */
  97.             *status_win;        /* Window for status */
  98.  
  99.  
  100. /***********************
  101.  *  internal routines  *
  102.  ***********************/
  103.  
  104.  
  105. /*
  106.  * cda_wgetch
  107.  *    Own version of curses wgetch. This interworks with cda_ungetch.
  108.  *
  109.  * Args:
  110.  *    None
  111.  *
  112.  * Return:
  113.  *    Input character or function key token.
  114.  */
  115. STATIC int
  116. cda_wgetch(WINDOW *win)
  117. {
  118.     int    ch;
  119.  
  120.     if (savch) {
  121.         /* Echo character now if echo on but not yet echoed */
  122.         if (!savch_echo && echflag &&
  123.             isprint(savch) && !iscntrl(savch)) {
  124.             waddch(win, savch);
  125.             wrefresh(win);
  126.         }
  127.         ch = savch;
  128.         savch = 0;
  129.         return (ch);
  130.     }
  131.  
  132.     ch = wgetch(win);
  133.  
  134.     /* Do our own echoing because switching it on and off doesn't
  135.      * seem to work on some platforms.
  136.      */
  137.     if (echflag && isprint(ch) && !iscntrl(ch)) {
  138.         waddch(win, ch);
  139.         wrefresh(win);
  140.     }
  141.  
  142.     return (ch);
  143. }
  144.  
  145.  
  146. /*
  147.  * cda_ungetch
  148.  *    Own version of ungetch, because some systems don't have it.
  149.  *    Also, we need to remember the echo status of the ungotten
  150.  *    character.
  151.  *
  152.  * Args:
  153.  *    Char or function key token to return.
  154.  *
  155.  * Return:
  156.  *    Nothing
  157.  */
  158. STATIC void
  159. cda_ungetch(int ch)
  160. {
  161.     savch = ch;
  162.     /* Remember whether the character has been echoed */
  163.     savch_echo = echflag;
  164. }
  165.  
  166.  
  167. /*
  168.  * cda_wgetstr
  169.  *    Own version of wgetstr, using cda_wgetch and cda_ungetch.
  170.  *
  171.  * Args:
  172.  *    Buffer to be filled with input string.
  173.  *
  174.  * Return:
  175.  *    Nothing.
  176.  */
  177. STATIC void
  178. cda_wgetstr(WINDOW *win, char *ipbuff, int max)
  179. {
  180.     int    ch,
  181.         n,
  182.         x,
  183.         y;
  184.     char    *p;
  185.     bool_t    eos = FALSE;
  186.  
  187.     p = ipbuff;
  188.     n = 0;
  189.  
  190.     while (!eos) {
  191.         if (n > max) {
  192.             beep();
  193.             break;
  194.         }
  195.  
  196.         ch = cda_wgetch(win);
  197.  
  198.         switch (ch) {
  199.         case KEY_BACKSPACE:
  200.         case KEY_LEFT:
  201.         case '\010':
  202.             if (n > 0) {
  203.                 p--;
  204.                 n--;
  205.  
  206.                 /* Echo the effect of backspace */
  207.                 getyx(win, y, x);
  208.                 wmove(win, y, x-1);
  209.                 waddch(win, ' ');
  210.                 wmove(win, y, x-1);
  211.             }
  212.             break;
  213.  
  214.         case KEY_DOWN:
  215.         case '\n':
  216.         case '\r':
  217.             /* End-of-string */
  218.             eos = TRUE;
  219.             break;
  220.  
  221.         default:
  222.             if (!isprint(ch) || iscntrl(ch))
  223.                 beep();
  224.             else {
  225.                 *p++ = (char) ch;
  226.                 n++;
  227.             }
  228.  
  229.             break;
  230.         }
  231.  
  232.         wrefresh(win);
  233.     }
  234.  
  235.     *p = '\0';
  236. }
  237.  
  238.  
  239. /*
  240.  * cda_echo
  241.  *    Own versions of curses echo function.
  242.  *
  243.  * Args: None
  244.  *
  245.  * Return: Nothing
  246.  *
  247.  */
  248. STATIC void
  249. cda_echo(void)
  250. {
  251.     echflag = TRUE;
  252. }
  253.  
  254.  
  255. /*
  256.  * cda_noecho
  257.  *    Own versions of curses noecho function.
  258.  *
  259.  * Args: None
  260.  *
  261.  * Return: Nothing
  262.  *
  263.  */
  264. STATIC void
  265. cda_noecho(void)
  266. {
  267.     echflag = FALSE;
  268. }
  269.  
  270.  
  271. /*
  272.  * cda_screen
  273.  *    Paints the screen in visual mode, geting status and extinfo
  274.  *    as required.
  275.  *
  276.  * Args:
  277.  *    None.
  278.  *
  279.  * Return:
  280.  *    void
  281.  */
  282. /*ARGSUSED*/
  283. STATIC void
  284. cda_screen(int signo)
  285. {
  286.     word32_t    cmd,
  287.             arg[CDA_NARGS],
  288.             astat0,
  289.             astat1;
  290.     int        x,
  291.             y,
  292.             i,
  293.             trkno,
  294.             ntrks,
  295.             min,
  296.             sec,
  297.             rptcnt;
  298.     bool_t        cddb = FALSE,
  299.             playing;
  300.     char        *p;
  301.  
  302.     /* Need to refresh function key labels? */
  303.     if (refresh_fkeys) {
  304.         refresh_fkeys = FALSE;
  305.         wmove(status_win, 3, 0);
  306.         waddstr(status_win, STATUS_LINE0);
  307.         wmove(status_win, 4, 0);
  308.         waddstr(status_win, STATUS_LINE1);
  309.         wmove(status_win, 5, 0);
  310.         waddstr(status_win, STATUS_LINE2);
  311.         wrefresh(status_win);
  312.  
  313.         box(status_win, 0, 0);
  314.     }
  315.  
  316.     /* If daemon running, get status and update display */
  317.     if (!stat_on) {
  318.         /* Daemon not running - just update display to "off" */
  319.         if (stat_on != ostat_on) {
  320.             wmove(status_win, ON_Y, ON_X);
  321.             waddstr(status_win, "On");
  322.  
  323.             wmove(status_win, OFF_Y, OFF_X);
  324.             wattron(status_win, A_STANDOUT);
  325.             waddstr(status_win, "Off");
  326.             wattroff(status_win, A_STANDOUT);
  327.  
  328.             wmove(status_win, LOAD_Y, LOAD_X);
  329.             waddstr(status_win, "Load");
  330.  
  331.             wmove(status_win, EJECT_Y, EJECT_X);
  332.             waddstr(status_win, "Eject");
  333.  
  334.             wmove(status_win, PLAY_Y, PLAY_X);
  335.             waddstr(status_win, "Play");
  336.  
  337.             wmove(status_win, PAUSE_Y, PAUSE_X);
  338.             waddstr(status_win, "Pause");
  339.  
  340.             wmove(status_win, STOP_Y, STOP_X);
  341.             waddstr(status_win, "Stop");
  342.  
  343.             wmove(status_win, LOCK_Y, LOCK_X);
  344.             waddstr(status_win, "Lock");
  345.  
  346.             wmove(status_win, UNLOCK_Y, UNLOCK_X);
  347.             waddstr(status_win, "Unlock");
  348.  
  349.             wmove(status_win, SHUFFLE_Y, SHUFFLE_X);
  350.             waddstr(status_win, "Shuffle");
  351.  
  352.             wmove(status_win, PROGRAM_Y, PROGRAM_X);
  353.             waddstr(status_win, "Program");
  354.  
  355.             wmove(status_win, REPEAT_ON_Y, REPEAT_ON_X);
  356.             waddstr(status_win, "On");
  357.  
  358.             wmove(status_win, REPEAT_OFF_Y, REPEAT_OFF_X);
  359.             waddstr(status_win, "Off");
  360.         }
  361.     }
  362.     else {
  363.         /* Daemon running - get status and update display */
  364.         cmd = CDA_STATUS;
  365.         memset(arg, 0, CDA_NARGS * sizeof(word32_t));
  366.         if (!cda_sendcmd(cmd, arg)) {
  367.             cd_quit(&status);
  368.             exit(2);
  369.         }
  370.  
  371.         wmove(status_win, ON_Y, ON_X);
  372.         wattron(status_win, A_STANDOUT);
  373.         waddstr(status_win, "On");
  374.         wattroff(status_win, A_STANDOUT);
  375.  
  376.         wmove(status_win, OFF_Y, OFF_X);
  377.         waddstr(status_win, "Off");
  378.  
  379.         astat0 = arg[0];
  380.         astat1 = arg[1];
  381.         rptcnt = (int) arg[2];
  382.  
  383.         if (astat0 != oastat0 || astat1 != oastat1) {
  384.             switch (RD_ARG_MODE(astat0)) {
  385.             case M_NODISC:
  386.                 wmove(status_win, LOAD_Y, LOAD_X);
  387.                 waddstr(status_win, "Load");
  388.                  
  389.                 wmove(status_win, EJECT_Y, EJECT_X);
  390.                 wattron(status_win, A_STANDOUT);
  391.                 waddstr(status_win, "Eject");
  392.                 wattroff(status_win, A_STANDOUT);
  393.  
  394.                 wmove(status_win, PLAY_Y, PLAY_X);
  395.                 waddstr(status_win, "Play");
  396.  
  397.                 wmove(status_win, PAUSE_Y, PAUSE_X);
  398.                 waddstr(status_win, "Pause");
  399.  
  400.                 wmove(status_win, STOP_Y, STOP_X);
  401.                 wattron(status_win, A_STANDOUT);
  402.                 waddstr(status_win, "Stop");
  403.                 wattroff(status_win, A_STANDOUT);
  404.  
  405.                 break;
  406.  
  407.             case M_STOP:
  408.                 wmove(status_win, LOAD_Y, LOAD_X);
  409.                 wattron(status_win, A_STANDOUT);
  410.                 waddstr(status_win, "Load");
  411.                 wattroff(status_win, A_STANDOUT);
  412.  
  413.                 wmove(status_win, EJECT_Y, EJECT_X);
  414.                 waddstr(status_win, "Eject");
  415.  
  416.                 wmove(status_win, PLAY_Y, PLAY_X);
  417.                 waddstr(status_win, "Play");
  418.  
  419.                 wmove(status_win, PAUSE_Y, PAUSE_X);
  420.                 waddstr(status_win, "Pause");
  421.  
  422.                 wmove(status_win, STOP_Y, STOP_X);
  423.                 wattron(status_win, A_STANDOUT);
  424.                 waddstr(status_win, "Stop");
  425.                 wattroff(status_win, A_STANDOUT);
  426.  
  427.                 break;
  428.  
  429.             case M_PLAY:
  430.                 wmove(status_win, LOAD_Y, LOAD_X);
  431.                 wattron(status_win, A_STANDOUT);
  432.                 waddstr(status_win, "Load");
  433.                 wattroff(status_win, A_STANDOUT);
  434.  
  435.                 wmove(status_win, EJECT_Y, EJECT_X);
  436.                 waddstr(status_win, "Eject");
  437.  
  438.                 wmove(status_win, PLAY_Y, PLAY_X);
  439.                 wattron(status_win, A_STANDOUT);
  440.                 waddstr(status_win, "Play");
  441.                 wattroff(status_win, A_STANDOUT);
  442.  
  443.                 wmove(status_win, PAUSE_Y, PAUSE_X);
  444.                 waddstr(status_win, "Pause");
  445.  
  446.                 wmove(status_win, STOP_Y, STOP_X);
  447.                 waddstr(status_win, "Stop");
  448.  
  449.                 break;
  450.  
  451.             case M_PAUSE:
  452.                 wmove(status_win, LOAD_Y, LOAD_X);
  453.                 wattron(status_win, A_STANDOUT);
  454.                 waddstr(status_win, "Load");
  455.                 wattroff(status_win, A_STANDOUT);
  456.  
  457.                 wmove(status_win, EJECT_Y, EJECT_X);
  458.                 waddstr(status_win, "Eject");
  459.  
  460.                 wmove(status_win, PLAY_Y, PLAY_X);
  461.                 waddstr(status_win, "Play");
  462.  
  463.                 wmove(status_win, PAUSE_Y, PAUSE_X);
  464.                 wattron(status_win, A_STANDOUT);
  465.                 waddstr(status_win, "Pause");
  466.                 wattroff(status_win, A_STANDOUT);
  467.  
  468.                 wmove(status_win, STOP_Y, STOP_X);
  469.                 waddstr(status_win, "Stop");
  470.  
  471.                 break;
  472.             }
  473.  
  474.             wmove(status_win, LOCK_Y, LOCK_X);
  475.             if (RD_ARG_LOCK(astat0))
  476.                 wattron(status_win, A_STANDOUT);
  477.             waddstr(status_win, "Lock");
  478.             wattroff(status_win, A_STANDOUT);
  479.  
  480.             wmove(status_win, UNLOCK_Y, UNLOCK_X);
  481.             if (!RD_ARG_LOCK(astat0))
  482.                 wattron(status_win, A_STANDOUT);
  483.             waddstr(status_win, "Unlock");
  484.             wattroff(status_win, A_STANDOUT);
  485.  
  486.             wmove(status_win, SHUFFLE_Y, SHUFFLE_X);
  487.             if (RD_ARG_SHUF(astat0))
  488.                 wattron(status_win, A_STANDOUT);
  489.             waddstr(status_win, "Shuffle");
  490.             wattroff(status_win, A_STANDOUT);
  491.  
  492.             wmove(status_win, PROGRAM_Y, PROGRAM_X);
  493.             if (stat_on && RD_ARG_MODE(astat0) != M_NODISC &&
  494.                 !RD_ARG_SHUF(astat0)) {
  495.                 cmd = CDA_PROGRAM;
  496.                 arg[0] = 1;
  497.                 if (!cda_sendcmd(cmd, arg)) {
  498.                     cd_quit(&status);
  499.                     exit(2);
  500.                 }
  501.                 if (arg[0] > 0)
  502.                     wattron(status_win, A_STANDOUT);
  503.             }
  504.             waddstr(status_win, "Program");
  505.             wattroff(status_win, A_STANDOUT);
  506.  
  507.             wmove(status_win, REPEAT_ON_Y, REPEAT_ON_X);
  508.             if (RD_ARG_REPT(astat0))
  509.                 wattron(status_win, A_STANDOUT);
  510.             waddstr(status_win, "On");
  511.             wattroff(status_win, A_STANDOUT);
  512.  
  513.             wmove(status_win, REPEAT_OFF_Y, REPEAT_OFF_X);
  514.             if (!RD_ARG_REPT(astat0))
  515.                 wattron(status_win, A_STANDOUT);
  516.             waddstr(status_win, "Off");
  517.             wattroff(status_win, A_STANDOUT);
  518.         }
  519.  
  520.         wmove(status_win, 1, 1);
  521.         if (RD_ARG_MODE(astat0) == M_PLAY ||
  522.             RD_ARG_MODE(astat0) == M_PAUSE ||
  523.             RD_ARG_MODE(astat0) == M_NODISC) {
  524.  
  525.             if (RD_ARG_MODE(astat0) != M_NODISC) {
  526.                 wprintw(status_win,
  527.                     "Track %02u Index %02u %02u:%02u  ",
  528.                     RD_ARG_TRK(astat1),
  529.                     RD_ARG_IDX(astat1),
  530.                     RD_ARG_MIN(astat1),
  531.                     RD_ARG_SEC(astat1));
  532.             }
  533.  
  534.             cmd = CDA_VOLUME;
  535.             arg[0] = 0;
  536.             if (!cda_sendcmd(cmd, arg)) {
  537.                 cd_quit(&status);
  538.                 exit(2);
  539.             }
  540.  
  541.             wprintw(status_win, "Volume %3u%% ", arg[1]);
  542.             cmd = CDA_BALANCE;
  543.             arg[0] = 0;
  544.             if (!cda_sendcmd(cmd, arg)) {
  545.                 cd_quit(&status);
  546.                 exit(2);
  547.             }
  548.  
  549.             wprintw(status_win, "Balance %3u%%  ", arg[1]);
  550.  
  551.             switch (route) {
  552.             case 0:
  553.                 wprintw(status_win, "Stereo    ");
  554.                 break;
  555.             case 1:
  556.                 wprintw(status_win, "Reverse   ");
  557.                 break;
  558.             case 2:
  559.                 wprintw(status_win, "Mono-L    ");
  560.                 break;
  561.             case 3:
  562.                 wprintw(status_win, "Mono-R    ");
  563.                 break;
  564.             case 4:
  565.                 wprintw(status_win, "Mono-L+R  ");
  566.                 break;
  567.             }
  568.  
  569.             if (rptcnt >= 0)
  570.                 wprintw(status_win, "Count %u", rptcnt);
  571.  
  572.             getyx(status_win, y, x);
  573.             for (i = x; i < COLS-1; i++)
  574.                 waddch(status_win, ' ');
  575.  
  576.             wmove(status_win, 1, 1);
  577.         }
  578.         else if (refresh_sts) {
  579.             refresh_sts = FALSE;
  580.             for (i = 0; i < COLS-2; i++)
  581.                 waddch(status_win, ' ');
  582.             wmove(status_win, 1, 1);
  583.  
  584.             if (stat_on && RD_ARG_MODE(astat0) != M_NODISC &&
  585.                 !RD_ARG_SHUF(astat0)) {
  586.                 cmd = CDA_PROGRAM;
  587.                 arg[0] = 1;
  588.                 if (!cda_sendcmd(cmd, arg)) {
  589.                     cd_quit(&status);
  590.                     exit(2);
  591.                 }
  592.  
  593.                 if (arg[0] > 0) {
  594.                     wprintw(status_win, "Program: ");
  595.                     for (i = 0; i < arg[0]; i++) {
  596.                         wprintw(status_win, " %02u",
  597.                             arg[i+1]);
  598.                     }
  599.                 }
  600.             }
  601.         }
  602.     }
  603.     wrefresh(status_win);
  604.  
  605.     /* See if we want to display help info */
  606.     if (help) {
  607.         if (!old_help) {
  608.             wclear(info_win);
  609.             wmove(info_win, 0, 0);
  610.             wprintw(info_win, HELP_INFO);
  611.             scroll_line = 0;
  612.             prefresh(info_win, scroll_line, 0, 0, 0,
  613.                  LINES-8, COLS-1);
  614.             old_help = help;
  615.         }
  616.         signal(SIGALRM, cda_screen);
  617.         alarm(1);
  618.         return;
  619.     }
  620.     else if (old_help) {
  621.         wclear(info_win);
  622.         scroll_line = 0;
  623.         track = -2;    /* force display of version/device */
  624.     }
  625.  
  626.     /* If state is unchanged since last time, no more to do */
  627.     if (stat_on == ostat_on && old_help == help && old_route == route &&
  628.         RD_ARG_MODE(astat0) == RD_ARG_MODE(oastat0) &&
  629.         RD_ARG_TRK(astat1) == RD_ARG_TRK(oastat1) &&
  630.         RD_ARG_IDX(astat1) == RD_ARG_IDX(oastat1)) {
  631.         ostat_on = stat_on;
  632.         oastat0 = astat0;
  633.         oastat1 = astat1;
  634.         old_help = help;
  635.  
  636.         /* Call us again - nothing is too much trouble! */
  637.         signal(SIGALRM, cda_screen);
  638.         alarm(1);
  639.         return;
  640.     }
  641.  
  642.     old_help = help;
  643.     old_route = route;
  644.     ostat_on = stat_on;
  645.     oastat0 = astat0;
  646.     oastat1 = astat1;
  647.  
  648.     /* Now display data, according to our state: */
  649.  
  650.     /* Off, or no disc, display version and device */
  651.     if (!stat_on || RD_ARG_MODE(astat0) == M_NODISC) {
  652.         if (track != -1) {
  653.             track = -1;
  654.             wclear(info_win);
  655.             wmove(info_win, 0,0);
  656.             wprintw(info_win,
  657.                 "CDA - Command Line CD Audio Player");
  658.             wmove(info_win, 0, COLS-18);
  659.             wprintw(info_win, "Press ");
  660.             wattron(info_win, A_STANDOUT);
  661.             wprintw(info_win, "?");
  662.             wattroff(info_win, A_STANDOUT);
  663.             wprintw(info_win, " for help.\n\n");
  664.  
  665.             wprintw(info_win, "CD audio        v%s%s PL%d\n",
  666.                 VERSION, VERSION_EXT, PATCHLEVEL);
  667.             if (stat_on) {
  668.                 cmd = CDA_VERSION;
  669.                 if (!cda_sendcmd(cmd, arg)) {
  670.                     cd_quit(&status);
  671.                     exit(2);
  672.                 }
  673.             }
  674.             else sprintf((char *) arg, "%s%s PL%d\n%s",
  675.                 VERSION, VERSION_EXT, PATCHLEVEL, di_vers());
  676.             wprintw(info_win, "CD audio daemon v%s\n",
  677.                 (char *) arg);
  678.             wprintw(info_win, COPYRIGHT);
  679.                 
  680.             wprintw(info_win, "\nDevice: %s\n", app_data.device);
  681.             if (stat_on) {
  682.                 cmd = CDA_DEVICE;
  683.                 if (!cda_sendcmd(cmd, arg)) {
  684.                     cd_quit(&status);
  685.                     exit(2);
  686.                 }
  687.                 wprintw(info_win, "%s\n", (char *) arg);
  688.             }
  689.  
  690.             prefresh(info_win, scroll_line, 0, 0, 0,
  691.                  LINES-8, COLS-1);
  692.             getyx(info_win, scroll_length, i);
  693.             --scroll_length;
  694.         }
  695.     }
  696.     else if (track != RD_ARG_TRK(astat1)) {
  697.         /* If disc loaded, display extinfo */
  698.  
  699.         wclear(info_win);
  700.         wmove(info_win, 0, 0);
  701.  
  702.         /* Get database entry */
  703.         if (RD_ARG_MODE(astat0) == M_PLAY ||
  704.             RD_ARG_MODE(astat0) == M_PAUSE) {
  705.             track = RD_ARG_TRK(astat1);
  706.         }
  707.         else
  708.             track = -1;
  709.  
  710.         cmd = CDA_EXTINFO;
  711.         arg[0] = 0;
  712.         arg[1] = track;
  713.         if (!cda_sendcmd(cmd, arg)) {
  714.             cd_quit(&status);
  715.             exit(2);
  716.         }
  717.  
  718.         /* Load CD database entry */
  719.         dbprog_dbclear(&status);
  720.         cddb = dbprog_dbload(arg[0]);
  721.  
  722.         ntrks = arg[0] & 0xff;
  723.         if (!cddb || RD_ARG_MODE(astat0) == M_STOP) {
  724.             /* No database entry */
  725.             cmd = CDA_TOC;
  726.             arg[0] = 0;
  727.             if (!cda_sendcmd(cmd, arg)) {
  728.                 cd_quit(&status);
  729.                 exit(2);
  730.             }
  731.  
  732.             wprintw(info_win, "Disc ID: %08x%s",
  733.                 arg[0], (cur_db.extd == NULL) ? "" : " *");
  734.  
  735.             wmove(info_win, 0, COLS-18);
  736.             wprintw(info_win, "Press ");
  737.             wattron(info_win, A_STANDOUT);
  738.             wprintw(info_win, "?");
  739.             wattroff(info_win, A_STANDOUT);
  740.             wprintw(info_win, " for help.\n\n");
  741.  
  742.             wprintw(info_win, "%s\n\n",
  743.                 cddb ? cur_db.dtitle : "(unknown disc title)");
  744.  
  745.             for (i = 0; i < (int) ntrks; i++) {
  746.                 RD_ARG_TOC(arg[i+1], trkno, playing, min, sec);
  747.                 wprintw(info_win, "%s%02u %02u:%02u %s %s\n",
  748.                     playing ? ">" : " ",
  749.                     trkno, min, sec,
  750.                     (cur_db.extt[i] == NULL) ? " " : "*",
  751.                     cddb ? cur_db.trklist[i] : "??");
  752.             }
  753.  
  754.             RD_ARG_TOC(arg[i+1], trkno, playing, min, sec);
  755.             wprintw(info_win, "\nTotal Time: %02u:%02u\n",
  756.                 min, sec);
  757.         }
  758.         else {
  759.             /* Have database entry */
  760.             if (cur_db.extd == NULL) {
  761.                 wprintw(info_win,
  762.                     "No Extended Information for this CD.\n");
  763.             }
  764.             else {
  765.                 wprintw(info_win, "%s\n\n", cur_db.dtitle);
  766.  
  767.                 /* Not using wprintw here to avoid a bug
  768.                  * with very long strings
  769.                  */
  770.                 p = cur_db.extd;
  771.                 for (; *p != '\0'; p++)
  772.                        waddch(info_win, *p);
  773.                 waddch(info_win, '\n');
  774.             }
  775.  
  776.             for (i = 0; i < COLS-1; i++)
  777.                 waddch(info_win, ACS_HLINE);
  778.             waddch(info_win, '\n');
  779.  
  780.             /* If Play or Pause, display track info */
  781.             if (RD_ARG_MODE(astat0) == M_PLAY ||
  782.                 RD_ARG_MODE(astat0) == M_PAUSE) {
  783.                 if (cur_db.trklist[arg[2]] == NULL) {
  784.                     wprintw(info_win,
  785.                         "(No title for track %02u.)\n",
  786.                         arg[1]);
  787.                 }
  788.                 else {
  789.                     wprintw(info_win, "%s\n",
  790.                         cur_db.trklist[arg[2]]);
  791.  
  792.                     if (cur_db.extt[arg[2]] != NULL) {
  793.                         /* Not using wprintw here
  794.                          * to avoid a bug with very
  795.                          * long strings
  796.                          */
  797.                         p = cur_db.extt[arg[2]];
  798.                         waddch(info_win, '\n');
  799.                         for (; *p != '\0'; p++)
  800.                                waddch(info_win, *p);
  801.                         waddch(info_win, '\n');
  802.                     }
  803.                 }
  804.             }
  805.         }
  806.         scroll_line = 0;
  807.         getyx(info_win, scroll_length, i);
  808.         prefresh(info_win, scroll_line, 0, 0, 0, LINES-8, COLS-1);
  809.     }
  810.     oastat0 = astat0;
  811.     oastat1 = astat1;
  812.  
  813.     /* Come back in 1 sec */
  814.     signal(SIGALRM, cda_screen);
  815.     alarm(1);
  816. }
  817.  
  818.  
  819. #if defined(SIGTSTP) && defined(SIGCONT)
  820. /*
  821.  * ontstp
  822.  *    Handler for job control stop signal
  823.  *
  824.  * Args:
  825.  *    signo - The signal number
  826.  *
  827.  * Return:
  828.  *    Nothing
  829.  */
  830. void
  831. ontstp(int signo)
  832. {
  833.     if (signo != SIGTSTP)
  834.         return;
  835.  
  836.     /* Cancel alarms */
  837.     alarm(0);
  838.  
  839.     /* Put screen in sane state */
  840.     move(LINES-1, 0);
  841.     printw("\r\n\n");
  842.     putp(cursor_normal);
  843.     refresh();
  844.     reset_shell_mode();
  845.  
  846.     /* Now stop the process */
  847.     signal(SIGTSTP, SIG_DFL);
  848.     kill(getpid(), SIGTSTP);
  849. }
  850.  
  851.  
  852. /*
  853.  * oncont
  854.  *    Handler for job control continue signal
  855.  *
  856.  * Args:
  857.  *    signo - The signal number
  858.  *
  859.  * Return:
  860.  *    Nothing
  861.  */
  862. void
  863. oncont(int signo)
  864. {
  865.     if (signo != SIGCONT)
  866.         return;
  867.  
  868.     signal(SIGTSTP, ontstp);
  869.     signal(SIGCONT, oncont);
  870.  
  871.     /* Restore visual attributes */
  872.     reset_prog_mode();
  873.     putp(cursor_invisible);
  874.  
  875.     /* Set up for auto refresh */
  876.     wclear(info_win);
  877.     wclear(status_win);
  878.     oastat0 = oastat1 = (word32_t) -1;
  879.     ostat_on = !stat_on;
  880.     old_help = !help;
  881.     old_route = !route;
  882.     refresh_fkeys = TRUE;
  883. }
  884. #endif    /* SIGTSTP SIGCONT */
  885.  
  886.  
  887. /***********************
  888.  *   public routines   *
  889.  ***********************/
  890.  
  891.  
  892. /*
  893.  * cda_vtidy
  894.  *    Tidy up and go home for visual mode.
  895.  *
  896.  * Args:
  897.  *    None
  898.  *
  899.  * Return:
  900.  *    Nothing
  901.  */
  902. void
  903. cda_vtidy(void)
  904. {
  905.     if (isvisual) {
  906.         keypad(stdscr, FALSE);
  907.         putp(cursor_normal);
  908.         clear();
  909.  
  910.         move(LINES-1, 0);
  911.         refresh();
  912.         echo();
  913.         nocbreak();
  914.         endwin();
  915.     }
  916.  
  917.     printf("%s\n", errmsg != NULL ? errmsg : "Goodbye!");
  918. }
  919.  
  920.  
  921. /*
  922.  * cda_visual
  923.  *    Visual (curses mode) interface.
  924.  *
  925.  * Args:
  926.  *    None.
  927.  *
  928.  * Return:
  929.  *    Return code for exit()
  930.  */
  931. void
  932. cda_visual(void)
  933. {
  934.     word32_t    cmd;
  935.     word32_t    arg[CDA_NARGS];
  936.     word32_t    astat0,
  937.             astat1;
  938.     int        inp,
  939.             i,
  940.             j,
  941.             mins,
  942.             secs;
  943.     char        ipbuff[80],
  944.             *p;
  945.  
  946.     stat_on = FALSE;
  947.      
  948.     /* Open FIFOs - command side */
  949.     cda_sfd[1] = open(spipe, O_WRONLY);
  950.     if (cda_sfd[1] >= 0) {
  951.         stat_on = TRUE;
  952.  
  953.         cda_rfd[1] = open(rpipe, O_RDONLY);
  954.         if (cda_rfd[1] < 0)
  955.             cd_fatal_popup(NULL, "cannot open recv pipe");
  956.     }
  957.  
  958. #if defined(SIGTSTP) && defined(SIGCONT)
  959.     /* Handle job control */
  960.     signal(SIGTSTP, ontstp);
  961.     signal(SIGCONT, oncont);
  962. #endif
  963.  
  964.     /* Initialize curses and paint initial screen */
  965.     initscr();
  966.     isvisual = TRUE;
  967.  
  968.     noecho();
  969.     cbreak();
  970.     putp(cursor_invisible);
  971.  
  972.     if ((info_win = newpad(MAXTRACK * 2, COLS)) == (WINDOW *) NULL) {
  973.         cd_quit(&status);
  974.         exit(2);
  975.     }
  976.  
  977.     keypad(info_win, TRUE);
  978.  
  979.     if ((status_win = newwin(7, COLS, LINES-7, 0)) == (WINDOW *) NULL) {
  980.         cd_quit(&status);
  981.         exit(2);
  982.     }
  983.  
  984.     keypad(status_win, TRUE);
  985.  
  986.     wmove(status_win, 3, 0);
  987.     waddstr(status_win, STATUS_LINE0);
  988.     wmove(status_win, 4, 0);
  989.     waddstr(status_win, STATUS_LINE1);
  990.     wmove(status_win, 5, 0);
  991.     waddstr(status_win, STATUS_LINE2);
  992.     wrefresh(status_win);
  993.  
  994.     box(status_win, 0, 0);
  995.  
  996.     /* Paint the screen every second */
  997.     cda_screen(0);
  998.  
  999.     /* Main processing loop */
  1000.     while ((inp = cda_wgetch(status_win)) != KEY_F(8)) {
  1001.         if (inp == 'q' || inp == 'Q')
  1002.             break;
  1003.  
  1004.         /* Cancel alarm so we don't nest */
  1005.         alarm(0);
  1006.  
  1007.         /* Get current status */
  1008.         if (stat_on) {
  1009.             cmd = CDA_STATUS;
  1010.             if (!cda_sendcmd(cmd, arg)) {
  1011.                 cd_quit(&status);
  1012.                 exit(2);
  1013.             }
  1014.             astat0 = arg[0];
  1015.             astat1 = arg[1];
  1016.         }
  1017.  
  1018.         switch (inp) {
  1019.         case KEY_F(1):    /* On/Off */
  1020.         case 'o':
  1021.         case 'O':
  1022.             if (!stat_on) {
  1023.                 if (cda_daemon(&status)) {
  1024.                     cd_quit(&status);
  1025.                     exit(0);
  1026.                 }
  1027.  
  1028.                 stat_on = FALSE;
  1029.  
  1030.                 /* Open FIFOs - command side */
  1031.                 cda_sfd[1] = open(spipe, O_WRONLY);
  1032.                 if (cda_sfd[1] >= 0) {
  1033.                     stat_on = TRUE;
  1034.  
  1035.                     cda_rfd[1] = open(rpipe, O_RDONLY);
  1036.                     if (cda_rfd[1] < 0) {
  1037.                         cd_fatal_popup(
  1038.                             NULL,
  1039.                             "cannot open recv pipe"
  1040.                         );
  1041.                     }
  1042.                 }
  1043.  
  1044.                 cmd = CDA_ON;
  1045.                 arg[0] = 0;
  1046.                 if (!cda_sendcmd(cmd, arg)) {
  1047.                     cd_quit(&status);
  1048.                     exit(2);
  1049.                 }
  1050.  
  1051.                 wmove(status_win, 1, 1);
  1052.                 for (i = 0; i < COLS-2; i++)
  1053.                     waddch(status_win, ' ');
  1054.                 wmove(status_win, 1, 1);
  1055.                 wprintw(status_win,
  1056.                     "CD audio daemon pid=%d dev=%s started.",
  1057.                     arg[0], app_data.device);
  1058.                 wrefresh(status_win);
  1059.             }
  1060.             else {
  1061.                 cmd = CDA_OFF;
  1062.                 if (!cda_sendcmd(cmd, arg)) {
  1063.                     cd_quit(&status);
  1064.                     exit(2);
  1065.                 }
  1066.  
  1067.                 wmove(status_win, 1, 1);
  1068.                 for (i = 0; i < COLS-2; i++)
  1069.                     waddch(status_win, ' ');
  1070.                 wmove(status_win, 1, 1);
  1071.                 wprintw(status_win,
  1072.                     "CD audio daemon pid=%d dev=%s exited.",
  1073.                     arg[0], app_data.device);
  1074.                 wrefresh(status_win);
  1075.  
  1076.                 stat_on = FALSE;
  1077.                 close(cda_sfd[1]);
  1078.                 close(cda_rfd[1]);
  1079.                 cda_sfd[1] = cda_rfd[1] = -1;
  1080.             }
  1081.             track = -2; /* force redisplay of version info */
  1082.             scroll_line = 0;
  1083.             break;
  1084.  
  1085.         case KEY_F(2):    /* Load/Eject */
  1086.         case 'j':
  1087.         case 'J':
  1088.             if (!stat_on) {
  1089.                 beep();
  1090.                 break;
  1091.             }
  1092.  
  1093.             cmd = CDA_DISC;
  1094.             arg[0] = (RD_ARG_MODE(astat0) == M_NODISC) ? 0 : 1;
  1095.  
  1096.             if (!cda_sendcmd(cmd, arg)) {
  1097.                 cd_quit(&status);
  1098.                 exit(2);
  1099.             }
  1100.  
  1101.             track = -2; /* force redisplay of version info */
  1102.             refresh_sts = TRUE;
  1103.             break;
  1104.  
  1105.         case KEY_F(3): /* Play/Pause */
  1106.         case 'p':
  1107.         case 'P':
  1108.             if (!stat_on || RD_ARG_MODE(astat0) == M_NODISC) {
  1109.                 beep();
  1110.                 break;
  1111.             }
  1112.             if (RD_ARG_MODE(astat0) == M_PLAY)
  1113.                 cmd = CDA_PAUSE;
  1114.             else
  1115.                 cmd = CDA_PLAY;
  1116.  
  1117.             arg[0] = 0;
  1118.             if (!cda_sendcmd(cmd, arg)) {
  1119.                 cd_quit(&status);
  1120.                 exit(2);
  1121.             }
  1122.             break;
  1123.  
  1124.         case KEY_F(4): /* Stop */
  1125.         case 's':
  1126.         case 'S':
  1127.             if (!stat_on || RD_ARG_MODE(astat0) == M_NODISC) {
  1128.                 beep();
  1129.                 break;
  1130.             }
  1131.  
  1132.             if (RD_ARG_MODE(astat0) != M_PLAY &&
  1133.                 RD_ARG_MODE(astat0) != M_PAUSE) {
  1134.                 beep();
  1135.                 break;
  1136.             }
  1137.  
  1138.             cmd = CDA_STOP;
  1139.             if (!cda_sendcmd(cmd, arg)) {
  1140.                 cd_quit(&status);
  1141.                 exit(2);
  1142.             }
  1143.  
  1144.             refresh_sts = TRUE;
  1145.             break;
  1146.  
  1147.         case KEY_F(5):    /* Lock/Unlock */
  1148.         case 'k':
  1149.         case 'K':
  1150.             if (!stat_on || RD_ARG_MODE(astat0) == M_NODISC) {
  1151.                 beep();
  1152.                 break;
  1153.             }
  1154.  
  1155.             cmd = CDA_LOCK;
  1156.             arg[0] = RD_ARG_LOCK(astat0) ? 0 : 1;
  1157.  
  1158.             if (!cda_sendcmd(cmd, arg)) {
  1159.                 cd_quit(&status);
  1160.                 exit(2);
  1161.             }
  1162.             break;
  1163.  
  1164.         case KEY_F(6):    /* Shuffle/Program */
  1165.         case 'u':
  1166.         case 'U':
  1167.             if (!stat_on || RD_ARG_MODE(astat0) == M_NODISC) {
  1168.                 beep();
  1169.                 break;
  1170.             }
  1171.  
  1172.             /* Not allowed if play or pause */
  1173.             if (RD_ARG_MODE(astat0) == M_PLAY ||
  1174.                 RD_ARG_MODE(astat0) == M_PAUSE) {
  1175.                 beep();
  1176.                 break;
  1177.             }
  1178.  
  1179.             /* See if program on */
  1180.             cmd = CDA_PROGRAM;
  1181.             arg[0] = 1;
  1182.             if (!cda_sendcmd(cmd, arg)) {
  1183.                 cd_quit(&status);
  1184.                 exit(2);
  1185.             }
  1186.  
  1187.             /* If neither program nor shuffle, set shuffle */
  1188.             if (!RD_ARG_SHUF(astat0) && arg[0] == 0) {
  1189.                 cmd = CDA_SHUFFLE;
  1190.                 arg[0] = 1;
  1191.                 if (!cda_sendcmd(cmd, arg)) {
  1192.                     cd_quit(&status);
  1193.                     exit(2);
  1194.                 }
  1195.                 break;
  1196.             }
  1197.             else if (RD_ARG_SHUF(astat0)) {
  1198.                 /* If shuffle, turn it off and prompt
  1199.                  * for program
  1200.                  */
  1201.                 cmd = CDA_SHUFFLE;
  1202.                 arg[0] = 0;
  1203.                 if (!cda_sendcmd(cmd, arg)) {
  1204.                     cd_quit(&status);
  1205.                     exit(2);
  1206.                 }
  1207.  
  1208.                 wmove(status_win, SHUFFLE_Y, SHUFFLE_X);
  1209.                 waddstr(status_win, "Shuffle");
  1210.  
  1211.                 wmove(status_win, PROGRAM_Y, PROGRAM_X);
  1212.                 wattron(status_win, A_STANDOUT);
  1213.                 waddstr(status_win, "Program");
  1214.                 wattroff(status_win, A_STANDOUT);
  1215.  
  1216.                 wmove(status_win, 1, 1);
  1217.                 for (i = 0; i < COLS-2; i++)
  1218.                     waddch(status_win, ' ');
  1219.  
  1220.                 wmove(status_win, 1, 1);
  1221.                 wprintw(status_win, "Program: ");
  1222.                 cda_echo();
  1223.                 putp(cursor_normal);
  1224.                 wrefresh(status_win);
  1225.  
  1226.                 /* Before reading the program list, check for
  1227.                  * F6 or "u", and dismiss prog mode if found
  1228.                  */
  1229.                 i = cda_wgetch(status_win);
  1230.                 if (i != KEY_F(6) && i != 'u') {
  1231.                     cda_ungetch(i);
  1232.  
  1233.                     cda_wgetstr(status_win, ipbuff,
  1234.                             COLS-12);
  1235.  
  1236.                     /* Is the string just read was
  1237.                      * terminated by F6, it will
  1238.                      * have been ungotten, but must be
  1239.                      * thrown away or it will cause
  1240.                      * return to shuffle mode.
  1241.                      */
  1242.                     if (savch == KEY_F(6))
  1243.                         savch = 0;
  1244.  
  1245.                     j = 0;
  1246.                     p = ipbuff;
  1247.                     while ((i = strtol(p, &p, 10)) != 0)
  1248.                     {
  1249.                         arg[++j] = i;
  1250.  
  1251.                         if (p == NULL)
  1252.                             break;
  1253.  
  1254.                         while (*p != '\0' &&
  1255.                                !isdigit(*p))
  1256.                             p++;
  1257.                     }
  1258.  
  1259.                     arg[0] = (word32_t) -j;
  1260.                     cmd = CDA_PROGRAM;
  1261.                     if (!cda_sendcmd(cmd, arg)) {
  1262.                         errmsg = NULL;
  1263.                         beep();
  1264.                     }
  1265.                 }
  1266.  
  1267.                 cda_noecho();
  1268.                 putp(cursor_invisible);
  1269.  
  1270.                 refresh_sts = TRUE;
  1271.                 break;
  1272.             }
  1273.             else {
  1274.                 /* Program is on - reset it */
  1275.                 arg[0] = 0;
  1276.                 cmd = CDA_PROGRAM;
  1277.                 if (!cda_sendcmd(cmd, arg)) {
  1278.                     cd_quit(&status);
  1279.                     exit(2);
  1280.                 }
  1281.                 refresh_sts = TRUE;
  1282.             }
  1283.             break;
  1284.  
  1285.         case KEY_F(7):    /* Repeat On/Off */
  1286.         case 'e':
  1287.         case 'E':
  1288.             if (!stat_on) {
  1289.                 beep();
  1290.                 break;
  1291.             }
  1292.  
  1293.             cmd = CDA_REPEAT;
  1294.             arg[0] = RD_ARG_REPT(astat0) ? 0 : 1;
  1295.             if (!cda_sendcmd(cmd, arg)) {
  1296.                 cd_quit(&status);
  1297.                 exit(2);
  1298.             }
  1299.             break;
  1300.  
  1301.         case KEY_LEFT:    /* Prev track */
  1302.         case KEY_RIGHT:    /* Next track */
  1303.         case 'C':
  1304.         case 'c':
  1305.             if (!stat_on || RD_ARG_MODE(astat0) == M_NODISC ||
  1306.                 RD_ARG_MODE(astat0) != M_PLAY) {
  1307.                 beep();
  1308.                 break;
  1309.             }
  1310.  
  1311.             arg[0] = (inp == KEY_LEFT || inp == 'C') ? 0 : 1;
  1312.             cmd = CDA_TRACK;
  1313.             if (!cda_sendcmd(cmd, arg)) {
  1314.                 cd_quit(&status);
  1315.                 exit(2);
  1316.             }
  1317.             break;
  1318.  
  1319.         case '<':    /* Prev index */
  1320.         case '>':    /* Next index */
  1321.             if (!stat_on || RD_ARG_MODE(astat0) == M_NODISC) {
  1322.                 beep();
  1323.                 break;
  1324.             }
  1325.  
  1326.             arg[0] = (inp == '<') ? 0 : 1;
  1327.             cmd = CDA_INDEX;
  1328.             if (!cda_sendcmd(cmd, arg)) {
  1329.                 errmsg = NULL;
  1330.                 beep();
  1331.             }
  1332.             break;
  1333.  
  1334.         case '+':    /* Vol up */
  1335.         case '-':    /* Vol down */
  1336.             if (!stat_on) {
  1337.                 beep();
  1338.                 break;
  1339.             }
  1340.  
  1341.             cmd = CDA_VOLUME;
  1342.             arg[0] = 0;
  1343.             if (!cda_sendcmd(cmd, arg)) {
  1344.                 cd_quit(&status);
  1345.                 exit(2);
  1346.             }
  1347.  
  1348.             if (inp == '+') {
  1349.                 if (arg[1] <= 95)
  1350.                     arg[1] += 5;
  1351.             }
  1352.             else if (arg[1] >= 5) {
  1353.                 arg[1] -= 5;
  1354.             }
  1355.             arg[0] = 1;
  1356.             if (!cda_sendcmd(cmd, arg)) {
  1357.                 cd_quit(&status);
  1358.                 exit(2);
  1359.             }
  1360.             break;
  1361.  
  1362.         case 'l':    /* Bal left */
  1363.         case 'L':
  1364.         case 'r':    /* Bal right */
  1365.         case 'R':
  1366.             if (!stat_on) {
  1367.                 beep();
  1368.                 break;
  1369.             }
  1370.  
  1371.             cmd = CDA_BALANCE;
  1372.             arg[0] = 0;
  1373.             if (!cda_sendcmd(cmd, arg)) {
  1374.                 cd_quit(&status);
  1375.                 exit(2);
  1376.             }
  1377.  
  1378.             if (inp == 'r' || inp == 'R') {
  1379.                 if (arg[1] <= 95)
  1380.                     arg[1] += 5;
  1381.             }
  1382.             else if (arg[1] >= 5) {
  1383.                 arg[1] -= 5;
  1384.             }
  1385.             arg[0] = 1;
  1386.             if (!cda_sendcmd(cmd, arg)) {
  1387.                 cd_quit(&status);
  1388.                 exit(2);
  1389.             }
  1390.             break;
  1391.  
  1392.         case '?':    /* Help */
  1393.             help = TRUE;
  1394.             scroll_length = HELP_SCROLL_LENGTH;
  1395.             break;
  1396.  
  1397.         case ' ':    /* Return from help */
  1398.             help = FALSE;
  1399.             break;
  1400.  
  1401.         case '\014':    /* ^l - repaint screen */
  1402.         case '\022':    /* ^r - repaint screen */
  1403.             wclear(info_win);
  1404.             wclear(status_win);
  1405.             oastat0 = oastat1 = (word32_t) -1;
  1406.             ostat_on = !stat_on;
  1407.             old_help = !help;
  1408.             old_route = !route;
  1409.             refresh_fkeys = TRUE;
  1410.             break;
  1411.  
  1412.         case KEY_UP:    /* Scroll up */
  1413.         case KEY_DOWN:    /* Scroll down */
  1414.         case '^':
  1415.         case 'v':
  1416.         case 'V':
  1417.             if (inp == KEY_UP || inp == '^')
  1418.                 scroll_line--;
  1419.             else
  1420.                 scroll_line++;
  1421.  
  1422.             if (scroll_line < 0) {
  1423.                 beep();
  1424.                 scroll_line = 0;
  1425.             }
  1426.             if (scroll_line > (scroll_length - 1)) {
  1427.                 beep();
  1428.                 scroll_line = scroll_length - 1;
  1429.             }
  1430.             prefresh(info_win, scroll_line, 0, 0, 0,
  1431.                  LINES-8, COLS-1);
  1432.             break;
  1433.  
  1434.         case '\011':    /* Route */
  1435.             if (!stat_on) {
  1436.                 beep();
  1437.                 break;
  1438.             }
  1439.  
  1440.             if (--route < 0)
  1441.                 route = 4;
  1442.  
  1443.             cmd = CDA_ROUTE;
  1444.             arg[0] = 1;
  1445.             arg[1] = route;
  1446.             if (!cda_sendcmd(cmd, arg)) {
  1447.                 cd_quit(&status);
  1448.                 exit(2);
  1449.             }
  1450.             break;
  1451.  
  1452.         default:
  1453.             if (isdigit(inp)) {
  1454.                 if (!stat_on) {
  1455.                     beep();
  1456.                     break;
  1457.                 }
  1458.  
  1459.                 cda_ungetch(inp);
  1460.  
  1461.                 wmove(status_win, 1, 1);
  1462.                 for (i = 0; i < COLS-2; i++)
  1463.                     waddch(status_win, ' ');
  1464.  
  1465.                 wmove(status_win, 1, 1);
  1466.                 wprintw(status_win, "Track n [mins secs] : ");
  1467.                 cda_echo();
  1468.                 putp(cursor_normal);
  1469.                 wrefresh(status_win);
  1470.  
  1471.                 cda_wgetstr(status_win, ipbuff, 20);
  1472.                 i = strtol(ipbuff, &p, 10);
  1473.                 if (i != 0) {
  1474.                     mins = secs = 0;
  1475.                     if (p != NULL) {
  1476.                         while (*p != '\0' &&
  1477.                                !isdigit(*p))
  1478.                             p++;
  1479.  
  1480.                         mins = strtol(p, &p, 10);
  1481.  
  1482.                         if (p != NULL) {
  1483.                             while (*p != '\0' &&
  1484.                                    !isdigit(*p))
  1485.                                 p++;
  1486.  
  1487.                             secs = strtol(p,&p,10);
  1488.                         }
  1489.                     }
  1490.  
  1491.                     cmd = CDA_PLAY;
  1492.                     arg[0] = i;
  1493.                     arg[1] = mins;
  1494.                     arg[2] = secs;
  1495.                     if (!cda_sendcmd(cmd, arg)) {
  1496.                         errmsg = NULL;
  1497.                         beep();
  1498.                     }
  1499.                 }
  1500.                 else
  1501.                     beep(); /* i = 0 */
  1502.  
  1503.                 cda_noecho();
  1504.                 putp(cursor_invisible);
  1505.                 refresh_sts = TRUE;
  1506.  
  1507.                 break;
  1508.  
  1509.             }
  1510.             break;
  1511.         }
  1512.  
  1513.         /* Repaint screen */
  1514.         cda_screen(0);
  1515.     }
  1516.  
  1517.     /* Tidy up and go home */
  1518.     alarm(0);
  1519.     errmsg = NULL;
  1520.  
  1521.     if (stat_on) {
  1522.         cmd = CDA_ON;
  1523.         arg[0] = 0;
  1524.         if (!cda_sendcmd(cmd, arg)) {
  1525.             cd_quit(&status);
  1526.             exit(2);
  1527.         }
  1528.         sprintf(emsg, "CD audio daemon pid=%d still running.", arg[0]);
  1529.         errmsg = emsg;
  1530.     }
  1531.  
  1532.     cd_quit(&status);
  1533.     exit(0);
  1534. }
  1535.  
  1536.  
  1537. #endif    /* NOVISUAL */
  1538.  
  1539.