~ubuntu-branches/debian/squeeze/alsa-utils/squeeze

« back to all changes in this revision

Viewing changes to alsamixer/alsamixer.c

  • Committer: Bazaar Package Importer
  • Author(s): Masato Taruishi
  • Date: 2002-04-03 14:58:38 UTC
  • Revision ID: james.westby@ubuntu.com-20020403145838-ylq6q55c9ifhpixw
Tags: upstream-0.9.0beta12
ImportĀ upstreamĀ versionĀ 0.9.0beta12

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* AlsaMixer - Commandline mixer for the ALSA project Copyright (C) 1998,
 
2
 * 1999 Tim Janik <timj@gtk.org> and Jaroslav Kysela <perex@suse.cz>
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or
 
5
 * modify it under the terms of the GNU General Public License
 
6
 * as published by the Free Software Foundation; either version 2
 
7
 * of the License, or (at your option) any later version.
 
8
 *
 
9
 * This program is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 * GNU General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU Library General Public
 
15
 * License along with this library; if not, write to the Free Software
 
16
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 
17
 *
 
18
 *
 
19
 * ChangeLog:
 
20
 *
 
21
 * Wed Feb 14 13:08:17 CET 2001   Jaroslav Kysela <perex@suse.cz>
 
22
 *
 
23
 *      * ported to the latest mixer 0.9.x API (function based)
 
24
 *
 
25
 * Fri Jun 23 14:10:00 MEST 2000  Jaroslav Kysela <perex@suse.cz>
 
26
 *
 
27
 *      * ported to new mixer 0.9.x API (simple control)
 
28
 *      * improved error handling (mixer_abort)
 
29
 *
 
30
 * Thu Mar  9 22:54:16 MET 2000  Takashi iwai <iwai@ww.uni-erlangen.de>
 
31
 *
 
32
 *      * a group is split into front, rear, center and woofer elements.
 
33
 *
 
34
 * Mon Jan  3 23:33:42 MET 2000  Jaroslav Kysela <perex@suse.cz>
 
35
 *
 
36
 *      * version 1.00
 
37
 *
 
38
 *      * ported to new mixer API (scontrol control)
 
39
 *
 
40
 * Sun Feb 21 19:55:01 1999  Tim Janik  <timj@gtk.org>
 
41
 *
 
42
 *      * bumped version to 0.10.
 
43
 *
 
44
 *      * added scrollable text views.
 
45
 *      we now feature an F1 Help screen and an F2 /proc info screen.
 
46
 *      the help screen does still require lots of work though.
 
47
 *
 
48
 *      * keys are evaluated view specific now.
 
49
 *
 
50
 *      * we feature meta-keys now, e.g. M-Tab as back-tab.
 
51
 *
 
52
 *      * if we are already in channel view and the user still hits Return,
 
53
 *      we do a refresh nonetheless, since 'r'/'R' got removed as a redraw
 
54
 *      key (reserved for capture volumes). 'l'/'L' is still preserved though,
 
55
 *      and actually needs to be to e.g. get around the xterm bold-artefacts.
 
56
 *
 
57
 *      * support terminals that can't write into lower right corner.
 
58
 *
 
59
 *      * undocumented '-s' option that will keep the screen to its
 
60
 *      minimum size, usefull for debugging only.
 
61
 *
 
62
 * Sun Feb 21 02:23:52 1999  Tim Janik  <timj@gtk.org>
 
63
 *
 
64
 *      * don't abort if snd_mixer_* functions failed due to EINTR,
 
65
 *      we simply retry on the next cycle. hopefully asoundlib preserves
 
66
 *      errno states correctly (Jaroslav can you asure that?).
 
67
 *
 
68
 *      * feature WINCH correctly, so we make a complete relayout on
 
69
 *      screen resizes. don't abort on too-small screen sizes anymore,
 
70
 *      but simply beep.
 
71
 *
 
72
 *      * redid the layout algorithm to fix some bugs and to preserve
 
73
 *      space for a flag indication line. the channels are
 
74
 *      nicer spread horizontally now (i.e. we also pad on the left and
 
75
 *      right screen bounds now).
 
76
 *
 
77
 *      * various other minor fixes.
 
78
 *
 
79
 *      * indicate whether ExactMode is active or not.
 
80
 *
 
81
 *      * fixed coding style to follow the GNU coding conventions.
 
82
 *
 
83
 *      * reverted capture volume changes since they broke ExactMode display.
 
84
 *
 
85
 *      * composed ChangeLog entries.
 
86
 *
 
87
 * 1998/11/04 19:43:45  perex
 
88
 *
 
89
 *      * Stereo capture source and route selection...
 
90
 *      provided by Carl van Schaik <carl@dreamcoat.che.uct.ac.za>.
 
91
 *
 
92
 * 1998/09/20 08:05:24  perex
 
93
 *
 
94
 *      * Fixed -m option...
 
95
 *
 
96
 * 1998/10/29 22:50:10
 
97
 *
 
98
 *      * initial checkin of alsamixer.c, written by Tim Janik, modified by
 
99
 *      Jaroslav Kysela to feature asoundlib.h instead of plain ioctl()s and
 
100
 *      automated updates after select() (i always missed that with OSS!).
 
101
 */
 
102
 
 
103
#include <stdio.h>
 
104
#include <unistd.h>
 
105
#include <fcntl.h>
 
106
#include <sys/ioctl.h>
 
107
 
 
108
#include <errno.h>
 
109
 
 
110
#include <string.h>
 
111
#include <stdlib.h>
 
112
#include <unistd.h>
 
113
#include <sys/signal.h>
 
114
#include <sys/time.h>
 
115
 
 
116
#ifndef CURSESINC
 
117
#include <ncurses.h>
 
118
#else
 
119
#include CURSESINC
 
120
#endif
 
121
#include <time.h>
 
122
 
 
123
#include <alsa/asoundlib.h>
 
124
 
 
125
/* example compilation commandline:
 
126
 * clear; gcc -Wall -pipe -O2 alsamixer.c -o alsamixer -lasound -lncurses
 
127
 */
 
128
 
 
129
/* --- defines --- */
 
130
#define PRGNAME          "alsamixer"
 
131
#define PRGNAME_UPPER    "AlsaMixer"
 
132
#define VERSION          "v1.00"
 
133
#define CHECK_ABORT(e,s,n) ({ if ((n) != -EINTR) mixer_abort ((e), (s), (n)); })
 
134
#define GETCH_BLOCK(w)   ({ timeout ((w) ? -1 : 0); })
 
135
 
 
136
#undef MAX
 
137
#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
 
138
#undef MIN
 
139
#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
 
140
#undef ABS
 
141
#define ABS(a)     (((a) < 0) ? -(a) : (a))
 
142
#undef CLAMP
 
143
#define CLAMP(x, low, high)  (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
 
144
 
 
145
#define MIXER_MIN_X     (18)                    /* abs minimum: 18 */
 
146
#define MIXER_TEXT_Y    (10)
 
147
#define MIXER_MIN_Y     (MIXER_TEXT_Y + 3)      /* abs minimum: 11 */
 
148
 
 
149
#define MIXER_BLACK     (COLOR_BLACK)
 
150
#define MIXER_DARK_RED  (COLOR_RED)
 
151
#define MIXER_RED       (COLOR_RED | A_BOLD)
 
152
#define MIXER_GREEN     (COLOR_GREEN | A_BOLD)
 
153
#define MIXER_ORANGE    (COLOR_YELLOW)
 
154
#define MIXER_YELLOW    (COLOR_YELLOW | A_BOLD)
 
155
#define MIXER_MARIN     (COLOR_BLUE)
 
156
#define MIXER_BLUE      (COLOR_BLUE | A_BOLD)
 
157
#define MIXER_MAGENTA   (COLOR_MAGENTA)
 
158
#define MIXER_DARK_CYAN (COLOR_CYAN)
 
159
#define MIXER_CYAN      (COLOR_CYAN | A_BOLD)
 
160
#define MIXER_GREY      (COLOR_WHITE)
 
161
#define MIXER_GRAY      (MIXER_GREY)
 
162
#define MIXER_WHITE     (COLOR_WHITE | A_BOLD)
 
163
 
 
164
 
 
165
/* --- views --- */
 
166
enum {
 
167
  VIEW_CHANNELS,
 
168
  VIEW_HELP,
 
169
  VIEW_PROCINFO
 
170
};
 
171
 
 
172
 
 
173
/* --- variables --- */
 
174
static WINDOW   *mixer_window = NULL;
 
175
static int       mixer_needs_resize = 0;
 
176
static int       mixer_minimize = 0;
 
177
static int       mixer_no_lrcorner = 0;
 
178
static int       mixer_view = VIEW_CHANNELS;
 
179
static int       mixer_max_x = 0;
 
180
static int       mixer_max_y = 0;
 
181
static int       mixer_ofs_x = 0;
 
182
static float     mixer_extra_space = 0;
 
183
static int       mixer_cbar_height = 0;
 
184
 
 
185
static char      card_id[64] = "default";
 
186
static snd_mixer_t *mixer_handle;
 
187
static char      mixer_card_name[128];
 
188
static char      mixer_device_name[128];
 
189
 
 
190
/* mixer bar channel : left or right */
 
191
#define MIXER_CHN_LEFT          0
 
192
#define MIXER_CHN_RIGHT         1
 
193
/* mask for toggle mute and capture */
 
194
#define MIXER_MASK_LEFT         (1 << 0)
 
195
#define MIXER_MASK_RIGHT        (1 << 1)
 
196
#define MIXER_MASK_STEREO       (MIXER_MASK_LEFT|MIXER_MASK_RIGHT)
 
197
 
 
198
/* mixer split types */
 
199
enum {
 
200
  MIXER_ELEM_FRONT, MIXER_ELEM_REAR,
 
201
  MIXER_ELEM_CENTER, MIXER_ELEM_WOOFER,
 
202
  MIXER_ELEM_END
 
203
};
 
204
 
 
205
/* left and right channels for each type */
 
206
static snd_mixer_selem_channel_id_t mixer_elem_chn[][2] = {
 
207
  { SND_MIXER_SCHN_FRONT_LEFT, SND_MIXER_SCHN_FRONT_RIGHT },
 
208
  { SND_MIXER_SCHN_REAR_LEFT, SND_MIXER_SCHN_REAR_RIGHT },
 
209
  { SND_MIXER_SCHN_FRONT_CENTER, SND_MIXER_SCHN_UNKNOWN },
 
210
  { SND_MIXER_SCHN_WOOFER, SND_MIXER_SCHN_UNKNOWN },
 
211
};
 
212
 
 
213
static void     *mixer_sid = NULL;
 
214
static int       mixer_n_selems = 0;
 
215
static int       mixer_changed_state = 1;
 
216
 
 
217
/* split scontrols */
 
218
static int       mixer_n_elems = 0;
 
219
static int       mixer_n_vis_elems = 0;
 
