home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / xmmix-1.1 / mixer.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-07-05  |  29.4 KB  |  1,499 lines

  1. /*
  2.  *   xmmix - Motif(tm) Audio Mixer
  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. #ifndef LINT
  23. static char *_mixer_c_ident_ = "@(#)mixer.c    2.15 95/05/13";
  24. #endif
  25.  
  26. #include "appenv.h"
  27. #include "patchlevel.h"
  28. #include "widget.h"
  29. #include "mixer.h"
  30.  
  31.  
  32. /* Ioctl data direction flags */
  33. #define IOC_DATA_NONE    0
  34. #define IOC_DATA_IN    1
  35. #define IOC_DATA_OUT    2
  36.  
  37.  
  38. extern appdata_t    app_data;
  39. extern widgets_t    widgets;
  40. extern bool_t        exit_flag;
  41. extern FILE        *errfp;
  42.  
  43.  
  44. int            maxdevs;    /* Maximum number of devices */
  45.  
  46. STATIC int        dev_fd = -1,    /* Mixer device file descriptor */
  47.             fsmode;        /* File selection mode */
  48.  
  49. STATIC char        drv_ver[128];    /* Sound driver version */
  50.  
  51. STATIC ctlinfo_t    ctlinfo = {    /* Current state of all controls */
  52.     0, 0, 0, 0, 0, 0, FALSE, FALSE,
  53.     {
  54.         {  0,  0, TRUE },    /* 0 */
  55.         { 50, 50, TRUE },    /* 1 */
  56.         { 50, 50, TRUE },    /* 2 */
  57.         {  0,  0, TRUE },    /* 3 */
  58.         {  0,  0, TRUE },    /* 4 */
  59.         {  0,  0, TRUE },    /* 5 */
  60.         {  0,  0, TRUE },    /* 6 */
  61.         {  0,  0, TRUE },    /* 7 */
  62.         {  0,  0, TRUE },    /* 8 */
  63.         {  0,  0, TRUE },    /* 9 */
  64.         {  0,  0, TRUE },    /* 10 */
  65.         {  0,  0, TRUE },    /* 11 */
  66.         {  0,  0, TRUE },    /* 12 */
  67.         {  0,  0, TRUE },    /* 13 */
  68.         {  0,  0, TRUE },    /* 14 */
  69.         {  0,  0, TRUE },    /* 15 */
  70.         {  0,  0, TRUE },    /* 16 */
  71.         {  0,  0, TRUE },    /* 17 */
  72.         {  0,  0, TRUE },    /* 18 */
  73.         {  0,  0, TRUE },    /* 19 */
  74.         {  0,  0, TRUE },    /* 20 */
  75.         {  0,  0, TRUE },    /* 21 */
  76.         {  0,  0, TRUE },    /* 22 */
  77.         {  0,  0, TRUE },    /* 23 */
  78.         {  0,  0, TRUE },    /* 24 */
  79.         {  0,  0, TRUE },    /* 25 */
  80.         {  0,  0, TRUE },    /* 26 */
  81.         {  0,  0, TRUE },    /* 27 */
  82.         {  0,  0, TRUE },    /* 28 */
  83.         {  0,  0, TRUE },    /* 29 */
  84.         {  0,  0, TRUE },    /* 30 */
  85.         {  0,  0, TRUE }    /* 31 */
  86.     }
  87. };
  88.  
  89. STATIC ctlinfo_t    ctlsav;        /* Saved state of all controls */
  90.  
  91.  
  92. /***********************
  93.  *  internal routines  *
  94.  ***********************/
  95.  
  96.  
  97. /*
  98.  * do_ioctl
  99.  *    Perform ioctl command.  If file is not yet open or if we
  100.  *    are in demo mode, just quietly return.  If an error
  101.  *    status is returned, print an appropriate error message
  102.  *    and exit.
  103.  *
  104.  * Args:
  105.  *    cmd - The ioctl command
  106.  *    arg - The ioctl argument
  107.  *    name - The ioctl command string
  108.  *    dir - Data direction
  109.  *        IOC_DATA_IN    
  110.  *        IOC_DATA_OUT
  111.  *        IOC_DATA_NONE
  112.  *
  113.  * Return:
  114.  *    Nothing
  115.  */
  116. STATIC void
  117. do_ioctl(int cmd, int *arg, char *name, int dir)
  118. {
  119.     int    ret;
  120.     char    errmsg[STR_BUF_SZ];
  121.  
  122.     if (dev_fd < 0 || app_data.demo)
  123.         return;
  124.  
  125.     sprintf(errmsg, "\n%s ioctl failed", name);
  126.     if (app_data.debug) {
  127.         fprintf(errfp, "%s: cmd=0x%x (%s) ", PROGNAME, cmd, name);
  128.  
  129.         if (dir == IOC_DATA_OUT)
  130.             fprintf(errfp, "*arg=0x%x", *arg);
  131.     }
  132.  
  133.     if ((ret = ioctl(dev_fd, cmd, arg)) < 0) {
  134.         perror(errmsg);
  135.         exit_flag = TRUE;
  136.     }
  137.  
  138.     if (app_data.debug) {
  139.         if (ret == 0 && dir == IOC_DATA_IN)
  140.             fprintf(errfp, "*arg=0x%x", *arg);
  141.         fprintf(errfp, "\n");
  142.     }
  143. }
  144.  
  145.  
  146. /*
  147.  * mx_queryhw
  148.  *    Query mixer settings and update current ctlinfo state.
  149.  *
  150.  * Args:
  151.  *    m - Pointer to the main widgets structure
  152.  *
  153.  * Return:
  154.  *    Nothing
  155.  */
  156. STATIC void
  157. mx_queryhw(widgets_t *m)
  158. {
  159.     int        i,
  160.             level,
  161.             curmask;
  162.     char        iocstr[STR_BUF_SZ];
  163.     static bool_t    first = TRUE;
  164.  
  165.     if (app_data.demo) {
  166.         if (first) {
  167.             /* Save start-up settings */
  168.             first = FALSE;
  169.             ctlsav = ctlinfo;    /* Structure copy */
  170.         }
  171.         return;
  172.     }
  173.  
  174.     /* Read record source */
  175.     do_ioctl(
  176.         SOUND_MIXER_READ_RECSRC,
  177.         &ctlinfo.recsrc,
  178.         "SOUND_MIXER_READ_RECSRC",
  179.         IOC_DATA_IN
  180.     );
  181.  
  182.     /* Read control settings */
  183.     for (i = 0; i < maxdevs; i++) {
  184.         curmask = (1 << i);
  185.  
  186.         if (m->sl[i].supp) {
  187.             sprintf(iocstr, "MIXER_READ[%d]:%s", i, m->sl[i].name);
  188.             do_ioctl(MIXER_READ(i), &level, iocstr, IOC_DATA_IN);
  189.  
  190.             ctlinfo.slinfo[i].left = (level & 0xff);
  191.             ctlinfo.slinfo[i].right = ((level >> 8) & 0xff);
  192.  
  193.             /* Sanity check */
  194.             if (ctlinfo.slinfo[i].left > 100)
  195.                 ctlinfo.slinfo[i].left = 100;
  196.             else if (ctlinfo.slinfo[i].left < 0)
  197.                 ctlinfo.slinfo[i].left = 0;
  198.             if (ctlinfo.slinfo[i].right > 100)
  199.                 ctlinfo.slinfo[i].right = 100;
  200.             else if (ctlinfo.slinfo[i].right < 0)
  201.                 ctlinfo.slinfo[i].right = 0;
  202.  
  203.             if ((curmask & ctlinfo.stereodevs) == 0) {
  204.                 ctlinfo.slinfo[i].left =
  205.                     ctlinfo.slinfo[i].right =
  206.                     (ctlinfo.slinfo[i].left +
  207.                      ctlinfo.slinfo[i].right) / 2;
  208.                 ctlinfo.slinfo[i].locked = TRUE;
  209.             }
  210.  
  211.             if (ctlinfo.slinfo[i].left != ctlinfo.slinfo[i].right)
  212.                 ctlinfo.slinfo[i].locked = FALSE;
  213.         }
  214.     }
  215.  
  216. #ifdef SOUND_ONOFF_MAX
  217.     /* Additional toggle capabilitites */
  218.     for (i = SOUND_ONOFF_MIN; i <= SOUND_ONOFF_MAX; i++) {
  219.         curmask = (1 << i);
  220.  
  221.         if (curmask & ctlinfo.devmask) {
  222.             sprintf(iocstr, "MIXER_READ[%d]:%s", i, m->sl[i].name);
  223.             do_ioctl(MIXER_READ(i), &level, iocstr, IOC_DATA_IN);
  224.  
  225.             switch (i) {
  226.             case SOUND_MIXER_MUTE:
  227.                 ctlinfo.mute = (level != 0);
  228.                 break;
  229.             case SOUND_MIXER_LOUD:
  230.                 ctlinfo.loudness = (level != 0);
  231.                 break;
  232.             case SOUND_MIXER_ENHANCE:
  233.                 /* Hack: The enhance values are hard wired. */
  234.                 switch (level) {
  235.                 case 80:
  236.                     ctlinfo.enhance = 3;
  237.                     break;
  238.                 case 60:
  239.                     ctlinfo.enhance = 2;
  240.                     break;
  241.                 case 40:
  242.                     ctlinfo.enhance = 1;
  243.                     break;
  244.                 case 0:
  245.                 default:
  246.                     ctlinfo.enhance = 0;
  247.                     break;
  248.                 }
  249.                 break;
  250.             default:
  251.                 /* Unsupported feature: ignore */
  252.                 break;
  253.             }
  254.         }
  255.     }
  256. #endif    /* SOUND_ONOFF_MAX */
  257.  
  258.     if (first) {
  259.         /* Save start-up settings */
  260.         first = FALSE;
  261.         ctlsav = ctlinfo;    /* Structure copy */
  262.     }
  263. }
  264.  
  265.  
  266. /*
  267.  * mx_sethw
  268.  *    Set all sound hardware settings to the current ctlinfo state.
  269.  *
  270.  * Args:
  271.  *    m - Pointer to the main widgets structure
  272.  *
  273.  * Return:
  274.  *    Nothing
  275.  */
  276. STATIC void
  277. mx_sethw(widgets_t *m)
  278. {
  279.     int                i;
  280.     XmScaleCallbackStruct        s;
  281.     XmToggleButtonCallbackStruct    t;
  282.     XmPushButtonCallbackStruct    p;
  283.  
  284.     /* Fake callbacks */
  285.     s.reason = XmCR_VALUE_CHANGED;
  286.     t.reason = XmCR_VALUE_CHANGED;
  287.     p.reason = XmCR_ACTIVATE;
  288.  
  289.     for (i = 0; i < maxdevs; i++) {
  290.         if (!m->sl[i].supp)
  291.             continue;
  292.  
  293.         /* Left slider */
  294.         s.value = ctlinfo.slinfo[i].left;
  295.         mx_slider_l(m->sl[i].widget_l, (XtPointer) i, (XtPointer) &s);
  296.  
  297.         /* Right slider */
  298.         s.value = ctlinfo.slinfo[i].right;
  299.         mx_slider_r(m->sl[i].widget_r, (XtPointer) i, (XtPointer) &s);
  300.  
  301.         /* Lock button */
  302.         t.set = (Boolean) ctlinfo.slinfo[i].locked;
  303.         mx_lock_btn(m->sl[i].widget_lock_btn, (XtPointer) i,
  304.                 (XtPointer) &t);
  305.  
  306.         /* Rec button */
  307.         if (m->sl[i].recsupp) {
  308. #ifdef SOUND_CAP_EXCL_INPUT
  309.             if ((ctlinfo.recsrc & (1 << i)) ||
  310.                 ctlinfo.caps != SOUND_CAP_EXCL_INPUT)
  311. #endif
  312.             {
  313.                 if (ctlinfo.recsrc & (1 << i))
  314.                     t.set = True;
  315.                 else
  316.                     t.set = False;
  317.  
  318.                 mx_rec_btn(m->sl[i].widget_rec_btn,
  319.                        (XtPointer) i, (XtPointer) &t);
  320.             }
  321.         }
  322.     }
  323.  
  324.     /* Mute button */
  325.     if (m->mute_supp) {
  326.         t.set = (Boolean) ctlinfo.mute;
  327.         mx_mute_btn(m->mute_btn, NULL, (XtPointer) &t);
  328.     }
  329.  
  330.     /* Loudness button */
  331.     if (m->loud_supp) {
  332.         t.set = (Boolean) ctlinfo.loudness;
  333.         mx_loud_btn(m->loud_btn, NULL, (XtPointer) &t);
  334.     }
  335.  
  336.     /* Enhance buttons */
  337.     if (m->enh_supp) {
  338.         mx_enhance_btn(m->enh_btn[ctlinfo.enhance],
  339.                    (XtPointer) ctlinfo.enhance, (XtPointer) &p);
  340.     }
  341. }
  342.  
  343.  
  344. /*
  345.  * mx_updctl
  346.  *    Update all control positions on screen to the current ctlinfo
  347.  *    state.
  348.  *
  349.  * Args:
  350.  *    m - Pointer to the main widgets structure
  351.  *
  352.  * Return:
  353.  *    Nothing
  354.  */
  355. STATIC void
  356. mx_updctl(widgets_t *m)
  357. {
  358.     int    i;
  359.  
  360.     for (i = 0; i < maxdevs; i++) {
  361.         if (!m->sl[i].supp)
  362.             continue;
  363.  
  364.         /* Left and right sliders */
  365.         XmScaleSetValue(
  366.             m->sl[i].widget_l,
  367.             ctlinfo.slinfo[i].left
  368.         );
  369.         XmScaleSetValue(
  370.             m->sl[i].widget_r,
  371.             ctlinfo.slinfo[i].right
  372.         );
  373.  
  374.         /* Lock button */
  375.         XmToggleButtonSetState(
  376.             m->sl[i].widget_lock_btn,
  377.             ctlinfo.slinfo[i].locked, False
  378.         );
  379.  
  380.         /* Rec button */
  381.         if (m->sl[i].recsupp) {
  382.             if (ctlinfo.recsrc & (1 << i)) {
  383.                 XmToggleButtonSetState(
  384.                     m->sl[i].widget_rec_btn,
  385.                     True, False
  386.                 );
  387.             }
  388.             else {
  389.                 XmToggleButtonSetState(
  390.                     m->sl[i].widget_rec_btn,
  391.                     False, False
  392.                 );
  393.             }
  394.         }
  395.     }
  396.  
  397.     /* Mute button */
  398.     if (m->mute_supp)
  399.         XmToggleButtonSetState(m->mute_btn, ctlinfo.mute, False);
  400.  
  401.     /* Loudness button */
  402.     if (m->loud_supp)
  403.         XmToggleButtonSetState(m->loud_btn, ctlinfo.loudness, False);
  404.  
  405.     /* Stereo enhance menu */
  406.     if (m->enh_supp) {
  407.         XtVaSetValues(m->enh_opt,
  408.             XmNmenuHistory, m->enh_btn[ctlinfo.enhance],
  409.             NULL
  410.         );
  411.     }
  412. }
  413.  
  414.  
  415. /*
  416.  * mx_readfile
  417.  *    Read mixer settings from file and update control settings.
  418.  *
  419.  * Args:
  420.  *    path - File path string
  421.  *
  422.  * Return:
  423.  *    TRUE on success, FALSE on failure
  424.  */
  425. STATIC bool_t
  426. mx_readfile(char *path)
  427. {
  428.     FILE    *fp;
  429.     int    i,
  430.         curmask,
  431.         val1,
  432.         val2,
  433.         val3,
  434.         val4;
  435.     char    *p,
  436.         buf[STR_BUF_SZ];
  437.  
  438.     if (app_data.debug)
  439.         fprintf(errfp, "Reading mixer file: %s\n", path);
  440.  
  441.     /* Open file for reading */
  442.     if ((fp = fopen(path, "r")) == NULL)
  443.         return FALSE;
  444.  
  445.     /* Read first line of mixer settings file */
  446.     if (fgets(buf, sizeof(buf), fp) == NULL) {
  447.         fclose(fp);
  448.         return FALSE;
  449.     }
  450.  
  451.     /* Mixer settings file signature check */
  452.     if (strncmp(buf, "# xmmix ", 8) != 0) {
  453.         fclose(fp);
  454.         return FALSE;
  455.     }
  456.  
  457.     /* Read the rest of the mixer settings file */
  458.     while (fgets(buf, sizeof(buf), fp) != NULL) {
  459.         if (buf[0] == '#')
  460.             /* Comment line */
  461.             continue;
  462.  
  463.         if ((p = strchr(buf, '=')) == NULL)
  464.             /* Invalid line */
  465.             continue;
  466.  
  467.         *p = '\0';
  468.  
  469.         if (sscanf(p+1, "%d,%d,%d,%d\n",
  470.                &val1, &val2, &val3, &val4) == 4) {
  471.  
  472.             for (i = 0; i < maxdevs; i++) {
  473.                 if (strcmp(buf, widgets.sl[i].name) == 0)
  474.                     break;
  475.             }
  476.  
  477.             if (i >= maxdevs)
  478.                 /* Unrecognized keyword */
  479.                 continue;
  480.  
  481.             curmask = (1 << i);
  482.  
  483.             if (!(ctlinfo.devmask & curmask))
  484.                 /* Device not supported */
  485.                 continue;
  486.  
  487.             ctlinfo.slinfo[i].left = val1;
  488.             ctlinfo.slinfo[i].right = val2;
  489.  
  490.             if (val1 == val2)
  491.                 ctlinfo.slinfo[i].locked = (bool_t) (val3 > 0);
  492.             else
  493.                 ctlinfo.slinfo[i].locked = FALSE;
  494.  
  495.             if (val4 > 0 && widgets.sl[i].recsupp &&
  496.                 (ctlinfo.recmask & curmask))
  497.                 ctlinfo.recsrc |= (1 << i);
  498.             else
  499.                 ctlinfo.recsrc &= ~(1 << i);
  500.         }
  501. #ifdef SOUND_ONOFF_MAX
  502.         else if (sscanf(p+1, "%d\n", &val1) == 1) {
  503.             if (strcmp(buf, "mute") == 0 &&
  504.                 ctlinfo.devmask & (1 << SOUND_MIXER_MUTE)) {
  505.                 ctlinfo.mute = (bool_t) (val1 > 0);
  506.             }
  507.             else if (strcmp(buf, "enhance") == 0 &&
  508.                  ctlinfo.devmask & (1 << SOUND_MIXER_ENHANCE)) {
  509.                 ctlinfo.enhance = val1;
  510.             }
  511.             else if (strcmp(buf, "loudness") == 0 &&
  512.                  ctlinfo.devmask & (1 << SOUND_MIXER_LOUD)) {
  513.                 ctlinfo.loudness = (bool_t) (val1 > 0);
  514.             }
  515.         }
  516. #endif
  517.     }
  518.  
  519.     fclose(fp);
  520.  
  521.     /* Update controls to match current status */
  522.     mx_updctl(&widgets);
  523.  
  524.     /* Set all controls */
  525.     mx_sethw(&widgets);
  526.  
  527.     return TRUE;
  528. }
  529.  
  530.  
  531. /*
  532.  * mx_writefile
  533.  *    Write current mixer settings to file.
  534.  *
  535.  * Args:
  536.  *    path - File path string
  537.  *
  538.  * Return:
  539.  *    TRUE on success, FALSE on failure
  540.  */
  541. STATIC bool_t
  542. mx_writefile(char *path)
  543. {
  544.     FILE    *fp;
  545.     int    i;
  546.  
  547.     if (app_data.debug)
  548.         fprintf(errfp, "Writing mixer file: %s\n", path);
  549.  
  550.     /* Open file for writing */
  551.     if ((fp = fopen(path, "w")) == NULL)
  552.         return FALSE;
  553.  
  554.     /* Write first two lines of mixer settings file */
  555.     fprintf(fp, "# xmmix %s Mixer Settings File\n", VERSION);
  556.     fprintf(fp, "# Copyright (C) 1995 Ti Kan\n#\n");
  557.  
  558.     /* Write all control settings */
  559.     for (i = 0; i < MAXDEVS; i++) {
  560.         if (widgets.sl[i].name == NULL)
  561.             break;
  562.  
  563.         fprintf(fp, "%s=%d,%d,%d,%d\n",
  564.             widgets.sl[i].name,
  565.             ctlinfo.slinfo[i].left,
  566.             ctlinfo.slinfo[i].right,
  567.             ctlinfo.slinfo[i].locked,
  568.             (int) ((ctlinfo.recsrc & (1 << i)) > 0));
  569.     }
  570.  
  571. #ifdef SOUND_ONOFF_MAX
  572.     fprintf(fp, "%s=%d\n", "mute", (int) ctlinfo.mute);
  573.     fprintf(fp, "%s=%d\n", "enhance", ctlinfo.enhance);
  574.     fprintf(fp, "%s=%d\n", "loudness", (int) ctlinfo.loudness);
  575. #endif
  576.  
  577.     return (fclose(fp) == 0);
  578. }
  579.  
  580.  
  581. /*
  582.  * mx_warning
  583.  *    Pop up a warning message dialog box.
  584.  *
  585.  * Args:
  586.  *    msg - The message string to display
  587.  *
  588.  * Return:
  589.  *    Nothing
  590.  */
  591. STATIC void
  592. mx_warning(char *msg)
  593. {
  594.     XmString    xs;
  595.  
  596.     xs = XmStringCreateLtoR(msg, XmSTRING_DEFAULT_CHARSET);
  597.     XtVaSetValues(widgets.warning, XmNmessageString, xs, NULL);
  598.     XmStringFree(xs);
  599.  
  600.     if (!XtIsManaged(widgets.warning))
  601.         XtManageChild(widgets.warning);
  602. }
  603.  
  604.  
  605. /***********************
  606.  *   public routines   *
  607.  ***********************/
  608.  
  609.  
  610. /*
  611.  * mx_init_drv
  612.  *    Sound driver version-specific initialization routine.
  613.  *    Query sound driver version and set appropriate capabilities.
  614.  *    Note: this routine will likely require modification if
  615.  *    future sound driver versions add new mixer features.
  616.  *
  617.  * Args:
  618.  *    None
  619.  *
  620.  * Return:
  621.  *    Nothing
  622.  */
  623. void
  624. mx_init_drv(void)
  625. {
  626.     FILE    *fp;
  627.     char    *p,
  628.         tmpbuf[128];
  629.  
  630.     if (app_data.debug) {
  631.         fprintf(errfp, "XMMIX v%s%s PL%d DEBUG MODE\n\n",
  632.             VERSION, VERSION_EXT, PATCHLEVEL);
  633.  
  634.         if (app_data.demo)
  635.             fprintf(errfp, "DEMO MODE!!!\n");
  636.         fprintf(errfp, "Compiled with soundcard.h version %d\n",
  637.             SOUND_VERSION);
  638.         fprintf(errfp, "device=%s\n", app_data.device);
  639.         fprintf(errfp, "helpPath=%s\n", app_data.helppath);
  640.         fprintf(errfp, "autoLoadOnStartUp=%s\n",
  641.             app_data.autoload == NULL ? "" : app_data.autoload);
  642.         fprintf(errfp, "resetOnExit=%s\n",
  643.             app_data.exitreset ? "True" : "False");
  644.     }
  645.  
  646.     /* Set maximum number of devices supported */
  647.     maxdevs = MAXDEVS_V3;
  648.  
  649.     strcpy(drv_ver, "unknown");
  650.  
  651.     /* Query sound driver version */
  652.     if ((fp = fopen("/dev/sndstat", "r")) == NULL) {
  653.         if (!app_data.demo) {
  654.             fprintf(errfp, "%s: %s: %s\n",
  655.                 PROGNAME,
  656.                 "Cannot open /dev/sndstat",
  657.                 "assuming pre-3.0 VoxWare driver.");
  658.             maxdevs = MAXDEVS_V2;
  659.         }
  660.         return;
  661.     }
  662.  
  663.     if (fgets(tmpbuf, sizeof(tmpbuf), fp) == NULL) {
  664.         if (!app_data.demo) {
  665.             fprintf(errfp, "%s: %s: %s\n",
  666.                 PROGNAME,
  667.                 "Cannot read /dev/sndstat",
  668.                 "assuming pre-3.0 VoxWare driver.");
  669.             maxdevs = MAXDEVS_V2;
  670.         }
  671.         fclose(fp);
  672.         return;
  673.     }
  674.  
  675.     fclose(fp);
  676.  
  677.     /* Eat newline */
  678.     tmpbuf[strlen(tmpbuf) - 1] = '\0';
  679.  
  680.     /* Skip forward to the first colon character */
  681.     for (p = tmpbuf; *p != '\0' && *p != ':'; p++)
  682.         ;
  683.  
  684.     if (*p != ':') {
  685.         if (!app_data.demo) {
  686.             fprintf(errfp, "%s: %s: %s\n",
  687.                 PROGNAME,
  688.                 "Cannot parse info from /dev/sndstat",
  689.                 "assuming pre-3.0 VoxWare driver.");
  690.             maxdevs = MAXDEVS_V2;
  691.         }
  692.         strcpy(drv_ver, tmpbuf);
  693.         return;
  694.     }
  695.  
  696.     /* Skip blanks */
  697.     p++;
  698.     while (*p == ' ' || *p == '\t')
  699.         p++;
  700.  
  701.     strcpy(drv_ver, p);
  702.  
  703.     if (!app_data.demo && *p < '3')
  704.         /* Running on VoxWare 2.x: set max devices accordingly */
  705.         maxdevs = MAXDEVS_V2;
  706.  
  707.     if (app_data.debug) {
  708.         fprintf(errfp, "Sound driver [%s]\n", drv_ver);
  709.         fprintf(errfp, "Running with maxdevs=%d\n\n", maxdevs);
  710.     }
  711. }
  712.  
  713.  
  714. /*
  715.  * mx_init_hw
  716.  *    Pre-realize mixer initialization routine.
  717.  *    Query sound board mixer feature capabilities and current
  718.  *    hardware settings.
  719.  *
  720.  * Args:
  721.  *    m - Pointer to the main widgets structure
  722.  *
  723.  * Return:
  724.  *    Nothing
  725.  */
  726. void
  727. mx_init_hw(widgets_t *m)
  728. {
  729.     int    i,
  730.         level,
  731.         curmask;
  732.     char    errmsg[STR_BUF_SZ],
  733.         iocstr[STR_BUF_SZ];
  734.  
  735.     if (app_data.demo) {
  736.         /* Fake all capabilities */
  737.         for (i = 0; i < maxdevs; i++) {
  738.             curmask = (1 << i);
  739.  
  740.             ctlinfo.devmask |= curmask;
  741.             ctlinfo.recmask |= curmask;
  742.             ctlinfo.stereodevs |= curmask;
  743.  
  744.             m->sl[i].supp = TRUE;
  745.             if (m->sl[i].type == CTL_INPUT)
  746.                 m->sl[i].recsupp = TRUE;
  747.             else
  748.                 m->sl[i].recsupp = FALSE;
  749.         }
  750.  
  751. #ifdef SOUND_ONOFF_MAX
  752.         for (i = SOUND_ONOFF_MIN; i <= SOUND_ONOFF_MAX; i++)
  753.             ctlinfo.devmask |= (1 << i);
  754.  
  755.         m->mute_supp = TRUE;
  756.         m->loud_supp = TRUE;
  757.         m->enh_supp = TRUE;
  758. #endif
  759.  
  760.         /* Save start-up settings */
  761.         ctlsav = ctlinfo;    /* Structure copy */
  762.         return;
  763.     }
  764.  
  765.     /* Open device */
  766.     sprintf(errmsg, "%s: Cannot open device %s",
  767.         PROGNAME, app_data.device);
  768.     if ((dev_fd = open(app_data.device, O_RDONLY)) < 0) {
  769.         perror(errmsg);
  770.         exit_flag = TRUE;
  771.     }
  772.  
  773.     /* Read device mask */
  774.     do_ioctl(
  775.         SOUND_MIXER_READ_DEVMASK,
  776.         &ctlinfo.devmask,
  777.         "SOUND_MIXER_READ_DEVMASK",
  778.         IOC_DATA_IN
  779.     );
  780.  
  781.     /* Read record mask */
  782.     do_ioctl(
  783.         SOUND_MIXER_READ_RECMASK,
  784.         &ctlinfo.recmask,
  785.         "SOUND_MIXER_READ_RECMASK",
  786.         IOC_DATA_IN
  787.     );
  788.  
  789.     /* Read stereo devices */
  790.     do_ioctl(
  791.         SOUND_MIXER_READ_STEREODEVS,
  792.         &ctlinfo.stereodevs,
  793.         "SOUND_MIXER_READ_STEREODEVS",
  794.         IOC_DATA_IN
  795.     );
  796.  
  797. #ifdef SOUND_MIXER_CAPS
  798.     /* Check mixer capability */
  799.     do_ioctl(
  800.         SOUND_MIXER_READ_CAPS,
  801.         &ctlinfo.caps,
  802.         "SOUND_MIXER_READ_CAPS",
  803.         IOC_DATA_IN
  804.     );
  805. #endif
  806.  
  807.     /* Set flags */
  808.     for (i = 0; i < maxdevs; i++) {
  809.         curmask = (1 << i);
  810.  
  811.         if ((curmask & ctlinfo.devmask) == 0)
  812.             m->sl[i].supp = FALSE;
  813.         else
  814.             m->sl[i].supp = TRUE;
  815.  
  816.         if (m->sl[i].type == CTL_INPUT && (curmask & ctlinfo.recmask))
  817.             m->sl[i].recsupp = TRUE;
  818.         else
  819.             m->sl[i].recsupp = FALSE;
  820.     }
  821.  
  822. #ifdef SOUND_ONOFF_MAX
  823.     /* Additional toggle capabilitites */
  824.     for (i = SOUND_ONOFF_MIN; i <= SOUND_ONOFF_MAX; i++) {
  825.         curmask = (1 << i);
  826.  
  827.         switch (i) {
  828.         case SOUND_MIXER_MUTE:
  829.             if (curmask & ctlinfo.devmask)
  830.                 m->mute_supp = TRUE;
  831.             else
  832.                 m->mute_supp = FALSE;
  833.             break;
  834.         case SOUND_MIXER_LOUD:
  835.             if (curmask & ctlinfo.devmask)
  836.                 m->loud_supp = TRUE;
  837.             else
  838.                 m->loud_supp = FALSE;
  839.             break;
  840.         case SOUND_MIXER_ENHANCE:
  841.             if (curmask & ctlinfo.devmask)
  842.                 m->enh_supp = TRUE;
  843.             else
  844.                 m->enh_supp = FALSE;
  845.             break;
  846.         default:
  847.             /* Unsupported feature: ignore */
  848.             break;
  849.         }
  850.     }
  851. #else
  852.     m->mute_supp = FALSE;
  853.     m->loud_supp = FALSE;
  854.     m->enh_supp = FALSE;
  855. #endif    /* SOUND_ONOFF_MAX */
  856. }
  857.  
  858.  
  859. /*
  860.  * mx_start
  861.  *    Post-realize mixer initialization routine
  862.  *
  863.  * Args:
  864.  *    m - Pointer to the main widgets structure
  865.  *
  866.  * Return:
  867.  *    Nothing
  868.  */
  869. void
  870. mx_start(widgets_t *m)
  871. {
  872.     char    msg[256];
  873.  
  874.     if (app_data.autoload != NULL && app_data.autoload[0] != '\0' &&
  875.         strcmp(app_data.autoload, "/dev/null") != 0) {
  876.         if (mx_readfile(app_data.autoload)) {
  877.             /* Successful auto-load */
  878.             return;
  879.         }
  880.         else {
  881.             sprintf(msg,
  882.                 "Autoload: Cannot read mixer settings file:\n%s",
  883.                 app_data.autoload);
  884.             mx_warning(msg);
  885.         }
  886.     }
  887.  
  888.     /* Update screen controls to match current status */
  889.     mx_updctl(m);
  890. }
  891.  
  892.  
  893. /**************** vv Callback routines vv ****************/
  894.  
  895.  
  896. /*
  897.  * mx_slider_l
  898.  *    Left slider callback routine
  899.  */
  900. /*ARGSUSED*/
  901. void
  902. mx_slider_l(Widget w, XtPointer client_data, XtPointer call_data)
  903. {
  904.     int            level,
  905.                 i = (int)(void *) client_data;
  906.     XmScaleCallbackStruct    *p =
  907.         (XmScaleCallbackStruct *)(void *) call_data;
  908.     char            iocstr[STR_BUF_SZ];
  909.  
  910.     ctlinfo.slinfo[i].left = p->value;
  911.  
  912.     if (ctlinfo.slinfo[i].locked) {
  913.         ctlinfo.slinfo[i].right = p->value;
  914.         XmScaleSetValue(widgets.sl[i].widget_r, p->value);
  915.     }
  916.  
  917.     level = (ctlinfo.slinfo[i].right << 8) | ctlinfo.slinfo[i].left;
  918.  
  919.     sprintf(iocstr, "MIXER_WRITE[%d]:%s", i, widgets.sl[i].name);
  920.     do_ioctl(MIXER_WRITE(i), &level, iocstr, IOC_DATA_OUT);
  921. }
  922.  
  923.  
  924. /*
  925.  * mx_slider_r
  926.  *    Right slider callback routine
  927.  */
  928. /*ARGSUSED*/
  929. void
  930. mx_slider_r(Widget w, XtPointer client_data, XtPointer call_data)
  931. {
  932.     int            level,
  933.                 i = (int)(void *) client_data;
  934.     XmScaleCallbackStruct    *p =
  935.         (XmScaleCallbackStruct *)(void *) call_data;
  936.     char            iocstr[STR_BUF_SZ];
  937.  
  938.     ctlinfo.slinfo[i].right = p->value;
  939.  
  940.     if (ctlinfo.slinfo[i].locked) {
  941.         ctlinfo.slinfo[i].left = p->value;
  942.         XmScaleSetValue(widgets.sl[i].widget_l, p->value);
  943.     }
  944.  
  945.     level = (ctlinfo.slinfo[i].right << 8) | ctlinfo.slinfo[i].left;
  946.  
  947.     sprintf(iocstr, "MIXER_WRITE[%d]:%s", i, widgets.sl[i].name);
  948.     do_ioctl(MIXER_WRITE(i), &level, iocstr, IOC_DATA_OUT);
  949. }
  950.  
  951.  
  952. /*
  953.  * mx_lock_btn
  954.  *    Slider lock button callback routine
  955.  */
  956. void
  957. mx_lock_btn(Widget w, XtPointer client_data, XtPointer call_data)
  958. {
  959.     int                level,
  960.                     i = (int)(void *) client_data;
  961.     XmToggleButtonCallbackStruct    *p =
  962.         (XmToggleButtonCallbackStruct *)(void *) call_data;
  963.     char                iocstr[STR_BUF_SZ];
  964.  
  965.     if (ctlinfo.slinfo[i].locked == p->set)
  966.         /* No change */
  967.         return;
  968.  
  969.     if (p->set) {
  970.         ctlinfo.slinfo[i].locked = TRUE;
  971.         ctlinfo.slinfo[i].left = ctlinfo.slinfo[i].right =
  972.             (ctlinfo.slinfo[i].left + ctlinfo.slinfo[i].right) / 2;
  973.  
  974.         XmScaleSetValue(
  975.             widgets.sl[i].widget_l,
  976.             ctlinfo.slinfo[i].left
  977.         );
  978.         XmScaleSetValue(
  979.             widgets.sl[i].widget_r,
  980.             ctlinfo.slinfo[i].right
  981.         );
  982.  
  983.         level = (ctlinfo.slinfo[i].right << 8) | ctlinfo.slinfo[i].left;
  984.  
  985.         sprintf(iocstr, "MIXER_WRITE[%d]:%s", i, widgets.sl[i].name);
  986.         do_ioctl(MIXER_WRITE(i), &level, iocstr, IOC_DATA_OUT);
  987.     }
  988.     else {
  989.         if ((1 << i) & ctlinfo.stereodevs)
  990.             ctlinfo.slinfo[i].locked = FALSE;
  991.         else {
  992.             XmToggleButtonSetState(w, (Boolean) !p->set, False);
  993.             XBell(XtDisplay(w), 50);
  994.         }
  995.     }
  996. }
  997.  
  998.  
  999. /*
  1000.  * mx_rec_btn
  1001.  *    Record source select button callback routine
  1002.  */
  1003. void
  1004. mx_rec_btn(Widget w, XtPointer client_data, XtPointer call_data)
  1005. {
  1006.     int                i = (int)(void *) client_data,
  1007.                     j,
  1008.                     curmask;
  1009.     XmToggleButtonCallbackStruct    *p =
  1010.         (XmToggleButtonCallbackStruct *)(void *) call_data;
  1011.  
  1012. #ifdef SOUND_CAP_EXCL_INPUT
  1013.     if (ctlinfo.caps == SOUND_CAP_EXCL_INPUT) {
  1014.         if (!p->set) {
  1015.             XmToggleButtonSetState(w, True, False);
  1016.             return;
  1017.         }
  1018.  
  1019.         for (j = 0; j < maxdevs; j++) {
  1020.             if (!widgets.sl[j].supp || i == j)
  1021.                 continue;
  1022.  
  1023.             curmask = (1 << j);
  1024.  
  1025.             if ((curmask & ctlinfo.recmask) &&
  1026.                 (curmask & ctlinfo.recsrc)) {
  1027.                 XmToggleButtonSetState(
  1028.                     widgets.sl[j].widget_rec_btn,
  1029.                     False,
  1030.                     False
  1031.                 );
  1032.             }
  1033.  
  1034.         }
  1035.  
  1036.         ctlinfo.recsrc = 0;
  1037.     }
  1038. #endif
  1039.  
  1040.     if (p->set)
  1041.         ctlinfo.recsrc |= (1 << i);
  1042.     else
  1043.         ctlinfo.recsrc &= ~(1 << i);
  1044.  
  1045.     do_ioctl(
  1046.         SOUND_MIXER_WRITE_RECSRC,
  1047.         &ctlinfo.recsrc,
  1048.         "SOUND_MIXER_WRITE_RECSRC",
  1049.         IOC_DATA_OUT
  1050.     );
  1051. }
  1052.  
  1053.  
  1054. /*
  1055.  * mx_flat_btn
  1056.  *    Bass/Treble Flat button callback routine.
  1057.  */
  1058. /*ARGSUSED*/
  1059. void
  1060. mx_flat_btn(Widget w, XtPointer client_data, XtPointer call_data)
  1061. {
  1062.     int    level;
  1063.  
  1064.     ctlinfo.slinfo[SOUND_MIXER_BASS].left = 50;
  1065.     ctlinfo.slinfo[SOUND_MIXER_BASS].right = 50;
  1066.     ctlinfo.slinfo[SOUND_MIXER_TREBLE].left = 50;
  1067.     ctlinfo.slinfo[SOUND_MIXER_TREBLE].right = 50;
  1068.  
  1069.     XmScaleSetValue(widgets.sl[SOUND_MIXER_BASS].widget_l, 50);
  1070.     XmScaleSetValue(widgets.sl[SOUND_MIXER_BASS].widget_r, 50);
  1071.     XmScaleSetValue(widgets.sl[SOUND_MIXER_TREBLE].widget_l, 50);
  1072.     XmScaleSetValue(widgets.sl[SOUND_MIXER_TREBLE].widget_r, 50);
  1073.  
  1074.     level = (50 << 8) | 50;
  1075.  
  1076.     do_ioctl(
  1077.         SOUND_MIXER_WRITE_BASS,
  1078.         &level,
  1079.         "SOUND_MIXER_WRITE_BASS",
  1080.         IOC_DATA_OUT
  1081.     );
  1082.     do_ioctl(
  1083.         SOUND_MIXER_WRITE_TREBLE,
  1084.         &level,
  1085.         "SOUND_MIXER_WRITE_TREBLE",
  1086.         IOC_DATA_OUT
  1087.     );
  1088. }
  1089.  
  1090.  
  1091. /*
  1092.  * mx_mute_btn
  1093.  *    Mute button callback routine.
  1094.  */
  1095. /*ARGSUSED*/
  1096. void
  1097. mx_mute_btn(Widget w, XtPointer client_data, XtPointer call_data)
  1098. {
  1099.     int                level;
  1100.     XmToggleButtonCallbackStruct    *p =
  1101.         (XmToggleButtonCallbackStruct *)(void *) call_data;
  1102.  
  1103.     if (ctlinfo.mute == p->set)
  1104.         /* No change */
  1105.         return;
  1106.  
  1107.     ctlinfo.mute = p->set;
  1108.  
  1109.     level = (int) p->set;
  1110.  
  1111. #ifdef SOUND_MIXER_WRITE_MUTE
  1112.     do_ioctl(
  1113.         SOUND_MIXER_WRITE_MUTE,
  1114.         &level,
  1115.         "SOUND_MIXER_WRITE_MUTE",
  1116.         IOC_DATA_OUT
  1117.     );
  1118. #endif
  1119. }
  1120.  
  1121.  
  1122. /*
  1123.  * mx_loud_btn
  1124.  *    Loudness button callback routine.
  1125.  */
  1126. /*ARGSUSED*/
  1127. void
  1128. mx_loud_btn(Widget w, XtPointer client_data, XtPointer call_data)
  1129. {
  1130.     int                level;
  1131.     XmToggleButtonCallbackStruct    *p =
  1132.         (XmToggleButtonCallbackStruct *)(void *) call_data;
  1133.  
  1134.     if (ctlinfo.loudness == p->set)
  1135.         /* No change */
  1136.         return;
  1137.  
  1138.     ctlinfo.loudness = p->set;
  1139.  
  1140.     level = (int) p->set;
  1141.  
  1142. #ifdef SOUND_MIXER_WRITE_LOUD
  1143.     do_ioctl(
  1144.         SOUND_MIXER_WRITE_LOUD,
  1145.         &level,
  1146.         "SOUND_MIXER_WRITE_LOUD",
  1147.         IOC_DATA_OUT
  1148.     );
  1149. #endif
  1150. }
  1151.  
  1152.  
  1153. /*
  1154.  * mx_enhance_btn
  1155.  *    Stereo enhance button callback routine.
  1156.  */
  1157. /*ARGSUSED*/
  1158. void
  1159. mx_enhance_btn(Widget w, XtPointer client_data, XtPointer call_data)
  1160. {
  1161.     int    level,
  1162.         i = (int)(void *) client_data;
  1163.  
  1164.     if (ctlinfo.enhance == i)
  1165.         /* No change */
  1166.         return;
  1167.  
  1168.     ctlinfo.enhance = i;
  1169.  
  1170.     /* Hack: The enhance values are hard wired */
  1171.     switch (i) {
  1172.     case 0:
  1173.         level = 0;
  1174.         break;
  1175.     case 1:
  1176.         level = 40;
  1177.         break;
  1178.     case 2:
  1179.         level = 60;
  1180.         break;
  1181.     case 3:
  1182.         level = 80;
  1183.         break;
  1184.     default:
  1185.         return;
  1186.     }
  1187.  
  1188. #ifdef SOUND_MIXER_WRITE_ENHANCE
  1189.     do_ioctl(
  1190.         SOUND_MIXER_WRITE_ENHANCE,
  1191.         &level,
  1192.         "SOUND_MIXER_WRITE_ENHANCE",
  1193.         IOC_DATA_OUT
  1194.     );
  1195. #endif
  1196. }
  1197.  
  1198.  
  1199. /*
  1200.  * mx_load
  1201.  *    Load button callback routine.
  1202.  */
  1203. /*ARGSUSED*/
  1204. void
  1205. mx_load(Widget w, XtPointer client_data, XtPointer call_data)
  1206. {
  1207.     XmString    xs;
  1208.  
  1209.     /* Pop up file selection box window */
  1210.     if (!XtIsManaged(widgets.fsform)) {
  1211.         fsmode = FS_LOAD;
  1212.         xs = XmStringCreateSimple("Load Mixer Settings");
  1213.         XtVaSetValues(widgets.fsform, XmNdialogTitle, xs, NULL);
  1214.         XtManageChild(widgets.fsform);
  1215.         XmStringFree(xs);
  1216.     }
  1217. }
  1218.  
  1219.  
  1220. /*
  1221.  * mx_save
  1222.  *    Save button callback routine.
  1223.  */
  1224. /*ARGSUSED*/
  1225. void
  1226. mx_save(Widget w, XtPointer client_data, XtPointer call_data)
  1227. {
  1228.     XmString    xs;
  1229.  
  1230.     /* Pop up file selection box window */
  1231.     if (!XtIsManaged(widgets.fsform)) {
  1232.         fsmode = FS_SAVE;
  1233.         xs = XmStringCreateSimple("Save Mixer Settings");
  1234.         XtVaSetValues(widgets.fsform, XmNdialogTitle, xs, NULL);
  1235.         XtManageChild(widgets.fsform);
  1236.         XmStringFree(xs);
  1237.     }
  1238. }
  1239.  
  1240.  
  1241. /*
  1242.  * mx_exit
  1243.  *    Exit button callback routine.
  1244.  */
  1245. /*ARGSUSED*/
  1246. void
  1247. mx_exit(Widget w, XtPointer client_data, XtPointer call_data)
  1248. {
  1249.     if (app_data.exitreset)
  1250.         mx_reset(w, client_data, call_data);
  1251.  
  1252.     exit_flag = TRUE;
  1253. }
  1254.  
  1255.  
  1256. /*
  1257.  * mx_reset
  1258.  *    Reset button callback routine.
  1259.  */
  1260. /*ARGSUSED*/
  1261. void
  1262. mx_reset(Widget w, XtPointer client_data, XtPointer call_data)
  1263. {
  1264.     if (app_data.debug)
  1265.         fprintf(errfp, "Resetting mixer\n");
  1266.  
  1267.     /* Restore start-up settings */
  1268.     ctlinfo = ctlsav;    /* Structure copy */
  1269.  
  1270.     /* Update controls to match current status */
  1271.     mx_updctl(&widgets);
  1272.  
  1273.     /* Set all controls */
  1274.     mx_sethw(&widgets);
  1275. }
  1276.  
  1277.  
  1278. /*
  1279.  * mx_manpg
  1280.  *    Man Page button callback routine.
  1281.  */
  1282. /*ARGSUSED*/
  1283. void
  1284. mx_manpg(Widget w, XtPointer client_data, XtPointer call_data)
  1285. {
  1286.     FILE        *fp;
  1287.     char        *helptext = NULL,
  1288.             buf[STR_BUF_SZ * 2];
  1289.  
  1290.     if (XtIsManaged(widgets.helpform))
  1291.         return;
  1292.  
  1293.     if ((fp = fopen(app_data.helppath, "r")) == NULL) {
  1294.         /* Can't read help file on this widget */
  1295.         sprintf(buf, "Cannot open help file: %s", app_data.helppath);
  1296.         XmTextSetString(widgets.helptxt, buf);
  1297.         XtManageChild(widgets.helpform);
  1298.         return;
  1299.     }
  1300.  
  1301.     while (fgets(buf, sizeof(buf), fp) != NULL) {
  1302.         if (buf[0] == '#')
  1303.             /* Comment */
  1304.             continue;
  1305.  
  1306.         if (helptext == NULL) {
  1307.             helptext = (char *) XtMalloc(strlen(buf) + 1);
  1308.  
  1309.             if (helptext != NULL)
  1310.                 *helptext = '\0';
  1311.         }
  1312.         else {
  1313.             helptext = (char *) XtRealloc(
  1314.                 helptext,
  1315.                 strlen(helptext) + strlen(buf) + 1
  1316.             );
  1317.         }
  1318.  
  1319.         if (helptext == NULL) {
  1320.             fprintf(errfp, "%s: Out of memory\n", PROGNAME);
  1321.             exit_flag = TRUE;
  1322.         }
  1323.  
  1324.         strcat(helptext, buf);
  1325.     }
  1326.  
  1327.     fclose(fp);
  1328.  
  1329.     XmTextSetString(widgets.helptxt, helptext);
  1330.     XtFree(helptext);
  1331.     XtManageChild(widgets.helpform);
  1332. }
  1333.  
  1334.  
  1335. /*
  1336.  * mx_about
  1337.  *    About button callback routine.
  1338.  */
  1339. /*ARGSUSED*/
  1340. void
  1341. mx_about(Widget w, XtPointer client_data, XtPointer call_data)
  1342. {
  1343.     char        txt[512];
  1344.     XmString    xs,
  1345.             xs_progname,
  1346.             xs_desc,
  1347.             xs_info,
  1348.             xs_tmp;
  1349.  
  1350.     if (XtIsManaged(widgets.about))
  1351.         return;
  1352.  
  1353.     xs_progname = XmStringCreateLtoR(PROGNAME, CHSET1);
  1354.  
  1355.     sprintf(txt, "   v%s%s PL%d\n%s\n%s\n%s\n\n",
  1356.         VERSION,
  1357.         VERSION_EXT,
  1358.         PATCHLEVEL,
  1359.         "Motif(tm) Audio Mixer",
  1360.         "Copyright (C) 1995  Ti Kan",
  1361.         "E-mail: ti@amb.org");
  1362.  
  1363.     xs_desc = XmStringCreateLtoR(txt, CHSET2);
  1364.  
  1365.     sprintf(txt, "%s %d\n\nDevice: %s\nDriver: %s\n\n%s\n%s\n",
  1366.         "Compiled with VoxWare soundcard.h version",
  1367.         SOUND_VERSION,
  1368.         app_data.demo ?
  1369.         "DEMO MODE - does not operate sound hardware" :
  1370.         app_data.device,
  1371.         drv_ver,
  1372.         "This is free software and comes with no warranty.",
  1373.         "See the GNU General Public License for details.");
  1374.  
  1375.     xs_info = XmStringCreateLtoR(txt, CHSET3);
  1376.  
  1377.     /* Set the dialog box message */
  1378.     xs_tmp = XmStringConcat(xs_progname, xs_desc);
  1379.     xs = XmStringConcat(xs_tmp, xs_info);
  1380.  
  1381.     XtVaSetValues(widgets.about, XmNmessageString, xs, NULL);
  1382.     XmStringFree(xs_progname);
  1383.     XmStringFree(xs_desc);
  1384.     XmStringFree(xs_info);
  1385.     XmStringFree(xs_tmp);
  1386.     XmStringFree(xs);
  1387.  
  1388.     /* Pop up the about dialog box */
  1389.     XtManageChild(widgets.about);
  1390. }
  1391.  
  1392.  
  1393. /*
  1394.  * mx_fsok_btn
  1395.  *    File selection box OK button callback routine.
  1396.  */
  1397. /*ARGSUSED*/
  1398. void
  1399. mx_fsok_btn(Widget w, XtPointer client_data, XtPointer call_data)
  1400. {
  1401.     XmFileSelectionBoxCallbackStruct    *p =
  1402.         (XmFileSelectionBoxCallbackStruct *)(void *) call_data;
  1403.     struct stat                stbuf;
  1404.     char                    *file,
  1405.                         msg[256];
  1406.  
  1407.     if (!XmStringGetLtoR(p->value, XmSTRING_DEFAULT_CHARSET, &file) ||
  1408.         file == NULL) {
  1409.         XBell(XtDisplay(w), 50);
  1410.         return;
  1411.     }
  1412.  
  1413.     if (stat(file, &stbuf) < 0) {
  1414.         if (errno != ENOENT) {
  1415.             sprintf(msg, "Invalid mixer settings file:\n%s",
  1416.                 file);
  1417.             mx_warning(msg);
  1418.             return;
  1419.         }
  1420.     }
  1421.     else {
  1422.         switch (stbuf.st_mode & S_IFMT) {
  1423.         case S_IFREG:
  1424.             break;
  1425.         default:
  1426.             sprintf(msg, "Invalid mixer settings file:\n%s",
  1427.                 file);
  1428.             mx_warning(msg);
  1429.             return;
  1430.         }
  1431.     }
  1432.  
  1433.     switch (fsmode) {
  1434.     case FS_LOAD:
  1435.         if (!mx_readfile(file)) {
  1436.             sprintf(msg, "Cannot read mixer settings file:\n%s",
  1437.                 file);
  1438.             mx_warning(msg);
  1439.             return;
  1440.         }
  1441.         break;
  1442.     case FS_SAVE:
  1443.         if (!mx_writefile(file)) {
  1444.             sprintf(msg, "Cannot write mixer settings file:\n%s",
  1445.                 file);
  1446.             mx_warning(msg);
  1447.             return;
  1448.         }
  1449.         break;
  1450.     default:
  1451.         XBell(XtDisplay(w), 50);
  1452.         return;
  1453.     }
  1454.  
  1455.     /* Pop down file selection box window */
  1456.     if (XtIsManaged(widgets.fsform))
  1457.         XtUnmanageChild(widgets.fsform);
  1458. }
  1459.  
  1460.  
  1461. /*
  1462.  * mx_fscancel_btn
  1463.  *    File selection box Cancel button callback routine.
  1464.  */
  1465. /*ARGSUSED*/
  1466. void
  1467. mx_fscancel_btn(Widget w, XtPointer client_data, XtPointer call_data)
  1468. {
  1469.     /* Pop down file selection box window */
  1470.     if (XtIsManaged(widgets.fsform))
  1471.         XtUnmanageChild(widgets.fsform);
  1472. }
  1473.  
  1474.  
  1475. /*
  1476.  * mx_focuschg
  1477.  *    Main window focus change callback routine.
  1478.  */
  1479. /*ARGSUSED*/
  1480. void
  1481. mx_focuschg(Widget w, XtPointer client_data, XtPointer call_data)
  1482. {
  1483.     XmAnyCallbackStruct    *p = (XmAnyCallbackStruct *)(void *) call_data;
  1484.     Widget            form = (Widget) client_data;
  1485.  
  1486.     if (p->reason != XmCR_FOCUS || form == (Widget) NULL)
  1487.         return;
  1488.  
  1489.     /* Query hardware mixer settings */
  1490.     mx_queryhw(&widgets);
  1491.  
  1492.     /* Update screen controls */
  1493.     mx_updctl(&widgets);
  1494. }
  1495.  
  1496.  
  1497. /**************** ^^ Callback routines ^^ ****************/
  1498.  
  1499.