]> git.alsa-project.org Git - alsa-utils.git/commitdiff
Merged new-mixer branch...
authorJaroslav Kysela <perex@perex.cz>
Mon, 8 Mar 1999 16:51:53 +0000 (16:51 +0000)
committerJaroslav Kysela <perex@perex.cz>
Mon, 8 Mar 1999 16:51:53 +0000 (16:51 +0000)
14 files changed:
README
acinclude.m4 [new file with mode: 0644]
alsactl/alsactl.h
alsactl/alsactl_lexer.l
alsactl/alsactl_parser.y
alsactl/setup.c
alsamixer/alsamixer.c
alsamixer/alsamixer_tim.c [new file with mode: 0644]
amixer/amixer.cpp
amixer/amixer.h
aplay/aplay.1
aplay/aplay.c
configure.in
version [deleted file]

diff --git a/README b/README
index ecf5a008faf5d39f9815c74f66f4e0d2463a97bf..64c9294a074ce7634dd80d3a48e59cd586fc108c 100644 (file)
--- a/README
+++ b/README
@@ -6,6 +6,7 @@ This packages contains command line utilities for the ALSA project.
 Package should be compiled only with installed ALSA driver and
 ALSA C library.
 
+alsactl                - utility for store / restore of soundcard settings
 aplay/arecord  - utility for playback / record of .wav,.voc,.au files
 amixer         - a command line mixer
 alsamixer      - ncurses mixer
diff --git a/acinclude.m4 b/acinclude.m4
new file mode 100644 (file)
index 0000000..a7bfd8d
--- /dev/null
@@ -0,0 +1,12 @@
+AC_DEFUN(SAVE_UTIL_VERSION, [
+SND_UTIL_VERSION=$VERSION
+echo $VERSION > $srcdir/version
+AC_DEFINE_UNQUOTED(VERSION, "$SND_UTIL_VERSION")
+AC_SUBST(SND_UTIL_VERSION)
+SND_UTIL_MAJOR=`echo $VERSION | cut -d . -f 1`
+AC_SUBST(SND_UTIL_MAJOR)
+SND_UTIL_MINOR=`echo $VERSION | cut -d . -f 2`
+AC_SUBST(SND_UTIL_MINOR)
+SND_UTIL_SUBMINOR=`echo $VERSION | cut -d . -f 3 | sed -e 's/pre[[0-9]]*//g'`
+AC_SUBST(SND_UTIL_SUBMINOR)
+])
index e17f9ef6f7fe89edcc188acb055705ade0242b85..271e98ae2738f1a527c51da3e3eea1fbbae57181 100644 (file)
 
 #define ALSACTL_FILE   "/etc/asound.conf"
 
+#define LEFT 1
+#define RIGHT 2
+
+#define OUTPUT 0
+#define INPUT 1
+
 extern int debugflag;
 
 extern void error(const char *fmt,...);