220
static int       mixer_first_vis_elem = 0;
 
221
static int       mixer_focus_elem = 0;
 
222
static int       mixer_have_old_focus = 0;
 
223
static int *mixer_grpidx;
 
224
static int *mixer_type;
 
225
 
 
226
static int       mixer_volume_delta[2];         /* left/right volume delta in % */
 
227
static int       mixer_balance_volumes = 0;     /* boolean */
 
228
static unsigned  mixer_toggle_mute = 0;         /* left/right mask */
 
229
static unsigned  mixer_toggle_capture = 0;      /* left/right mask */
 
230
 
 
231
static int       mixer_hscroll_delta = 0;
 
232
static int       mixer_vscroll_delta = 0;
 
233
 
 
234
 
 
235
/* --- text --- */
 
236
static int       mixer_procinfo_xoffs = 0;
 
237
static int       mixer_procinfo_yoffs = 0;
 
238
static int       mixer_help_xoffs = 0;
 
239
static int       mixer_help_yoffs = 0;
 
240
static char     *mixer_help_text =
 
241
(
 
242
 "\n"
 
243
 " Esc     exit alsamixer\n"
 
244
 " F1      show Help screen\n"
 
245
 " F2      show /proc info screen\n"
 
246
 " Return  return to main screen\n"
 
247
 " Space   toggle Capture facility\n"
 
248
 " Tab     toggle ExactMode\n"
 
249
 " m M     mute both channels\n"
 
250
 " < >     mute left/right channel\n"
 
251
 " Up      increase left and right volume\n"
 
252
 " Down    decrease left and right volume\n"
 
253
 " Right   move (scroll) to the right next channel\n"
 
254
 " Left    move (scroll) to the left next channel\n"
 
255
 "\n"
 
256
 "Alsamixer has been written and is Copyrighted in 1998, 1999 by\n"
 
257
 "Tim Janik <timj@gtk.org> and Jaroslav Kysela <perex@suse.cz>.\n"
 
258
 );
 
259
 
 
260
 
 
261
/* --- draw contexts --- */
 
262
enum {
 
263
  DC_DEFAULT,
 
264
  DC_BACK,
 
265
  DC_TEXT,
 
266
  DC_PROMPT,
 
267
  DC_CBAR_MUTE,
 
268
  DC_CBAR_NOMUTE,
 
269
  DC_CBAR_CAPTURE,
 
270
  DC_CBAR_NOCAPTURE,
 
271
  DC_CBAR_EMPTY,
 
272
  DC_CBAR_LABEL,
 
273
  DC_CBAR_FOCUS_LABEL,
 
274
  DC_FOCUS,
 
275
  DC_ANY_1,
 
276
  DC_ANY_2,
 
277
  DC_ANY_3,
 
278
  DC_ANY_4,
 
279
  DC_LAST
 
280
};
 
281
 
 
282
static int dc_fg[DC_LAST] = { 0 };
 
283
static int dc_attrib[DC_LAST] = { 0 };
 
284
static int dc_char[DC_LAST] = { 0 };
 
285
static int mixer_do_color = 1;
 
286
 
 
287
static void
 
288
mixer_init_dc (int c,
 
289
               int n,
 
290
               int f,
 
291
               int b,
 
292
               int a)
 
293
{
 
294
  dc_fg[n] = f;
 
295
  dc_attrib[n] = a;
 
296
  dc_char[n] = c;
 
297
  if (n > 0)
 
298
    init_pair (n, dc_fg[n] & 0xf, b & 0x0f);
 
299
}
 
300
 
 
301
static int
 
302
mixer_dc (int n)
 
303
{
 
304
  if (mixer_do_color)
 
305
    attrset (COLOR_PAIR (n) | (dc_fg[n] & 0xfffffff0));
 
306
  else
 
307
    attrset (dc_attrib[n]);
 
308
  
 
309
  return dc_char[n];
 
310
}
 
311
 
 
312
static void
 
313
mixer_init_draw_contexts (void)
 
314
{
 
315
  start_color ();
 
316
  
 
317
  mixer_init_dc ('.', DC_BACK, MIXER_WHITE, MIXER_BLACK, A_NORMAL);
 
318
  mixer_init_dc ('.', DC_TEXT, MIXER_YELLOW, MIXER_BLACK, A_BOLD);
 
319
  mixer_init_dc ('.', DC_PROMPT, MIXER_DARK_CYAN, MIXER_BLACK, A_NORMAL);
 
320
  mixer_init_dc ('M', DC_CBAR_MUTE, MIXER_CYAN, MIXER_BLACK, A_BOLD);
 
321
  mixer_init_dc (ACS_HLINE, DC_CBAR_NOMUTE, MIXER_CYAN, MIXER_BLACK, A_BOLD);
 
322
  mixer_init_dc ('x', DC_CBAR_CAPTURE, MIXER_DARK_RED, MIXER_BLACK, A_BOLD);
 
323
  mixer_init_dc ('-', DC_CBAR_NOCAPTURE, MIXER_GRAY, MIXER_BLACK, A_NORMAL);
 
324
  mixer_init_dc (' ', DC_CBAR_EMPTY, MIXER_GRAY, MIXER_BLACK, A_DIM);
 
325
  mixer_init_dc ('.', DC_CBAR_LABEL, MIXER_WHITE, MIXER_BLUE, A_REVERSE | A_BOLD);
 
326
  mixer_init_dc ('.', DC_CBAR_FOCUS_LABEL, MIXER_RED, MIXER_BLUE, A_REVERSE | A_BOLD);
 
327
  mixer_init_dc ('.', DC_FOCUS, MIXER_RED, MIXER_BLACK, A_BOLD);
 
328
  mixer_init_dc (ACS_BLOCK, DC_ANY_1, MIXER_WHITE, MIXER_BLACK, A_BOLD);
 
329
  mixer_init_dc (ACS_BLOCK, DC_ANY_2, MIXER_GREEN, MIXER_BLACK, A_BOLD);
 
330
  mixer_init_dc (ACS_BLOCK, DC_ANY_3, MIXER_RED, MIXER_BLACK, A_BOLD);
 
331
  mixer_init_dc ('.', DC_ANY_4, MIXER_WHITE, MIXER_GREEN, A_BOLD);
 
332
  mixer_init_dc ('.', DC_ANY_4, MIXER_WHITE, MIXER_BLUE, A_BOLD);
 
333
}
 
334
 
 
335
#define DC_CBAR_FRAME   (DC_CBAR_MUTE)
 
336
#define DC_FRAME        (DC_PROMPT)
 
337
 
 
338
 
 
339
/* --- error types --- */
 
340
typedef enum
 
341
{
 
342
  ERR_NONE,
 
343
  ERR_OPEN,
 
344
  ERR_FCN,
 
345
  ERR_SIGNAL,
 
346
  ERR_WINSIZE,
 
347
} ErrType;
 
348
 
 
349
 
 
350
/* --- prototypes --- */
 
351
static void
 
352
mixer_abort (ErrType error,
 
353
             const char *err_string,
 
354
             int xerrno)
 
355
     __attribute__
 
356
((noreturn));
 
357
 
 
358
 
 
359
/* --- functions --- */
 
360
static void
 
361
mixer_clear (int full_redraw)
 
362
{
 
363
  int x, y;
 
364
  int f = full_redraw ? 0 : 1;
 
365
 
 
366
  mixer_dc (DC_BACK);
 
367
 
 
368
  if (full_redraw)
 
369
    clearok (mixer_window, TRUE);
 
370
 
 
371
  /* buggy ncurses doesn't really write spaces with the specified
 
372
   * color into the screen on clear () or erase ()
 
373
   */
 
374
  for (x = f; x < mixer_max_x - f; x++)
 
375
    for (y = f; y < mixer_max_y - f; y++)
 
376
      mvaddch (y, x, ' ');
 
377
}
 
378
 
 
379
static void
 
380
mixer_abort (ErrType     error,
 
381
             const char *err_string,
 
382
             int         xerrno)
 
383
{
 
384
  if (mixer_window)
 
385
    {
 
386
      mixer_clear (TRUE);
 
387
      refresh ();
 
388
      keypad (mixer_window, FALSE);
 
389
      leaveok (mixer_window, FALSE);
 
390
      endwin ();
 
391
      mixer_window = NULL;
 
392
    }
 
393
  printf ("\n");
 
394
  
 
395
  switch (error)
 
396
    {
 
397
    case ERR_OPEN:
 
398
      fprintf (stderr,
 
399
               PRGNAME ": function %s failed for %s: %s\n",
 
400
               err_string,
 
401
               card_id,
 
402
               snd_strerror (xerrno));
 
403
      break;
 
404
    case ERR_FCN:
 
405
      fprintf (stderr,
 
406
               PRGNAME ": function %s failed: %s\n",
 
407
               err_string,
 
408
               snd_strerror (xerrno));
 
409
      break;
 
410
    case ERR_SIGNAL:
 
411
      fprintf (stderr,
 
412
               PRGNAME ": aborting due to signal `%s'\n",
 
413
               err_string);
 
414
      break;
 
415
    case ERR_WINSIZE:
 
416
      fprintf (stderr,
 
417
               PRGNAME ": screen size too small (%dx%d)\n",
 
418
               mixer_max_x,
 
419
               mixer_max_y);
 
420
      break;
 
421
    default:
 
422
      break;
 
423
    }
 
424
  
 
425
  exit (error);
 
426
}
 
427
 
 
428
static int
 
429
mixer_cbar_get_pos (int  elem_index,
 
430
                    int *x_p,
 
431
                    int *y_p)
 
432
{
 
433
  int x;
 
434
  int y;
 
435
  
 
436
  if (elem_index < mixer_first_vis_elem ||
 
437
      elem_index - mixer_first_vis_elem >= mixer_n_vis_elems)
 
438
    return FALSE;
 
439
  
 
440
  elem_index -= mixer_first_vis_elem;
 
441
  
 
442
  x = mixer_ofs_x;
 
443
  x += (3 + 2 + 3 + 1) * elem_index + mixer_extra_space * (elem_index + 1);
 
444
 
 
445
  if (MIXER_TEXT_Y + 10 < mixer_max_y)
 
446
    y = mixer_max_y / 2 + 3;
 
447
  else
 
448
    y = (mixer_max_y + 1) / 2 + 3;
 
449
  y += mixer_cbar_height / 2;
 
450
  
 
451
  if (x_p)
 
452
    *x_p = x;
 
453
  if (y_p)
 
454
    *y_p = y;
 
455
  
 
456
  return TRUE;
 
457
}
 
458
 
 
459
static int
 
460
mixer_conv(int val, int omin, int omax, int nmin, int nmax)
 
461
{
 
462
        int orange = omax - omin, nrange = nmax - nmin;
 
463
        
 
464
        if (orange == 0)
 
465
                return 0;
 
466
        return ((nrange * (val - omin)) + (orange / 2)) / orange + nmin;
 
467
}
 
468
 
 
469
static int
 
470
mixer_calc_volume(snd_mixer_elem_t *elem,
 
471
                  int vol,
 
472
                  snd_mixer_selem_channel_id_t chn)
 
473
{
 
474
  int vol1;
 
475
  long v;
 
476
  long min, max;
 
477
  snd_mixer_selem_get_playback_volume_range(elem, &min, &max);
 
478
  vol1 = (vol < 0) ? -vol : vol;
 
479
  if (vol1 > 0) {
 
480
    if (vol1 > 100)
 
481
      vol1 = max;
 
482
    else
 
483
      vol1 = mixer_conv(vol1, 0, 100, min, max);
 
484
    if (vol1 <= 0)
 
485
      vol1 = 1;
 
486
    if (vol < 0)
 
487
      vol1 = -vol1;
 
488
  }
 
489
  snd_mixer_selem_get_playback_volume(elem, chn, &v);
 
490
  vol1 += v;
 
491
  return CLAMP(vol1, min, max);
 
492
}
 
493
 
 
494
/* set new channel values
 
495
 */
 
496
static void
 
497
mixer_write_cbar (int elem_index)
 
498
{
 
499
  snd_mixer_elem_t *elem;
 
500
  int vleft, vright, vbalance;
 
501
  int type;
 
502
  snd_mixer_selem_id_t *sid;
 
503
  snd_mixer_selem_channel_id_t chn_left, chn_right, chn;
 
504
  int sw;
 
505
 
 
506
  if (mixer_sid == NULL)
 
507
    return;
 
508
  
 
509
  sid = (snd_mixer_selem_id_t *)(((char *)mixer_sid) + snd_mixer_selem_id_sizeof() * mixer_grpidx[elem_index]);
 
510
  elem = snd_mixer_find_selem(mixer_handle, sid);
 
511
  if (elem == NULL)
 
512
    CHECK_ABORT (ERR_FCN, __FUNCTION__ ": snd_mixer_find_selem()", -EINVAL);
 
513
  type = mixer_type[elem_index];
 
514
  chn_left = mixer_elem_chn[type][MIXER_CHN_LEFT];
 
515
  if (!snd_mixer_selem_has_playback_channel(elem, chn_left))
 
516
    return; /* ..??.. */
 
517
  chn_right = mixer_elem_chn[type][MIXER_CHN_RIGHT];
 
518
  if (chn_right != SND_MIXER_SCHN_UNKNOWN && 
 
519
      !snd_mixer_selem_has_playback_channel(elem, chn_right))
 
520
    chn_right = SND_MIXER_SCHN_UNKNOWN;
 
521
 
 
522
  /* volume
 
523
   */
 
524
  if ((mixer_volume_delta[MIXER_CHN_LEFT] ||
 
525
       mixer_volume_delta[MIXER_CHN_RIGHT] ||
 
526
       mixer_balance_volumes) &&
 
527
       snd_mixer_selem_has_playback_volume(elem)) {
 
528
    int mono = 
 
529
      (chn_right == SND_MIXER_SCHN_UNKNOWN ||
 
530
       snd_mixer_selem_has_playback_volume_joined(elem));
 
531
    if (mono && !mixer_volume_delta[MIXER_CHN_LEFT])
 
532
      mixer_volume_delta[MIXER_CHN_LEFT] = mixer_volume_delta[MIXER_CHN_RIGHT];
 
533
    vleft = mixer_calc_volume(elem, mixer_volume_delta[MIXER_CHN_LEFT], chn_left);
 
534
    vbalance = vleft;
 
535
    if (! mono) {
 
536
      vright = mixer_calc_volume(elem, mixer_volume_delta[MIXER_CHN_RIGHT], chn_right);
 
537
      vbalance += vright;
 
538
      vbalance /= 2;
 
539
    } else
 
540
      vright = vleft;
 
541
    if (vleft >= 0 && vright >= 0) {
 
542
      if (snd_mixer_selem_has_playback_volume_joined(elem)) {
 
543
        for (chn = 0; chn < SND_MIXER_SCHN_LAST; chn++)
 
544
          if (snd_mixer_selem_has_playback_channel(elem, chn))
 
545
            snd_mixer_selem_set_playback_volume(elem, chn, vleft);
 
546
          if (snd_mixer_selem_has_capture_channel(elem, chn))
 
547
            snd_mixer_selem_set_capture_volume(elem, chn, vleft);
 
548
      } else {
 
549
        if (mixer_balance_volumes)
 
550
          vleft = vright = vbalance;
 
551
        if (snd_mixer_selem_has_playback_volume(elem) &&
 
552
            snd_mixer_selem_has_playback_channel(elem, chn_left))
 
553
          snd_mixer_selem_set_playback_volume(elem, chn_left, vleft);
 
554
        if (snd_mixer_selem_has_capture_volume(elem) &&
 
555
            snd_mixer_selem_has_capture_channel(elem, chn_left))
 
556
          snd_mixer_selem_set_capture_volume(elem, chn_left, vleft);
 
557
        if (! mono) {
 
558
          if (snd_mixer_selem_has_playback_volume(elem) &&
 
559
            snd_mixer_selem_has_playback_channel(elem, chn_right))
 
560
            snd_mixer_selem_set_playback_volume(elem, chn_right, vright);
 
561
          if (snd_mixer_selem_has_capture_volume(elem) &&
 
562
            snd_mixer_selem_has_capture_channel(elem, chn_right))
 
563
            snd_mixer_selem_set_capture_volume(elem, chn_right, vright);
 
564
        }
 
565
      }
 
566
    }
 
567
  }
 
568
  mixer_volume_delta[MIXER_CHN_LEFT] = mixer_volume_delta[MIXER_CHN_RIGHT] = 0;
 
569
  mixer_balance_volumes = 0;
 
570
 
 
571
  /* mute
 
572
   */
 
573
  if (mixer_toggle_mute && snd_mixer_selem_has_playback_switch(elem)) {
 
574
    if (snd_mixer_selem_has_playback_switch_joined(elem)) {
 
575
      snd_mixer_selem_get_playback_switch(elem, chn_left, &sw);
 
576
      snd_mixer_selem_set_playback_switch_all(elem, !sw);
 
577
    } else {
 
578
      if (mixer_toggle_mute & MIXER_MASK_LEFT) {
 
579
        snd_mixer_selem_get_playback_switch(elem, chn_left, &sw);
 
580
        snd_mixer_selem_set_playback_switch(elem, chn_left, !sw);
 
581
      }
 
582
      if (chn_right != SND_MIXER_SCHN_UNKNOWN && 
 
583
          (mixer_toggle_mute & MIXER_MASK_RIGHT)) {
 
584
        snd_mixer_selem_get_playback_switch(elem, chn_right, &sw);
 
585
        snd_mixer_selem_set_playback_switch(elem, chn_right, !sw);
 
586
      }
 
587
    }
 
588
  }
 
589
  mixer_toggle_mute = 0;
 
590
 
 
591
  /* capture
 
592
   */
 
593
  if (mixer_toggle_capture && snd_mixer_selem_has_capture_switch(elem)) {
 
594
    if (snd_mixer_selem_has_capture_switch_joined(elem)) {
 
595
      snd_mixer_selem_get_capture_switch(elem, chn_left, &sw);
 
596
      snd_mixer_selem_set_capture_switch_all(elem, !sw);
 
597
    } else {
 
598
      if (mixer_toggle_capture & MIXER_MASK_LEFT) {
 
599
        snd_mixer_selem_get_capture_switch(elem, chn_left, &sw);
 
600
        snd_mixer_selem_set_capture_switch(elem, chn_left, !sw);
 
601
      }
 
602
      if (chn_right != SND_MIXER_SCHN_UNKNOWN && 
 
603
          (mixer_toggle_capture & MIXER_MASK_RIGHT)) {
 
604
        snd_mixer_selem_get_capture_switch(elem, chn_right, &sw);
 
605
        snd_mixer_selem_set_capture_switch(elem, chn_right, !sw);
 
606
      }
 
607
    }
 
608
  }
 
609
  mixer_toggle_capture = 0;
 
610
}
 
611
 
 
612
 
 
613
static void
 
614
mixer_update_cbar (int elem_index)
 