@@ -44,10 +50,12 @@ struct ctl {
 
 struct mixer_channel {
        int no;
-       int change;
-       snd_mixer_channel_info_t i;
-       snd_mixer_channel_t c;
-       snd_mixer_channel_t cr;
+       int direction;
+       int voice;
+       snd_mixer_channel_info_t info;
+       snd_mixer_channel_t data;
+       snd_mixer_channel_direction_info_t dinfo[2];
+       snd_mixer_channel_direction_t ddata[2];
        struct mixer_channel *next;
 };
 
index a0e37128aaad00a0ee2f32e2db9f412da7202698..8d7f962823ac0b5824e52079f56a1bec106e9801 100644 (file)
@@ -72,8 +72,7 @@ gstatus                       return L_GSTATUS;
 enable                 return L_ENABLE;
 disable                        return L_DISABLE;
 mute                   return L_MUTE;
-swout                  return L_SWOUT;
-swin                   return L_SWIN;
+swap                   return L_SWAP;
 
        /* boolean */
 
index 5948eed1db397f3e83d575f5b506da5d458077cb..eee80c7a0555c37ef0f58d2cc2211aa326a58d72 100644 (file)
@@ -42,10 +42,12 @@ static void select_pcm(char *name);
 static void select_rawmidi(char *name);
 
 static void select_mixer_channel(char *name);
-static void set_mixer_channel(int left, int right);
-static void set_mixer_channel_record(int left, int right);
-static void set_mixer_channel_flags(unsigned int mask, unsigned int flags);
-static void set_mixer_channel_end(void);
+static void select_mixer_direction(int direction);
+static void select_mixer_voice(int voice);
+static void set_mixer_volume(int volume);
+static void set_mixer_flags(int flags);
+static void select_mixer_channel_end(void);
+
 
 #define SWITCH_CONTROL         0
 #define SWITCH_MIXER           1
@@ -70,8 +72,7 @@ static struct soundcard *Xsoundcard = NULL;
 static struct mixer *Xmixer = NULL;
 static struct pcm *Xpcm = NULL;
 static struct rawmidi *Xrawmidi = NULL;
-static struct mixer_channel *Xmixerchannel = NULL;
-static unsigned int Xmixerchannelflags = 0;
+static struct mixer_channel *Xchannel = NULL;
 static int Xswitchtype = SWITCH_CONTROL;
 static int *Xswitchchange = NULL;
 static void *Xswitch = NULL;
@@ -104,7 +105,7 @@ static unsigned short Xswitchiec958ocs1[16];
 %token L_SOUNDCARD L_MIXER L_CHANNEL L_STEREO L_MONO L_SWITCH L_RAWDATA
 %token L_CONTROL L_PCM L_RAWMIDI L_PLAYBACK L_RECORD L_OUTPUT L_INPUT
 %token L_IEC958OCS L_3D L_RESET L_USER L_VALID L_DATA L_PROTECT L_PRE2
-%token L_FSUNLOCK L_TYPE L_GSTATUS L_ENABLE L_DISABLE L_MUTE L_SWOUT L_SWIN
+%token L_FSUNLOCK L_TYPE L_GSTATUS L_ENABLE L_DISABLE L_MUTE L_SWAP
 
 %type <b_value> boolean
 %type <i_value> integer
@@ -144,55 +145,42 @@ mixers    : mixer
        | mixers mixer
        ;
 
-mixer  : L_CHANNEL '(' string { select_mixer_channel( $3 ); } ',' mvalues ')' { set_mixer_channel_end(); select_mixer_channel( NULL ); }
-       | L_SWITCH '(' string { select_mixer_switch( $3 ); } ',' switches ')' { select_mixer_switch( NULL ); }
-       | error                 { yyerror( "unknown keyword in mixer{} level" ); }
-       ;
-
-mvalues : mvalue
-       | mvalues mvalue
+mixer  : L_CHANNEL '(' string  { select_mixer_channel( $3 ); } 
+         ',' settings ')'      { select_mixer_channel_end(); 
+                                 select_mixer_channel( NULL ); }
+       | L_SWITCH '(' string   { select_mixer_switch( $3 ); }
+         ',' switches ')'      { select_mixer_switch( NULL ); }
+       | error                 { yyerror( "unknown keyword in mixer level" ); }
        ;
 
-mvalue : L_STEREO '(' xmchls ',' xmchrs ')'
-       | L_MONO '(' xmchs ')'
-       | error                 { yyerror( "unknown keyword in mixer channel{} level" ); }
-       ;
 
-xmchls : xmchl
-       | xmchls xmchl
+settings: setting
+       | settings ',' setting
        ;
 
-xmchl  : L_INTEGER             { set_mixer_channel( $1, -1 ); }
-       | '[' L_INTEGER ']'     { set_mixer_channel_record( $2, -1 ); }
-       | L_MUTE                { set_mixer_channel_flags( SND_MIXER_FLG_MUTE_LEFT, SND_MIXER_FLG_MUTE_LEFT ); }
-       | L_RECORD              { set_mixer_channel_flags( SND_MIXER_FLG_RECORD_LEFT, SND_MIXER_FLG_RECORD_LEFT ); }
-       | L_SWOUT               { set_mixer_channel_flags( SND_MIXER_FLG_LTOR_OUT, SND_MIXER_FLG_LTOR_OUT ); }
-       | L_SWIN                { set_mixer_channel_flags( SND_MIXER_FLG_LTOR_IN, SND_MIXER_FLG_LTOR_IN ); }
-       | error                 { yyerror( "unknown keyword in left channel section..." ); }
+setting        : L_OUTPUT              { select_mixer_direction(OUTPUT); }
+         dsetting
+       | L_INPUT               { select_mixer_direction(INPUT); }
+         dsetting
+       | error                 { yyerror( "unknown keyword in mixer channel level" ); }
        ;
 
-xmchrs : xmchr
-       | xmchrs xmchr
+dsetting: L_STEREO '('         { select_mixer_voice(LEFT); }
+       vsettings ','           { select_mixer_voice(RIGHT); }
+       vsettings ')'
+       | L_MONO '('            { select_mixer_voice(LEFT|RIGHT); }
+         vsettings ')'
+       | error                 { yyerror( "unknown keyword in mixer direction level" ); }
        ;
 
-xmchr  : L_INTEGER             { set_mixer_channel( -1, $1 ); }
-       | '[' L_INTEGER ']'     { set_mixer_channel_record( -1, $2 ); }
-       | L_MUTE                { set_mixer_channel_flags( SND_MIXER_FLG_MUTE_RIGHT, SND_MIXER_FLG_MUTE_RIGHT ); }
-       | L_RECORD              { set_mixer_channel_flags( SND_MIXER_FLG_RECORD_RIGHT, SND_MIXER_FLG_RECORD_RIGHT ); }
-       | L_SWOUT               { set_mixer_channel_flags( SND_MIXER_FLG_RTOL_OUT, SND_MIXER_FLG_RTOL_OUT ); }
-       | L_SWIN                { set_mixer_channel_flags( SND_MIXER_FLG_RTOL_IN, SND_MIXER_FLG_RTOL_IN ); }
-       | error                 { yyerror( "unknown keyword in right channel section..." ); }
+vsettings: vsetting
+       | vsettings vsetting
        ;
 
-xmchs  : xmch
-       | xmchs xmch
-       ;
-
-xmch   : L_INTEGER             { set_mixer_channel( $1, $1 ); }
-       | '[' L_INTEGER ']'     { set_mixer_channel_record( $2, $2 ); }
-       | L_MUTE                { set_mixer_channel_flags( SND_MIXER_FLG_MUTE, SND_MIXER_FLG_MUTE ); }
-       | L_RECORD              { set_mixer_channel_flags( SND_MIXER_FLG_RECORD, SND_MIXER_FLG_RECORD ); }
-       | error                 { yyerror( "unknown keyword in mono channel section..." ); }
+vsetting: L_INTEGER            { set_mixer_volume($1); }
+       | L_MUTE                { set_mixer_flags(SND_MIXER_DFLG_MUTE); }
+       | L_SWAP                { set_mixer_flags(SND_MIXER_DFLG_SWAP); }
+       | error                 { yyerror( "unknown keyword in mixer voice level" ); }
        ;
 
 pcms   : pcm
@@ -383,19 +371,14 @@ static void select_mixer_channel(char *name)
        struct mixer_channel *channel;
 
        if (!name) {
-               Xmixerchannel = NULL;
+               Xchannel = NULL;
                return;
        }
        for (channel = Xmixer->channels; channel; channel = channel->next)
-               if (!strcmp(channel->i.name, name)) {
-                       Xmixerchannel = channel;
-                       Xmixerchannelflags = Xmixerchannel->c.flags &
-                           ~(SND_MIXER_FLG_RECORD |
-                             SND_MIXER_FLG_MUTE |
-                             SND_MIXER_FLG_SWITCH_OUT |
-                             SND_MIXER_FLG_SWITCH_IN |
-                             SND_MIXER_FLG_DECIBEL |
-                             SND_MIXER_FLG_FORCE);
+               if (!strcmp(channel->info.name, name)) {
+                       Xchannel = channel;
+                       Xchannel->ddata[OUTPUT].flags = 0;
+                       Xchannel->ddata[INPUT].flags = 0;
                        free(name);
                        return;
                }
@@ -403,54 +386,51 @@ static void select_mixer_channel(char *name)
        free(name);
 }
 
-static void set_mixer_channel(int left, int right)
+static void select_mixer_direction(int direction)
 {
-       if (left >= 0) {
-               if (Xmixerchannel->i.min > left || Xmixerchannel->i.max < left)
-                       yyerror("Value out of range (%i-%i)...", Xmixerchannel->i.min, Xmixerchannel->i.max);
-               if (Xmixerchannel->c.left != left)
-                       Xmixerchannel->change = 1;
-               Xmixerchannel->c.left = left;
-       }
-       if (right >= 0) {
-               if (Xmixerchannel->i.min > right || Xmixerchannel->i.max < right)
-                       yyerror("Value out of range (%i-%i)...", Xmixerchannel->i.min, Xmixerchannel->i.max);
-               if (Xmixerchannel->c.right != right)
-                       Xmixerchannel->change = 1;
-               Xmixerchannel->c.right = right;
-       }
+       Xchannel->direction = direction;
+}
+
+static void select_mixer_voice(int voice)
+{
+       Xchannel->voice = voice;
 }
 
-static void set_mixer_channel_record(int left, int right)
+static void set_mixer_volume(int volume)
 {
-       if (left >= 0) {
-               if (Xmixerchannel->i.min > left || Xmixerchannel->i.max < left)
-                       yyerror("Value out of range (%i-%i)...", Xmixerchannel->i.min, Xmixerchannel->i.max);
-               if (Xmixerchannel->cr.left != left)
-                       Xmixerchannel->change = 1;
-               Xmixerchannel->cr.left = left;
+       snd_mixer_channel_direction_info_t *i = &Xchannel->dinfo[Xchannel->direction];
+       snd_mixer_channel_direction_t *d = &Xchannel->ddata[Xchannel->direction];
+       if (Xchannel->voice & LEFT) {
+               if (i->min > volume || i->max < volume)
+                       yyerror("Value out of range (%i-%i)...", i->min, i->max);
+               d->left = volume;
        }
-       if (right >= 0) {
-               if (Xmixerchannel->i.min > right || Xmixerchannel->i.max < right)
-                       yyerror("Value out of range (%i-%i)...", Xmixerchannel->i.min, Xmixerchannel->i.max);
-               if (Xmixerchannel->cr.right != right)
-                       Xmixerchannel->change = 1;
-               Xmixerchannel->cr.right = right;
+       if (Xchannel->voice & RIGHT) {
+               if (i->min > volume || i->max < volume)
+                       yyerror("Value out of range (%i-%i)...", i->min, i->max);
+               d->right = volume;
        }
 }
 
-static void set_mixer_channel_flags(unsigned int mask, unsigned int flags)
+static void set_mixer_flags(int flags)
 {
-       Xmixerchannelflags &= ~mask;
-       Xmixerchannelflags |= flags;
+       snd_mixer_channel_direction_t *d = &Xchannel->ddata[Xchannel->direction];
+       if (Xchannel->voice & LEFT) {
+               if (flags & SND_MIXER_DFLG_MUTE)
+                       d->flags |= SND_MIXER_DFLG_MUTE_LEFT;
+               if (flags & SND_MIXER_DFLG_SWAP)
+                       d->flags |= SND_MIXER_DFLG_LTOR;
+       }
+       if (Xchannel->voice & RIGHT) {
+               if (flags & SND_MIXER_DFLG_MUTE)
+                       d->flags |= SND_MIXER_DFLG_MUTE_RIGHT;
+               if (flags & SND_MIXER_DFLG_SWAP)
+                       d->flags |= SND_MIXER_DFLG_RTOL;
+       }
 }
 
-static void set_mixer_channel_end(void)
+static void select_mixer_channel_end(void)
 {
-       if (Xmixerchannel->c.flags != Xmixerchannelflags) {
-               Xmixerchannel->change = 1;
-       }
-       Xmixerchannel->c.flags = Xmixerchannelflags;
 }
 
 #define FIND_SWITCH( xtype, first, name, err ) \
index 3742017d93acd93d66cbd999532c59ad63249ea6..44f66b2c3cf9b79f6c07a69ef1578d7e5d185c36 100644 (file)
@@ -350,20 +350,38 @@ int soundcard_setup_collect(int cardno)
                        }
                        bzero(mixerchannel, sizeof(struct mixer_channel));
                        mixerchannel->no = idx;
-                       if ((err = snd_mixer_channel_info(mhandle, idx, &mixerchannel->i)) < 0) {
+                       if ((err = snd_mixer_channel_info(mhandle, idx, &mixerchannel->info)) < 0) {
                                free(mixerchannel);
                                error("MIXER channel info error (%s) - skipping", snd_strerror(err));
                                break;
                        }
-                       if ((err = snd_mixer_channel_read(mhandle, idx, &mixerchannel->c)) < 0) {
+                       if ((mixerchannel->info.caps & SND_MIXER_CINFO_CAP_OUTPUT) &&
+                           (err = snd_mixer_channel_output_info(mhandle, idx, &mixerchannel->dinfo[OUTPUT])) < 0) {
+                               free(mixerchannel);
+                               error("MIXER channel output info error (%s) - skipping", snd_strerror(err));
+                               break;
+                       }
+                       if ((mixerchannel->info.caps & SND_MIXER_CINFO_CAP_INPUT) &&
+                           (err = snd_mixer_channel_input_info(mhandle, idx, &mixerchannel->dinfo[INPUT])) < 0) {
+                               free(mixerchannel);
+                               error("MIXER channel input info error (%s) - skipping", snd_strerror(err));
+                               break;
+                       }
+                       if ((err = snd_mixer_channel_read(mhandle, idx, &mixerchannel->data)) < 0) {
                                free(mixerchannel);
                                error("MIXER channel read error (%s) - skipping", snd_strerror(err));
                                break;
                        }
-                       if ((mixerchannel->i.caps & SND_MIXER_CINFO_CAP_RECORDVOLUME) &&
-                           (err = snd_mixer_channel_record_read(mhandle, idx, &mixerchannel->cr)) < 0) {
+                       if ((mixerchannel->info.caps & SND_MIXER_CINFO_CAP_OUTPUT) &&
+                           (err = snd_mixer_channel_output_read(mhandle, idx, &mixerchannel->ddata[OUTPUT])) < 0) {
                                free(mixerchannel);
-                               error("MIXER channel record read error (%s) - skipping", snd_strerror(err));
+                               error("MIXER channel output read error (%s) - skipping", snd_strerror(err));
+                               break;
+                       }
+                       if ((mixerchannel->info.caps & SND_MIXER_CINFO_CAP_INPUT) &&
+                           (err = snd_mixer_channel_input_read(mhandle, idx, &mixerchannel->ddata[INPUT])) < 0) {
+                               free(mixerchannel);
+                               error("MIXER channel input read error (%s) - skipping", snd_strerror(err));
                                break;
                        }
                        if (!mixerchannelprev) {
@@ -651,55 +669,91 @@ static void soundcard_setup_write_switch(FILE * out, int interface, const unsign
 }
 
 
-static void soundcard_setup_write_mixer_channel(FILE * out, snd_mixer_channel_info_t * info, snd_mixer_channel_t * channel, snd_mixer_channel_t * record_channel)
+static void soundcard_setup_write_mixer_channel(FILE * out, struct mixer_channel * channel)
 {
-       fprintf(out, "    ; Capabilities:%s%s%s%s%s%s%s%s%s%s%s%s%s.\n",
-               info->caps & SND_MIXER_CINFO_CAP_RECORD ?       " record" : "",
-               info->caps & SND_MIXER_CINFO_CAP_JOINRECORD ?   " join-record" : "",
-               info->caps & SND_MIXER_CINFO_CAP_STEREO ?       " stereo" : "",
-               info->caps & SND_MIXER_CINFO_CAP_HWMUTE ?       " hardware-mute" : "",
-               info->caps & SND_MIXER_CINFO_CAP_JOINMUTE ?     " join-mute" : "",
-               info->caps & SND_MIXER_CINFO_CAP_DIGITAL ?      " digital" : "",
-               info->caps & SND_MIXER_CINFO_CAP_INPUT ?        " external-input" : "",
-               info->caps & SND_MIXER_CINFO_CAP_LTOR_OUT ?     " ltor-out" : "",
-               info->caps & SND_MIXER_CINFO_CAP_RTOL_OUT ?     " rtol-out" : "",
-               info->caps & SND_MIXER_CINFO_CAP_SWITCH_OUT ?   " switch-out" : "",
-               info->caps & SND_MIXER_CINFO_CAP_LTOR_IN ?      " ltor-in" : "",
-               info->caps & SND_MIXER_CINFO_CAP_RTOL_IN ?      " rtol-in" : "",
-               info->caps & SND_MIXER_CINFO_CAP_RECORDVOLUME ? " record-volume" : "");
-       fprintf(out, "    ; Accepted channel range is from %i to %i.\n", info->min, info->max);
-       fprintf(out, "    channel( \"%s\", ", info->name);
-       if (info->caps & SND_MIXER_CINFO_CAP_STEREO) {
-               char bufl[16] = "";
-               char bufr[16] = "";
-               if (info->caps & SND_MIXER_CINFO_CAP_RECORDVOLUME) {
-                       sprintf(bufl, " [%i]", record_channel->left);
-                       sprintf(bufr, " [%i]", record_channel->right);
-               }
-               fprintf(out, "stereo( %i%s%s%s%s%s, %i%s%s%s%s%s )",
-                       channel->left,
-                       channel->flags & SND_MIXER_FLG_MUTE_LEFT ? " mute" : "",
-                       channel->flags & SND_MIXER_FLG_RECORD_LEFT ? " record" : "",
-                       bufl,
-                       channel->flags & SND_MIXER_FLG_LTOR_OUT ? " swout" : "",
-                       channel->flags & SND_MIXER_FLG_LTOR_IN ? " swin" : "",
-                       channel->right,
-                       channel->flags & SND_MIXER_FLG_MUTE_RIGHT ? " mute" : "",
-                       channel->flags & SND_MIXER_FLG_RECORD_RIGHT ? " record" : "",
-                       bufr,
-                       channel->flags & SND_MIXER_FLG_RTOL_OUT ? " swout" : "",
-                       channel->flags & SND_MIXER_FLG_RTOL_IN ? " swin" : ""
-                   );
-       } else {
-               char buf[16] = "";
-               if (info->caps & SND_MIXER_CINFO_CAP_RECORDVOLUME)
-                       sprintf(buf, " [%i]", (record_channel->left+record_channel->right) /2);
-               fprintf(out, "mono( %i%s%s%s )",
-                       (channel->left + channel->right) / 2,
-                       channel->flags & SND_MIXER_FLG_MUTE ? " mute" : "",
-                       channel->flags & SND_MIXER_FLG_RECORD ? " record" : "",
-                       buf
-                   );
+       int k, d;
+       struct capdes {
+               unsigned int flag;
+               char* description;
+       };
+       static struct capdes caps[] = {
+               { SND_MIXER_CINFO_CAP_OUTPUT, "output" },
+               { SND_MIXER_CINFO_CAP_INPUT, "input" },
+               { SND_MIXER_CINFO_CAP_EXTINPUT, "external-input" },
+               { SND_MIXER_CINFO_CAP_EFFECT, "effect" }
+       };
+       static struct capdes dcaps[] = {
+               { SND_MIXER_CINFO_DCAP_STEREO, "stereo" },
+               { SND_MIXER_CINFO_DCAP_HWMUTE, "hardware-mute" },
+               { SND_MIXER_CINFO_DCAP_JOINMUTE, "join-mute" },
+               { SND_MIXER_CINFO_DCAP_ROUTE, "route" },
+               { SND_MIXER_CINFO_DCAP_SWAPROUTE, "swap-route" },
+               { SND_MIXER_CINFO_DCAP_DIGITAL, "digital" },
+               { SND_MIXER_CINFO_DCAP_RECORDBYMUTE, "recordbymute" },
+       };
+
+       fprintf(out, "    ; Capabilities:");
+       for (k = 0; k < sizeof(caps)/sizeof(*caps); ++k) {
+               if (channel->info.caps & caps[k].flag)
+                       fprintf(out, " %s", caps[k].description);
+       }
+       fprintf(out, "\n");
+       for (d = OUTPUT; d <= INPUT; ++d) {
+               snd_mixer_channel_direction_info_t *di;
+               if (d == OUTPUT && 
+                   !(channel->info.caps & SND_MIXER_CINFO_CAP_OUTPUT))
+                       continue;
+               if (d == INPUT && 
+                   !(channel->info.caps & SND_MIXER_CINFO_CAP_INPUT))
+                       continue;
+               di = &channel->dinfo[d];
+               fprintf(out, "    ; %s capabilities:",
+                       d == OUTPUT ? "Output" : "Input" );
+               if (di->caps & SND_MIXER_CINFO_DCAP_VOLUME)
+                       fprintf(out, " volume(%i, %i)", di->min, di->max);
+               for (k = 0; k < sizeof(caps)/sizeof(*caps); ++k) {
+                       if (di->caps & dcaps[k].flag)
+                               fprintf(out, " %s", dcaps[k].description);
+               }
+               fprintf(out, "\n");
+       }
+       fprintf(out, "    channel( \"%s\"", channel->info.name);
+       for (d = OUTPUT; d <= INPUT; ++d) {
+               snd_mixer_channel_direction_info_t *di;
+               snd_mixer_channel_direction_t *dd;
+               if (d == OUTPUT && 
+                   !(channel->info.caps & SND_MIXER_CINFO_CAP_OUTPUT))
+                       continue;
+               if (d == INPUT && 
+                   !(channel->info.caps & SND_MIXER_CINFO_CAP_INPUT))
+                       continue;
+               dd = &channel->ddata[d];
+               di = &channel->dinfo[d];
+               fprintf(out, ", %s ", d == OUTPUT ? "output" : "input" );
+               if (di->caps & SND_MIXER_CINFO_DCAP_STEREO) {
+                       fprintf(out, "stereo(");
+                       if (di->caps & SND_MIXER_CINFO_DCAP_VOLUME)
+                               fprintf(out, " %i", dd->left);
+                       fprintf(out, "%s%s,", 
+                               dd->flags & SND_MIXER_DFLG_MUTE_LEFT ? " mute" : "",
+                               dd->flags & SND_MIXER_DFLG_LTOR ? " swap" : ""
+                               );
+                       if (di->caps & SND_MIXER_CINFO_DCAP_VOLUME)
+                               fprintf(out, " %i", dd->right);
+
+                       fprintf(out, "%s%s )",
+                               dd->flags & SND_MIXER_DFLG_MUTE_RIGHT ? " mute" : "",
+                               dd->flags & SND_MIXER_DFLG_RTOL ? " swap" : ""
+                               );
+               }
+               else {
+                       fprintf(out, "mono(");
+                       if (di->caps & SND_MIXER_CINFO_DCAP_VOLUME)
+                               fprintf(out, " %i", (dd->left + dd->right)/2);
+                       fprintf(out, "%s )",
+                               dd->flags & SND_MIXER_DFLG_MUTE ? " mute" : ""
+                               );
+               }
        }
        fprintf(out, " )\n");
 }
@@ -735,7 +789,7 @@ int soundcard_setup_write(const char *cfgfile)
                for (mixer = first->mixers; mixer; mixer = mixer->next) {
                        fprintf(out, "  mixer( \"%s\" ) {\n", mixer->info.name);
                        for (mixerchannel = mixer->channels; mixerchannel; mixerchannel = mixerchannel->next)
-                               soundcard_setup_write_mixer_channel(out, &mixerchannel->i, &mixerchannel->c, &mixerchannel->cr);
+                               soundcard_setup_write_mixer_channel(out, mixerchannel);
                        for (mixersw = mixer->switches; mixersw; mixersw = mixersw->next)
                                soundcard_setup_write_switch(out, SND_INTERFACE_MIXER, mixersw->s.name, mixersw->s.type, mixersw->s.low, mixersw->s.high, (void *) (&mixersw->s.value));
                        fprintf(out, "  }\n");
@@ -839,14 +893,14 @@ int soundcard_setup_process(int cardno)
                }
                for (mixer = soundcard->mixers; mixer; mixer = mixer->next) {
                        for (channel = mixer->channels; channel; channel = channel->next)
-                               if (channel->change)
-                                       if (!soundcard_open_mix(&mixhandle, soundcard, mixer)) {
-                                               if ((err = snd_mixer_channel_write(mixhandle, channel->no, &channel->c)) < 0)
-                                                       error("Mixer channel '%s' write error: %s", channel->i.name, snd_strerror(err));
-                                               if ((channel->i.caps & SND_MIXER_CINFO_CAP_RECORDVOLUME) &&
-                                                   (err = snd_mixer_channel_record_write(mixhandle, channel->no, &channel->cr)) < 0)
-                                                       error("Mixer channel '%s' record write error: %s", channel->i.name, snd_strerror(err));
-                                       }
+                               if (!soundcard_open_mix(&mixhandle, soundcard, mixer)) {
+                                       if ((channel->info.caps & SND_MIXER_CINFO_CAP_OUTPUT) &&
+                                           (err = snd_mixer_channel_output_write(mixhandle, channel->no, &channel->ddata[OUTPUT])) < 0)
+                                               error("Mixer channel '%s' write error: %s", channel->info.name, snd_strerror(err));
+                                       if ((channel->info.caps & SND_MIXER_CINFO_CAP_INPUT) &&
+                                           (err = snd_mixer_channel_input_write(mixhandle, channel->no, &channel->ddata[INPUT])) < 0)
+                                               error("Mixer channel '%s' record write error: %s", channel->info.name, snd_strerror(err));
+                               }
                        if (mixhandle) {
                                snd_mixer_close(mixhandle);
                                mixhandle = NULL;
@@ -869,7 +923,7 @@ int soundcard_setup_process(int cardno)
                        for (pcmsw = pcm->rswitches; pcmsw; pcmsw = pcmsw->next) {
                                if (pcmsw->change)
                                        if (!soundcard_open_ctl(&ctlhandle, soundcard)) {
-                                               if ((err = snd_ctl_pcm_playback_switch_write(ctlhandle, pcm->no, pcmsw->no, &pcmsw->s)) < 0)
+                                               if ((err = snd_ctl_pcm_record_switch_write(ctlhandle, pcm->no, pcmsw->no, &pcmsw->s)) < 0)
                                                        error("PCM record switch '%s' write error: %s", pcmsw->s.name, snd_strerror(err));
                                        }
                        }
index b7d516d91568e5a96fb0c0c6d3272be07c90e313..31cae798d7bd5416384fd0cd882a60064f5c7f01 100644 (file)
@@ -1,5 +1,7 @@
 /* AlsaMixer - Commandline mixer for the ALSA project
- * Copyright (C) 1998, 1999 Tim Janik <timj@gtk.org> and Jaroslav Kysela <perex@jcu.cz>
+ * Copyright (C) 1998 Jaroslav Kysela <perex@jcu.cz>,
+ *                    Tim Janik <timj@gtk.org>,
+ *                    Carl van Schaik <carl@dreamcoat.che.uct.ac.za>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * You should have received a copy of the GNU Library General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- *
- *
- * ChangeLog:
- *
- * Sun Feb 21 19:55:01 1999  Tim Janik  <timj@gtk.org>
- *
- *     * bumped version to 0.10.
- *
- *     * added scrollable text views.
- *     we now feature an F1 Help screen and an F2 /proc info screen.
- *     the help screen does still require lots of work though.
- *
- *     * keys are evaluated view specific now.
- *
- *     * we feature meta-keys now, e.g. M-Tab as back-tab.
- *
- *     * if we are already in channel view and the user still hits Return,
- *     we do a refresh nonetheless, since 'r'/'R' got removed as a redraw
- *     key (reserved for record volumes). 'l'/'L' is still preserved though,
- *     and actually needs to be to e.g. get around the xterm bold-artefacts.
- *
- *     * support terminals that can't write into lower right corner.
- *
- *     * undocumented '-s' option that will keep the screen to its
- *     minimum size, usefull for debugging only.
- *
- * Sun Feb 21 02:23:52 1999  Tim Janik  <timj@gtk.org>
- *
- *     * don't abort if snd_mixer_* functions failed due to EINTR,
- *     we simply retry on the next cycle. hopefully asoundlib preserves
- *     errno states correctly (Jaroslav can you asure that?).
- *
- *     * feature WINCH correctly, so we make a complete relayout on
- *     screen resizes. don't abort on too-small screen sizes anymore,
- *     but simply beep.
- *
- *     * redid the layout algorithm to fix some bugs and to preserve
- *     space for a flag indication line. the channels are
- *     nicer spread horizontally now (i.e. we also pad on the left and
- *     right screen bounds now).
- *
- *     * various other minor fixes.
- *
- *     * indicate whether ExactMode is active or not.
- *
- *     * fixed coding style to follow the GNU coding conventions.
- *
- *     * reverted record volume changes since they broke ExactMode display.
- *
- *     * composed ChangeLog entries.
- *
- * 1998/11/04 19:43:45  perex
- *
- *     * Stereo record source and route selection...
- *     provided by Carl van Schaik <carl@dreamcoat.che.uct.ac.za>.
- *
- * 1998/09/20 08:05:24  perex
- *
- *     * Fixed -m option...
- *
- * 1998/10/29 22:50:10
- *
- *     * initial checkin of alsamixer.c, written by Tim Janik, modified by
- *     Jaroslav Kysela to feature asoundlib.h instead of plain ioctl()s and
- *     automated updates after select() (i always missed that with OSS!).
  */
 
 #include <stdio.h>
 #include <sys/asoundlib.h>
 
 /* example compilation commandline:
- * clear; gcc -Wall -pipe -O2 alsamixer.c -o alsamixer -lasound -lncurses
+ * clear; gcc -Wall -pipe -O2 alsamixer.c -o alsamixer -lncurses
  */
 
 /* --- defines --- */
-#define        PRGNAME          "alsamixer"
-#define        PRGNAME_UPPER    "AlsaMixer"
-#define        VERSION          "v0.10"
-#define        CHECK_ABORT(e,s) ({ if (errno != EINTR) mixer_abort ((e), (s)); })
-#define GETCH_BLOCK(w)  ({ timeout ((w) ? -1 : 0); })
+#define        PRGNAME "alsamixer"
+#define        PRGNAME_UPPER "AlsaMixer"
+#define        VERSION "v0.9"
 
 #undef MAX
 #define MAX(a, b)  (((a) > (b)) ? (a) : (b))
 #undef CLAMP
 #define CLAMP(x, low, high)  (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
 
-#define MIXER_MIN_X    (18)                    /* abs minimum: 18 */
-#define        MIXER_TEXT_Y    (10)
-#define        MIXER_MIN_Y     (MIXER_TEXT_Y + 3)      /* abs minimum: 11 */
+#define MIXER_MIN_X    (23)    /* minimum: 23 */
+#define        MIXER_MIN_Y     (19)    /* minimum: 19 */
 
 #define MIXER_BLACK    (COLOR_BLACK)
 #define MIXER_DARK_RED  (COLOR_RED)
 #define MIXER_WHITE     (COLOR_WHITE | A_BOLD)
 
 
-/* --- views --- */
-enum {
-  VIEW_CHANNELS,
-  VIEW_HELP,
-  VIEW_PROCINFO
-};
-
-
 /* --- variables --- */
-static WINDOW  *mixer_window = NULL;
-static int      mixer_needs_resize = 0;
-static int      mixer_minimize = 0;
-static int      mixer_no_lrcorner = 0;
-static int      mixer_view = VIEW_CHANNELS;
-static int      mixer_max_x = 0;
-static int      mixer_max_y = 0;
-static int      mixer_ofs_x = 0;
-static float    mixer_extra_space = 0;
-static int      mixer_cbar_height = 0;
-
-static int      card_id = 0;
-static int      mixer_id = 0;
-static void    *mixer_handle;
-static char    *mixer_card_name = NULL;
-static char    *mixer_device_name = NULL;
-
-static int      mixer_n_channels = 0;
-static int      mixer_n_vis_channels = 0;
-static int      mixer_first_vis_channel = 0;
-static int      mixer_focus_channel = 0;
-static int      mixer_have_old_focus = 0;
-static int      mixer_exact = 0;
-
-static int      mixer_lvolume_delta = 0;
-static int      mixer_rvolume_delta = 0;
-static int      mixer_balance_volumes = 0;
-static int      mixer_toggle_mute_left = 0;
-static int      mixer_toggle_mute_right = 0;
-static int      mixer_toggle_record = 0;
-
-static int      mixer_hscroll_delta = 0;
-static int      mixer_vscroll_delta = 0;
+static WINDOW *mixer_window = NULL;
+static int mixer_max_x = 0;
+static int mixer_max_y = 0;
+static int mixer_ofs_x = 0;
+static float mixer_extra_space = 0;
+static int mixer_ofs_y = 0;
+static int mixer_cbar_height = 0;
+
+static int card_id = 0;
+static int mixer_id = 0;
+static void *mixer_handle;
+static char *mixer_card_name = NULL;
+static char *mixer_device_name = NULL;
+
+static int mixer_n_channels = 0;
+static int mixer_n_vis_channels = 0;
+static int mixer_first_vis_channel = 0;
+static int mixer_focus_channel = 0;
+static int mixer_exact = 0;
+
+static int mixer_input_volumes = 0;
+
+static int mixer_lvolume_delta = 0;
+static int mixer_rvolume_delta = 0;
+static int mixer_balance_volumes = 0;
+static int mixer_toggle_mute_left = 0;
+static int mixer_toggle_mute_right = 0;
 
 /* By Carl */
-static int      mixer_toggle_rec_left = 0;
-static int      mixer_toggle_rec_right = 0;
-static int      mixer_route_ltor_in = 0;
-static int      mixer_route_rtol_in = 0;
-
-
-/* --- text --- */
-static int      mixer_procinfo_xoffs = 0;
-static int      mixer_procinfo_yoffs = 0;
-static int      mixer_help_xoffs = 0;
-static int      mixer_help_yoffs = 0;
-static char     *mixer_help_text =
-(
- "\n"
- " Esc     exit alsamixer\n"
- " F1      show Help screen\n"
- " F2      show /proc info screen\n"
- " Return  return to main screen\n"
- " Space   toggle Record facility\n"
- " Tab     toggle ExactMode\n"
- " m M     mute both channels\n"
- " < >     mute left/right channel\n"
- " Up      increase left and right volume\n"
- " Down    decrease left and right volume\n"
- " Right   move (scroll) to the right next channel\n"
- " Left    move (scroll) to the left next channel\n"
- "\n"
- "Alsamixer has been written and is Copyrighted in 1998, 1999 by\n"
- "Tim Janik <timj@gtk.org> and Jaroslav Kysela <perex@jcu.cz>.\n"
- );
+static int mixer_toggle_record_left = 0;
+static int mixer_toggle_record_right = 0;
+static int mixer_route_ltor_in = 0;
+static int mixer_route_rtol_in = 0;
+#if 0
+static int mixer_route_ltor_out = 0;
+static int mixer_route_rtol_out = 0;
+#endif
 
 
 /* --- draw contexts --- */
 enum {
-  DC_DEFAULT,
-  DC_BACK,
-  DC_TEXT,
-  DC_PROMPT,
-  DC_CBAR_MUTE,
-  DC_CBAR_NOMUTE,
-  DC_CBAR_RECORD,
-  DC_CBAR_NORECORD,
-  DC_CBAR_EMPTY,
-  DC_CBAR_LABEL,
-  DC_CBAR_FOCUS_LABEL,
-  DC_FOCUS,
-  DC_ANY_1,
-  DC_ANY_2,
-  DC_ANY_3,
-  DC_ANY_4,
-  DC_LAST
+       DC_DEFAULT,
+       DC_BACK,
+       DC_TEXT,
+       DC_PROMPT,
+       DC_CBAR_MUTE,
+       DC_CBAR_NOMUTE,
+       DC_CBAR_RECORD,
+       DC_CBAR_NORECORD,
+       DC_CBAR_EMPTY,
+       DC_CBAR_FULL_1,
+       DC_CBAR_FULL_2,
+       DC_CBAR_FULL_3,
+       DC_CBAR_LABEL,
+       DC_CBAR_FOCUS_LABEL,
+       DC_FOCUS,
+       DC_LAST
 };
 
-static int dc_fg[DC_LAST] = { 0 };
-static int dc_attrib[DC_LAST] = { 0 };
-static int dc_char[DC_LAST] = { 0 };
+static int dc_fg[DC_LAST] =
+{0};
+static int dc_attrib[DC_LAST] =
+{0};
+static int dc_char[DC_LAST] =
+{0};
 static int mixer_do_color = 1;
 
-static void
-mixer_init_dc (int c,
-              int n,
-              int f,
-              int b,
-              int a)
+static void mixer_init_dc(int c,
+                         int n,
+                         int f,
+                         int b,
+                         int a)
 {
-  dc_fg[n] = f;
-  dc_attrib[n] = a;
-  dc_char[n] = c;
-  if (n > 0)
-    init_pair (n, dc_fg[n] & 0xf, b & 0x0f);
+       dc_fg[n] = f;
+       dc_attrib[n] = a;
+       dc_char[n] = c;
+       if (n > 0)
+               init_pair(n, dc_fg[n] & 0xf, b & 0x0f);
 }
 
-static int
-mixer_dc (int n)
+static int mixer_dc(int n)
 {
-  if (mixer_do_color)
-    attrset (COLOR_PAIR (n) | (dc_fg[n] & 0xfffffff0));
-  else
-    attrset (dc_attrib[n]);
-  
-  return dc_char[n];
+       if (mixer_do_color)
+               attrset(COLOR_PAIR(n) | (dc_fg[n] & 0xfffffff0));
+       else
+               attrset(dc_attrib[n]);
+
+       return dc_char[n];
 }
 
-static void
-mixer_init_draw_contexts (void)
+static void mixer_init_draw_contexts(void)
 {
-  start_color ();
-  
-  mixer_init_dc ('.', DC_BACK, MIXER_WHITE, MIXER_BLACK, A_NORMAL);
-  mixer_init_dc ('.', DC_TEXT, MIXER_YELLOW, MIXER_BLACK, A_BOLD);
-  mixer_init_dc ('.', DC_PROMPT, MIXER_DARK_CYAN, MIXER_BLACK, A_NORMAL);
-  mixer_init_dc ('M', DC_CBAR_MUTE, MIXER_CYAN, MIXER_BLACK, A_BOLD);
-  mixer_init_dc ('-', DC_CBAR_NOMUTE, MIXER_CYAN, MIXER_BLACK, A_NORMAL);
-  mixer_init_dc ('x', DC_CBAR_RECORD, MIXER_DARK_RED, MIXER_BLACK, A_BOLD);
-  mixer_init_dc ('-', DC_CBAR_NORECORD, MIXER_GRAY, MIXER_BLACK, A_NORMAL);
-  mixer_init_dc (' ', DC_CBAR_EMPTY, MIXER_GRAY, MIXER_BLACK, A_DIM);
-  mixer_init_dc ('.', DC_CBAR_LABEL, MIXER_WHITE, MIXER_BLUE, A_REVERSE | A_BOLD);
-  mixer_init_dc ('.', DC_CBAR_FOCUS_LABEL, MIXER_RED, MIXER_BLUE, A_REVERSE | A_BOLD);
-  mixer_init_dc ('.', DC_FOCUS, MIXER_RED, MIXER_BLACK, A_BOLD);
-  mixer_init_dc ('#', DC_ANY_1, MIXER_WHITE, MIXER_BLACK, A_BOLD);
-  mixer_init_dc ('#', DC_ANY_2, MIXER_GREEN, MIXER_BLACK, A_BOLD);
-  mixer_init_dc ('#', DC_ANY_3, MIXER_RED, MIXER_BLACK, A_BOLD);
-  mixer_init_dc ('.', DC_ANY_4, MIXER_WHITE, MIXER_GREEN, A_BOLD);
-  mixer_init_dc ('.', DC_ANY_4, MIXER_WHITE, MIXER_BLUE, A_BOLD);
+       start_color();
+
+       mixer_init_dc('.', DC_BACK, MIXER_WHITE, MIXER_BLACK, A_NORMAL);
+       mixer_init_dc('.', DC_TEXT, MIXER_YELLOW, MIXER_BLACK, A_BOLD);
+       mixer_init_dc('.', DC_PROMPT, MIXER_DARK_CYAN, MIXER_BLACK, A_NORMAL);
+       mixer_init_dc('M', DC_CBAR_MUTE, MIXER_CYAN, MIXER_BLACK, A_BOLD);
+       mixer_init_dc('-', DC_CBAR_NOMUTE, MIXER_CYAN, MIXER_BLACK, A_NORMAL);
+       mixer_init_dc('x', DC_CBAR_RECORD, MIXER_DARK_RED, MIXER_BLACK, A_BOLD);
+       mixer_init_dc('-', DC_CBAR_NORECORD, MIXER_GRAY, MIXER_BLACK, A_NORMAL);
+       mixer_init_dc(' ', DC_CBAR_EMPTY, MIXER_GRAY, MIXER_BLACK, A_DIM);
+       mixer_init_dc('#', DC_CBAR_FULL_1, MIXER_WHITE, MIXER_BLACK, A_BOLD);
+       mixer_init_dc('#', DC_CBAR_FULL_2, MIXER_GREEN, MIXER_BLACK, A_BOLD);
+       mixer_init_dc('#', DC_CBAR_FULL_3, MIXER_RED, MIXER_BLACK, A_BOLD);
+       mixer_init_dc('.', DC_CBAR_LABEL, MIXER_WHITE, MIXER_BLUE, A_REVERSE | A_BOLD);
+       mixer_init_dc('.', DC_CBAR_FOCUS_LABEL, MIXER_RED, MIXER_BLUE, A_REVERSE | A_BOLD);
+       mixer_init_dc('.', DC_FOCUS, MIXER_RED, MIXER_BLACK, A_BOLD);
 }
 
 #define        DC_CBAR_FRAME   (DC_CBAR_MUTE)
@@ -297,1344 +192,773 @@ mixer_init_draw_contexts (void)
 
 
 /* --- error types --- */
-typedef enum
-{
-  ERR_NONE,
-  ERR_OPEN,
-  ERR_FCN,
-  ERR_SIGNAL,
-  ERR_WINSIZE,
+typedef enum {
+       ERR_NONE,
+       ERR_OPEN,
+       ERR_FCN,
+       ERR_SIGNAL,
+       ERR_WINSIZE,
 } ErrType;
 
 
 /* --- prototypes --- */
-static void
-mixer_abort (ErrType error,
-            const char *err_string)
-     __attribute__
-((noreturn));
+static void mixer_abort(ErrType error,
+                       const char *err_string)
+ __attribute__
+ ((noreturn));
 
 
 /* --- functions --- */
-static void
-mixer_clear (int full_redraw)
+static void mixer_clear(void)
 {
-  int x, y;
-  int f = full_redraw ? 0 : 1;
-
-  mixer_dc (DC_BACK);
-
-  if (full_redraw)
-    clearok (mixer_window, TRUE);
-
-  /* buggy ncurses doesn't really write spaces with the specified
-   * color into the screen on clear () or erase ()
-   */
-  for (x = f; x < mixer_max_x - f; x++)
-    for (y = f; y < mixer_max_y - f; y++)
-      mvaddch (y, x, ' ');
+       int x, y;
+
+       mixer_dc(DC_BACK);
+       clear();
+
+       /* buggy ncurses doesn't really write spaces with the specified
+        * color into the screen on clear ();
+        */
+       for (x = 0; x < mixer_max_x; x++)
+               for (y = 0; y < mixer_max_y; y++)
+                       mvaddch(y, x, ' ');
+       refresh();
 }
 
-static void
-mixer_abort (ErrType     error,
-            const char *err_string)
+static void mixer_abort(ErrType error,
+                       const char *err_string)
 {
-  if (mixer_window)
-    {
-      mixer_clear (TRUE);
-      refresh ();
-      keypad (mixer_window, FALSE);
-      leaveok (mixer_window, FALSE);
-      endwin ();
-      mixer_window = NULL;
-    }
-  printf ("\n");
-  
-  switch (error)
-    {
-    case ERR_OPEN:
-      fprintf (stderr,
-              PRGNAME ": failed to open mixer #%i/#%i: %s\n",
-              card_id,
-              mixer_id,
-              snd_strerror (errno));
-      break;
-    case ERR_FCN:
-      fprintf (stderr,
-              PRGNAME ": function %s failed: %s\n",
-              err_string,
-              snd_strerror (errno));
-      break;
-    case ERR_SIGNAL:
-      fprintf (stderr,
-              PRGNAME ": aborting due to signal `%s'\n",
-              err_string);
-      break;
-    case ERR_WINSIZE:
-      fprintf (stderr,
-              PRGNAME ": screen size too small (%dx%d)\n",
-              mixer_max_x,
-              mixer_max_y);
-      break;
-    default:
-      break;
-    }
-  
-  exit (error);
+       if (mixer_window) {
+               mixer_clear();
+               endwin();
+               mixer_window = NULL;
+       }
+       printf("\n");
+
+       switch (error) {
+       case ERR_OPEN:
+               fprintf(stderr,
+                       PRGNAME ": failed to open mixer #%i/#%i: %s\n",
+                       card_id,
+                       mixer_id,
+                       snd_strerror(errno));
+               break;
+       case ERR_FCN:
+               fprintf(stderr,
+                       PRGNAME ": function %s failed: %s\n",
+                       err_string,
+                       snd_strerror(errno));
+               break;
+       case ERR_SIGNAL:
+               fprintf(stderr,
+                       PRGNAME ": aborting due to signal `%s'\n",
+                       err_string);
+               break;
+       case ERR_WINSIZE:
+               fprintf(stderr,
+                       PRGNAME ": screen size too small (%dx%d)\n",
+                       mixer_max_x,
+                       mixer_max_y);
+               break;
+       default:
+               break;
+       }
+
+       exit(error);
 }
 
-static int
-mixer_cbar_get_pos (int  channel_index,
-                   int *x_p,
-                   int *y_p)
+static int mixer_cbar_get_pos(int channel_index,
+                             int *x_p,
+                             int *y_p)
 {
-  int x;
-  int y;
-  
-  if (channel_index < mixer_first_vis_channel ||
-      channel_index - mixer_first_vis_channel >= mixer_n_vis_channels)
-    return FALSE;
-  
-  channel_index -= mixer_first_vis_channel;
-  
-  x = mixer_ofs_x;
-  x += (3 + 2 + 3 + 1) * channel_index + mixer_extra_space * (channel_index + 1);
-
-  if (MIXER_TEXT_Y + 10 < mixer_max_y)
-    y = mixer_max_y / 2 + 3;
-  else
-    y = (mixer_max_y + 1) / 2 + 3;
-  y += mixer_cbar_height / 2;
-  
-  if (x_p)
-    *x_p = x;
-  if (y_p)
-    *y_p = y;
-  
-  return TRUE;
+       int x;
+       int y;
+
+       if (channel_index < mixer_first_vis_channel ||
+        channel_index - mixer_first_vis_channel >= mixer_n_vis_channels)
+               return FALSE;
+
+       channel_index -= mixer_first_vis_channel;
+
+       x = mixer_ofs_x + 1;
+       y = mixer_ofs_y;
+       x += channel_index * (3 + 2 + 3 + 1 + mixer_extra_space);
+       y += mixer_max_y / 2;
+       y += mixer_cbar_height / 2 + 1;
+
+       if (x_p)
+               *x_p = x;
+       if (y_p)
+               *y_p = y;
+
+       return TRUE;
 }
 
-static void
-mixer_update_cbar (int channel_index)
+static void mixer_update_cbar(int channel_index)
 {
-  char string[64];
-  char c;
-  snd_mixer_channel_info_t cinfo = { 0, };
-  snd_mixer_channel_t cdata = { 0, };
-  int vleft, vright;
-  int x, y, i;
-  
-  
-  /* set specified EXACT mode
-   */
-  if (snd_mixer_exact_mode (mixer_handle, mixer_exact) < 0)
-    CHECK_ABORT (ERR_FCN, "snd_mixer_exact_mode()");
-  
-  /* set new channel indices and read info
-   */
-  if (snd_mixer_channel_info (mixer_handle, channel_index, &cinfo) < 0)
-    CHECK_ABORT (ERR_FCN, "snd_mixer_channel_info()");
-  
-  /* set new channel values
-   */
-  if (channel_index == mixer_focus_channel &&
-      (mixer_lvolume_delta || mixer_rvolume_delta ||
-       mixer_toggle_mute_left || mixer_toggle_mute_right ||
-       mixer_balance_volumes ||
-       mixer_toggle_record || mixer_toggle_rec_left ||
-       mixer_toggle_rec_right ||
-       mixer_route_rtol_in || mixer_route_ltor_in))
-    {
-      if (snd_mixer_channel_read (mixer_handle, channel_index, &cdata) < 0)
-       CHECK_ABORT (ERR_FCN, "snd_mixer_channel_read()");
-      
-      cdata.flags &= ~SND_MIXER_FLG_DECIBEL;
-      cdata.left = CLAMP (cdata.left + mixer_lvolume_delta, cinfo.min, cinfo.max);
-      cdata.right = CLAMP (cdata.right + mixer_rvolume_delta, cinfo.min, cinfo.max);
-      mixer_lvolume_delta = mixer_rvolume_delta = 0;
-      if (mixer_balance_volumes)
-       {
-         cdata.left = (cdata.left + cdata.right) / 2;
-         cdata.right = cdata.left;
-         mixer_balance_volumes = 0;
-       }
-      if (mixer_toggle_mute_left)
-       {
-         if (cdata.flags & SND_MIXER_FLG_MUTE_LEFT)
-           cdata.flags &= ~SND_MIXER_FLG_MUTE_LEFT;
-         else
-           cdata.flags |= SND_MIXER_FLG_MUTE_LEFT;
-       }
-      if (mixer_toggle_mute_right)
-       {
-         if (cdata.flags & SND_MIXER_FLG_MUTE_RIGHT)
-           cdata.flags &= ~SND_MIXER_FLG_MUTE_RIGHT;
-         else
-           cdata.flags |= SND_MIXER_FLG_MUTE_RIGHT;
-       }
-      mixer_toggle_mute_left = mixer_toggle_mute_right = 0;
-      if (mixer_toggle_record)
-       {
-         if (cdata.flags & SND_MIXER_FLG_RECORD)
-           cdata.flags &= ~SND_MIXER_FLG_RECORD;
-         else
-           cdata.flags |= SND_MIXER_FLG_RECORD;
+       char string[64];
+       char c;
+       snd_mixer_channel_info_t cinfo =
+       {0};
+       snd_mixer_channel_direction_info_t cpinfo =
+       {0};
+       snd_mixer_channel_direction_info_t crinfo =
+       {0};
+       snd_mixer_channel_direction_t cpdata =
+       {0};
+       snd_mixer_channel_direction_t crdata =
+       {0};
+       int vleft, vright;
+       int x, y, i;
+       int output = 0, input = 0, volume;
+
+       /* set specified EXACT mode
+        */
+       if (snd_mixer_exact_mode(mixer_handle, mixer_exact) < 0)
+               mixer_abort(ERR_FCN, "snd_mixer_exact");
+
+       /* set new channel indices and read info
+        */
+       if (snd_mixer_channel_info(mixer_handle, channel_index, &cinfo) < 0)
+               mixer_abort(ERR_FCN, "snd_mixer_channel_info");
+       if (cinfo.caps & SND_MIXER_CINFO_CAP_OUTPUT) {
+               if (snd_mixer_channel_output_info(mixer_handle, channel_index, &cpinfo) < 0)
+                       mixer_abort(ERR_FCN, "snd_mixer_channel_output_info");
+               output = 1;
        }
-      mixer_toggle_record = 0;
-      
-      if (mixer_toggle_rec_left)
-       {
-         if (cdata.flags & SND_MIXER_FLG_RECORD_LEFT)
-           cdata.flags &= ~SND_MIXER_FLG_RECORD_LEFT;
-         else
-           cdata.flags |= SND_MIXER_FLG_RECORD_LEFT;
+       if (cinfo.caps & SND_MIXER_CINFO_CAP_INPUT) {
+               if (snd_mixer_channel_input_info(mixer_handle, channel_index, &crinfo) < 0)
+                       mixer_abort(ERR_FCN, "snd_mixer_channel_input_info");
+               input = 1;
        }
-      mixer_toggle_rec_left = 0;
-      
-      if (mixer_toggle_rec_right)
-       {
-         if (cdata.flags & SND_MIXER_FLG_RECORD_RIGHT)
-           cdata.flags &= ~SND_MIXER_FLG_RECORD_RIGHT;
-         else
-           cdata.flags |= SND_MIXER_FLG_RECORD_RIGHT;
-       }
-      mixer_toggle_rec_right = 0;
-      
-      if (mixer_route_ltor_in)
-       {
-         if (cdata.flags & SND_MIXER_FLG_LTOR_IN)
-           cdata.flags &= ~SND_MIXER_FLG_LTOR_IN;
-         else
-           cdata.flags |= SND_MIXER_FLG_LTOR_IN;
-         /*      printf ("state : \n %d \n",cdata.flags & SND_MIXER_FLG_LTOR_IN);
-          */ 
-       }
-      mixer_route_ltor_in = 0;
-      
-      if (mixer_route_rtol_in)
-       {
-         if (cdata.flags & SND_MIXER_FLG_RTOL_IN)
-           cdata.flags &= ~SND_MIXER_FLG_RTOL_IN;
-         else
-           cdata.flags |= SND_MIXER_FLG_RTOL_IN;
+       if (mixer_input_volumes)
+               volume=(input && (crinfo.caps & SND_MIXER_CINFO_DCAP_VOLUME));
+       else
+               volume=(output && (cpinfo.caps & SND_MIXER_CINFO_DCAP_VOLUME));
+
+       /* set new channel values
+        */
+       if (channel_index == mixer_focus_channel &&
+           (mixer_lvolume_delta || mixer_rvolume_delta ||
+            mixer_toggle_mute_left || mixer_toggle_mute_right ||
+            mixer_balance_volumes ||
+            mixer_toggle_record_left || mixer_toggle_record_right ||
+            mixer_route_rtol_in || mixer_route_ltor_in)) {
+               if (output && snd_mixer_channel_output_read(mixer_handle, channel_index, &cpdata) < 0)
+                       mixer_abort(ERR_FCN, "snd_mixer_channel_output_read");
+               if (input && snd_mixer_channel_input_read(mixer_handle, channel_index, &crdata) < 0)
+                       mixer_abort(ERR_FCN, "snd_mixer_channel_input_read");
+
+               cpdata.flags &= ~SND_MIXER_DFLG_DECIBEL;
+               crdata.flags &= ~SND_MIXER_DFLG_DECIBEL;
+               if (volume) {
+                       if (mixer_input_volumes) {
+                               crdata.left = CLAMP(crdata.left + mixer_lvolume_delta, crinfo.min, crinfo.max);
+                               crdata.right = CLAMP(crdata.right + mixer_rvolume_delta, crinfo.min, crinfo.max);
+                               if (mixer_balance_volumes) {
+                                       crdata.left = (crdata.left + crdata.right) / 2;
+                                       crdata.right = crdata.left;
+                               }
+                       }
+                       else {
+                               cpdata.left = CLAMP(cpdata.left + mixer_lvolume_delta, cpinfo.min, cpinfo.max);
+                               cpdata.right = CLAMP(cpdata.right + mixer_rvolume_delta, cpinfo.min, cpinfo.max);
+                               if (mixer_balance_volumes) {
+                                       cpdata.left = (cpdata.left + cpdata.right) / 2;
+                                       cpdata.right = cpdata.left;
+                               }
+                       }
+               }
+               mixer_lvolume_delta = 0;
+               mixer_rvolume_delta = 0;
+               mixer_balance_volumes = 0;
+
+               if (output) {
+                       if (mixer_toggle_mute_left) {
+                               cpdata.flags ^= SND_MIXER_DFLG_MUTE_LEFT;
+                       }
+                       if (mixer_toggle_mute_right) {
+                               cpdata.flags ^= SND_MIXER_DFLG_MUTE_RIGHT;
+                       }
+               }
+               mixer_toggle_mute_left = mixer_toggle_mute_right = 0;
+
+               if (input) {
+                       if (mixer_toggle_record_left) {
+                               crdata.flags ^= SND_MIXER_DFLG_MUTE_LEFT;
+                       }
+                       if (mixer_toggle_record_right) {
+                               crdata.flags ^= SND_MIXER_DFLG_MUTE_RIGHT;
+                       }
+
+                       if (mixer_route_ltor_in) {
+                               crdata.flags ^= SND_MIXER_DFLG_LTOR;
+                       }
+                       if (mixer_route_rtol_in) {
+                               crdata.flags ^= SND_MIXER_DFLG_RTOL;
+                       }
+               }
+               mixer_toggle_record_left = mixer_toggle_record_right = 0;
+               mixer_route_ltor_in = mixer_route_rtol_in = 0;
+
+               if (output &&
+                   snd_mixer_channel_output_write(mixer_handle, channel_index, &cpdata) < 0)
+                       mixer_abort(ERR_FCN, "snd_mixer_channel_output_write");
+               if (input &&
+                   snd_mixer_channel_input_write(mixer_handle, channel_index, &crdata) < 0)
+                       mixer_abort(ERR_FCN, "snd_mixer_channel_input_write");
        }
-      mixer_route_rtol_in = 0;
-      
-      if (snd_mixer_channel_write (mixer_handle, channel_index, &cdata) < 0)
-       CHECK_ABORT (ERR_FCN, "snd_mixer_channel_write()");
-    }
-  /* first, read values for the numbers to be displayed in
-   * specified EXACT mode
-   */
-  if (snd_mixer_channel_read (mixer_handle, channel_index, &cdata) < 0)
-    CHECK_ABORT (ERR_FCN, "snd_mixer_channel_read()");
-  vleft = cdata.left;
-  vright = cdata.right;
-  
-  /* then, always use percentage values for the bars. if we don't do
-   * this, we will see aliasing effects on specific circumstances.
-   * (actually they don't really dissapear, but they are transfered
-   *  to bar<->smaller-scale ambiguities).
-   */
-  if (mixer_exact)
-    {
-      i = 0;
-      if (snd_mixer_exact_mode (mixer_handle, 0) < 0)
-       CHECK_ABORT (ERR_FCN, "snd_mixer_exact_mode()");
-      if (snd_mixer_channel_read (mixer_handle, channel_index, &cdata) < 0)
-       CHECK_ABORT (ERR_FCN, "snd_mixer_channel_read()");
-    }
-  /* get channel bar position
-   */
-  if (!mixer_cbar_get_pos (channel_index, &x, &y))
-    return;
-
-  /* setup colors
-   */
-  mixer_init_dc ('#', DC_ANY_1, MIXER_WHITE, MIXER_BLACK, A_BOLD);
-  mixer_init_dc ('#', DC_ANY_2, MIXER_GREEN, MIXER_BLACK, A_BOLD);
-  mixer_init_dc ('#', DC_ANY_3, MIXER_RED, MIXER_BLACK, A_BOLD);
-  
-  /* channel bar name
-   */
-  mixer_dc (channel_index == mixer_focus_channel ? DC_CBAR_FOCUS_LABEL : DC_CBAR_LABEL);
-  cinfo.name[8] = 0;
-  for (i = 0; i < 8; i++)
-    {
-      string[i] = ' ';
-    }
-  sprintf (string + (8 - strlen (cinfo.name)) / 2, "%s          ", cinfo.name);
-  string[8] = 0;
-  mvaddstr (y, x, string);
-  y--;
-  
-  /* current channel values
-   */
-  mixer_dc (DC_BACK);
-  mvaddstr (y, x, "         ");
-  mixer_dc (DC_TEXT);
-  sprintf (string, "%d", vleft);
-  mvaddstr (y, x + 3 - strlen (string), string);
-  mixer_dc (DC_CBAR_FRAME);
-  mvaddch (y, x + 3, '<');
-  mvaddch (y, x + 4, '>');
-  mixer_dc (DC_TEXT);
-  sprintf (string, "%d", vright);
-  mvaddstr (y, x + 5, string);
-  y--;
-  
-  /* left/right bar
-   */
-  mixer_dc (DC_CBAR_FRAME);
-  mvaddstr (y, x, "         ");
-  mvaddch (y, x + 2, ACS_LLCORNER);
-  mvaddch (y, x + 3, ACS_HLINE);
-  mvaddch (y, x + 4, ACS_HLINE);
-  mvaddch (y, x + 5, ACS_LRCORNER);
-  y--;
-  for (i = 0; i < mixer_cbar_height; i++)
-    {
-      mvaddstr (y - i, x, "         ");
-      mvaddch (y - i, x + 2, ACS_VLINE);
-      mvaddch (y - i, x + 5, ACS_VLINE);
-    }
-  string[2] = 0;
-  for (i = 0; i < mixer_cbar_height; i++)
-    {
-      int dc;
-      
-      if (i + 1 >= 0.8 * mixer_cbar_height)
-       dc = DC_ANY_3;
-      else if (i + 1 >= 0.4 * mixer_cbar_height)
-       dc = DC_ANY_2;
-      else
-       dc = DC_ANY_1;
-      mvaddch (y, x + 3, mixer_dc (cdata.left > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY));
-      mvaddch (y, x + 4, mixer_dc (cdata.right > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY));
-      y--;
-    }
-  
-  /* muted?
-   */
-  mixer_dc (DC_BACK);
-  mvaddstr (y, x, "         ");
-  c = cinfo.caps & SND_MIXER_CINFO_CAP_MUTE ? '-' : ' ';
-  mixer_dc (DC_CBAR_FRAME);
-  mvaddch (y, x + 2, ACS_ULCORNER);
-  mvaddch (y, x + 3, mixer_dc (cdata.flags & SND_MIXER_FLG_MUTE_LEFT ?
-                              DC_CBAR_MUTE : DC_CBAR_NOMUTE));
-  mvaddch (y, x + 4, mixer_dc (cdata.flags & SND_MIXER_FLG_MUTE_RIGHT ?
-                              DC_CBAR_MUTE : DC_CBAR_NOMUTE));
-  mixer_dc (DC_CBAR_FRAME);
-  mvaddch (y, x + 5, ACS_URCORNER);
-  y--;
-  
-  /* record input?
-   */
-  if (cdata.flags & SND_MIXER_FLG_RECORD)
-    {
-      mixer_dc (DC_CBAR_RECORD);
-      mvaddstr (y, x + 1, "RECORD");
-      if (cdata.flags & SND_MIXER_FLG_RECORD_LEFT)
-       {
-         if (cdata.flags & SND_MIXER_FLG_LTOR_IN)
-           mvaddstr (y + 2, x + 6, "L");
-         else
-           mvaddstr (y + 1, x + 1, "L");
+       /* first, read values for the numbers to be displayed in
+        * specified EXACT mode
+        */
+       if (output &&
+           snd_mixer_channel_output_read(mixer_handle, channel_index, &cpdata) < 0)
+               mixer_abort(ERR_FCN, "snd_mixer_ioctl_channel_output_read");
+       if (input &&
+           snd_mixer_channel_input_read(mixer_handle, channel_index, &crdata) < 0)
+               mixer_abort(ERR_FCN, "snd_mixer_channel_input_read");
+       if (mixer_input_volumes) {
+               if (input) {
+                       vleft = crdata.left;
+                       vright = crdata.right;
+               }
+               else {
+                       vleft = vright = 0;
+               }
        }
-      if (cdata.flags & SND_MIXER_FLG_RECORD_RIGHT)
-       {
-         if (cdata.flags & SND_MIXER_FLG_RTOL_IN)
-           mvaddstr (y + 2, x + 1, "R");
-         else
-           mvaddstr (y + 1, x + 6, "R");
+       else {
+               if (output) {
+                       vleft = cpdata.left;
+                       vright = cpdata.right;
+               }
+               else {
+                       vleft = vright = 0;
+               }
        }
-    }
-  else if (cinfo.caps & SND_MIXER_CINFO_CAP_RECORD)
-    for (i = 0; i < 6; i++)
-      mvaddch (y, x + 1 + i, mixer_dc (DC_CBAR_NORECORD));
-  else
-    {
-      mixer_dc (DC_BACK);
-      mvaddstr (y, x, "         ");
-    }
-  y--;
-}
-
-static void
-mixer_update_cbars (void)
-{
-  static int o_x = 0;
-  static int o_y = 0;
-  int i, x, y;
-  
-  if (!mixer_cbar_get_pos (mixer_focus_channel, &x, &y))
-    {
-      if (mixer_focus_channel < mixer_first_vis_channel)
-       mixer_first_vis_channel = mixer_focus_channel;
-      else if (mixer_focus_channel >= mixer_first_vis_channel + mixer_n_vis_channels)
-       mixer_first_vis_channel = mixer_focus_channel - mixer_n_vis_channels + 1;
-      mixer_cbar_get_pos (mixer_focus_channel, &x, &y);
-    }
-  for (i = 0; i < mixer_n_vis_channels; i++)
-    mixer_update_cbar (i + mixer_first_vis_channel);
-  
-  /* draw focused cbar
-   */
-  if (mixer_have_old_focus)
-    {
-      mixer_dc (DC_BACK);
-      mvaddstr (o_y, o_x, " ");
-      mvaddstr (o_y, o_x + 9, " ");
-    }
-  o_x = x - 1;
-  o_y = y;
-  mixer_dc (DC_FOCUS);
-  mvaddstr (o_y, o_x, "<");
-  mvaddstr (o_y, o_x + 9, ">");
-  mixer_have_old_focus = 1;
-}
-
-static void
-mixer_draw_frame (void)
-{
-  char string[128];
-  int i;
-  int max_len;
-  
-  mixer_dc (DC_FRAME);
-  
-  /* card name
-   */
-  mixer_dc (DC_PROMPT);
-  mvaddstr (1, 2, "Card: ");
-  mixer_dc (DC_TEXT);
-  sprintf (string, "%s", mixer_card_name);
-  max_len = mixer_max_x - 2 - 6 - 2;
-  if (strlen (string) > max_len)
-    string[max_len] = 0;
-  addstr (string);
-  
-  /* device name
-   */
-  mixer_dc (DC_PROMPT);
-  mvaddstr (2, 2, "Chip: ");
-  mixer_dc (DC_TEXT);
-  sprintf (string, "%s", mixer_device_name);
-  max_len = mixer_max_x - 2 - 6 - 2;
-  if (strlen (string) > max_len)
-    string[max_len] = 0;
-  addstr (string);
-
-  /* indicate exact mode
-   */
-  if (mixer_exact)
-    {
-      mixer_dc (DC_PROMPT);
-      mvaddstr (3, 2, "[");
-      mixer_dc (DC_TEXT);
-      addstr ("ExactMode");
-      mixer_dc (DC_PROMPT);
-      addstr ("]");
-    }
-  else
-    mvaddstr (3, 2, "           ");
-
-  /* lines
-   */
-  mixer_dc (DC_PROMPT);
-  for (i = 1; i < mixer_max_y - 1; i++)
-    {
-      mvaddch (i, 0, ACS_VLINE);
-      mvaddch (i, mixer_max_x - 1, ACS_VLINE);
-    }
-  for (i = 1; i < mixer_max_x - 1; i++)
-    {
-      mvaddch (0, i, ACS_HLINE);
-      mvaddch (mixer_max_y - 1, i, ACS_HLINE);
-    }
-  
-  /* corners
-   */
-  mixer_dc (DC_PROMPT);
-  mvaddch (0, 0, ACS_ULCORNER);
-  mvaddch (0, mixer_max_x - 1, ACS_URCORNER);
-  mvaddch (mixer_max_y - 1, 0, ACS_LLCORNER);
-  if (!mixer_no_lrcorner)
-    mvaddch (mixer_max_y - 1, mixer_max_x - 1, ACS_LRCORNER);
-  else
-    {
-      mvaddch (mixer_max_y - 2, mixer_max_x - 1, ACS_LRCORNER);
-      mvaddch (mixer_max_y - 2, mixer_max_x - 2, ACS_ULCORNER);
-      mvaddch (mixer_max_y - 1, mixer_max_x - 2, ACS_LRCORNER);
-    }
-
-  /* program title
-   */
-  sprintf (string, "%s %s", PRGNAME_UPPER, VERSION);
-  max_len = strlen (string);
-  if (mixer_max_x >= max_len + 4)
-    {
-      mixer_dc (DC_PROMPT);
-      mvaddch (0, mixer_max_x / 2 - max_len / 2 - 1, '[');
-      mvaddch (0, mixer_max_x / 2 - max_len / 2 + max_len, ']');
-    }
-  if (mixer_max_x >= max_len + 2)
-    {
-      mixer_dc (DC_TEXT);
-      mvaddstr (0, mixer_max_x / 2 - max_len / 2, string);
-    }
-}
-
-static char*
-mixer_offset_text (char **t,
-                  int    col,
-                  int   *length)
-{
-  char *p = *t;
-  char *r;
-
-  while (*p && *p != '\n' && col--)
-    p++;
-  if (*p == '\n' || !*p)
-    {
-      if (*p == '\n')
-       p++;
-      *length = 0;
-      *t = p;
-      return p;
-    }
-
-  r = p;
-  while (*r && *r != '\n' && (*length)--)
-    r++;
-
-  *length = r - p;
-  while (*r && *r != '\n')
-    r++;
-  if (*r == '\n')
-    r++;
-  *t = r;
-
-  return p;
-}
 
-static void
-mixer_show_text (char *title,
-                char *text,
-                int  *xoffs,
-                int  *yoffs)
-{
-  int tlines = 0, tcols = 0;
-  float hscroll, vscroll;
-  float hoffs, voffs;
-  char *p, *text_offs = text;
-  int x1, x2, y1, y2;
-  int i, n, l, r, block, stipple;
-
-  /* coords
-   */
-  x1 = 2;
-  x2 = mixer_max_x - 3;
-  y1 = 4;
-  y2 = mixer_max_y - 2;
-
-  if ((y2 - y1) < 3 || (x2 - x1) < 3)
-    return;
-
-  /* text dimensions
-   */
-  l = 0;
-  for (p = text; *p; p++)
-    if (*p == '\n')
-      {
-       tlines++;
-       tcols = MAX (l, tcols);
-       l = 0;
-      }
-    else
-      l++;
-  tcols = MAX (l, tcols);
-  if (p > text && *(p - 1) != '\n')
-    tlines++;
-
-  /* scroll areas / offsets
-   */
-  l = x2 - x1 - 2;
-  if (l > tcols)
-    {
-      x1 += (l - tcols) / 2;
-      x2 = x1 + tcols + 1;
-    }
-  if (mixer_hscroll_delta)
-    {
-      *xoffs += mixer_hscroll_delta;
-      mixer_hscroll_delta = 0;
-      if (*xoffs < 0)
-       {
-         *xoffs = 0;
-         beep ();
+       /* then, always use percentage values for the bars. if we don't do
+        * this, we will see aliasing effects on specific circumstances.
+        * (actually they don't really dissapear, but they are transfered
+        *  to bar<->smaller-scale ambiguities).
+        */
+       if (mixer_exact) {
+               i = 0;
+               if (snd_mixer_exact_mode(mixer_handle, 0) < 0)
+                       mixer_abort(ERR_FCN, "snd_mixer_exact");
+               if (output &&
+                   snd_mixer_channel_output_read(mixer_handle, channel_index, &cpdata) < 0)
+                       mixer_abort(ERR_FCN, "snd_mixer_channel_output_read");
+               if (input &&
+                   snd_mixer_channel_input_read(mixer_handle, channel_index, &crdata) < 0)
+                       mixer_abort(ERR_FCN, "snd_mixer_channel_input_read");
        }
-      else if (*xoffs > tcols - l - 1)
-       {
-         *xoffs = MAX (0, tcols - l - 1);
-         beep ();
+       /* get channel bar position
+        */
+       if (!mixer_cbar_get_pos(channel_index, &x, &y))
+               return;
+
+       /* channel bar name
+        */
+       mixer_dc(channel_index == mixer_focus_channel ? DC_CBAR_FOCUS_LABEL : DC_CBAR_LABEL);
+       cinfo.name[8] = 0;
+       for (i = 0; i < 8; i++) {
+               string[i] = ' ';
        }
-    }
-  if (tcols - l - 1 <= 0)
-    {
-      hscroll = 1;
-      hoffs = 0;
-    }
-  else
-    {
-      hscroll = ((float) l) / tcols;
-      hoffs = ((float) *xoffs) / (tcols - l - 1);
-    }
-
-  l = y2 - y1 - 2;
-  if (l > tlines)
-    {
-      y1 += (l - tlines) / 2;
-      y2 = y1 + tlines + 1;
-    }
-  if (mixer_vscroll_delta)
-    {
-      *yoffs += mixer_vscroll_delta;
-      mixer_vscroll_delta = 0;
-      if (*yoffs < 0)
-       {
-         *yoffs = 0;
-         beep ();
+       sprintf(string + (8 - strlen(cinfo.name)) / 2, "%s          ", cinfo.name);
+       string[8] = 0;
+       mvaddstr(y, x, string);
+       y--;
+
+       /* current channel values
+        */
+       mixer_dc(DC_BACK);
+       mvaddstr(y, x, "         ");
+       mixer_dc(DC_TEXT);
+       sprintf(string, "%d", vleft);
+       mvaddstr(y, x + 3 - strlen(string), string);
+       mixer_dc(DC_CBAR_FRAME);
+       mvaddch(y, x + 3, '<');
+       mvaddch(y, x + 4, '>');
+       mixer_dc(DC_TEXT);
+       sprintf(string, "%d", vright);
+       mvaddstr(y, x + 5, string);
+       y--;
+
+       /* left/right bar
+        */
+       mixer_dc(DC_CBAR_FRAME);
+       mvaddstr(y, x, "         ");
+       mvaddch(y, x + 2, ACS_LLCORNER);
+       mvaddch(y, x + 3, ACS_HLINE);
+       mvaddch(y, x + 4, ACS_HLINE);
+       mvaddch(y, x + 5, ACS_LRCORNER);
+       y--;
+       for (i = 0; i < mixer_cbar_height; i++) {
+               mvaddstr(y - i, x, "         ");
+               mvaddch(y - i, x + 2, ACS_VLINE);
+               mvaddch(y - i, x + 5, ACS_VLINE);
        }
-      else if (*yoffs > tlines - l - 1)
-       {
-         *yoffs = MAX (0, tlines - l - 1);
-         beep ();
+       string[2] = 0;
+       for (i = 0; i < mixer_cbar_height; i++) {
+               int dc;
+
+               if (i + 1 >= 0.8 * mixer_cbar_height)
+                       dc = DC_CBAR_FULL_3;
+               else if (i + 1 >= 0.4 * mixer_cbar_height)
+                       dc = DC_CBAR_FULL_2;
+               else
+                       dc = DC_CBAR_FULL_1;
+               mvaddch(y, x + 3, mixer_dc(vleft > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY));
+               mvaddch(y, x + 4, mixer_dc(vright > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY));
+               y--;
        }
-    }
-  if (tlines - l - 1 <= 0)
-    {
-      voffs = 0;
-      vscroll = 1;
-    }
-  else
-    {
-      vscroll = ((float) l) / tlines;
-      voffs = ((float) *yoffs) / (tlines - l - 1);
-    }
-
-  /* colors
-   */
-  mixer_init_dc ('.', DC_ANY_3, MIXER_YELLOW, MIXER_BLUE, A_BOLD);
-  mixer_init_dc ('.', DC_ANY_4, MIXER_WHITE, MIXER_BLUE, A_BOLD);
-  mixer_dc (DC_ANY_4);
-
-  /* corners
-   */
-  mvaddch (y2, x2, ACS_LRCORNER);
-  mvaddch (y2, x1, ACS_LLCORNER);
-  mvaddch (y1, x1, ACS_ULCORNER);
-  mvaddch (y1, x2, ACS_URCORNER);
-
-  /* left + upper border
-   */
-  for (i = y1 + 1; i < y2; i++)
-    mvaddch (i, x1, ACS_VLINE);
-  for (i = x1 + 1; i < x2; i++)
-    mvaddch (y1, i, ACS_HLINE);
-  if (title)
-    {
-      l = strlen (title);
-      if (l <= x2 - x1 - 3)
-       {
-         mvaddch (y1, x1 + 1 + (x2 - x1 - l) / 2 - 1, '[');
-         mvaddch (y1, x1 + 1 + (x2 - x1 - l) / 2 + l, ']');
+
+       /* muted?
+        */
+       mixer_dc(DC_BACK);
+       mvaddstr(y, x, "         ");
+       if (output) {
+               c = (cpinfo.caps & SND_MIXER_CINFO_DCAP_MUTE) ? '-' : ' ';
+               mixer_dc(DC_CBAR_FRAME);
+               mvaddch(y, x + 2, ACS_ULCORNER);
+               mvaddch(y, x + 3, mixer_dc(cpdata.flags & SND_MIXER_DFLG_MUTE_LEFT ?
+                                          DC_CBAR_MUTE : DC_CBAR_NOMUTE));
+               mvaddch(y, x + 4, mixer_dc(cpdata.flags & SND_MIXER_DFLG_MUTE_RIGHT ?
+                                          DC_CBAR_MUTE : DC_CBAR_NOMUTE));
+               mixer_dc(DC_CBAR_FRAME);
+               mvaddch(y, x + 5, ACS_URCORNER);
        }
-      if (l <= x2 - x1 - 1)
-       {
-         mixer_dc (DC_ANY_3);
-         mvaddstr (y1, x1 + 1 + (x2 - x1 - l) / 2, title);
+       y--;
+
+       /* record input?
+        */
+       mixer_dc(DC_BACK);
+       mvaddstr(y, x, "         ");
+       if (input) {
+               if ((crdata.flags & SND_MIXER_DFLG_MUTE) != SND_MIXER_DFLG_MUTE) {
+                       mixer_dc(DC_CBAR_RECORD);
+                       mvaddstr(y, x + 1, "RECORD");
+                       if (!(crdata.flags & SND_MIXER_DFLG_MUTE_LEFT)) {
+                               if (crdata.flags & SND_MIXER_DFLG_LTOR)
+                                       mvaddstr(y + 2, x + 6, "L");
+                               else
+                                       mvaddstr(y + 1, x + 1, "L");
+                       }
+                       if (!(crdata.flags & SND_MIXER_DFLG_MUTE_RIGHT)) {
+                               if (crdata.flags & SND_MIXER_DFLG_RTOL)
+                                       mvaddstr(y + 2, x + 1, "R");
+                               else
+                                       mvaddstr(y + 1, x + 6, "R");
+                       }
+               } else {
+                       for (i = 0; i < 6; i++)
+                               mvaddch(y, x + 1 + i, mixer_dc(DC_CBAR_NORECORD));
+               }
        }
-      mixer_dc (DC_ANY_4);
-    }
-
-  stipple = ACS_CKBOARD;
-  block = ACS_BLOCK;
-  if (block == '#' && ACS_BOARD == '#')
-    {
-      block = stipple;
-      stipple = ACS_BLOCK;
-    }
-
-  /* lower scroll border
-   */
-  l = x2 - x1 - 1;
-  n = hscroll * l;
-  r = (hoffs + 1.0 / (2 * (l - n - 1))) * (l - n - 1);
-  for (i = 0; i < l; i++)
-    mvaddch (y2, i + x1 + 1, hscroll >= 1 ? ACS_HLINE :
-            i >= r && i <= r + n ? block : stipple);
-
-  /* right scroll border
-   */
-  l = y2 - y1 - 1;
-  n = vscroll * l;
-  r = (voffs + 1.0 / (2 * (l - n - 1))) * (l - n - 1);
-  for (i = 0; i < l; i++)
-    mvaddch (i + y1 + 1, x2, vscroll >= 1 ? ACS_VLINE :
-            i >= r && i <= r + n ? block : stipple);
-
-  /* show text
-   */
-  x1++; y1++;
-  for (i = 0; i < *yoffs; i++)
-    {
-      l = 0;
-      mixer_offset_text (&text_offs, 0, &l);
-    }
-  for (i = y1; i < y2; i++)
-    {
-      l = x2 - x1;
-      p = mixer_offset_text (&text_offs, *xoffs, &l);
-      n = x1;
-      while (l--)
-       mvaddch (i, n++, *p++);
-      while (n < x2)
-       mvaddch (i, n++, ' ');
-    }
-}
-
-struct vbuffer
-{
-  char *buffer;
-  int size;
-  int len;
-};
-
-static void
-vbuffer_kill (struct vbuffer *vbuf)
-{
-  if (vbuf->size)
-    free (vbuf->buffer);
-  vbuf->buffer = NULL;
-  vbuf->size = 0;
-  vbuf->len = 0;
-}
-
-#define vbuffer_append_string(vb,str)  vbuffer_append (vb, str, strlen (str))
-static void
-vbuffer_append (struct vbuffer *vbuf,
-               char           *text,
-               int             len)
-{
-  if (vbuf->size - vbuf->len <= len)
-    {
-      vbuf->size += len + 1;
-      vbuf->buffer = realloc (vbuf->buffer, vbuf->size);
-    }
-  memcpy (vbuf->buffer + vbuf->len, text, len);
-  vbuf->len += len;
-  vbuf->buffer[vbuf->len] = 0;
+       y--;
 }
 
-static int
-vbuffer_append_file (struct vbuffer *vbuf,
-                    char           *name)
+static void mixer_update_cbars(void)
 {
-  int fd;
-
-  fd = open (name, O_RDONLY);
-  if (fd >= 0)
-    {
-      char buffer[1025];
-      int l;
-
-      do
-       {
-         l = read (fd, buffer, 1024);
-         
-         vbuffer_append (vbuf, buffer, MAX (0, l));
+       static int o_x = 0;
+       static int o_y = 0;
+       int i, x, y;
+
+       if (!mixer_cbar_get_pos(mixer_focus_channel, &x, &y)) {
+               if (mixer_focus_channel < mixer_first_vis_channel)
+                       mixer_first_vis_channel = mixer_focus_channel;
+               else if (mixer_focus_channel >= mixer_first_vis_channel + mixer_n_vis_channels)
+                       mixer_first_vis_channel = mixer_focus_channel - mixer_n_vis_channels + 1;
+               mixer_cbar_get_pos(mixer_focus_channel, &x, &y);
        }
-      while (l > 0 || (l < 0 && (errno == EAGAIN || errno == EINTR)));
-
-      close (fd);
-
-      return 0;
-    }
-  else
-    return 1;
-}
-
-static void
-mixer_show_procinfo (void)
-{
-  struct vbuffer vbuf = { NULL, 0, 0 };
-
-  vbuffer_append_string (&vbuf, "\n");
-  vbuffer_append_string (&vbuf, "/proc/asound/version:\n");
-  vbuffer_append_string (&vbuf, "====================\n");
-  if (vbuffer_append_file (&vbuf, "/proc/asound/version"))
-    {
-      vbuffer_kill (&vbuf);
-      mixer_procinfo_xoffs = mixer_procinfo_yoffs = 0;
-      mixer_show_text ("/proc",
-                      " No /proc information available. ",
-                      &mixer_procinfo_xoffs, &mixer_procinfo_yoffs);
-      return;
-    }
-  else
-    vbuffer_append_file (&vbuf, "/proc/asound/meminfo");
-
-  vbuffer_append_string (&vbuf, "\n");
-  vbuffer_append_string (&vbuf, "/proc/asound/cards:\n");
-  vbuffer_append_string (&vbuf, "===================\n");
-  if (vbuffer_append_file (&vbuf, "/proc/asound/cards"))
-    vbuffer_append_string (&vbuf, "No information available.\n");
-
-  vbuffer_append_string (&vbuf, "\n");
-  vbuffer_append_string (&vbuf, "/proc/asound/devices:\n");
-  vbuffer_append_string (&vbuf, "=====================\n");
-  if (vbuffer_append_file (&vbuf, "/proc/asound/devices"))
-    vbuffer_append_string (&vbuf, "No information available.\n");
-
-  vbuffer_append_string (&vbuf, "\n");
-  vbuffer_append_string (&vbuf, "/proc/asound/oss-devices:\n");
-  vbuffer_append_string (&vbuf, "=========================\n");
-  if (vbuffer_append_file (&vbuf, "/proc/asound/oss-devices"))
-    vbuffer_append_string (&vbuf, "No information available.\n");
-
-  vbuffer_append_string (&vbuf, "\n");
-  vbuffer_append_string (&vbuf, "/proc/asound/timers:\n");
-  vbuffer_append_string (&vbuf, "====================\n");
-  if (vbuffer_append_file (&vbuf, "/proc/asound/timers"))
-    vbuffer_append_string (&vbuf, "No information available.\n");
-
-  vbuffer_append_string (&vbuf, "\n");
-  vbuffer_append_string (&vbuf, "/proc/asound/pcm:\n");
-  vbuffer_append_string (&vbuf, "=================\n");
-  if (vbuffer_append_file (&vbuf, "/proc/asound/pcm"))
-    vbuffer_append_string (&vbuf, "No information available.\n");
-
-  mixer_show_text ("/proc", vbuf.buffer,
-                  &mixer_procinfo_xoffs, &mixer_procinfo_yoffs);
-  vbuffer_kill (&vbuf);
+       for (i = 0; i < mixer_n_vis_channels; i++)
+               mixer_update_cbar(i + mixer_first_vis_channel);
+
+       /* draw focused cbar
+        */
+       mixer_dc(DC_BACK);
+       mvaddstr(o_y, o_x, " ");
+       mvaddstr(o_y, o_x + 9, " ");
+       o_x = x - 1;
+       o_y = y;
+       mixer_dc(DC_FOCUS);
+       mvaddstr(o_y, o_x, "<");
+       mvaddstr(o_y, o_x + 9, ">");
 }
 
-static void
-mixer_init (void)
+static void mixer_draw_frame(void)
 {
-  static snd_mixer_info_t mixer_info = { 0, };
-  static struct snd_ctl_hw_info hw_info;
-  void *ctl_handle;
-  
-  if (snd_ctl_open (&ctl_handle, card_id) < 0)
-    mixer_abort (ERR_OPEN, "snd_ctl_open");
-  if (snd_ctl_hw_info (ctl_handle, &hw_info) < 0)
-    mixer_abort (ERR_FCN, "snd_ctl_hw_info");
-  snd_ctl_close (ctl_handle);
-  /* open mixer device
-   */
-  if (snd_mixer_open (&mixer_handle, card_id, mixer_id) < 0)
-    mixer_abort (ERR_OPEN, "snd_mixer_open");
-  
-  /* setup global variables
-   */
-  if (snd_mixer_info (mixer_handle, &mixer_info) < 0)
-    mixer_abort (ERR_FCN, "snd_mixer_info");
-  mixer_n_channels = mixer_info.channels;
-  mixer_card_name = hw_info.name;
-  mixer_device_name = mixer_info.name;
-}
+       char string[128];
+       int i;
+       int max_len;
+
+       mixer_dc(DC_FRAME);
+
+       /* corners
+        */
+       mvaddch(0, 0, ACS_ULCORNER);
+       mvaddch(mixer_max_y - 1, 0, ACS_LLCORNER);
+       mvaddch(mixer_max_y - 1, mixer_max_x - 1, ACS_LRCORNER);
+       mvaddch(0, mixer_max_x - 1, ACS_URCORNER);
+
+       /* lines
+        */
+       for (i = 1; i < mixer_max_y - 1; i++) {
+               mvaddch(i, 0, ACS_VLINE);
+               mvaddch(i, mixer_max_x - 1, ACS_VLINE);
+       }
+       for (i = 1; i < mixer_max_x - 1; i++) {
+               mvaddch(0, i, ACS_HLINE);
+               mvaddch(mixer_max_y - 1, i, ACS_HLINE);
+       }
 
-static void
-mixer_init_window (void)
-{
-  /* initialize ncurses
-   */
-  mixer_window = initscr ();
-
-  mixer_no_lrcorner = tigetflag ("xenl") != 1 && tigetflag ("am") != 1;
-
-  if (mixer_do_color)
-    mixer_do_color = has_colors ();
-  mixer_init_draw_contexts ();
-
-  /* react on key presses
-   */
-  cbreak ();
-  noecho ();
-  leaveok (mixer_window, TRUE);
-  keypad (mixer_window, TRUE);
-  GETCH_BLOCK (1);
-
-  /* init mixer screen
-   */
-  getmaxyx (mixer_window, mixer_max_y, mixer_max_x);
-  if (mixer_minimize)
-    {
-      mixer_max_x = MIXER_MIN_X;
-      mixer_max_y = MIXER_MIN_Y;
-    }
-  mixer_ofs_x = 2 /* extra begin padding: */ + 1;
-
-  /* required allocations */
-  mixer_n_vis_channels = (mixer_max_x - mixer_ofs_x * 2 + 1) / 9;
-  mixer_n_vis_channels = CLAMP (mixer_n_vis_channels, 1, mixer_n_channels);
-  mixer_extra_space = mixer_max_x - mixer_ofs_x * 2 + 1 - mixer_n_vis_channels * 9;
-  mixer_extra_space = MAX (0, mixer_extra_space / (mixer_n_vis_channels + 1));
-  if (MIXER_TEXT_Y + 10 < mixer_max_y)
-    mixer_cbar_height = 10 + MAX (0, mixer_max_y - MIXER_TEXT_Y - 10 ) / 2;
-  else
-    mixer_cbar_height = MAX (1, mixer_max_y - MIXER_TEXT_Y);
-
-  mixer_clear (TRUE);
+       /* program title
+        */
+       sprintf(string, "%s %s", PRGNAME_UPPER, VERSION);
+       max_len = strlen(string);
+       mvaddch(0, mixer_max_x / 2 - max_len / 2 - 1, '[');
+       mvaddch(0, mixer_max_x / 2 - max_len / 2 + max_len, ']');
+       mixer_dc(DC_TEXT);
+       mvaddstr(0, mixer_max_x / 2 - max_len / 2, string);
+
+       /* card name
+        */
+       mixer_dc(DC_PROMPT);
+       mvaddstr(1, 2, "Card:");
+       mixer_dc(DC_TEXT);
+       sprintf(string, "%s", mixer_card_name);
+       max_len = mixer_max_x - 2 - 6 - 2;
+       if (strlen(string) > max_len)
+               string[max_len] = 0;
+       mvaddstr(1, 2 + 6, string);
+
+       /* device name
+        */
+       mixer_dc(DC_PROMPT);
+       mvaddstr(2, 2, "Chip: ");
+       mixer_dc(DC_TEXT);
+       sprintf(string, "%s", mixer_device_name);
+       max_len = mixer_max_x - 2 - 6 - 2;
+       if (strlen(string) > max_len)
+               string[max_len] = 0;
+       mvaddstr(2, 2 + 6, string);
+       if (mixer_input_volumes)
+               mvaddstr(3, 2, "Record mixer");
+       else
+               mvaddstr(3, 2, "            ");
 }
 
-static void
-mixer_resize (void)
+static void mixer_init(void)
 {
-  struct winsize winsz = { 0, };
-
-  mixer_needs_resize = 0;
-  
-  if (ioctl (fileno (stdout), TIOCGWINSZ, &winsz) >= 0 &&
-      winsz.ws_row && winsz.ws_col)
-    {
-      keypad (mixer_window, FALSE);
-      leaveok (mixer_window, FALSE);
-
-      endwin ();
-      
-      mixer_max_x = MAX (2, winsz.ws_col);
-      mixer_max_y = MAX (2, winsz.ws_row);
-      
-      /* humpf, i don't get it, if only the number of rows change,
-       * ncurses will segfault shortly after (could trigger that with mc as well).
-       */
-      resizeterm (mixer_max_y + 1, mixer_max_x + 1);
-      resizeterm (mixer_max_y, mixer_max_x);
-      
-      mixer_init_window ();
-      
-      if (mixer_max_x < MIXER_MIN_X ||
-         mixer_max_y < MIXER_MIN_Y)
-       beep (); // mixer_abort (ERR_WINSIZE, "");
-
-      mixer_have_old_focus = 0;
-    }
+       static snd_mixer_info_t mixer_info =
+       {0};
+       static struct snd_ctl_hw_info hw_info;
+       void *ctl_handle;
+
+       if (snd_ctl_open(&ctl_handle, card_id) < 0)
+               mixer_abort(ERR_OPEN, "snd_ctl_open");
+       if (snd_ctl_hw_info(ctl_handle, &hw_info) < 0)
+               mixer_abort(ERR_FCN, "snd_ctl_hw_info");
+       snd_ctl_close(ctl_handle);
+       /* open mixer device
+        */
+       if (snd_mixer_open(&mixer_handle, card_id, mixer_id) < 0)
+               mixer_abort(ERR_OPEN, "snd_mixer_open");
+
+       /* setup global variables
+        */
+       if (snd_mixer_info(mixer_handle, &mixer_info) < 0)
+               mixer_abort(ERR_FCN, "snd_mixer_info");
+       mixer_n_channels = mixer_info.channels;
+       mixer_card_name = hw_info.name;
+       mixer_device_name = mixer_info.name;
 }
 
-static void
-mixer_channel_changed (void *private_data,
-                      int   channel)
+static void mixer_iteration_update(void *dummy, int channel)
 {
-  /* we don't actually need to update the individual channels because
-   * we redraw the whole screen upon every main iteration anyways.
-   */
 #if 0
-  fprintf (stderr, "*** channel = %i\n", channel);
-  mixer_update_cbar (channel);
+       fprintf(stderr, "*** channel = %i\n", channel);
 #endif
+       mixer_update_cbar(channel);
+       refresh();
 }
 
-static int
-mixer_iteration (void)
+static int mixer_iteration(void)
 {
-  struct timeval delay = { 0, };
-  snd_mixer_callbacks_t callbacks = { 0, };
-  int mixer_fd;
-  fd_set rfds;
-  int finished = 0;
-  int key = 0;
-  int old_view;
-  
-  callbacks.channel_was_changed = mixer_channel_changed;
-
-  /* setup for select on stdin and the mixer fd */
-  mixer_fd = snd_mixer_file_descriptor (mixer_handle);
-  FD_ZERO (&rfds);
-  FD_SET (fileno (stdin), &rfds);
-  FD_SET (mixer_fd, &rfds);
-
-  delay.tv_sec = 0;
-  delay.tv_usec = 0 * 100 * 1000;
-
-  finished = select (mixer_fd + 1, &rfds, NULL, NULL, mixer_needs_resize ? &delay : NULL) < 0;
-
-  /* don't abort on handled signals */
-  if (finished && errno == EINTR)
-    {
-      FD_ZERO (&rfds);
-      finished = 0;
-    }
-  else if (mixer_needs_resize)
-    mixer_resize ();
-
-  if (FD_ISSET (mixer_fd, &rfds))
-    snd_mixer_read (mixer_handle, &callbacks);
-
-  if (FD_ISSET (fileno (stdin), &rfds))
-    key = getch ();
-
-  old_view = mixer_view;
-  
-  /* feature Escape prefixing for some keys */
-  if (key == 27)
-    {
-      GETCH_BLOCK (0);
-      key = getch ();
-      GETCH_BLOCK (1);
-      switch (key)
-       {
-       case 9: /* Tab */
-         key = KEY_BTAB;
-         break;
-       default:
-         key = 27;
-         break;
+       snd_mixer_callbacks_t callbacks;
+       int key;
+       int finished = 0;
+       int mixer_fd;
+       fd_set in;
+
+       bzero(&callbacks, sizeof(callbacks));
+       callbacks.channel_was_changed = mixer_iteration_update;
+       callbacks.output_channel_was_changed = mixer_iteration_update;
+       callbacks.input_channel_was_changed = mixer_iteration_update;
+       mixer_fd = snd_mixer_file_descriptor(mixer_handle);
+       while (1) {
+               FD_ZERO(&in);
+               FD_SET(fileno(stdin), &in);
+               FD_SET(mixer_fd, &in);
+               if (select(mixer_fd + 1, &in, NULL, NULL, NULL) <= 0)
+                       return 1;
+               if (FD_ISSET(mixer_fd, &in))
+                       snd_mixer_read(mixer_handle, &callbacks);
+               if (FD_ISSET(fileno(stdin), &in))
+                       break;
        }
-    }
-  
-  /* general keys */
-  switch (key)
-    {
-    case 0:
-      /* ignore */
-      break;
-    case 27:   /* Escape */
-      finished = 1;
-      key = 0;
-      break;
-    case 13:   /* Return */
-    case 10:   /* NewLine */
-      if (mixer_view == VIEW_CHANNELS)
-       mixer_clear (FALSE);
-      mixer_view = VIEW_CHANNELS;
-      key = 0;
-      break;
-    case 'h':
-    case 'H':
-    case KEY_F (1):
-      mixer_view = VIEW_HELP;
-      key = 0;
-      break;
-    case '/':
-    case KEY_F (2):
-      mixer_view = VIEW_PROCINFO;
-      key = 0;
-      break;
-    case 'L':
-    case 'l':
-      mixer_clear (TRUE);
-      break;
-    }
-  
-  if (key && (mixer_view == VIEW_HELP ||
-             mixer_view == VIEW_PROCINFO))
-    switch (key)
-      {
-      case 9:          /* Tab */
-       mixer_hscroll_delta += 8;
-       break;
-      case KEY_BTAB:
-       mixer_hscroll_delta -= 8;
-       break;
-      case KEY_A1:
-       mixer_hscroll_delta -= 1;
-       mixer_vscroll_delta -= 1;
-       break;
-      case KEY_A3:
-       mixer_hscroll_delta += 1;
-       mixer_vscroll_delta -= 1;
-       break;
-      case KEY_C1:
-       mixer_hscroll_delta -= 1;
-       mixer_vscroll_delta += 1;
-       break;
-      case KEY_C3:
-       mixer_hscroll_delta += 1;
-       mixer_vscroll_delta += 1;
-       break;
-      case KEY_RIGHT:
-      case 'n':
-       mixer_hscroll_delta += 1;
-       break;
-      case KEY_LEFT:
-      case 'p':
-       mixer_hscroll_delta -= 1;
-       break;
-      case KEY_UP:
-      case 'w':
-      case 'W':
-       mixer_vscroll_delta -= 1;
-       break;
-      case KEY_DOWN:
-      case 'x':
-      case 'X':
-       mixer_vscroll_delta += 1;
-       break;
-      case KEY_PPAGE:
-      case 'B':
-      case 'b':
-       mixer_vscroll_delta -= (mixer_max_y - 5) / 2;
-       break;
-      case KEY_NPAGE:
-      case ' ':
-       mixer_vscroll_delta += (mixer_max_y - 5) / 2;
-       break;
-      case KEY_BEG:
-      case KEY_HOME:
-       mixer_hscroll_delta -= 0xffffff;
-       break;
-      case KEY_LL:
-      case KEY_END:
-       mixer_hscroll_delta += 0xffffff;
-       break;
-      }
-  
-  if (key && mixer_view == VIEW_CHANNELS)
-    switch (key)
-      {
-      case 9:          /* Tab */
-       mixer_exact = !mixer_exact;
-       break;
-      case KEY_RIGHT:
-      case 'n':
-       mixer_focus_channel += 1;
-       break;
-      case KEY_LEFT:
-      case 'p':
-       mixer_focus_channel -= 1;
-       break;
-      case KEY_PPAGE:
-       if (mixer_exact)
-         {
-           mixer_lvolume_delta = 8;
-           mixer_rvolume_delta = 8;
-         }
-       else
-         {
-           mixer_lvolume_delta = 10;
-           mixer_rvolume_delta = 10;
-         }
-       break;
-      case KEY_NPAGE:
-       if (mixer_exact)
-         {
-           mixer_lvolume_delta = -8;
-           mixer_rvolume_delta = -8;
-         }
-       else
-         {
-           mixer_lvolume_delta = -10;
-           mixer_rvolume_delta = -10;
-         }
-       break;
-      case KEY_BEG:
-      case KEY_HOME:
-       mixer_lvolume_delta = 512;
-       mixer_rvolume_delta = 512;
-       break;
-      case KEY_LL:
-      case KEY_END:
-       mixer_lvolume_delta = -512;
-       mixer_rvolume_delta = -512;
-       break;
-      case '+':
-       mixer_lvolume_delta = 1;
-       mixer_rvolume_delta = 1;
-       break;
-      case '-':
-       mixer_lvolume_delta = -1;
-       mixer_rvolume_delta = -1;
-       break;
-      case 'w':
-      case KEY_UP:
-       mixer_lvolume_delta = 1;
-       mixer_rvolume_delta = 1;
-      case 'W':
-       mixer_lvolume_delta += 1;
-       mixer_rvolume_delta += 1;
-       break;
-      case 'x':
-      case KEY_DOWN:
-       mixer_lvolume_delta = -1;
-       mixer_rvolume_delta = -1;
-      case 'X':
-       mixer_lvolume_delta += -1;
-       mixer_rvolume_delta += -1;
-       break;
-      case 'q':
-       mixer_lvolume_delta = 1;
-      case 'Q':
-       mixer_lvolume_delta += 1;
-       break;
-      case 'y':
-      case 'z':
-       mixer_lvolume_delta = -1;
-      case 'Y':
-      case 'Z':
-       mixer_lvolume_delta += -1;
-       break;
-      case 'e':
-       mixer_rvolume_delta = 1;
-      case 'E':
-       mixer_rvolume_delta += 1;
-       break;
-      case 'c':
-       mixer_rvolume_delta = -1;
-      case 'C':
-       mixer_rvolume_delta += -1;
-       break;
-      case 'm':
-      case 'M':
-       mixer_toggle_mute_left = 1;
-       mixer_toggle_mute_right = 1;
-       break;
-      case 'b':
-      case 'B':
-      case '=':
-       mixer_balance_volumes = 1;
-       break;
-      case '<':
-      case ',':
-       mixer_toggle_mute_left = 1;
-       break;
-      case '>':
-      case '.':
-       mixer_toggle_mute_right = 1;
-       break;
-      case ' ':
-       mixer_toggle_record = 1;
-       break;
-      case KEY_IC:
-      case ';':
-       mixer_toggle_rec_left = 1;
-       break;
-      case '\'':
-      case KEY_DC:
-       mixer_toggle_rec_right = 1;
-       break;
-      case '1':
-       mixer_route_rtol_in = 1;
-       break;
-      case '2':
-       mixer_route_ltor_in = 1;
-       break;
-      }
-  
-  if (old_view != mixer_view)
-    mixer_clear (FALSE);
-  
-  mixer_focus_channel = CLAMP (mixer_focus_channel, 0, mixer_n_channels - 1);
-  
-  return finished;
+       key = getch();
+       switch (key) {
+       case 27:                /* Escape */
+               finished = 1;
+               break;
+       case 9:         /* Tab */
+               mixer_exact = !mixer_exact;
+               break;
+       case KEY_RIGHT:
+       case 'n':
+               mixer_focus_channel += 1;
+               break;
+       case KEY_LEFT:
+       case 'p':
+               mixer_focus_channel -= 1;
+               break;
+       case KEY_PPAGE:
+               if (mixer_exact) {
+                       mixer_lvolume_delta = 8;
+                       mixer_rvolume_delta = 8;
+               } else {
+                       mixer_lvolume_delta = 10;
+                       mixer_rvolume_delta = 10;
+               }
+               break;
+       case KEY_NPAGE:
+               if (mixer_exact) {
+                       mixer_lvolume_delta = -8;
+                       mixer_rvolume_delta = -8;
+               } else {
+                       mixer_lvolume_delta = -10;
+                       mixer_rvolume_delta = -10;
+               }
+               break;
+       case KEY_BEG:
+       case KEY_HOME:
+               mixer_lvolume_delta = 512;
+               mixer_rvolume_delta = 512;
+               break;
+       case KEY_LL:
+       case KEY_END:
+               mixer_lvolume_delta = -512;
+               mixer_rvolume_delta = -512;
+               break;
+       case '+':
+               mixer_lvolume_delta = 1;
+               mixer_rvolume_delta = 1;
+               break;
+       case '-':
+               mixer_lvolume_delta = -1;
+               mixer_rvolume_delta = -1;
+               break;
+       case 'w':
+       case KEY_UP:
+               mixer_lvolume_delta = 1;
+               mixer_rvolume_delta = 1;
+       case 'W':
+               mixer_lvolume_delta += 1;
+               mixer_rvolume_delta += 1;
+               break;
+       case 'x':
+       case KEY_DOWN:
+               mixer_lvolume_delta = -1;
+               mixer_rvolume_delta = -1;
+       case 'X':
+               mixer_lvolume_delta += -1;
+               mixer_rvolume_delta += -1;
+               break;
+       case 'q':
+               mixer_lvolume_delta = 1;
+       case 'Q':
+               mixer_lvolume_delta += 1;
+               break;
+       case 'y':
+       case 'z':
+               mixer_lvolume_delta = -1;
+       case 'Y':
+       case 'Z':
+               mixer_lvolume_delta += -1;
+               break;
+       case 'e':
+               mixer_rvolume_delta = 1;
+       case 'E':
+               mixer_rvolume_delta += 1;
+               break;
+       case 'c':
+               mixer_rvolume_delta = -1;
+       case 'C':
+               mixer_rvolume_delta += -1;
+               break;
+       case 'm':
+       case 'M':
+               mixer_input_volumes = 0;
+               mixer_toggle_mute_left = 1;
+               mixer_toggle_mute_right = 1;
+               break;
+       case 'b':
+       case 'B':
+       case '=':
+               mixer_balance_volumes = 1;
+               break;
+       case '<':
+       case ',':
+               mixer_input_volumes = 0;
+               mixer_toggle_mute_left = 1;
+               break;
+       case '>':
+       case '.':
+               mixer_input_volumes = 0;
+               mixer_toggle_mute_right = 1;
+               break;
+       case 'R':
+       case 'r':
+               mixer_input_volumes = !mixer_input_volumes;
+               break;
+       case 'L':
+       case 'l':
+               mixer_clear();
+               break;
+       case ' ':
+               mixer_input_volumes = 1;
+               mixer_toggle_record_left = 1;
+               mixer_toggle_record_right = 1;
+               break;
+       case KEY_IC:
+       case ';':
+               mixer_input_volumes = 1;
+               mixer_toggle_record_left = 1;
+               break;
+       case '\'':
+       case KEY_DC:
+               mixer_input_volumes = 1;
+               mixer_toggle_record_right = 1;
+               break;
+       case '1':
+               mixer_input_volumes = 1;
+               mixer_route_rtol_in = 1;
+               break;
+       case '2':
+               mixer_input_volumes = 1;
+               mixer_route_ltor_in = 1;
+               break;
+       }
+       mixer_focus_channel = CLAMP(mixer_focus_channel, 0, mixer_n_channels - 1);
+
+       return finished;
 }
 
-static void
-mixer_winch (void)
+static void mixer_init_screen(void)
 {
-  signal (SIGWINCH, (void*) mixer_winch);
-
-  mixer_needs_resize++;
+       signal(SIGWINCH, (void *) mixer_init_screen);
+
+       getmaxyx(mixer_window, mixer_max_y, mixer_max_x);
+       mixer_clear();
+       mixer_max_x = MAX(MIXER_MIN_X, mixer_max_x);
+       mixer_max_y = MAX(MIXER_MIN_Y, mixer_max_y);
+       mixer_clear();
+       mixer_ofs_x = 2;
+       mixer_ofs_y = 2;
+       mixer_extra_space = 0;
+       mixer_n_vis_channels = MIN((mixer_max_x - 2 * mixer_ofs_x + 1) / (9 + mixer_extra_space),
+                                  mixer_n_channels);
+       mixer_extra_space = ((mixer_max_x - 2 * mixer_ofs_x - 1 - mixer_n_vis_channels * 9.0) /
+                            (mixer_n_vis_channels - 1));
+       if (mixer_n_vis_channels < mixer_n_channels) {
+               /* recalc
+                */
+               mixer_extra_space = MAX(mixer_extra_space, 1);
+               mixer_n_vis_channels = MIN((mixer_max_x - 2 * mixer_ofs_x + 1) / (9 + mixer_extra_space),
+                                          mixer_n_channels);
+               mixer_extra_space = ((mixer_max_x - 2 * mixer_ofs_x - 1 - mixer_n_vis_channels * 9.0) /
+                                    (mixer_n_vis_channels - 1));
+       }
+       mixer_first_vis_channel = 0;
+       mixer_cbar_height = 10 + MAX(0, (mixer_max_y - MIXER_MIN_Y - 1)) / 2;
 }
 
-static void
-mixer_signal_handler (int signal)
+static void mixer_signal_handler(int signal)
 {
-  if (signal != SIGSEGV)
-    mixer_abort (ERR_SIGNAL, sys_siglist[signal]);
-  else
-    {
-      fprintf (stderr, "\nSegmentation fault.\n");
-      _exit (11);
-    }
+       mixer_abort(ERR_SIGNAL, sys_siglist[signal]);
 }
 
-int
-main (int    argc,
-      char **argv)
+int main(int argc,
+        char **argv)
 {
-  int opt;
-  
-  /* parse args
-   */
-  do
-    {
-      opt = getopt (argc, argv, "c:m:eshg");
-      switch (opt)
-       {
-       case '?':
-       case 'h':
-         fprintf (stderr, "%s %s\n", PRGNAME_UPPER, VERSION);
-         fprintf (stderr, "Usage: %s [-e] [-c <card: 1..%i>] [-m <mixer: 0..1>] [-z]\n", PRGNAME, snd_cards ());
-         mixer_abort (ERR_NONE, "");
-       case 'c':
-         card_id = snd_card_name (optarg);
-         break;
-       case 'e':
-         mixer_exact = !mixer_exact;
-         break;
-       case 'g':
-         mixer_do_color = !mixer_do_color;
-         break;
-       case 'm':
-         mixer_id = CLAMP (optarg[0], '0', '1') - '0';
-         break;
-       case 's':
-         mixer_minimize = 1;
-         break;
+       int opt;
+
+       /* parse args
+        */
+       do {
+               opt = getopt(argc, argv, "c:m:ehg");
+               switch (opt) {
+               case '?':
+               case 'h':
+                       fprintf(stderr, "%s %s\n", PRGNAME_UPPER, VERSION);
+                       fprintf(stderr, "Usage: %s [-e] [-c <card: 1..%i>] [-m <mixer: 0..1>]\n", PRGNAME, snd_cards());
+                       mixer_abort(ERR_NONE, "");
+               case 'c':
+                       card_id = snd_card_name(optarg);
+                       break;
+               case 'e':
+                       mixer_exact = !mixer_exact;
+                       break;
+               case 'g':
+                       mixer_do_color = !mixer_do_color;
+                       break;
+               case 'm':
+                       mixer_id = CLAMP(optarg[0], '0', '1') - '0';
+                       break;
+               }
        }
-    }
-  while (opt > 0);
-  
-  /* initialize mixer
-   */
-  mixer_init ();
-  
-  /* setup signal handlers
-   */
-  signal (SIGINT, mixer_signal_handler);
-  signal (SIGTRAP, mixer_signal_handler);
-  signal (SIGABRT, mixer_signal_handler);
-  signal (SIGQUIT, mixer_signal_handler);
-  signal (SIGBUS, mixer_signal_handler);
-  signal (SIGSEGV, mixer_signal_handler);
-  signal (SIGPIPE, mixer_signal_handler);
-  signal (SIGTERM, mixer_signal_handler);
-  
-  /* initialize ncurses
-   */
-  mixer_init_window ();
-  if (mixer_max_x < MIXER_MIN_X ||
-      mixer_max_y < MIXER_MIN_Y)
-    beep (); // mixer_abort (ERR_WINSIZE, "");
-  
-  signal (SIGWINCH, (void*) mixer_winch);
-
-  do
-    {
-      /* draw window upon every iteration */
-      if (!mixer_needs_resize)
-       {
-         switch (mixer_view)
-           {
-           case VIEW_CHANNELS:
-             mixer_update_cbars ();
-             break;
-           case VIEW_HELP:
-             mixer_show_text ("Help", mixer_help_text, &mixer_help_xoffs, &mixer_help_yoffs);
-             break;
-           case VIEW_PROCINFO:
-             mixer_show_procinfo ();
-             break;
-           }
-         mixer_draw_frame ();
-         refresh ();
+       while (opt > 0);
+
+       /* initialize mixer
+        */
+       mixer_init();
+
+       /* setup signal handlers
+        */
+       signal(SIGINT, mixer_signal_handler);
+       signal(SIGTRAP, mixer_signal_handler);
+       signal(SIGABRT, mixer_signal_handler);
+       signal(SIGQUIT, mixer_signal_handler);
+       signal(SIGBUS, mixer_signal_handler);
+       signal(SIGSEGV, mixer_signal_handler);
+       signal(SIGPIPE, mixer_signal_handler);
+       signal(SIGTERM, mixer_signal_handler);
+
+       /* initialize ncurses
+        */
+       mixer_window = initscr();
+       if (mixer_do_color)
+               mixer_do_color = has_colors();
+       mixer_init_draw_contexts();
+       mixer_init_screen();
+       if (mixer_max_x < MIXER_MIN_X ||
+           mixer_max_y < MIXER_MIN_Y)
+               mixer_abort(ERR_WINSIZE, "");
+
+       /* react on key presses
+        * and draw window
+        */
+       keypad(mixer_window, TRUE);
+       leaveok(mixer_window, TRUE);
+       cbreak();
+       noecho();
+       do {
+               mixer_update_cbars();
+               mixer_draw_frame();
+               refresh();
        }
-    }
-  while (!mixer_iteration ());
-  
-  mixer_abort (ERR_NONE, "");
+       while (!mixer_iteration());
+
+       mixer_abort(ERR_NONE, "");
 };
diff --git a/alsamixer/alsamixer_tim.c b/alsamixer/alsamixer_tim.c
new file mode 100644 (file)
index 0000000..b7d516d
--- /dev/null
@@ -0,0 +1,1640 @@
+/* AlsaMixer - Commandline mixer for the ALSA project
+ * Copyright (C) 1998, 1999 Tim Janik <timj@gtk.org> and Jaroslav Kysela <perex@jcu.cz>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ * ChangeLog:
+ *
+ * Sun Feb 21 19:55:01 1999  Tim Janik  <timj@gtk.org>
+ *
+ *     * bumped version to 0.10.
+ *
+ *     * added scrollable text views.
+ *     we now feature an F1 Help screen and an F2 /proc info screen.
+ *     the help screen does still require lots of work though.
+ *
+ *     * keys are evaluated view specific now.
+ *
+ *     * we feature meta-keys now, e.g. M-Tab as back-tab.
+ *
+ *     * if we are already in channel view and the user still hits Return,
+ *     we do a refresh nonetheless, since 'r'/'R' got removed as a redraw
+ *     key (reserved for record volumes). 'l'/'L' is still preserved though,
+ *     and actually needs to be to e.g. get around the xterm bold-artefacts.
+ *
+ *     * support terminals that can't write into lower right corner.
+ *
+ *     * undocumented '-s' option that will keep the screen to its
+ *     minimum size, usefull for debugging only.
+ *
+ * Sun Feb 21 02:23:52 1999  Tim Janik  <timj@gtk.org>
+ *
+ *     * don't abort if snd_mixer_* functions failed due to EINTR,
+ *     we simply retry on the next cycle. hopefully asoundlib preserves
+ *     errno states correctly (Jaroslav can you asure that?).
+ *
+ *     * feature WINCH correctly, so we make a complete relayout on
+ *     screen resizes. don't abort on too-small screen sizes anymore,
+ *     but simply beep.
+ *
+ *     * redid the layout algorithm to fix some bugs and to preserve
+ *     space for a flag indication line. the channels are
+ *     nicer spread horizontally now (i.e. we also pad on the left and
+ *     right screen bounds now).
+ *
+ *     * various other minor fixes.
+ *
+ *     * indicate whether ExactMode is active or not.
+ *
+ *     * fixed coding style to follow the GNU coding conventions.
+ *
+ *     * reverted record volume changes since they broke ExactMode display.
+ *
+ *     * composed ChangeLog entries.
+ *
+ * 1998/11/04 19:43:45  perex
+ *
+ *     * Stereo record source and route selection...
+ *     provided by Carl van Schaik <carl@dreamcoat.che.uct.ac.za>.
+ *
+ * 1998/09/20 08:05:24  perex
+ *
+ *     * Fixed -m option...
+ *
+ * 1998/10/29 22:50:10
+ *
+ *     * initial checkin of alsamixer.c, written by Tim Janik, modified by
+ *     Jaroslav Kysela to feature asoundlib.h instead of plain ioctl()s and
+ *     automated updates after select() (i always missed that with OSS!).
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include <errno.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/signal.h>
+
+#ifndef CURSESINC
+#include <ncurses.h>
+#else
+#include CURSESINC
+#endif
+#include <time.h>
+
+#include <sys/asoundlib.h>
+
+/* example compilation commandline:
+ * clear; gcc -Wall -pipe -O2 alsamixer.c -o alsamixer -lasound -lncurses
+ */
+
+/* --- defines --- */
+#define        PRGNAME          "alsamixer"
+#define        PRGNAME_UPPER    "AlsaMixer"
+#define        VERSION          "v0.10"
+#define        CHECK_ABORT(e,s) ({ if (errno != EINTR) mixer_abort ((e), (s)); })
+#define GETCH_BLOCK(w)  ({ timeout ((w) ? -1 : 0); })
+
+#undef MAX
+#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
+#undef MIN
+#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
+#undef ABS
+#define ABS(a)     (((a) < 0) ? -(a) : (a))
+#undef CLAMP
+#define CLAMP(x, low, high)  (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
+
+#define MIXER_MIN_X    (18)                    /* abs minimum: 18 */
+#define        MIXER_TEXT_Y    (10)
+#define        MIXER_MIN_Y     (MIXER_TEXT_Y + 3)      /* abs minimum: 11 */
+
+#define MIXER_BLACK    (COLOR_BLACK)
+#define MIXER_DARK_RED  (COLOR_RED)
+#define MIXER_RED       (COLOR_RED | A_BOLD)
+#define MIXER_GREEN     (COLOR_GREEN | A_BOLD)
+#define MIXER_ORANGE    (COLOR_YELLOW)
+#define MIXER_YELLOW    (COLOR_YELLOW | A_BOLD)
+#define MIXER_MARIN     (COLOR_BLUE)
+#define MIXER_BLUE      (COLOR_BLUE | A_BOLD)
+#define MIXER_MAGENTA   (COLOR_MAGENTA)
+#define MIXER_DARK_CYAN (COLOR_CYAN)
+#define MIXER_CYAN      (COLOR_CYAN | A_BOLD)
+#define MIXER_GREY      (COLOR_WHITE)
+#define MIXER_GRAY      (MIXER_GREY)
+#define MIXER_WHITE     (COLOR_WHITE | A_BOLD)
+
+
+/* --- views --- */
+enum {
+  VIEW_CHANNELS,
+  VIEW_HELP,
+  VIEW_PROCINFO
+};
+
+
+/* --- variables --- */
+static WINDOW  *mixer_window = NULL;
+static int      mixer_needs_resize = 0;
+static int      mixer_minimize = 0;
+static int      mixer_no_lrcorner = 0;
+static int      mixer_view = VIEW_CHANNELS;
+static int      mixer_max_x = 0;
+static int      mixer_max_y = 0;
+static int      mixer_ofs_x = 0;
+static float    mixer_extra_space = 0;
+static int      mixer_cbar_height = 0;
+
+static int      card_id = 0;
+static int      mixer_id = 0;
+static void    *mixer_handle;
+static char    *mixer_card_name = NULL;
+static char    *mixer_device_name = NULL;
+
+static int      mixer_n_channels = 0;
+static int      mixer_n_vis_channels = 0;
+static int      mixer_first_vis_channel = 0;
+static int      mixer_focus_channel = 0;
+static int      mixer_have_old_focus = 0;
+static int      mixer_exact = 0;
+
+static int      mixer_lvolume_delta = 0;
+static int      mixer_rvolume_delta = 0;
+static int      mixer_balance_volumes = 0;
+static int      mixer_toggle_mute_left = 0;
+static int      mixer_toggle_mute_right = 0;
+static int      mixer_toggle_record = 0;
+
+static int      mixer_hscroll_delta = 0;
+static int      mixer_vscroll_delta = 0;
+
+/* By Carl */
+static int      mixer_toggle_rec_left = 0;
+static int      mixer_toggle_rec_right = 0;
+static int      mixer_route_ltor_in = 0;
+static int      mixer_route_rtol_in = 0;
+
+
+/* --- text --- */
+static int      mixer_procinfo_xoffs = 0;
+static int      mixer_procinfo_yoffs = 0;
+static int      mixer_help_xoffs = 0;
+static int      mixer_help_yoffs = 0;
+static char     *mixer_help_text =
+(
+ "\n"
+ " Esc     exit alsamixer\n"
+ " F1      show Help screen\n"
+ " F2      show /proc info screen\n"
+ " Return  return to main screen\n"
+ " Space   toggle Record facility\n"
+ " Tab     toggle ExactMode\n"
+ " m M     mute both channels\n"
+ " < >     mute left/right channel\n"
+ " Up      increase left and right volume\n"
+ " Down    decrease left and right volume\n"
+ " Right   move (scroll) to the right next channel\n"
+ " Left    move (scroll) to the left next channel\n"
+ "\n"
+ "Alsamixer has been written and is Copyrighted in 1998, 1999 by\n"
+ "Tim Janik <timj@gtk.org> and Jaroslav Kysela <perex@jcu.cz>.\n"
+ );
+
+
+/* --- draw contexts --- */
+enum {
+  DC_DEFAULT,
+  DC_BACK,
+  DC_TEXT,
+  DC_PROMPT,
+  DC_CBAR_MUTE,
+  DC_CBAR_NOMUTE,
+  DC_CBAR_RECORD,
+  DC_CBAR_NORECORD,
+  DC_CBAR_EMPTY,
+  DC_CBAR_LABEL,
+  DC_CBAR_FOCUS_LABEL,
+  DC_FOCUS,
+  DC_ANY_1,
+  DC_ANY_2,
+  DC_ANY_3,
+  DC_ANY_4,
+  DC_LAST
+};
+
+static int dc_fg[DC_LAST] = { 0 };
+static int dc_attrib[DC_LAST] = { 0 };
+static int dc_char[DC_LAST] = { 0 };
+static int mixer_do_color = 1;
+
+static void
+mixer_init_dc (int c,
+              int n,
+              int f,
+              int b,
+              int a)
+{
+  dc_fg[n] = f;
+  dc_attrib[n] = a;
+  dc_char[n] = c;
+  if (n > 0)
+    init_pair (n, dc_fg[n] & 0xf, b & 0x0f);
+}
+
+static int
+mixer_dc (int n)
+{
+  if (mixer_do_color)
+    attrset (COLOR_PAIR (n) | (dc_fg[n] & 0xfffffff0));
+  else
+    attrset (dc_attrib[n]);
+  
+  return dc_char[n];
+}
+
+static void
+mixer_init_draw_contexts (void)
+{
+  start_color ();
+  
+  mixer_init_dc ('.', DC_BACK, MIXER_WHITE, MIXER_BLACK, A_NORMAL);
+  mixer_init_dc ('.', DC_TEXT, MIXER_YELLOW, MIXER_BLACK, A_BOLD);
+  mixer_init_dc ('.', DC_PROMPT, MIXER_DARK_CYAN, MIXER_BLACK, A_NORMAL);
+  mixer_init_dc ('M', DC_CBAR_MUTE, MIXER_CYAN, MIXER_BLACK, A_BOLD);
+  mixer_init_dc ('-', DC_CBAR_NOMUTE, MIXER_CYAN, MIXER_BLACK, A_NORMAL);
+  mixer_init_dc ('x', DC_CBAR_RECORD, MIXER_DARK_RED, MIXER_BLACK, A_BOLD);
+  mixer_init_dc ('-', DC_CBAR_NORECORD, MIXER_GRAY, MIXER_BLACK, A_NORMAL);
+  mixer_init_dc (' ', DC_CBAR_EMPTY, MIXER_GRAY, MIXER_BLACK, A_DIM);
+  mixer_init_dc ('.', DC_CBAR_LABEL, MIXER_WHITE, MIXER_BLUE, A_REVERSE | A_BOLD);
+  mixer_init_dc ('.', DC_CBAR_FOCUS_LABEL, MIXER_RED, MIXER_BLUE, A_REVERSE | A_BOLD);
+  mixer_init_dc ('.', DC_FOCUS, MIXER_RED, MIXER_BLACK, A_BOLD);
+  mixer_init_dc ('#', DC_ANY_1, MIXER_WHITE, MIXER_BLACK, A_BOLD);
+  mixer_init_dc ('#', DC_ANY_2, MIXER_GREEN, MIXER_BLACK, A_BOLD);
+  mixer_init_dc ('#', DC_ANY_3, MIXER_RED, MIXER_BLACK, A_BOLD);
+  mixer_init_dc ('.', DC_ANY_4, MIXER_WHITE, MIXER_GREEN, A_BOLD);
+  mixer_init_dc ('.', DC_ANY_4, MIXER_WHITE, MIXER_BLUE, A_BOLD);
+}
+
+#define        DC_CBAR_FRAME   (DC_CBAR_MUTE)
+#define        DC_FRAME        (DC_PROMPT)
+
+
+/* --- error types --- */
+typedef enum
+{
+  ERR_NONE,
+  ERR_OPEN,
+  ERR_FCN,
+  ERR_SIGNAL,
+  ERR_WINSIZE,
+} ErrType;
+
+
+/* --- prototypes --- */
+static void
+mixer_abort (ErrType error,
+            const char *err_string)
+     __attribute__
+((noreturn));
+
+
+/* --- functions --- */
+static void
+mixer_clear (int full_redraw)
+{
+  int x, y;
+  int f = full_redraw ? 0 : 1;
+
+  mixer_dc (DC_BACK);
+
+  if (full_redraw)
+    clearok (mixer_window, TRUE);
+
+  /* buggy ncurses doesn't really write spaces with the specified
+   * color into the screen on clear () or erase ()
+   */
+  for (x = f; x < mixer_max_x - f; x++)
+    for (y = f; y < mixer_max_y - f; y++)
+      mvaddch (y, x, ' ');
+}
+
+static void
+mixer_abort (ErrType     error,
+            const char *err_string)
+{
+  if (mixer_window)
+    {
+      mixer_clear (TRUE);
+      refresh ();
+      keypad (mixer_window, FALSE);
+      leaveok (mixer_window, FALSE);
+      endwin ();
+      mixer_window = NULL;
+    }
+  printf ("\n");
+  
+  switch (error)
+    {
+    case ERR_OPEN:
+      fprintf (stderr,
+              PRGNAME ": failed to open mixer #%i/#%i: %s\n",
+              card_id,
+              mixer_id,
+              snd_strerror (errno));
+      break;
+    case ERR_FCN:
+      fprintf (stderr,
+              PRGNAME ": function %s failed: %s\n",
+              err_string,
+              snd_strerror (errno));
+      break;
+    case ERR_SIGNAL:
+      fprintf (stderr,
+              PRGNAME ": aborting due to signal `%s'\n",
+              err_string);
+      break;
+    case ERR_WINSIZE:
+      fprintf (stderr,
+              PRGNAME ": screen size too small (%dx%d)\n",
+              mixer_max_x,
+              mixer_max_y);
+      break;
+    default:
+      break;
+    }
+  
+  exit (error);
+}
+
+static int
+mixer_cbar_get_pos (int  channel_index,
+                   int *x_p,
+                   int *y_p)
+{
+  int x;
+  int y;
+  
+  if (channel_index < mixer_first_vis_channel ||
+      channel_index - mixer_first_vis_channel >= mixer_n_vis_channels)
+    return FALSE;
+  
+  channel_index -= mixer_first_vis_channel;
+  
+  x = mixer_ofs_x;
+  x += (3 + 2 + 3 + 1) * channel_index + mixer_extra_space * (channel_index + 1);
+
+  if (MIXER_TEXT_Y + 10 < mixer_max_y)
+    y = mixer_max_y / 2 + 3;
+  else
+    y = (mixer_max_y + 1) / 2 + 3;
+  y += mixer_cbar_height / 2;
+  
+  if (x_p)
+    *x_p = x;
+  if (y_p)
+    *y_p = y;
+  
+  return TRUE;
+}
+
+static void
+mixer_update_cbar (int channel_index)
+{
+  char string[64];
+  char c;
+  snd_mixer_channel_info_t cinfo = { 0, };
+  snd_mixer_channel_t cdata = { 0, };
+  int vleft, vright;
+  int x, y, i;
+  
+  
+  /* set specified EXACT mode
+   */
+  if (snd_mixer_exact_mode (mixer_handle, mixer_exact) < 0)
+    CHECK_ABORT (ERR_FCN, "snd_mixer_exact_mode()");
+  
+  /* set new channel indices and read info
+   */
+  if (snd_mixer_channel_info (mixer_handle, channel_index, &cinfo) < 0)
+    CHECK_ABORT (ERR_FCN, "snd_mixer_channel_info()");
+  
+  /* set new channel values
+   */
+  if (channel_index == mixer_focus_channel &&
+      (mixer_lvolume_delta || mixer_rvolume_delta ||
+       mixer_toggle_mute_left || mixer_toggle_mute_right ||
+       mixer_balance_volumes ||
+       mixer_toggle_record || mixer_toggle_rec_left ||
+       mixer_toggle_rec_right ||
+       mixer_route_rtol_in || mixer_route_ltor_in))
+    {
+      if (snd_mixer_channel_read (mixer_handle, channel_index, &cdata) < 0)
+       CHECK_ABORT (ERR_FCN, "snd_mixer_channel_read()");
+      
+      cdata.flags &= ~SND_MIXER_FLG_DECIBEL;
+      cdata.left = CLAMP (cdata.left + mixer_lvolume_delta, cinfo.min, cinfo.max);
+      cdata.right = CLAMP (cdata.right + mixer_rvolume_delta, cinfo.min, cinfo.max);
+      mixer_lvolume_delta = mixer_rvolume_delta = 0;
+      if (mixer_balance_volumes)
+       {
+         cdata.left = (cdata.left + cdata.right) / 2;
+         cdata.right = cdata.left;
+         mixer_balance_volumes = 0;
+       }
+      if (mixer_toggle_mute_left)
+       {
+         if (cdata.flags & SND_MIXER_FLG_MUTE_LEFT)
+           cdata.flags &= ~SND_MIXER_FLG_MUTE_LEFT;
+         else
+           cdata.flags |= SND_MIXER_FLG_MUTE_LEFT;
+       }
+      if (mixer_toggle_mute_right)
+       {
+         if (cdata.flags & SND_MIXER_FLG_MUTE_RIGHT)
+           cdata.flags &= ~SND_MIXER_FLG_MUTE_RIGHT;
+         else
+           cdata.flags |= SND_MIXER_FLG_MUTE_RIGHT;
+       }
+      mixer_toggle_mute_left = mixer_toggle_mute_right = 0;
+      if (mixer_toggle_record)
+       {
+         if (cdata.flags & SND_MIXER_FLG_RECORD)
+           cdata.flags &= ~SND_MIXER_FLG_RECORD;
+         else
+           cdata.flags |= SND_MIXER_FLG_RECORD;
+       }
+      mixer_toggle_record = 0;
+      
+      if (mixer_toggle_rec_left)
+       {
+         if (cdata.flags & SND_MIXER_FLG_RECORD_LEFT)
+           cdata.flags &= ~SND_MIXER_FLG_RECORD_LEFT;
+         else
+           cdata.flags |= SND_MIXER_FLG_RECORD_LEFT;
+       }
+      mixer_toggle_rec_left = 0;
+      
+      if (mixer_toggle_rec_right)
+       {
+         if (cdata.flags & SND_MIXER_FLG_RECORD_RIGHT)
+           cdata.flags &= ~SND_MIXER_FLG_RECORD_RIGHT;
+         else
+           cdata.flags |= SND_MIXER_FLG_RECORD_RIGHT;
+       }
+      mixer_toggle_rec_right = 0;
+      
+      if (mixer_route_ltor_in)
+       {
+         if (cdata.flags & SND_MIXER_FLG_LTOR_IN)
+           cdata.flags &= ~SND_MIXER_FLG_LTOR_IN;
+         else
+           cdata.flags |= SND_MIXER_FLG_LTOR_IN;
+         /*      printf ("state : \n %d \n",cdata.flags & SND_MIXER_FLG_LTOR_IN);
+          */ 
+       }
+      mixer_route_ltor_in = 0;
+      
+      if (mixer_route_rtol_in)
+       {
+         if (cdata.flags & SND_MIXER_FLG_RTOL_IN)
+           cdata.flags &= ~SND_MIXER_FLG_RTOL_IN;
+         else
+           cdata.flags |= SND_MIXER_FLG_RTOL_IN;
+       }
+      mixer_route_rtol_in = 0;
+      
+      if (snd_mixer_channel_write (mixer_handle, channel_index, &cdata) < 0)
+       CHECK_ABORT (ERR_FCN, "snd_mixer_channel_write()");
+    }
+  /* first, read values for the numbers to be displayed in
+   * specified EXACT mode
+   */
+  if (snd_mixer_channel_read (mixer_handle, channel_index, &cdata) < 0)
+    CHECK_ABORT (ERR_FCN, "snd_mixer_channel_read()");
+  vleft = cdata.left;
+  vright = cdata.right;
+  
+  /* then, always use percentage values for the bars. if we don't do
+   * this, we will see aliasing effects on specific circumstances.
+   * (actually they don't really dissapear, but they are transfered
+   *  to bar<->smaller-scale ambiguities).
+   */
+  if (mixer_exact)
+    {
+      i = 0;
+      if (snd_mixer_exact_mode (mixer_handle, 0) < 0)
+       CHECK_ABORT (ERR_FCN, "snd_mixer_exact_mode()");
+      if (snd_mixer_channel_read (mixer_handle, channel_index, &cdata) < 0)
+       CHECK_ABORT (ERR_FCN, "snd_mixer_channel_read()");
+    }
+  /* get channel bar position
+   */
+  if (!mixer_cbar_get_pos (channel_index, &x, &y))
+    return;
+
+  /* setup colors
+   */
+  mixer_init_dc ('#', DC_ANY_1, MIXER_WHITE, MIXER_BLACK, A_BOLD);
+  mixer_init_dc ('#', DC_ANY_2, MIXER_GREEN, MIXER_BLACK, A_BOLD);
+  mixer_init_dc ('#', DC_ANY_3, MIXER_RED, MIXER_BLACK, A_BOLD);
+  
+  /* channel bar name
+   */
+  mixer_dc (channel_index == mixer_focus_channel ? DC_CBAR_FOCUS_LABEL : DC_CBAR_LABEL);
+  cinfo.name[8] = 0;
+  for (i = 0; i < 8; i++)
+    {
+      string[i] = ' ';
+    }
+  sprintf (string + (8 - strlen (cinfo.name)) / 2, "%s          ", cinfo.name);
+  string[8] = 0;
+  mvaddstr (y, x, string);
+  y--;
+  
+  /* current channel values
+   */
+  mixer_dc (DC_BACK);
+  mvaddstr (y, x, "         ");
+  mixer_dc (DC_TEXT);
+  sprintf (string, "%d", vleft);
+  mvaddstr (y, x + 3 - strlen (string), string);
+  mixer_dc (DC_CBAR_FRAME);
+  mvaddch (y, x + 3, '<');
+  mvaddch (y, x + 4, '>');
+  mixer_dc (DC_TEXT);
+  sprintf (string, "%d", vright);
+  mvaddstr (y, x + 5, string);
+  y--;
+  
+  /* left/right bar
+   */
+  mixer_dc (DC_CBAR_FRAME);
+  mvaddstr (y, x, "         ");
+  mvaddch (y, x + 2, ACS_LLCORNER);
+  mvaddch (y, x + 3, ACS_HLINE);
+  mvaddch (y, x + 4, ACS_HLINE);
+  mvaddch (y, x + 5, ACS_LRCORNER);
+  y--;
+  for (i = 0; i < mixer_cbar_height; i++)
+    {
+      mvaddstr (y - i, x, "         ");
+      mvaddch (y - i, x + 2, ACS_VLINE);
+      mvaddch (y - i, x + 5, ACS_VLINE);
+    }
+  string[2] = 0;
+  for (i = 0; i < mixer_cbar_height; i++)
+    {
+      int dc;
+      
+      if (i + 1 >= 0.8 * mixer_cbar_height)
+       dc = DC_ANY_3;
+      else if (i + 1 >= 0.4 * mixer_cbar_height)
+       dc = DC_ANY_2;
+      else
+       dc = DC_ANY_1;
+      mvaddch (y, x + 3, mixer_dc (cdata.left > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY));
+      mvaddch (y, x + 4, mixer_dc (cdata.right > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY));
+      y--;
+    }
+  
+  /* muted?
+   */
+  mixer_dc (DC_BACK);
+  mvaddstr (y, x, "         ");
+  c = cinfo.caps & SND_MIXER_CINFO_CAP_MUTE ? '-' : ' ';
+  mixer_dc (DC_CBAR_FRAME);
+  mvaddch (y, x + 2, ACS_ULCORNER);
+  mvaddch (y, x + 3, mixer_dc (cdata.flags & SND_MIXER_FLG_MUTE_LEFT ?
+                              DC_CBAR_MUTE : DC_CBAR_NOMUTE));
+  mvaddch (y, x + 4, mixer_dc (cdata.flags & SND_MIXER_FLG_MUTE_RIGHT ?
+                              DC_CBAR_MUTE : DC_CBAR_NOMUTE));
+  mixer_dc (DC_CBAR_FRAME);
+  mvaddch (y, x + 5, ACS_URCORNER);
+  y--;
+  
+  /* record input?
+   */
+  if (cdata.flags & SND_MIXER_FLG_RECORD)
+    {
+      mixer_dc (DC_CBAR_RECORD);
+      mvaddstr (y, x + 1, "RECORD");
+      if (cdata.flags & SND_MIXER_FLG_RECORD_LEFT)
+       {
+         if (cdata.flags & SND_MIXER_FLG_LTOR_IN)
+           mvaddstr (y + 2, x + 6, "L");
+         else
+           mvaddstr (y + 1, x + 1, "L");
+       }
+      if (cdata.flags & SND_MIXER_FLG_RECORD_RIGHT)
+       {
+         if (cdata.flags & SND_MIXER_FLG_RTOL_IN)
+           mvaddstr (y + 2, x + 1, "R");
+         else
+           mvaddstr (y + 1, x + 6, "R");
+       }
+    }
+  else if (cinfo.caps & SND_MIXER_CINFO_CAP_RECORD)
+    for (i = 0; i < 6; i++)
+      mvaddch (y, x + 1 + i, mixer_dc (DC_CBAR_NORECORD));
+  else
+    {
+      mixer_dc (DC_BACK);
+      mvaddstr (y, x, "         ");
+    }
+  y--;
+}
+
+static void
+mixer_update_cbars (void)
+{
+  static int o_x = 0;
+  static int o_y = 0;
+  int i, x, y;
+  
+  if (!mixer_cbar_get_pos (mixer_focus_channel, &x, &y))
+    {
+      if (mixer_focus_channel < mixer_first_vis_channel)
+       mixer_first_vis_channel = mixer_focus_channel;
+      else if (mixer_focus_channel >= mixer_first_vis_channel + mixer_n_vis_channels)
+       mixer_first_vis_channel = mixer_focus_channel - mixer_n_vis_channels + 1;
+      mixer_cbar_get_pos (mixer_focus_channel, &x, &y);
+    }
+  for (i = 0; i < mixer_n_vis_channels; i++)
+    mixer_update_cbar (i + mixer_first_vis_channel);
+  
+  /* draw focused cbar
+   */
+  if (mixer_have_old_focus)
+    {
+      mixer_dc (DC_BACK);
+      mvaddstr (o_y, o_x, " ");
+      mvaddstr (o_y, o_x + 9, " ");
+    }
+  o_x = x - 1;
+  o_y = y;
+  mixer_dc (DC_FOCUS);
+  mvaddstr (o_y, o_x, "<");
+  mvaddstr (o_y, o_x + 9, ">");
+  mixer_have_old_focus = 1;
+}
+
+static void
+mixer_draw_frame (void)
+{
+  char string[128];
+  int i;
+  int max_len;
+  
+  mixer_dc (DC_FRAME);
+  
+  /* card name
+   */
+  mixer_dc (DC_PROMPT);
+  mvaddstr (1, 2, "Card: ");
+  mixer_dc (DC_TEXT);
+  sprintf (string, "%s", mixer_card_name);
+  max_len = mixer_max_x - 2 - 6 - 2;
+  if (strlen (string) > max_len)
+    string[max_len] = 0;
+  addstr (string);
+  
+  /* device name
+   */
+  mixer_dc (DC_PROMPT);
+  mvaddstr (2, 2, "Chip: ");
+  mixer_dc (DC_TEXT);
+  sprintf (string, "%s", mixer_device_name);
+  max_len = mixer_max_x - 2 - 6 - 2;
+  if (strlen (string) > max_len)
+    string[max_len] = 0;
+  addstr (string);
+
+  /* indicate exact mode
+   */
+  if (mixer_exact)
+    {
+      mixer_dc (DC_PROMPT);
+      mvaddstr (3, 2, "[");
+      mixer_dc (DC_TEXT);
+      addstr ("ExactMode");
+      mixer_dc (DC_PROMPT);
+      addstr ("]");
+    }
+  else
+    mvaddstr (3, 2, "           ");
+
+  /* lines
+   */
+  mixer_dc (DC_PROMPT);
+  for (i = 1; i < mixer_max_y - 1; i++)
+    {
+      mvaddch (i, 0, ACS_VLINE);
+      mvaddch (i, mixer_max_x - 1, ACS_VLINE);
+    }
+  for (i = 1; i < mixer_max_x - 1; i++)
+    {
+      mvaddch (0, i, ACS_HLINE);
+      mvaddch (mixer_max_y - 1, i, ACS_HLINE);
+    }
+  
+  /* corners
+   */
+  mixer_dc (DC_PROMPT);
+  mvaddch (0, 0, ACS_ULCORNER);
+  mvaddch (0, mixer_max_x - 1, ACS_URCORNER);
+  mvaddch (mixer_max_y - 1, 0, ACS_LLCORNER);
+  if (!mixer_no_lrcorner)
+    mvaddch (mixer_max_y - 1, mixer_max_x - 1, ACS_LRCORNER);
+  else
+    {
+      mvaddch (mixer_max_y - 2, mixer_max_x - 1, ACS_LRCORNER);
+      mvaddch (mixer_max_y - 2, mixer_max_x - 2, ACS_ULCORNER);
+      mvaddch (mixer_max_y - 1, mixer_max_x - 2, ACS_LRCORNER);
+    }
+
+  /* program title
+   */
+  sprintf (string, "%s %s", PRGNAME_UPPER, VERSION);
+  max_len = strlen (string);
+  if (mixer_max_x >= max_len + 4)
+    {
+      mixer_dc (DC_PROMPT);
+      mvaddch (0, mixer_max_x / 2 - max_len / 2 - 1, '[');
+      mvaddch (0, mixer_max_x / 2 - max_len / 2 + max_len, ']');
+    }
+  if (mixer_max_x >= max_len + 2)
+    {
+      mixer_dc (DC_TEXT);
+      mvaddstr (0, mixer_max_x / 2 - max_len / 2, string);
+    }
+}
+
+static char*
+mixer_offset_text (char **t,
+                  int    col,
+                  int   *length)
+{
+  char *p = *t;
+  char *r;
+
+  while (*p && *p != '\n' && col--)
+    p++;
+  if (*p == '\n' || !*p)
+    {
+      if (*p == '\n')
+       p++;
+      *length = 0;
+      *t = p;
+      return p;
+    }
+
+  r = p;
+  while (*r && *r != '\n' && (*length)--)
+    r++;
+
+  *length = r - p;
+  while (*r && *r != '\n')
+    r++;
+  if (*r == '\n')
+    r++;
+  *t = r;
+
+  return p;
+}
+
+static void
+mixer_show_text (char *title,
+                char *text,
+                int  *xoffs,
+                int  *yoffs)
+{
+  int tlines = 0, tcols = 0;
+  float hscroll, vscroll;
+  float hoffs, voffs;
+  char *p, *text_offs = text;
+  int x1, x2, y1, y2;
+  int i, n, l, r, block, stipple;
+
+  /* coords
+   */
+  x1 = 2;
+  x2 = mixer_max_x - 3;
+  y1 = 4;
+  y2 = mixer_max_y - 2;
+
+  if ((y2 - y1) < 3 || (x2 - x1) < 3)
+    return;
+
+  /* text dimensions
+   */
+  l = 0;
+  for (p = text; *p; p++)
+    if (*p == '\n')
+      {
+       tlines++;
+       tcols = MAX (l, tcols);
+       l = 0;
+      }
+    else
+      l++;
+  tcols = MAX (l, tcols);
+  if (p > text && *(p - 1) != '\n')
+    tlines++;
+
+  /* scroll areas / offsets
+   */
+  l = x2 - x1 - 2;
+  if (l > tcols)
+    {
+      x1 += (l - tcols) / 2;
+      x2 = x1 + tcols + 1;
+    }
+  if (mixer_hscroll_delta)
+    {
+      *xoffs += mixer_hscroll_delta;
+      mixer_hscroll_delta = 0;
+      if (*xoffs < 0)
+       {
+         *xoffs = 0;
+         beep ();
+       }
+      else if (*xoffs > tcols - l - 1)
+       {
+         *xoffs = MAX (0, tcols - l - 1);
+         beep ();
+       }
+    }
+  if (tcols - l - 1 <= 0)
+    {
+      hscroll = 1;
+      hoffs = 0;
+    }
+  else
+    {
+      hscroll = ((float) l) / tcols;
+      hoffs = ((float) *xoffs) / (tcols - l - 1);
+    }
+
+  l = y2 - y1 - 2;
+  if (l > tlines)
+    {
+      y1 += (l - tlines) / 2;
+      y2 = y1 + tlines + 1;
+    }
+  if (mixer_vscroll_delta)
+    {
+      *yoffs += mixer_vscroll_delta;
+      mixer_vscroll_delta = 0;
+      if (*yoffs < 0)
+       {
+         *yoffs = 0;
+         beep ();
+       }
+      else if (*yoffs > tlines - l - 1)
+       {
+         *yoffs = MAX (0, tlines - l - 1);
+         beep ();
+       }
+    }
+  if (tlines - l - 1 <= 0)
+    {
+      voffs = 0;
+      vscroll = 1;
+    }
+  else
+    {
+      vscroll = ((float) l) / tlines;
+      voffs = ((float) *yoffs) / (tlines - l - 1);
+    }
+
+  /* colors
+   */
+  mixer_init_dc ('.', DC_ANY_3, MIXER_YELLOW, MIXER_BLUE, A_BOLD);
+  mixer_init_dc ('.', DC_ANY_4, MIXER_WHITE, MIXER_BLUE, A_BOLD);
+  mixer_dc (DC_ANY_4);
+
+  /* corners
+   */
+  mvaddch (y2, x2, ACS_LRCORNER);
+  mvaddch (y2, x1, ACS_LLCORNER);
+  mvaddch (y1, x1, ACS_ULCORNER);
+  mvaddch (y1, x2, ACS_URCORNER);
+
+  /* left + upper border
+   */
+  for (i = y1 + 1; i < y2; i++)
+    mvaddch (i, x1, ACS_VLINE);
+  for (i = x1 + 1; i < x2; i++)
+    mvaddch (y1, i, ACS_HLINE);
+  if (title)
+    {
+      l = strlen (title);
+      if (l <= x2 - x1 - 3)
+       {
+         mvaddch (y1, x1 + 1 + (x2 - x1 - l) / 2 - 1, '[');
+         mvaddch (y1, x1 + 1 + (x2 - x1 - l) / 2 + l, ']');
+       }
+      if (l <= x2 - x1 - 1)
+       {
+         mixer_dc (DC_ANY_3);
+         mvaddstr (y1, x1 + 1 + (x2 - x1 - l) / 2, title);
+       }
+      mixer_dc (DC_ANY_4);
+    }
+
+  stipple = ACS_CKBOARD;
+  block = ACS_BLOCK;
+  if (block == '#' && ACS_BOARD == '#')
+    {
+      block = stipple;
+      stipple = ACS_BLOCK;
+    }
+
+  /* lower scroll border
+   */
+  l = x2 - x1 - 1;
+  n = hscroll * l;
+  r = (hoffs + 1.0 / (2 * (l - n - 1))) * (l - n - 1);
+  for (i = 0; i < l; i++)
+    mvaddch (y2, i + x1 + 1, hscroll >= 1 ? ACS_HLINE :
+            i >= r && i <= r + n ? block : stipple);
+
+  /* right scroll border
+   */
+  l = y2 - y1 - 1;
+  n = vscroll * l;
+  r = (voffs + 1.0 / (2 * (l - n - 1))) * (l - n - 1);
+  for (i = 0; i < l; i++)
+    mvaddch (i + y1 + 1, x2, vscroll >= 1 ? ACS_VLINE :
+            i >= r && i <= r + n ? block : stipple);
+
+  /* show text
+   */
+  x1++; y1++;
+  for (i = 0; i < *yoffs; i++)
+    {
+      l = 0;
+      mixer_offset_text (&text_offs, 0, &l);
+    }
+  for (i = y1; i < y2; i++)
+    {
+      l = x2 - x1;
+      p = mixer_offset_text (&text_offs, *xoffs, &l);
+      n = x1;
+      while (l--)
+       mvaddch (i, n++, *p++);
+      while (n < x2)
+       mvaddch (i, n++, ' ');
+    }
+}
+
+struct vbuffer
+{
+  char *buffer;
+  int size;
+  int len;
+};
+
+static void
+vbuffer_kill (struct vbuffer *vbuf)
+{
+  if (vbuf->size)
+    free (vbuf->buffer);
+  vbuf->buffer = NULL;
+  vbuf->size = 0;
+  vbuf->len = 0;
+}
+
+#define vbuffer_append_string(vb,str)  vbuffer_append (vb, str, strlen (str))
+static void
+vbuffer_append (struct vbuffer *vbuf,
+               char           *text,
+               int             len)
+{
+  if (vbuf->size - vbuf->len <= len)
+    {
+      vbuf->size += len + 1;
+      vbuf->buffer = realloc (vbuf->buffer, vbuf->size);
+    }
+  memcpy (vbuf->buffer + vbuf->len, text, len);
+  vbuf->len += len;
+  vbuf->buffer[vbuf->len] = 0;
+}
+
+static int
+vbuffer_append_file (struct vbuffer *vbuf,
+                    char           *name)
+{
+  int fd;
+
+  fd = open (name, O_RDONLY);
+  if (fd >= 0)
+    {
+      char buffer[1025];
+      int l;
+
+      do
+       {
+         l = read (fd, buffer, 1024);
+         
+         vbuffer_append (vbuf, buffer, MAX (0, l));
+       }
+      while (l > 0 || (l < 0 && (errno == EAGAIN || errno == EINTR)));
+
+      close (fd);
+
+      return 0;
+    }
+  else
+    return 1;
+}
+
+static void
+mixer_show_procinfo (void)
+{
+  struct vbuffer vbuf = { NULL, 0, 0 };
+
+  vbuffer_append_string (&vbuf, "\n");
+  vbuffer_append_string (&vbuf, "/proc/asound/version:\n");
+  vbuffer_append_string (&vbuf, "====================\n");
+  if (vbuffer_append_file (&vbuf, "/proc/asound/version"))
+    {
+      vbuffer_kill (&vbuf);
+      mixer_procinfo_xoffs = mixer_procinfo_yoffs = 0;
+      mixer_show_text ("/proc",
+                      " No /proc information available. ",
+                      &mixer_procinfo_xoffs, &mixer_procinfo_yoffs);
+      return;
+    }
+  else
+    vbuffer_append_file (&vbuf, "/proc/asound/meminfo");
+
+  vbuffer_append_string (&vbuf, "\n");
+  vbuffer_append_string (&vbuf, "/proc/asound/cards:\n");
+  vbuffer_append_string (&vbuf, "===================\n");
+  if (vbuffer_append_file (&vbuf, "/proc/asound/cards"))
+    vbuffer_append_string (&vbuf, "No information available.\n");
+
+  vbuffer_append_string (&vbuf, "\n");
+  vbuffer_append_string (&vbuf, "/proc/asound/devices:\n");
+  vbuffer_append_string (&vbuf, "=====================\n");
+  if (vbuffer_append_file (&vbuf, "/proc/asound/devices"))
+    vbuffer_append_string (&vbuf, "No information available.\n");
+
+  vbuffer_append_string (&vbuf, "\n");
+  vbuffer_append_string (&vbuf, "/proc/asound/oss-devices:\n");
+  vbuffer_append_string (&vbuf, "=========================\n");
+  if (vbuffer_append_file (&vbuf, "/proc/asound/oss-devices"))
+    vbuffer_append_string (&vbuf, "No information available.\n");
+
+  vbuffer_append_string (&vbuf, "\n");
+  vbuffer_append_string (&vbuf, "/proc/asound/timers:\n");
+  vbuffer_append_string (&vbuf, "====================\n");
+  if (vbuffer_append_file (&vbuf, "/proc/asound/timers"))
+    vbuffer_append_string (&vbuf, "No information available.\n");
+
+  vbuffer_append_string (&vbuf, "\n");
+  vbuffer_append_string (&vbuf, "/proc/asound/pcm:\n");
+  vbuffer_append_string (&vbuf, "=================\n");
+  if (vbuffer_append_file (&vbuf, "/proc/asound/pcm"))
+    vbuffer_append_string (&vbuf, "No information available.\n");
+
+  mixer_show_text ("/proc", vbuf.buffer,
+                  &mixer_procinfo_xoffs, &mixer_procinfo_yoffs);
+  vbuffer_kill (&vbuf);
+}
+
+static void
+mixer_init (void)
+{
+  static snd_mixer_info_t mixer_info = { 0, };
+  static struct snd_ctl_hw_info hw_info;
+  void *ctl_handle;
+  
+  if (snd_ctl_open (&ctl_handle, card_id) < 0)
+    mixer_abort (ERR_OPEN, "snd_ctl_open");
+  if (snd_ctl_hw_info (ctl_handle, &hw_info) < 0)
+    mixer_abort (ERR_FCN, "snd_ctl_hw_info");
+  snd_ctl_close (ctl_handle);
+  /* open mixer device
+   */
+  if (snd_mixer_open (&mixer_handle, card_id, mixer_id) < 0)
+    mixer_abort (ERR_OPEN, "snd_mixer_open");
+  
+  /* setup global variables
+   */
+  if (snd_mixer_info (mixer_handle, &mixer_info) < 0)
+    mixer_abort (ERR_FCN, "snd_mixer_info");
+  mixer_n_channels = mixer_info.channels;
+  mixer_card_name = hw_info.name;
+  mixer_device_name = mixer_info.name;
+}
+
+static void
+mixer_init_window (void)
+{
+  /* initialize ncurses
+   */
+  mixer_window = initscr ();
+
+  mixer_no_lrcorner = tigetflag ("xenl") != 1 && tigetflag ("am") != 1;
+
+  if (mixer_do_color)
+    mixer_do_color = has_colors ();
+  mixer_init_draw_contexts ();
+
+  /* react on key presses
+   */
+  cbreak ();
+  noecho ();
+  leaveok (mixer_window, TRUE);
+  keypad (mixer_window, TRUE);
+  GETCH_BLOCK (1);
+
+  /* init mixer screen
+   */
+  getmaxyx (mixer_window, mixer_max_y, mixer_max_x);
+  if (mixer_minimize)
+    {
+      mixer_max_x = MIXER_MIN_X;
+      mixer_max_y = MIXER_MIN_Y;
+    }
+  mixer_ofs_x = 2 /* extra begin padding: */ + 1;
+
+  /* required allocations */
+  mixer_n_vis_channels = (mixer_max_x - mixer_ofs_x * 2 + 1) / 9;
+  mixer_n_vis_channels = CLAMP (mixer_n_vis_channels, 1, mixer_n_channels);
+  mixer_extra_space = mixer_max_x - mixer_ofs_x * 2 + 1 - mixer_n_vis_channels * 9;
+  mixer_extra_space = MAX (0, mixer_extra_space / (mixer_n_vis_channels + 1));
+  if (MIXER_TEXT_Y + 10 < mixer_max_y)
+    mixer_cbar_height = 10 + MAX (0, mixer_max_y - MIXER_TEXT_Y - 10 ) / 2;
+  else
+    mixer_cbar_height = MAX (1, mixer_max_y - MIXER_TEXT_Y);
+
+  mixer_clear (TRUE);
+}
+
+static void
+mixer_resize (void)
+{
+  struct winsize winsz = { 0, };
+
+  mixer_needs_resize = 0;
+  
+  if (ioctl (fileno (stdout), TIOCGWINSZ, &winsz) >= 0 &&
+      winsz.ws_row && winsz.ws_col)
+    {
+      keypad (mixer_window, FALSE);
+      leaveok (mixer_window, FALSE);
+
+      endwin ();
+      
+      mixer_max_x = MAX (2, winsz.ws_col);
+      mixer_max_y = MAX (2, winsz.ws_row);
+      
+      /* humpf, i don't get it, if only the number of rows change,
+       * ncurses will segfault shortly after (could trigger that with mc as well).
+       */
+      resizeterm (mixer_max_y + 1, mixer_max_x + 1);
+      resizeterm (mixer_max_y, mixer_max_x);
+      
+      mixer_init_window ();
+      
+      if (mixer_max_x < MIXER_MIN_X ||
+         mixer_max_y < MIXER_MIN_Y)
+       beep (); // mixer_abort (ERR_WINSIZE, "");
+
+      mixer_have_old_focus = 0;
+    }
+}
+
+static void
+mixer_channel_changed (void *private_data,
+                      int   channel)
+{
+  /* we don't actually need to update the individual channels because
+   * we redraw the whole screen upon every main iteration anyways.
+   */
+#if 0
+  fprintf (stderr, "*** channel = %i\n", channel);
+  mixer_update_cbar (channel);
+#endif
+}
+
+static int
+mixer_iteration (void)
+{
+  struct timeval delay = { 0, };
+  snd_mixer_callbacks_t callbacks = { 0, };
+  int mixer_fd;
+  fd_set rfds;
+  int finished = 0;
+  int key = 0;
+  int old_view;
+  
+  callbacks.channel_was_changed = mixer_channel_changed;
+
+  /* setup for select on stdin and the mixer fd */
+  mixer_fd = snd_mixer_file_descriptor (mixer_handle);
+  FD_ZERO (&rfds);
+  FD_SET (fileno (stdin), &rfds);
+  FD_SET (mixer_fd, &rfds);
+
+  delay.tv_sec = 0;
+  delay.tv_usec = 0 * 100 * 1000;
+
+  finished = select (mixer_fd + 1, &rfds, NULL, NULL, mixer_needs_resize ? &delay : NULL) < 0;
+
+  /* don't abort on handled signals */
+  if (finished && errno == EINTR)
+    {
+      FD_ZERO (&rfds);
+      finished = 0;
+    }
+  else if (mixer_needs_resize)
+    mixer_resize ();
+
+  if (FD_ISSET (mixer_fd, &rfds))
+    snd_mixer_read (mixer_handle, &callbacks);
+
+  if (FD_ISSET (fileno (stdin), &rfds))
+    key = getch ();
+
+  old_view = mixer_view;
+  
+  /* feature Escape prefixing for some keys */
+  if (key == 27)
+    {
+      GETCH_BLOCK (0);
+      key = getch ();
+      GETCH_BLOCK (1);
+      switch (key)
+       {
+       case 9: /* Tab */
+         key = KEY_BTAB;
+         break;
+       default:
+         key = 27;
+         break;
+       }
+    }
+  
+  /* general keys */
+  switch (key)
+    {
+    case 0:
+      /* ignore */
+      break;
+    case 27:   /* Escape */
+      finished = 1;
+      key = 0;
+      break;
+    case 13:   /* Return */
+    case 10:   /* NewLine */
+      if (mixer_view == VIEW_CHANNELS)
+       mixer_clear (FALSE);
+      mixer_view = VIEW_CHANNELS;
+      key = 0;
+      break;
+    case 'h':
+    case 'H':
+    case KEY_F (1):
+      mixer_view = VIEW_HELP;
+      key = 0;
+      break;
+    case '/':
+    case KEY_F (2):
+      mixer_view = VIEW_PROCINFO;
+      key = 0;
+      break;
+    case 'L':
+    case 'l':
+      mixer_clear (TRUE);
+      break;
+    }
+  
+  if (key && (mixer_view == VIEW_HELP ||
+             mixer_view == VIEW_PROCINFO))
+    switch (key)
+      {
+      case 9:          /* Tab */
+       mixer_hscroll_delta += 8;
+       break;
+      case KEY_BTAB:
+       mixer_hscroll_delta -= 8;
+       break;
+      case KEY_A1:
+       mixer_hscroll_delta -= 1;
+       mixer_vscroll_delta -= 1;
+       break;
+      case KEY_A3:
+       mixer_hscroll_delta += 1;
+       mixer_vscroll_delta -= 1;
+       break;
+      case KEY_C1:
+       mixer_hscroll_delta -= 1;
+       mixer_vscroll_delta += 1;
+       break;
+      case KEY_C3:
+       mixer_hscroll_delta += 1;
+       mixer_vscroll_delta += 1;
+       break;
+      case KEY_RIGHT:
+      case 'n':
+       mixer_hscroll_delta += 1;
+       break;
+      case KEY_LEFT:
+      case 'p':
+       mixer_hscroll_delta -= 1;
+       break;
+      case KEY_UP:
+      case 'w':
+      case 'W':
+       mixer_vscroll_delta -= 1;
+       break;
+      case KEY_DOWN:
+      case 'x':
+      case 'X':
+       mixer_vscroll_delta += 1;
+       break;
+      case KEY_PPAGE:
+      case 'B':
+      case 'b':
+       mixer_vscroll_delta -= (mixer_max_y - 5) / 2;
+       break;
+      case KEY_NPAGE:
+      case ' ':
+       mixer_vscroll_delta += (mixer_max_y - 5) / 2;
+       break;
+      case KEY_BEG:
+      case KEY_HOME:
+       mixer_hscroll_delta -= 0xffffff;
+       break;
+      case KEY_LL:
+      case KEY_END:
+       mixer_hscroll_delta += 0xffffff;
+       break;
+      }
+  
+  if (key && mixer_view == VIEW_CHANNELS)
+    switch (key)
+      {
+      case 9:          /* Tab */
+       mixer_exact = !mixer_exact;
+       break;
+      case KEY_RIGHT:
+      case 'n':
+       mixer_focus_channel += 1;
+       break;
+      case KEY_LEFT:
+      case 'p':
+       mixer_focus_channel -= 1;
+       break;
+      case KEY_PPAGE:
+       if (mixer_exact)
+         {
+           mixer_lvolume_delta = 8;
+           mixer_rvolume_delta = 8;
+         }
+       else
+         {
+           mixer_lvolume_delta = 10;
+           mixer_rvolume_delta = 10;
+         }
+       break;
+      case KEY_NPAGE:
+       if (mixer_exact)
+         {
+           mixer_lvolume_delta = -8;
+           mixer_rvolume_delta = -8;
+         }
+       else
+         {
+           mixer_lvolume_delta = -10;
+           mixer_rvolume_delta = -10;
+         }
+       break;
+      case KEY_BEG:
+      case KEY_HOME:
+       mixer_lvolume_delta = 512;
+       mixer_rvolume_delta = 512;
+       break;
+      case KEY_LL:
+      case KEY_END:
+       mixer_lvolume_delta = -512;
+       mixer_rvolume_delta = -512;
+       break;
+      case '+':
+       mixer_lvolume_delta = 1;
+       mixer_rvolume_delta = 1;
+       break;
+      case '-':
+       mixer_lvolume_delta = -1;
+       mixer_rvolume_delta = -1;
+       break;
+      case 'w':
+      case KEY_UP:
+       mixer_lvolume_delta = 1;
+       mixer_rvolume_delta = 1;
+      case 'W':
+       mixer_lvolume_delta += 1;
+       mixer_rvolume_delta += 1;
+       break;
+      case 'x':
+      case KEY_DOWN:
+       mixer_lvolume_delta = -1;
+       mixer_rvolume_delta = -1;
+      case 'X':
+       mixer_lvolume_delta += -1;
+       mixer_rvolume_delta += -1;
+       break;
+      case 'q':
+       mixer_lvolume_delta = 1;
+      case 'Q':
+       mixer_lvolume_delta += 1;
+       break;
+      case 'y':
+      case 'z':
+       mixer_lvolume_delta = -1;
+      case 'Y':
+      case 'Z':
+       mixer_lvolume_delta += -1;
+       break;
+      case 'e':
+       mixer_rvolume_delta = 1;
+      case 'E':
+       mixer_rvolume_delta += 1;
+       break;
+      case 'c':
+       mixer_rvolume_delta = -1;
+      case 'C':
+       mixer_rvolume_delta += -1;
+       break;
+      case 'm':
+      case 'M':
+       mixer_toggle_mute_left = 1;
+       mixer_toggle_mute_right = 1;
+       break;
+      case 'b':
+      case 'B':
+      case '=':
+       mixer_balance_volumes = 1;
+       break;
+      case '<':
+      case ',':
+       mixer_toggle_mute_left = 1;
+       break;
+      case '>':
+      case '.':
+       mixer_toggle_mute_right = 1;
+       break;
+      case ' ':
+       mixer_toggle_record = 1;
+       break;
+      case KEY_IC:
+      case ';':
+       mixer_toggle_rec_left = 1;
+       break;
+      case '\'':
+      case KEY_DC:
+       mixer_toggle_rec_right = 1;
+       break;
+      case '1':
+       mixer_route_rtol_in = 1;
+       break;
+      case '2':
+       mixer_route_ltor_in = 1;
+       break;
+      }
+  
+  if (old_view != mixer_view)
+    mixer_clear (FALSE);
+  
+  mixer_focus_channel = CLAMP (mixer_focus_channel, 0, mixer_n_channels - 1);
+  
+  return finished;
+}
+
+static void
+mixer_winch (void)
+{
+  signal (SIGWINCH, (void*) mixer_winch);
+
+  mixer_needs_resize++;
+}
+
+static void
+mixer_signal_handler (int signal)
+{
+  if (signal != SIGSEGV)
+    mixer_abort (ERR_SIGNAL, sys_siglist[signal]);
+  else
+    {
+      fprintf (stderr, "\nSegmentation fault.\n");
+      _exit (11);
+    }
+}
+
+int
+main (int    argc,
+      char **argv)
+{
+  int opt;
+  
+  /* parse args
+   */
+  do
+    {
+      opt = getopt (argc, argv, "c:m:eshg");
+      switch (opt)
+       {
+       case '?':
+       case 'h':
+         fprintf (stderr, "%s %s\n", PRGNAME_UPPER, VERSION);
+         fprintf (stderr, "Usage: %s [-e] [-c <card: 1..%i>] [-m <mixer: 0..1>] [-z]\n", PRGNAME, snd_cards ());
+         mixer_abort (ERR_NONE, "");
+       case 'c':
+         card_id = snd_card_name (optarg);
+         break;
+       case 'e':
+         mixer_exact = !mixer_exact;
+         break;
+       case 'g':
+         mixer_do_color = !mixer_do_color;
+         break;
+       case 'm':
+         mixer_id = CLAMP (optarg[0], '0', '1') - '0';
+         break;
+       case 's':
+         mixer_minimize = 1;
+         break;
+       }
+    }
+  while (opt > 0);
+  
+  /* initialize mixer
+   */
+  mixer_init ();
+  
+  /* setup signal handlers
+   */
+  signal (SIGINT, mixer_signal_handler);
+  signal (SIGTRAP, mixer_signal_handler);
+  signal (SIGABRT, mixer_signal_handler);
+  signal (SIGQUIT, mixer_signal_handler);
+  signal (SIGBUS, mixer_signal_handler);
+  signal (SIGSEGV, mixer_signal_handler);
+  signal (SIGPIPE, mixer_signal_handler);
+  signal (SIGTERM, mixer_signal_handler);
+  
+  /* initialize ncurses
+   */
+  mixer_init_window ();
+  if (mixer_max_x < MIXER_MIN_X ||
+      mixer_max_y < MIXER_MIN_Y)
+    beep (); // mixer_abort (ERR_WINSIZE, "");
+  
+  signal (SIGWINCH, (void*) mixer_winch);
+
+  do
+    {
+      /* draw window upon every iteration */
+      if (!mixer_needs_resize)
+       {
+         switch (mixer_view)
+           {
+           case VIEW_CHANNELS:
+             mixer_update_cbars ();
+             break;
+           case VIEW_HELP:
+             mixer_show_text ("Help", mixer_help_text, &mixer_help_xoffs, &mixer_help_yoffs);
+             break;
+           case VIEW_PROCINFO:
+             mixer_show_procinfo ();
+             break;
+           }
+         mixer_draw_frame ();
+         refresh ();
+       }
+    }
+  while (!mixer_iteration ());
+  
+  mixer_abort (ERR_NONE, "");
+};
index 189bd14195f228e070dad26afa11b9773d9ad7c9..46fcba27c67bc94876f9087dda24b1c1bb91898c 100644 (file)
@@ -97,7 +97,7 @@ char* Mixer::Name(int32 device)
 
 void Mixer::Update()
 {
-       if(snd_mixer_channel_read(mixer_handle, current_device, &ch_data) < 0) {
+       if(snd_mixer_channel_output_read(mixer_handle, current_device, &ch_data) < 0) {
                fprintf(stderr, "Can't read data from channel %i\n", current_device);
                return;         /* No fail code? */
        }
@@ -120,7 +120,7 @@ void Mixer::DeviceWrite(int32 device, int32 left, int32 right, int32 flags)
        ch_data.left = left;
        ch_data.right = right;
        ch_data.flags = flags;
-       if(snd_mixer_channel_write(mixer_handle, device, &ch_data) < 0) {
+       if(snd_mixer_channel_output_write(mixer_handle, device, &ch_data) < 0) {
                fprintf(stderr, "Can't write data to channel %i\n", device);
                return;         /* No fail code? */
        }
index 84600ffbab612851eab4dc22321d44400ab4cccd..64c9fa3b033fadf809400f817eba0c1bd8faadb5 100644 (file)
 #define                E_MIXER_SUCCESS         1
 #define                E_MIXER_NEED_CLOSE      2
 
-#define        E_MIXER_RECORD          SND_MIXER_FLG_RECORD
-#define        E_MIXER_MUTE_LEFT       SND_MIXER_FLG_MUTE_LEFT
-#define                E_MIXER_MUTE_RIGHT      SND_MIXER_FLG_MUTE_RIGHT
-#define                E_MIXER_MUTE            SND_MIXER_FLG_MUTE
+/* FIXME */
+#define        E_MIXER_RECORD          0
+
+#define        E_MIXER_MUTE_LEFT       SND_MIXER_DFLG_MUTE_LEFT
+#define                E_MIXER_MUTE_RIGHT      SND_MIXER_DFLG_MUTE_RIGHT
+#define                E_MIXER_MUTE            SND_MIXER_DFLG_MUTE
 
 
 class Mixer 
@@ -63,7 +65,7 @@ public:
                        }
 private:
        snd_mixer_info_t         info;
-        snd_mixer_channel_t     ch_data;
+        snd_mixer_channel_direction_t   ch_data;
        snd_mixer_channel_info_t ch_info;
        
        void *          mixer_handle;
index 043a8308a0e7a10bfbebd2cb63589a532f5fafe1..6e28e74b66609d2ffb0dd9ca2a5cd1ea8d505d88 100644 (file)
@@ -31,7 +31,7 @@ List all available soundcards and devices.
 .TP
 \fI-c\fP <card number>
 Select the soundcard to use, if you have more than one. Cards are
-numbered from 1 (the default).
+numbered from 0 (the default).
 .TP
 \fI-d\fP <device number>
 Select the soundcard device to use, if your card has more than
index 21e6f356f6e5a2d8a1dcba7032be20a597a4a37b..9dcc3b4ee4adabfc5f1932d6620eeb962cbfdf5e 100644 (file)
@@ -141,7 +141,7 @@ static void usage(char *command)
                "  -h,--help     help\n"
                "  -V,--version  print current version\n"
                "  -l            list all soundcards and digital audio devices\n"
-               "  -c <card>     select card # or card id (1-%i), defaults to 1\n"
+               "  -c <card>     select card # or card id (0-%i), defaults to 0\n"
                "  -d <device>   select device #, defaults to 0\n"
                "  -q            quiet mode\n"
                "  -v            file format Voc\n"
@@ -155,7 +155,7 @@ static void usage(char *command)
                "  -m            set CD-ROM quality (44100Hz,stereo,16-bit linear)\n"
                "  -M            set DAT quality (48000Hz,stereo,16-bit linear)\n"
                "  -p <type>     compression type (alaw, ulaw, adpcm)\n"
-               ,command, snd_cards());
+               ,command, snd_cards()-1);
 }
 
 static void device_list(void)
index c80a927404dfb2339333b5f581c9d5b25a5b23a2..3d92545a2dd7eca3f66dcb84ba8ce41f191b24f3 100644 (file)
@@ -1,7 +1,7 @@
 dnl Process this file with autoconf to produce a configure script.
 AC_INIT(alsamixer/alsamixer.c)
 AC_PREFIX_DEFAULT(/usr)
-AM_INIT_AUTOMAKE(alsa-utils, 0.3.0-pre3)
+AM_INIT_AUTOMAKE(alsa-utils, 0.3.0pre5)
 
 dnl Checks for programs.
 AC_PROG_CC
@@ -33,5 +33,7 @@ AC_HEADER_TIME
 dnl Checks for library functions.
 AC_PROG_GCC_TRADITIONAL
 
+SAVE_UTIL_VERSION
+
 AC_OUTPUT(Makefile alsactl/Makefile alsamixer/Makefile amixer/Makefile aplay/Makefile \
           include/Makefile utils/Makefile utils/alsa-utils.spec)
diff --git a/version b/version
deleted file mode 100644 (file)
index b180c90..0000000
--- a/version
+++ /dev/null
@@ -1 +0,0 @@
-0.3.0-pre3