615
{
 
616
  char string[128], string1[64];
 
617
  int dc;
 
618
  snd_mixer_elem_t *elem;
 
619
  long vleft, vright;
 
620
  int type;
 
621
  snd_mixer_selem_id_t *sid;
 
622
  snd_mixer_selem_channel_id_t chn_left, chn_right;
 
623
  snd_mixer_selem_channel_id_t cchn_right;
 
624
  int x, y, i;
 
625
  int swl, swr;
 
626
 
 
627
  /* set new scontrol indices and read info
 
628
   */
 
629
  if (mixer_sid == NULL)
 
630
    return;
 
631
 
 
632
  sid = (snd_mixer_selem_id_t *)(((char *)mixer_sid) + snd_mixer_selem_id_sizeof() * mixer_grpidx[elem_index]);
 
633
  elem = snd_mixer_find_selem(mixer_handle, sid);
 
634
  if (elem == NULL)
 
635
    CHECK_ABORT (ERR_FCN, __FUNCTION__ ": snd_mixer_find_selem()", -EINVAL);
 
636
 
 
637
  type = mixer_type[elem_index];
 
638
  chn_left = mixer_elem_chn[type][MIXER_CHN_LEFT];
 
639
  if (!snd_mixer_selem_has_playback_channel(elem, chn_left))
 
640
    return; /* ..??.. */
 
641
  cchn_right = chn_right = mixer_elem_chn[type][MIXER_CHN_RIGHT];
 
642
  if (chn_right != SND_MIXER_SCHN_UNKNOWN) {
 
643
    if (!snd_mixer_selem_has_playback_channel(elem, chn_right))
 
644
      chn_right = SND_MIXER_SCHN_UNKNOWN;
 
645
    if (!snd_mixer_selem_has_capture_channel(elem, chn_right))
 
646
      cchn_right = SND_MIXER_SCHN_UNKNOWN;
 
647
  }
 
648
 
 
649
  if (snd_mixer_selem_has_playback_volume(elem)) {
 
650
    long vmin, vmax;
 
651
    snd_mixer_selem_get_playback_volume_range(elem, &vmin, &vmax);
 
652
    snd_mixer_selem_get_playback_volume(elem, chn_left, &vleft);
 
653
    vleft = mixer_conv(vleft, vmin, vmax, 0, 100);
 
654
    if (chn_right != SND_MIXER_SCHN_UNKNOWN) {
 
655
      snd_mixer_selem_get_playback_volume(elem, chn_right, &vright);
 
656
      vright = mixer_conv(vright, vmin, vmax, 0, 100);
 
657
    } else {
 
658
      vright = vleft;
 
659
    }
 
660
  } else
 
661
    vleft = vright = 0;
 
662
  
 
663
  /* update the focused full bar name
 
664
   */
 
665
  if (elem_index == mixer_focus_elem) {
 
666
    mixer_dc (DC_PROMPT);
 
667
    mvaddstr (3, 2, "Item: ");
 
668
    mixer_dc (DC_TEXT);
 
669
    string1[8] = 0;
 
670
    for (i = 0; i < 63; i++)
 
671
      string1[i] = ' ';
 
672
    string1[63] = '\0';
 
673
    strcpy(string, snd_mixer_selem_id_get_name(sid));
 
674
    if (snd_mixer_selem_id_get_index(sid) > 0)
 
675
      sprintf(string + strlen(string), " %i", snd_mixer_selem_id_get_index(sid));
 
676
    string[63] = '\0';
 
677
    strncpy(string1, string, strlen(string));
 
678
    addstr(string1);
 
679
  }
 
680
 
 
681
  /* get channel bar position
 
682
   */
 
683
  if (!mixer_cbar_get_pos (elem_index, &x, &y))
 
684
    return;
 
685
 
 
686
  /* channel bar name
 
687
   */
 
688
  mixer_dc (elem_index == mixer_focus_elem ? DC_CBAR_FOCUS_LABEL : DC_CBAR_LABEL);
 
689
  if (snd_mixer_selem_id_get_index(sid) > 0)
 
690
    sprintf(string1, "%s %d", snd_mixer_selem_id_get_name(sid), snd_mixer_selem_id_get_index(sid));
 
691
  else
 
692
    strcpy(string1, snd_mixer_selem_id_get_name(sid));
 
693
  string1[8] = 0;
 
694
  for (i = 0; i < 8; i++)
 
695
    {
 
696
      string[i] = ' ';
 
697
    }
 
698
  sprintf (string + (8 - strlen (string1)) / 2, "%s          ", string1);
 
699
  string[8] = 0;
 
700
  mvaddstr (y, x, string);
 
701
  y--;
 
702
  
 
703
  /* current channel values
 
704
   */
 
705
  mixer_dc (DC_BACK);
 
706
  mvaddstr (y, x, "         ");
 
707
  mixer_dc (DC_TEXT);
 
708
  sprintf (string, "%ld", vleft);
 
709
  mvaddstr (y, x + 3 - strlen (string), string);
 
710
  mixer_dc (DC_CBAR_FRAME);
 
711
  mvaddch (y, x + 3, '<');
 
712
  mvaddch (y, x + 4, '>');
 
713
  mixer_dc (DC_TEXT);
 
714
  sprintf (string, "%ld", vright);
 
715
  mvaddstr (y, x + 5, string);
 
716
  y--;
 
717
  
 
718
  /* left/right bar
 
719
   */
 
720
  mixer_dc (DC_CBAR_FRAME);
 
721
  mvaddstr (y, x, "         ");
 
722
  mvaddch (y, x + 2, ACS_LLCORNER);
 
723
  mvaddch (y, x + 3, ACS_HLINE);
 
724
  mvaddch (y, x + 4, ACS_HLINE);
 
725
  mvaddch (y, x + 5, ACS_LRCORNER);
 
726
  y--;
 
727
  for (i = 0; i < mixer_cbar_height; i++)
 
728
    {
 
729
      mvaddstr (y - i, x, "         ");
 
730
      mvaddch (y - i, x + 2, ACS_VLINE);
 
731
      mvaddch (y - i, x + 5, ACS_VLINE);
 
732
    }
 
733
  string[2] = 0;
 
734
  for (i = 0; i < mixer_cbar_height; i++)
 
735
    {
 
736
      if (i + 1 >= 0.8 * mixer_cbar_height)
 
737
        dc = DC_ANY_3;
 
738
      else if (i + 1 >= 0.4 * mixer_cbar_height)
 
739
        dc = DC_ANY_2;
 
740
      else
 
741
        dc = DC_ANY_1;
 
742
      mvaddch (y, x + 3, mixer_dc (vleft > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY));
 
743
      mvaddch (y, x + 4, mixer_dc (vright > i * 100 / mixer_cbar_height ? dc : DC_CBAR_EMPTY));
 
744
      y--;
 
745
    }
 
746
  
 
747
  /* muted?
 
748
   */
 
749
  mixer_dc (DC_BACK);
 
750
  mvaddstr (y, x, "         ");
 
751
  if (snd_mixer_selem_has_playback_switch(elem)) {
 
752
    mixer_dc (DC_CBAR_FRAME);
 
753
    mvaddch (y, x + 2, ACS_ULCORNER);
 
754
    snd_mixer_selem_get_playback_switch(elem, chn_left, &swl);
 
755
    dc = swl ? DC_CBAR_NOMUTE : DC_CBAR_MUTE;
 
756
    mvaddch (y, x + 3, mixer_dc (dc));
 
757
    if (chn_right != SND_MIXER_SCHN_UNKNOWN) {
 
758
      snd_mixer_selem_get_playback_switch(elem, chn_right, &swr);
 
759
      dc = swr ? DC_CBAR_NOMUTE : DC_CBAR_MUTE;
 
760
    }
 
761
    mvaddch (y, x + 4, mixer_dc (dc));
 
762
    mixer_dc (DC_CBAR_FRAME);
 
763
    mvaddch (y, x + 5, ACS_URCORNER);
 
764
  } else {
 
765
    mixer_dc (DC_CBAR_FRAME);
 
766
    mvaddch (y, x + 2, ACS_ULCORNER);
 
767
    mvaddch (y, x + 3, ACS_HLINE);
 
768
    mvaddch (y, x + 4, ACS_HLINE);
 
769
    mvaddch (y, x + 5, ACS_URCORNER);
 
770
  }    
 
771
  y--;
 
772
  
 
773
  /* capture input?
 
774
   */
 
775
  if (snd_mixer_selem_has_capture_switch(elem)) {
 
776
    snd_mixer_selem_get_capture_switch(elem, chn_left, &swl);
 
777
    if (cchn_right != SND_MIXER_SCHN_UNKNOWN)
 
778
      snd_mixer_selem_get_capture_switch(elem, cchn_right, &swr);
 
779
    if (swl || (cchn_right != SND_MIXER_SCHN_UNKNOWN && swr)) {
 
780
      mixer_dc (DC_CBAR_CAPTURE);
 
781
      mvaddstr (y, x + 1, "CAPTUR");
 
782
      if (swl) {
 
783
        mvaddstr (y + 1, x + 1, "L");
 
784
        if (cchn_right == SND_MIXER_SCHN_UNKNOWN)
 
785
          mvaddstr (y + 1, x + 6, "R");
 
786
      }
 
787
      if (cchn_right != SND_MIXER_SCHN_UNKNOWN && swr)
 
788
        mvaddstr (y + 1, x + 6, "R");
 
789
    } else {
 
790
      for (i = 0; i < 6; i++)
 
791
        mvaddch (y, x + 1 + i, mixer_dc (DC_CBAR_NOCAPTURE));
 
792
    }
 
793
  } else {
 
794
    mixer_dc (DC_BACK);
 
795
    mvaddstr (y, x, "         ");
 
796
  }
 
797
  y--;
 
798
}
 
799
 
 
800
static void
 
801
mixer_update_cbars (void)
 
802
{
 
803
  static int o_x = 0;
 
804
  static int o_y = 0;
 
805
  int i, x, y;
 
806
  
 
807
  
 
808
  if (!mixer_cbar_get_pos (mixer_focus_elem, &x, &y))
 
809
    {
 
810
      if (mixer_focus_elem < mixer_first_vis_elem)
 
811
        mixer_first_vis_elem = mixer_focus_elem;
 
812
      else if (mixer_focus_elem >= mixer_first_vis_elem + mixer_n_vis_elems)
 
813
        mixer_first_vis_elem = mixer_focus_elem - mixer_n_vis_elems + 1;
 
814
      mixer_cbar_get_pos (mixer_focus_elem, &x, &y);
 
815
    }
 
816
  if (mixer_first_vis_elem + mixer_n_vis_elems >= mixer_n_elems) {
 
817
     mixer_first_vis_elem = mixer_n_elems - mixer_n_vis_elems;
 
818
     if (mixer_first_vis_elem < 0)
 
819
       mixer_first_vis_elem = 0;
 
820
     mixer_cbar_get_pos (mixer_focus_elem, &x, &y);
 
821
  }
 
822
  mixer_write_cbar(mixer_focus_elem);
 
823
  for (i = 0; i < mixer_n_vis_elems; i++) {
 
824
    if (i + mixer_first_vis_elem >= mixer_n_elems)
 
825
      continue;
 
826
    mixer_update_cbar (i + mixer_first_vis_elem);
 
827
  }
 
828
  
 
829
  /* draw focused cbar
 
830
   */
 
831
  if (mixer_have_old_focus)
 
832
    {
 
833
      mixer_dc (DC_BACK);
 
834
      mvaddstr (o_y, o_x, " ");
 
835
      mvaddstr (o_y, o_x + 9, " ");
 
836
    }
 
837
  o_x = x - 1;
 
838
  o_y = y;
 
839
  mixer_dc (DC_FOCUS);
 
840
  mvaddstr (o_y, o_x, "<");
 
841
  mvaddstr (o_y, o_x + 9, ">");
 
842
  mixer_have_old_focus = 1;
 
843
}
 
844
 
 
845
static void
 
846
mixer_draw_frame (void)
 
847
{
 
848
  char string[128];
 
849
  int i;
 
850
  int max_len;
 
851
  
 
852
  mixer_dc (DC_FRAME);
 
853
  
 
854
  /* card name
 
855
   */
 
856
  mixer_dc (DC_PROMPT);
 
857
  mvaddstr (1, 2, "Card: ");
 
858
  mixer_dc (DC_TEXT);
 
859
  sprintf (string, "%s", mixer_card_name);
 
860
  max_len = mixer_max_x - 2 - 6 - 2;
 
861
  if (strlen (string) > max_len)
 
862
    string[max_len] = 0;
 
863
  addstr (string);
 
864
  
 
865
  /* device name
 
866
   */
 
867
  mixer_dc (DC_PROMPT);
 
868
  mvaddstr (2, 2, "Chip: ");
 
869
  mixer_dc (DC_TEXT);
 
870
  sprintf (string, "%s", mixer_device_name);
 
871
  max_len = mixer_max_x - 2 - 6 - 2;
 
872
  if (strlen (string) > max_len)
 
873
    string[max_len] = 0;
 
874
  addstr (string);
 
875
 
 
876
  /* lines
 
877
   */
 
878
  mixer_dc (DC_PROMPT);
 
879
  for (i = 1; i < mixer_max_y - 1; i++)
 
880
    {
 
881
      mvaddch (i, 0, ACS_VLINE);
 
882
      mvaddch (i, mixer_max_x - 1, ACS_VLINE);
 
883
    }
 
884
  for (i = 1; i < mixer_max_x - 1; i++)
 
885
    {
 
886
      mvaddch (0, i, ACS_HLINE);
 
887
      mvaddch (mixer_max_y - 1, i, ACS_HLINE);
 
888
    }
 
889
  
 
890
  /* corners
 
891
   */
 
892
  mixer_dc (DC_PROMPT);
 
893
  mvaddch (0, 0, ACS_ULCORNER);
 
894
  mvaddch (0, mixer_max_x - 1, ACS_URCORNER);
 
895
  mvaddch (mixer_max_y - 1, 0, ACS_LLCORNER);
 
896
  if (!mixer_no_lrcorner)
 
897
    mvaddch (mixer_max_y - 1, mixer_max_x - 1, ACS_LRCORNER);
 
898
  else
 
899
    {
 
900
      mvaddch (mixer_max_y - 2, mixer_max_x - 1, ACS_LRCORNER);
 
901
      mvaddch (mixer_max_y - 2, mixer_max_x - 2, ACS_ULCORNER);
 
902
      mvaddch (mixer_max_y - 1, mixer_max_x - 2, ACS_LRCORNER);
 
903
    }
 
904
 
 
905
  /* program title
 
906
   */
 
907
  sprintf (string, "%s %s", PRGNAME_UPPER, VERSION);
 
908
  max_len = strlen (string);
 
909
  if (mixer_max_x >= max_len + 4)
 
910
    {
 
911
      mixer_dc (DC_PROMPT);
 
912
      mvaddch (0, mixer_max_x / 2 - max_len / 2 - 1, '[');
 
913
      mvaddch (0, mixer_max_x / 2 - max_len / 2 + max_len, ']');
 
914
    }
 
915
  if (mixer_max_x >= max_len + 2)
 
916
    {
 
917
      mixer_dc (DC_TEXT);
 
918
      mvaddstr (0, mixer_max_x / 2 - max_len / 2, string);
 
919
    }
 
920
}
 
921
 
 
922
static char*
 
923
mixer_offset_text (char **t,
 
924
                   int    col,
 
925
                   int   *length)
 
926
{
 
927
  char *p = *t;
 
928
  char *r;
 
929
 
 
930
  while (*p && *p != '\n' && col--)
 
931
    p++;
 
932
  if (*p == '\n' || !*p)
 
933
    {
 
934
      if (*p == '\n')
 
935
        p++;
 
936
      *length = 0;
 
937
      *t = p;
 
938
      return p;
 
939
    }
 
940
 
 
941
  r = p;
 
942
  while (*r && *r != '\n' && (*length)--)
 
943
    r++;
 
944
 
 
945
  *length = r - p;
 
946
  while (*r && *r != '\n')
 
947
    r++;
 
948
  if (*r == '\n')
 
949
    r++;
 
950
  *t = r;
 
951
 
 
952
  return p;
 
953
}
 
954
 
 
955
static void
 
956
mixer_show_text (char *title,
 
957
                 char *text,
 
958
                 int  *xoffs,
 
959
                 int  *yoffs)
 
960
{
 
961
  int tlines = 0, tcols = 0;
 
962
  float hscroll, vscroll;
 
963
  float hoffs, voffs;
 
964
  char *p, *text_offs = text;
 
965
  int x1, x2, y1, y2;
 
966
  int i, n, l, r, block, stipple;
 
967
 
 
968
  /* coords
 
969
   */
 
970
  x1 = 2;
 
971
  x2 = mixer_max_x - 3;
 
972
  y1 = 4;
 
973
  y2 = mixer_max_y - 2;
 
974
 
 
975
  if ((y2 - y1) < 3 || (x2 - x1) < 3)
 
976
    return;
 
977
 
 
978
  /* text dimensions
 
979
   */
 
980
  l = 0;
 
981
  for (p = text; *p; p++)
 
982
    if (*p == '\n')
 
983
      {
 
984
        tlines++;
 
985
        tcols = MAX (l, tcols);
 
986
        l = 0;
 
987
      }
 
988
    else
 
989
      l++;
 
990
  tcols = MAX (l, tcols);
 
991
  if (p > text && *(p - 1) != '\n')
 
992
    tlines++;
 
993
 
 
994
  /* scroll areas / offsets
 
995
   */
 
996
  l = x2 - x1 - 2;
 
997
  if (l > tcols)
 
998
    {
 
999
      x1 += (l - tcols) / 2;
 
1000
      x2 = x1 + tcols + 1;
 
1001
    }
 
1002
  if (mixer_hscroll_delta)
 
1003
    {
 
1004
      *xoffs += mixer_hscroll_delta;
 
1005
      mixer_hscroll_delta = 0;
 
1006
      if (*xoffs < 0)
 
1007
        {
 
1008
          *xoffs = 0;
 
1009
          beep ();
 
1010
        }
 
1011
      else if (*xoffs > tcols - l - 1)
 
1012
        {
 
1013
          *xoffs = MAX (0, tcols - l - 1);
 
1014
          beep ();
 
1015
        }
 
1016
    }
 
1017
  if (tcols - l - 1 <= 0)
 
1018
    {
 
1019
      hscroll = 1;
 
1020
      hoffs = 0;
 
1021
    }
 
1022
  else
 
1023
    {
 
1024
      hscroll = ((float) l) / tcols;
 
1025
      hoffs = ((float) *xoffs) / (tcols - l - 1);
 
1026
    }
 
1027
 
 
1028
  l = y2 - y1 - 2;
 
1029
  if (l > tlines)
 
1030
    {
 
1031
      y1 += (l - tlines) / 2;
 
1032
      y2 = y1 + tlines + 1;
 
1033
    }
 
1034
  if (mixer_vscroll_delta)
 
1035
    {
 
1036
      *yoffs += mixer_vscroll_delta;
 
1037
      mixer_vscroll_delta = 0;
 
1038
      if (*yoffs < 0)
 
1039
        {
 
1040
          *yoffs = 0;
 
1041
          beep ();
 
1042
        }
 
1043
      else if (*yoffs > tlines - l - 1)
 
1044
        {
 
1045
          *yoffs = MAX (0, tlines - l - 1);
 
1046
          beep ();
 
1047
        }
 
1048
    }
 
1049
  if (tlines - l - 1 <= 0)
 
1050
    {
 
1051
      voffs = 0;
 
1052
      vscroll = 1;
 
1053
    }
 
1054
  else
 
1055
    {
 
1056
      vscroll = ((float) l) / tlines;
 
1057
      voffs = ((float) *yoffs) / (tlines - l - 1);
 
1058
    }
 
1059
 
 
1060
  /* colors
 
1061
   */
 
1062
  mixer_dc (DC_ANY_4);
 
1063
 
 
1064
  /* corners
 
1065
   */
 
1066
  mvaddch (y2, x2, ACS_LRCORNER);
 
1067
  mvaddch (y2, x1, ACS_LLCORNER);
 
1068
  mvaddch (y1, x1, ACS_ULCORNER);
 
1069
  mvaddch (y1, x2, ACS_URCORNER);
 
1070
 
 
1071
  /* left + upper border
 
1072
   */
 
1073
  for (i = y1 + 1; i < y2; i++)
 
1074
    mvaddch (i, x1, ACS_VLINE);
 
1075
  for (i = x1 + 1; i < x2; i++)
 
1076
    mvaddch (y1, i, ACS_HLINE);
 
1077
  if (title)
 
1078
    {
 
1079
      l = strlen (title);
 
1080
      if (l <= x2 - x1 - 3)
 
1081
        {
 
1082
          mvaddch (y1, x1 + 1 + (x2 - x1 - l) / 2 - 1, '[');
 
1083
          mvaddch (y1, x1 + 1 + (x2 - x1 - l) / 2 + l, ']');
 
1084
        }
 
1085
      if (l <= x2 - x1 - 1)
 
1086
        {
 
1087
          mixer_dc (DC_ANY_3);
 
1088
          mvaddstr (y1, x1 + 1 + (x2 - x1 - l) / 2, title);
 
1089
        }
 
1090
      mixer_dc (DC_ANY_4);
 
1091
    }
 
1092
 
 
1093
  stipple = ACS_CKBOARD;
 
1094
  block = ACS_BLOCK;
 
1095
  if (block == '#' && ACS_BOARD == '#')
 
1096
    {
 
1097
      block = stipple;
 
1098
      stipple = ACS_BLOCK;
 
1099
    }
 
1100
 
 
1101
  /* lower scroll border
 
1102
   */
 
1103
  l = x2 - x1 - 1;
 
1104
  n = hscroll * l;
 
1105
  r = (hoffs + 1.0 / (2 * (l - n - 1))) * (l - n - 1);
 
1106
  for (i = 0; i < l; i++)
 
1107
    mvaddch (y2, i + x1 + 1, hscroll >= 1 ? ACS_HLINE :
 
1108
             i >= r && i <= r + n ? block : stipple);
 
1109
 
 
1110
  /* right scroll border
 
1111
   */
 
1112
  l = y2 - y1 - 1;
 
1113
  n = vscroll * l;
 
1114
  r = (voffs + 1.0 / (2 * (l - n - 1))) * (l - n - 1);
 
1115
  for (i = 0; i < l; i++)
 
1116
    mvaddch (i + y1 + 1, x2, vscroll >= 1 ? ACS_VLINE :
 
1117
             i >= r && i <= r + n ? block : stipple);
 
1118
 
 
1119
  /* show text
 
1120
   */
 
1121
  x1++; y1++;
 
1122
  for (i = 0; i < *yoffs; i++)
 
1123
    {
 
1124
      l = 0;
 
1125
      mixer_offset_text (&text_offs, 0, &l);
 
1126
    }
 
1127
  for (i = y1; i < y2; i++)
 
1128
    {
 
1129
      l = x2 - x1;
 
1130
      p = mixer_offset_text (&text_offs, *xoffs, &l);
 
1131
      n = x1;
 
1132
      while (l--)
 
1133
        mvaddch (i, n++, *p++);
 
1134
      while (n < x2)
 
1135
        mvaddch (i, n++, ' ');
 
1136
    }
 
1137
}
 
1138
 
 
1139
struct vbuffer
 
1140
{
 
1141
  char *buffer;
 
1142
  int size;
 
1143
  int len;
 
1144
};
 
1145
 
 
1146
static void
 
1147
vbuffer_kill (struct vbuffer *vbuf)
 
1148
{
 
1149
  if (vbuf->size)
 
1150
    free (vbuf->buffer);
 
1151
  vbuf->buffer = NULL;
 
1152
  vbuf->size = 0;
 
1153
  vbuf->len = 0;
 
1154
}
 
1155
 
 
1156
#define vbuffer_append_string(vb,str)   vbuffer_append (vb, str, strlen (str))
 
1157
static void
 
1158
vbuffer_append (struct vbuffer *vbuf,
 
1159
                char           *text,
 
1160
                int             len)
 
1161
{
 
1162
  if (vbuf->size - vbuf->len <= len)
 
1163
    {
 
1164
      vbuf->size += len + 1;
 
1165
      vbuf->buffer = realloc (vbuf->buffer, vbuf->size);
 
1166
    }
 
1167
  memcpy (vbuf->buffer + vbuf->len, text, len);
 
1168
  vbuf->len += len;
 
1169
  vbuf->buffer[vbuf->len] = 0;
 
1170
}
 
1171
 
 
1172
static int
 
1173
vbuffer_append_file (struct vbuffer *vbuf,
 
1174
                     char           *name)
 
1175
{
 
1176
  int fd;
 
1177
 
 
1178
  fd = open (name, O_RDONLY);
 
1179
  if (fd >= 0)
 
1180
    {
 
1181
      char buffer[1025];
 
1182
      int l;
 
1183
 
 
1184
      do
 
1185
        {
 
1186
          l = read (fd, buffer, 1024);
 
1187
          
 
1188
          vbuffer_append (vbuf, buffer, MAX (0, l));
 
1189
        }
 
1190
      while (l > 0 || (l < 0 && (errno == EAGAIN || errno == EINTR)));
 
1191
 
 
1192
      close (fd);
 
1193
 
 
1194
      return 0;
 
1195
    }
 
1196
  else
 
1197
    return 1;
 
1198
}
 
1199
 
 
1200
static void
 
1201
mixer_show_procinfo (void)
 
1202
{
 
1203
  struct vbuffer vbuf = { NULL, 0, 0 };
 
1204
 
 
1205
  vbuffer_append_string (&vbuf, "\n");
 
1206
  vbuffer_append_string (&vbuf, "/proc/asound/version:\n");
 
1207
  vbuffer_append_string (&vbuf, "====================\n");
 
1208
  if (vbuffer_append_file (&vbuf, "/proc/asound/version"))
 
1209
    {
 
1210
      vbuffer_kill (&vbuf);
 
1211
      mixer_procinfo_xoffs = mixer_procinfo_yoffs = 0;
 
1212
      mixer_show_text ("/proc",
 
1213
                       " No /proc information available. ",
 
1214
                       &mixer_procinfo_xoffs, &mixer_procinfo_yoffs);
 
1215
      return;
 
1216
    }
 
1217
  else
 
1218
    vbuffer_append_file (&vbuf, "/proc/asound/meminfo");
 
1219
 
 
1220
  vbuffer_append_string (&vbuf, "\n");
 
1221
  vbuffer_append_string (&vbuf, "/proc/asound/cards:\n");
 
1222
  vbuffer_append_string (&vbuf, "===================\n");
 
1223
  if (vbuffer_append_file (&vbuf, "/proc/asound/cards"))
 
1224
    vbuffer_append_string (&vbuf, "No information available.\n");
 
1225
 
 
1226
  vbuffer_append_string (&vbuf, "\n");
 
1227
  vbuffer_append_string (&vbuf, "/proc/asound/devices:\n");
 
1228
  vbuffer_append_string (&vbuf, "=====================\n");
 
1229
  if (vbuffer_append_file (&vbuf, "/proc/asound/devices"))
 
1230
    vbuffer_append_string (&vbuf, "No information available.\n");
 
1231
 
 
1232
  vbuffer_append_string (&vbuf, "\n");
 
1233
  vbuffer_append_string (&vbuf, "/proc/asound/oss-devices:\n");
 
1234
  vbuffer_append_string (&vbuf, "=========================\n");
 
1235
  if (vbuffer_append_file (&vbuf, "/proc/asound/oss-devices"))
 
1236
    vbuffer_append_string (&vbuf, "No information available.\n");
 
1237
 
 
1238
  vbuffer_append_string (&vbuf, "\n");
 
1239
  vbuffer_append_string (&vbuf, "/proc/asound/timers:\n");
 
1240
  vbuffer_append_string (&vbuf, "====================\n");
 
1241
  if (vbuffer_append_file (&vbuf, "/proc/asound/timers"))
 
1242
    vbuffer_append_string (&vbuf, "No information available.\n");
 
1243
 
 
1244
  vbuffer_append_string (&vbuf, "\n");
 
1245
  vbuffer_append_string (&vbuf, "/proc/asound/pcm:\n");
 
1246
  vbuffer_append_string (&vbuf, "=================\n");
 
1247
  if (vbuffer_append_file (&vbuf, "/proc/asound/pcm"))
 
1248
    vbuffer_append_string (&vbuf, "No information available.\n");
 
1249
 
 
1250
  mixer_show_text ("/proc", vbuf.buffer,
 
1251
                   &mixer_procinfo_xoffs, &mixer_procinfo_yoffs);
 
1252
  vbuffer_kill (&vbuf);
 
1253
}
 
1254
 
 
1255
static int
 
1256
mixer_event (snd_mixer_t *mixer, unsigned int mask, snd_mixer_elem_t *elem)
 
1257
{
 
1258
  mixer_changed_state = 1;
 
1259
  return 0;
 
1260
}
 
1261
 
 
1262
static void
 
1263
mixer_init (void)
 
1264
{
 
1265
  snd_ctl_card_info_t *hw_info;
 
1266
  snd_ctl_t *ctl_handle;
 
1267
  int err;
 
1268
  snd_ctl_card_info_alloca(&hw_info);
 
1269
  
 
1270
  if ((err = snd_ctl_open (&ctl_handle, card_id, 0)) < 0)
 
1271
    mixer_abort (ERR_OPEN, "snd_ctl_open", err);
 
1272
  if ((err = snd_ctl_card_info (ctl_handle, hw_info)) < 0)
 
1273
    mixer_abort (ERR_FCN, "snd_ctl_card_info", err);
 
1274
  snd_ctl_close (ctl_handle);
 
1275
  /* open mixer device
 
1276
   */
 
1277
  if ((err = snd_mixer_open (&mixer_handle, 0)) < 0)
 
1278
    mixer_abort (ERR_FCN, "snd_mixer_open", err);
 
1279
  if ((err = snd_mixer_attach (mixer_handle, card_id)) < 0)
 
1280
    mixer_abort (ERR_FCN, "snd_mixer_attach", err);
 
1281
  if ((err = snd_mixer_selem_register (mixer_handle, NULL, NULL)) < 0)
 
1282
    mixer_abort (ERR_FCN, "snd_mixer_selem_register", err);
 
1283
  snd_mixer_set_callback (mixer_handle, mixer_event);
 
1284
  if ((err = snd_mixer_load (mixer_handle)) < 0)
 
1285
    mixer_abort (ERR_FCN, "snd_mixer_load", err);
 
1286
  
 
1287
  /* setup global variables
 
1288
   */
 
1289
  strcpy(mixer_card_name, snd_ctl_card_info_get_name(hw_info));
 
1290
  strcpy(mixer_device_name, snd_ctl_card_info_get_mixername(hw_info));
 
1291
}
 
1292
 
 
1293
static void
 
1294
mixer_reinit (void)
 
1295
{
 
1296
  snd_mixer_elem_t *elem;
 
1297
  int idx, elem_index, i, j, selem_count;
 
1298
  snd_mixer_selem_id_t *sid;
 
1299
  snd_mixer_selem_id_t *focus_gid;
 
1300
  int focus_type = -1;
 
1301
  snd_mixer_selem_id_alloca(&focus_gid);
 
1302
  
 
1303
  if (!mixer_changed_state)
 
1304
    return;
 
1305
  if (mixer_sid) {
 
1306
    snd_mixer_selem_id_copy(focus_gid, (snd_mixer_selem_id_t *)(((char *)mixer_sid) + snd_mixer_selem_id_sizeof() * mixer_grpidx[mixer_focus_elem]));
 
1307
    focus_type = mixer_type[mixer_focus_elem];
 
1308
  }
 
1309
__again:
 
1310
  mixer_changed_state = 0;
 
1311
  if (mixer_sid != NULL)
 
1312
    free(mixer_sid);
 
1313
  selem_count = snd_mixer_get_count(mixer_handle);
 
1314
  mixer_sid = malloc(snd_mixer_selem_id_sizeof() * selem_count);
 
1315
  if (mixer_sid == NULL)
 
1316
    mixer_abort (ERR_FCN, "malloc", 0);
 
1317
  
 
1318
  mixer_n_selems = 0;
 
1319
  for (elem = snd_mixer_first_elem(mixer_handle); elem; elem = snd_mixer_elem_next(elem)) {
 
1320
    sid = (snd_mixer_selem_id_t *)(((char *)mixer_sid) + snd_mixer_selem_id_sizeof() * mixer_n_selems);
 
1321
    if (mixer_changed_state)
 
1322
      goto __again;
 
1323
    if (!snd_mixer_selem_is_active(elem))
 
1324
      continue;
 
1325
    snd_mixer_selem_get_id(elem, sid);
 
1326
    mixer_n_selems++;
 
1327
  }
 
1328
 
 
1329
  mixer_n_elems = 0;
 
1330
  for (idx = 0; idx < mixer_n_selems; idx++) {
 
1331
    sid = (snd_mixer_selem_id_t *)(((char *)mixer_sid) + snd_mixer_selem_id_sizeof() * idx);
 
1332
    if (mixer_changed_state)
 
1333
      goto __again;
 
1334
    elem = snd_mixer_find_selem(mixer_handle, sid);
 
1335
    if (elem == NULL)
 
1336
      CHECK_ABORT (ERR_FCN, __FUNCTION__ ": snd_mixer_find_selem()", -EINVAL);
 
1337
    for (i = 0; i < MIXER_ELEM_END; i++) {
 
1338
      int ok;
 
1339
      for (j = ok = 0; j < 2; j++) {
 
1340
        if (mixer_changed_state)
 
1341
          goto __again;
 
1342
        if (snd_mixer_selem_has_playback_channel(elem, mixer_elem_chn[i][j]))
 
1343
          ok++;
 
1344
      }
 
1345
      if (ok)
 
1346
        mixer_n_elems++;
 
1347
    }
 
1348
  }
 
1349
 
 
1350
  if (mixer_type)
 
1351
    free(mixer_type);
 
1352
  mixer_type = (int *)malloc(sizeof(int) * mixer_n_elems);
 
1353
  if (mixer_type == NULL)
 
1354
    mixer_abort(ERR_FCN, "malloc", 0);
 
1355
  if (mixer_grpidx)
 
1356
    free(mixer_grpidx);
 
1357
  mixer_grpidx = (int *)malloc(sizeof(int) * mixer_n_elems);
 
1358
  if (mixer_grpidx == NULL)
 
1359
    mixer_abort(ERR_FCN, "malloc", 0);
 
1360
  elem_index = 0;
 
1361
  for (idx = 0; idx < mixer_n_selems; idx++) {
 
1362
    sid = (snd_mixer_selem_id_t *)(((char *)mixer_sid) + snd_mixer_selem_id_sizeof() * idx);
 
1363
    if (mixer_changed_state)
 
1364
      goto __again;
 
1365
    elem = snd_mixer_find_selem(mixer_handle, sid);
 
1366
    if (elem == NULL)
 
1367
      CHECK_ABORT (ERR_FCN, __FUNCTION__ ": snd_mixer_find_selem()", -EINVAL);
 
1368
    for (i = 0; i < MIXER_ELEM_END; i++) {
 
1369
      int ok;
 
1370
      for (j = ok = 0; j < 2; j++) {
 
1371
        if (mixer_changed_state)
 
1372
          goto __again;
 
1373
        if (snd_mixer_selem_has_playback_channel(elem, mixer_elem_chn[i][j]))
 
1374
          ok++;
 
1375
      }
 
1376
      if (ok) {
 
1377
        mixer_grpidx[elem_index] = idx;
 
1378
        mixer_type[elem_index] = i;
 
1379
        elem_index++;
 
1380
        if (elem_index >= mixer_n_elems)
 
1381
          break;
 
1382
      }
 
1383
    }
 
1384
  }
 
1385
 
 
1386
  mixer_focus_elem = 0;
 
1387
  if (focus_type >= 0) {
 
1388
    for (elem_index = 0; elem_index < mixer_n_elems; elem_index++) {
 
1389
      sid = (snd_mixer_selem_id_t *)(((char *)mixer_sid) + snd_mixer_selem_id_sizeof() * mixer_grpidx[elem_index]);
 
1390
      if (!strcmp(snd_mixer_selem_id_get_name(focus_gid),
 
1391
                  snd_mixer_selem_id_get_name(sid)) &&
 
1392
          snd_mixer_selem_id_get_index(focus_gid) ==
 
1393
          snd_mixer_selem_id_get_index(sid) &&
 
1394
          mixer_type[elem_index] == focus_type) {
 
1395
        mixer_focus_elem = elem_index;
 
1396
        break;
 
1397
      }
 
1398
    }
 
1399
  }
 
1400
 
 
1401
  if (mixer_changed_state)
 
1402
    goto __again;
 
1403
}
 
1404
 
 
1405
static void
 
1406
mixer_init_window (void)
 
1407
{
 
1408
  /* initialize ncurses
 
1409
   */
 
1410
  mixer_window = initscr ();
 
1411
 
 
1412
  mixer_no_lrcorner = tigetflag ("xenl") != 1 && tigetflag ("am") != 1;
 
1413
 
 
1414
  if (mixer_do_color)
 
1415
    mixer_do_color = has_colors ();
 
1416
  mixer_init_draw_contexts ();
 
1417
 
 
1418
  /* react on key presses
 
1419
   */
 
1420
  cbreak ();
 
1421
  noecho ();
 
1422
  leaveok (mixer_window, TRUE);
 
1423
  keypad (mixer_window, TRUE);
 
1424
  GETCH_BLOCK (1);
 
1425
 
 
1426
  /* init mixer screen
 
1427
   */
 
1428
  getmaxyx (mixer_window, mixer_max_y, mixer_max_x);
 
1429
  if (mixer_minimize)
 
1430
    {
 
1431
      mixer_max_x = MIXER_MIN_X;
 
1432
      mixer_max_y = MIXER_MIN_Y;
 
1433
    }
 
1434
  mixer_ofs_x = 2 /* extra begin padding: */ + 1;
 
1435
 
 
1436
  /* required allocations */
 
1437
  mixer_n_vis_elems = (mixer_max_x - mixer_ofs_x * 2 + 1) / 9;
 
1438
  mixer_n_vis_elems = CLAMP (mixer_n_vis_elems, 1, mixer_n_elems);
 
1439
  mixer_extra_space = mixer_max_x - mixer_ofs_x * 2 + 1 - mixer_n_vis_elems * 9;
 
1440
  mixer_extra_space = MAX (0, mixer_extra_space / (mixer_n_vis_elems + 1));
 
1441
  if (MIXER_TEXT_Y + 10 < mixer_max_y)
 
1442
    mixer_cbar_height = 10 + MAX (0, mixer_max_y - MIXER_TEXT_Y - 10 ) / 2;
 
1443
  else
 
1444
    mixer_cbar_height = MAX (1, mixer_max_y - MIXER_TEXT_Y);
 
1445
 
 
1446
  mixer_clear (TRUE);
 
1447
}
 
1448
 
 
1449
static void
 
1450
mixer_resize (void)
 
1451
{
 
1452
  struct winsize winsz = { 0, };
 
1453
 
 
1454
  mixer_needs_resize = 0;
 
1455
  
 
1456
  if (ioctl (fileno (stdout), TIOCGWINSZ, &winsz) >= 0 &&
 
1457
      winsz.ws_row && winsz.ws_col)
 
1458
    {
 
1459
      keypad (mixer_window, FALSE);
 
1460
      leaveok (mixer_window, FALSE);
 
1461
 
 
1462
      endwin ();
 
1463
      
 
1464
      mixer_max_x = MAX (2, winsz.ws_col);
 
1465
      mixer_max_y = MAX (2, winsz.ws_row);
 
1466
      
 
1467
      /* humpf, i don't get it, if only the number of rows change,
 
1468
       * ncurses will segfault shortly after (could trigger that with mc as well).
 
1469
       */
 
1470
      resizeterm (mixer_max_y + 1, mixer_max_x + 1);
 
1471
      resizeterm (mixer_max_y, mixer_max_x);
 
1472
      
 
1473
      mixer_init_window ();
 
1474
      
 
1475
      if (mixer_max_x < MIXER_MIN_X ||
 
1476
          mixer_max_y < MIXER_MIN_Y)
 
1477
        beep (); // mixer_abort (ERR_WINSIZE, "");
 
1478
 
 
1479
      mixer_have_old_focus = 0;
 
1480
    }
 
1481
}
 
1482
 
 
1483
static void
 
1484
mixer_set_delta(int delta)
 
1485
{
 
1486
  int grp;
 
1487
  
 
1488
  for (grp = 0; grp < 2; grp++)
 
1489
    mixer_volume_delta[grp] = delta;
 
1490
}
 
1491
 
 
1492
static void
 
1493
mixer_add_delta(int delta)
 
1494
{
 
1495
  int grp;
 
1496
  
 
1497
  for (grp = 0; grp < 2; grp++)
 
1498
    mixer_volume_delta[grp] += delta;
 
1499
}
 
1500
 
 
1501
static int
 
1502
mixer_iteration (void)
 
1503
{
 
1504
  int idx, count, err;
 
1505
  struct pollfd *fds;
 
1506
  int finished = 0;
 
1507
  int key = 0;
 
1508
  int old_view;
 
1509
  unsigned short revents;
 
1510
  
 
1511
  /* setup for select on stdin and the mixer fd */
 
1512
  if ((count = snd_mixer_poll_descriptors_count(mixer_handle)) < 0)
 
1513
    mixer_abort (ERR_FCN, "snd_mixer_poll_descriptors_count", count);
 
1514
  fds = calloc(count + 1, sizeof(struct pollfd));
 
1515
  if (fds == NULL)
 
1516
    mixer_abort (ERR_FCN, "malloc", 0);
 
1517
  fds->fd = fileno(stdin);
 
1518
  fds->events = POLLIN;
 
1519
  if ((err = snd_mixer_poll_descriptors(mixer_handle, fds + 1, count)) < 0)
 
1520
    mixer_abort (ERR_FCN, "snd_mixer_poll_descriptors", err);
 
1521
  if (err != count)
 
1522
    mixer_abort (ERR_FCN, "snd_mixer_poll_descriptors (err != count)", 0);
 
1523
  
 
1524
  finished = poll(fds, count + 1, -1);
 
1525
 
 
1526
  /* don't abort on handled signals */
 
1527
  if (finished < 0 && errno == EINTR)
 
1528
    finished = 0;
 
1529
  if (mixer_needs_resize)
 
1530
    mixer_resize ();
 
1531
 
 
1532
  if (finished > 0) {
 
1533
    if (fds->revents & POLLIN) {
 
1534
      key = getch ();
 
1535
      finished--;
 
1536
    }
 
1537
  } else {
 
1538
    key = 0;
 
1539
  }
 
1540
  
 
1541
  if (finished > 0) {
 
1542
    if (snd_mixer_poll_descriptors_revents(mixer_handle, fds + 1, count, &revents) >= 0) {
 
1543
      if (revents & POLLIN)
 
1544
        snd_mixer_handle_events(mixer_handle);
 
1545
    }
 
1546
  }
 
1547
 
 
1548
  finished = 0;
 
1549
  free(fds);
 
1550
 
 
1551
  old_view = mixer_view;
 
1552
  
 
1553
  /* feature Escape prefixing for some keys */
 
1554
  if (key == 27)
 
1555
    {
 
1556
      GETCH_BLOCK (0);
 
1557
      key = getch ();
 
1558
      GETCH_BLOCK (1);
 
1559
      switch (key)
 
1560
        {
 
1561
        case 9: /* Tab */
 
1562
          key = KEY_BTAB;
 
1563
          break;
 
1564
        default:
 
1565
          key = 27;
 
1566
          break;
 
1567
        }
 
1568
    }
 
1569
  
 
1570
  /* general keys */
 
1571
  switch (key)
 
1572
    {
 
1573
    case 0:
 
1574
      /* ignore */
 
1575
      break;
 
1576
    case 27:    /* Escape */
 
1577
      finished = 1;
 
1578
      key = 0;
 
1579
      break;
 
1580
    case 13:    /* Return */
 
1581
    case 10:    /* NewLine */
 
1582
      if (mixer_view == VIEW_CHANNELS)
 
1583
        mixer_clear (FALSE);
 
1584
      mixer_view = VIEW_CHANNELS;
 
1585
      key = 0;
 
1586
      break;
 
1587
    case 'h':
 
1588
    case 'H':
 
1589
    case KEY_F (1):
 
1590
      mixer_view = VIEW_HELP;
 
1591
      key = 0;
 
1592
      break;
 
1593
    case '/':
 
1594
    case KEY_F (2):
 
1595
      mixer_view = VIEW_PROCINFO;
 
1596
      key = 0;
 
1597
      break;
 
1598
    case '\014':
 
1599
    case 'L':
 
1600
    case 'l':
 
1601
      mixer_clear (TRUE);
 
1602
      break;
 
1603
    }
 
1604
  
 
1605
  if (key && (mixer_view == VIEW_HELP ||
 
1606
              mixer_view == VIEW_PROCINFO))
 
1607
    switch (key)
 
1608
      {
 
1609
      case 9:           /* Tab */
 
1610
        mixer_hscroll_delta += 8;
 
1611
        break;
 
1612
      case KEY_BTAB:
 
1613
        mixer_hscroll_delta -= 8;
 
1614
        break;
 
1615
      case KEY_A1:
 
1616
        mixer_hscroll_delta -= 1;
 
1617
        mixer_vscroll_delta -= 1;
 
1618
        break;
 
1619
      case KEY_A3:
 
1620
        mixer_hscroll_delta += 1;
 
1621
        mixer_vscroll_delta -= 1;
 
1622
        break;
 
1623
      case KEY_C1:
 
1624
        mixer_hscroll_delta -= 1;
 
1625
        mixer_vscroll_delta += 1;
 
1626
        break;
 
1627
      case KEY_C3:
 
1628
        mixer_hscroll_delta += 1;
 
1629
        mixer_vscroll_delta += 1;
 
1630
        break;
 
1631
      case KEY_RIGHT:
 
1632
      case 'n':
 
1633
        mixer_hscroll_delta += 1;
 
1634
        break;
 
1635
      case KEY_LEFT:
 
1636
      case 'p':
 
1637
        mixer_hscroll_delta -= 1;
 
1638
        break;
 
1639
      case KEY_UP:
 
1640
      case 'w':
 
1641
      case 'W':
 
1642
        mixer_vscroll_delta -= 1;
 
1643
        break;
 
1644
      case KEY_DOWN:
 
1645
      case 'x':
 
1646
      case 'X':
 
1647
        mixer_vscroll_delta += 1;
 
1648
        break;
 
1649
      case KEY_PPAGE:
 
1650
      case 'B':
 
1651
      case 'b':
 
1652
        mixer_vscroll_delta -= (mixer_max_y - 5) / 2;
 
1653
        break;
 
1654
      case KEY_NPAGE:
 
1655
      case ' ':
 
1656
        mixer_vscroll_delta += (mixer_max_y - 5) / 2;
 
1657
        break;
 
1658
      case KEY_BEG:
 
1659
      case KEY_HOME:
 
1660
        mixer_hscroll_delta -= 0xffffff;
 
1661
        break;
 
1662
      case KEY_LL:
 
1663
      case KEY_END:
 
1664
        mixer_hscroll_delta += 0xffffff;
 
1665
        break;
 
1666
      }
 
1667
  
 
1668
  if (key && mixer_view == VIEW_CHANNELS)
 
1669
    switch (key)
 
1670
      {
 
1671
      case KEY_RIGHT:
 
1672
      case 'n':
 
1673
        mixer_focus_elem += 1;
 
1674
        break;
 
1675
      case KEY_LEFT:
 
1676
      case 'p':
 
1677
        mixer_focus_elem -= 1;
 
1678
        break;
 
1679
      case KEY_PPAGE:
 
1680
        mixer_set_delta(5);
 
1681
        break;
 
1682
      case KEY_NPAGE:
 
1683
        mixer_set_delta(-5);
 
1684
        break;
 
1685
#if 0
 
1686
      case KEY_BEG:
 
1687
      case KEY_HOME:
 
1688
        mixer_set_delta(100);
 
1689
        break;
 
1690
#endif
 
1691
      case KEY_LL:
 
1692
      case KEY_END:
 
1693
        mixer_set_delta(-100);
 
1694
        break;
 
1695
      case '+':
 
1696
        mixer_set_delta(1);
 
1697
        break;
 
1698
      case '-':
 
1699
        mixer_set_delta(-1);
 
1700
        break;
 
1701
      case 'w':
 
1702
      case KEY_UP:
 
1703
        mixer_set_delta(1);
 
1704
      case 'W':
 
1705
        mixer_add_delta(1);
 
1706
        break;
 
1707
      case 'x':
 
1708
      case KEY_DOWN:
 
1709
        mixer_set_delta(-1);
 
1710
      case 'X':
 
1711
        mixer_add_delta(-1);
 
1712
        break;
 
1713
      case 'q':
 
1714
        mixer_volume_delta[MIXER_CHN_LEFT] = 1;
 
1715
      case 'Q':
 
1716
        mixer_volume_delta[MIXER_CHN_LEFT] += 1;
 
1717
        break;
 
1718
      case 'y':
 
1719
      case 'z':
 
1720
        mixer_volume_delta[MIXER_CHN_LEFT] = -1;
 
1721
      case 'Y':
 
1722
      case 'Z':
 
1723
        mixer_volume_delta[MIXER_CHN_LEFT] += -1;
 
1724
        break;
 
1725
      case 'e':
 
1726
        mixer_volume_delta[MIXER_CHN_RIGHT] = 1;
 
1727
      case 'E':
 
1728
        mixer_volume_delta[MIXER_CHN_RIGHT] += 1;
 
1729
        break;
 
1730
      case 'c':
 
1731
        mixer_volume_delta[MIXER_CHN_RIGHT] = -1;
 
1732
      case 'C':
 
1733
        mixer_volume_delta[MIXER_CHN_RIGHT] += -1;
 
1734
        break;
 
1735
      case 'm':
 
1736
      case 'M':
 
1737
        mixer_toggle_mute |= MIXER_MASK_STEREO;
 
1738
        break;
 
1739
      case 'b':
 
1740
      case 'B':
 
1741
      case '=':
 
1742
        mixer_balance_volumes = 1;
 
1743
        break;
 
1744
      case '<':
 
1745
      case ',':
 
1746
        mixer_toggle_mute |= MIXER_MASK_LEFT;
 
1747
        break;
 
1748
      case '>':
 
1749
      case '.':
 
1750
        mixer_toggle_mute |= MIXER_MASK_RIGHT;
 
1751
        break;
 
1752
      case ' ':
 
1753
        mixer_toggle_capture |= MIXER_MASK_STEREO;
 
1754
        break;
 
1755
      case KEY_IC:
 
1756
      case ';':
 
1757
        mixer_toggle_capture |= MIXER_MASK_LEFT;
 
1758
        break;
 
1759
      case '\'':
 
1760
      case KEY_DC:
 
1761
        mixer_toggle_capture |= MIXER_MASK_RIGHT;
 
1762
        break;
 
1763
      }
 
1764
  
 
1765
  if (old_view != mixer_view)
 
1766
    mixer_clear (FALSE);
 
1767
  
 
1768
  mixer_focus_elem = CLAMP (mixer_focus_elem, 0, mixer_n_elems - 1);
 
1769
  
 
1770
  return finished;
 
1771
}
 
1772
 
 
1773
static void
 
1774
mixer_winch (void)
 
1775
{
 
1776
  signal (SIGWINCH, (void*) mixer_winch);
 
1777
 
 
1778
  mixer_needs_resize++;
 
1779
}
 
1780
 
 
1781
static void
 
1782
mixer_signal_handler (int signal)
 
1783
{
 
1784
  if (signal != SIGSEGV)
 
1785
    mixer_abort (ERR_SIGNAL, sys_siglist[signal], 0);
 
1786
  else
 
1787
    {
 
1788
      fprintf (stderr, "\nSegmentation fault.\n");
 
1789
      _exit (11);
 
1790
    }
 
1791
}
 
1792
 
 
1793
int
 
1794
main (int    argc,
 
1795
      char **argv)
 
1796
{
 
1797
  int opt;
 
1798
  
 
1799
  /* parse args
 
1800
   */
 
1801
  do
 
1802
    {
 
1803
      opt = getopt (argc, argv, "c:D:shg");
 
1804
      switch (opt)
 
1805
        {
 
1806
        case '?':
 
1807
        case 'h':
 
1808
          fprintf (stderr, "%s %s\n", PRGNAME_UPPER, VERSION);
 
1809
          fprintf (stderr, "Usage: %s [-h] [-c <card: 0...7 or id>] [-D <mixer device>] [-g] [-s]\n", PRGNAME);
 
1810
          mixer_abort (ERR_NONE, "", 0);
 
1811
        case 'c':
 
1812
          {
 
1813
            int i = snd_card_get_index(optarg);
 
1814
            if (i < 0 || i > 31) {
 
1815
              fprintf (stderr, "wrong -c argument '%s'\n", optarg);
 
1816
              mixer_abort (ERR_NONE, "", 0);
 
1817
            }
 
1818
            sprintf(card_id, "hw:%i", i);
 
1819
          }
 
1820
          break;
 
1821
        case 'D':
 
1822
          strncpy(card_id, optarg, sizeof(card_id));
 
1823
          card_id[sizeof(card_id)-1] = '\0';
 
1824
          break;
 
1825
        case 'g':
 
1826
          mixer_do_color = !mixer_do_color;
 
1827
          break;
 
1828
        case 's':
 
1829
          mixer_minimize = 1;
 
1830
          break;
 
1831
        }
 
1832
    }
 
1833
  while (opt > 0);
 
1834
  
 
1835
  /* initialize mixer
 
1836
   */
 
1837
  mixer_init ();
 
1838
  mixer_reinit ();
 
1839
  
 
1840
  if (mixer_n_elems == 0) {
 
1841
    fprintf(stderr, "No mixer elems found\n");
 
1842
    mixer_abort (ERR_NONE, "", 0);
 
1843
  }
 
1844
 
 
1845
  /* setup signal handlers
 
1846
   */
 
1847
  signal (SIGINT, mixer_signal_handler);
 
1848
  signal (SIGTRAP, mixer_signal_handler);
 
1849
  signal (SIGABRT, mixer_signal_handler);
 
1850
  signal (SIGQUIT, mixer_signal_handler);
 
1851
  signal (SIGBUS, mixer_signal_handler);
 
1852
  signal (SIGSEGV, mixer_signal_handler);
 
1853
  signal (SIGPIPE, mixer_signal_handler);
 
1854
  signal (SIGTERM, mixer_signal_handler);
 
1855
  
 
1856
  /* initialize ncurses
 
1857
   */
 
1858
  mixer_init_window ();
 
1859
  if (mixer_max_x < MIXER_MIN_X ||
 
1860
      mixer_max_y < MIXER_MIN_Y)
 
1861
    beep (); // mixer_abort (ERR_WINSIZE, "");
 
1862
  
 
1863
  signal (SIGWINCH, (void*) mixer_winch);
 
1864
 
 
1865
  do
 
1866
    {
 
1867
      /* draw window upon every iteration */
 
1868
      if (!mixer_needs_resize)
 
1869
        {
 
1870
          switch (mixer_view)
 
1871
            {
 
1872
            case VIEW_CHANNELS:
 
1873
              mixer_update_cbars ();
 
1874
              break;
 
1875
            case VIEW_HELP:
 
1876
              mixer_show_text ("Help", mixer_help_text, &mixer_help_xoffs, &mixer_help_yoffs);
 
1877
              break;
 
1878
            case VIEW_PROCINFO:
 
1879
              mixer_show_procinfo ();
 
1880
              break;
 
1881
            }
 
1882
          mixer_draw_frame ();
 
1883
          refresh ();
 
1884
        }
 
1885
    }
 
1886
  while (!mixer_iteration ());
 
1887
  
 
1888
  mixer_abort (ERR_NONE, "", 0);
 
1889
};