~ubuntu-branches/ubuntu/natty/alsa-utils/natty

« back to all changes in this revision

Viewing changes to alsamixer/mixer_display.c

  • Committer: Bazaar Package Importer
  • Author(s): Luke Yelavich, Daniel T Chen
  • Date: 2009-11-06 11:29:35 UTC
  • mfrom: (2.3.4 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091106112935-stzy6l7u9auw6hm0
Tags: 1.0.21-1ubuntu1
* Merge from debian unstable, remaining changes:
  - debian/init:
    + wait until /usr/bin and /var/lib/alsa exist
    + only display an error when dealing with alsactl if there is no card
      specified
    + Set sane level for 'Speaker' and 'Headphone', needed for Dell Mini 9
      and Dell E series
    + ute PC Beep on hda cards that support it during initial volume setup
    + update lsb header to indicate no running of the script unless the
      udev rule is run
    + Mute *Analog/Digital Control for Creative cards by default
    + Default Digital Input Source to be Digital Mic 1 so that users
      with digital mic will be able to use it out of the box
    + Make use of lsb-functions/log calls
    + Mute "IEC958 Optical Raw" by default (LP: #408370)
  - debian/rules:
    + ship udev rules file in /lib/udev/rules.d
    + Do not install start symlinks for the alsa-utils init script, it gets
      run from a udev rule
  - debian/udev.script: do not use hotplug functions
  - debian/README.init.cs4236: Include in /usr/share/doc/alsa-utils so that
    users of snd-cs4236 (e.g., ThinkPad 600) can have audible sound
  - debian/patches/unset_pulse_internal.patch: We don't want alsamixer to
    show the pulse mixer by default, since it can be controlled from
    pulseaudio itself.
  - debian/patches/fix_misspelling_speaker-test_man_page.patch: Fix
    misspelling in speaker-test(1)
  - Set sane level for headphone 1 for Dell Studio XPS with 2.6.30.
  - Remove alsaconf from build system and remove po files

[ Daniel T Chen ]
"The beginning of the great initscript-sectomy"

* debian/alsa-mixer-save.upstart: Create an upstart job specifically
  saving mixer levels to resolve race (LP: #454265)
* debian/control: Version build-dep to upstart-aware debhelper.
* debian/init:
  + Revert all initscript changes in 1.0.20-2ubuntu[456]. They were
    crackful.
  + Restore more sane behavior/compatibility with 8.10 by not mucking
    with sound card state if alsactl restore fails.
  + Don't wait for 1 second after alsactl store, which already is
    expensive. Also, if store is going to fail, this wait is useless.
* debian/preinst: Handle upgrades from upstart-unaware versions.
* debian/rules: Move the former initscript into /sbin. We now have an
  upstart job just for handling alsactl store.
* debian/NOTES:
  debian/README.Debian:
  debian/modprobe-post-install-part:
  debian/udev.script: Use the new script path.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * mixer_display.c - handles displaying of mixer widget and controls
 
3
 * Copyright (c) 1874 Lewis Carroll
 
4
 * Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de>
 
5
 *
 
6
 * This program is free software: you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation, either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
18
 */
 
19
 
 
20
#include "aconfig.h"
 
21
#include <stdlib.h>
 
22
#include <string.h>
 
23
#include <strings.h>
 
24
#include CURSESINC
 
25
#include <alsa/asoundlib.h>
 
26
#include "gettext_curses.h"
 
27
#include "utils.h"
 
28
#include "mem.h"
 
29
#include "colors.h"
 
30
#include "widget.h"
 
31
#include "mixer_widget.h"
 
32
#include "mixer_controls.h"
 
33
#include "mixer_display.h"
 
34
 
 
35
enum align {
 
36
        ALIGN_LEFT,
 
37
        ALIGN_RIGHT,
 
38
        ALIGN_CENTER,
 
39
};
 
40
 
 
41
static bool screen_too_small;
 
42
static bool has_info_items;
 
43
 
 
44
static int info_items_left;
 
45
static int info_items_width;
 
46
 
 
47
static int visible_controls;
 
48
static int first_visible_control_index;
 
49
static int first_control_x;
 
50
static int control_width;
 
51
static int control_name_width;
 
52
 
 
53
static int base_y;
 
54
static int volume_height;
 
55
static int cswitch_y;
 
56
static int values_y;
 
57
static int name_y;
 
58
static int channel_name_y;
 
59
 
 
60
static void display_string_in_field(int y, int x, const char *s, int width, enum align align)
 
61
{
 
62
        int string_width;
 
63
        const char *s_end;
 
64
        int spaces;
 
65
        int cur_y, cur_x;
 
66
 
 
67
        wmove(mixer_widget.window, y, x);
 
68
        string_width = width;
 
69
        s_end = mbs_at_width(s, &string_width, -1);
 
70
        if (string_width >= width) {
 
71
                waddnstr(mixer_widget.window, s, s_end - s);
 
72
        } else {
 
73
                if (align != ALIGN_LEFT) {
 
74
                        spaces = width - string_width;
 
75
                        if (align == ALIGN_CENTER)
 
76
                                spaces /= 2;
 
77
                        if (spaces > 0)
 
78
                                wprintw(mixer_widget.window, "%*s", spaces, "");
 
79
                }
 
80
                waddstr(mixer_widget.window, s);
 
81
                if (align != ALIGN_RIGHT) {
 
82
                        getyx(mixer_widget.window, cur_y, cur_x);
 
83
                        if (cur_y == y) {
 
84
                                spaces = x + width - cur_x;
 
85
                                if (spaces > 0)
 
86
                                        wprintw(mixer_widget.window, "%*s", spaces, "");
 
87
                        }
 
88
                }
 
89
        }
 
90
}
 
91
 
 
92
void init_mixer_layout(void)
 
93
{
 
94
        const char *labels_left[4] = {
 
95
                _("Card:"),
 
96
                _("Chip:"),
 
97
                _("View:"),
 
98
                _("Item:"),
 
99
        };
 
100
        const char *labels_right[4] = {
 
101
                _("F1:  Help"),
 
102
                _("F2:  System information"),
 
103
                _("F6:  Select sound card"),
 
104
                _("Esc: Exit"),
 
105
        };
 
106
        unsigned int label_width_left, label_width_right;
 
107
        unsigned int right_x, i;
 
108
 
 
109
        screen_too_small = screen_lines < 14 || screen_cols < 12;
 
110
        has_info_items = screen_lines >= 6;
 
111
        if (!has_info_items)
 
112
                return;
 
113
 
 
114
        label_width_left = get_max_mbs_width(labels_left, 4);
 
115
        label_width_right = get_max_mbs_width(labels_right, 4);
 
116
        if (2 + label_width_left + 1 + 28 + label_width_right + 2 > screen_cols)
 
117
                label_width_right = 0;
 
118
        if (2 + label_width_left + 1 + 28 + label_width_right + 2 > screen_cols)
 
119
                label_width_left = 0;
 
120
 
 
121
        info_items_left = label_width_left ? 3 + label_width_left : 2;
 
122
        right_x = screen_cols - label_width_right - 2;
 
123
        info_items_width = right_x - info_items_left;
 
124
        if (info_items_width < 1) {
 
125
                has_info_items = FALSE;
 
126
                return;
 
127
        }
 
128
 
 
129
        wattrset(mixer_widget.window, attr_mixer_text);
 
130
        if (label_width_left)
 
131
                for (i = 0; i < 4; ++i)
 
132
                        display_string_in_field(1 + i, 2, labels_left[i],
 
133
                                                label_width_left, ALIGN_RIGHT);
 
134
        if (label_width_right)
 
135
                for (i = 0; i < 4; ++i)
 
136
                        display_string_in_field(1 + i, right_x, labels_right[i],
 
137
                                                label_width_right, ALIGN_LEFT);
 
138
}
 
139
 
 
140
void display_card_info(void)
 
141
{
 
142
        snd_hctl_t *hctl;
 
143
        snd_ctl_t *ctl;
 
144
        snd_ctl_card_info_t *card_info;
 
145
        const char *card_name = NULL;
 
146
        const char *mixer_name = NULL;
 
147
        int err;
 
148
 
 
149
        if (!has_info_items)
 
150
                return;
 
151
 
 
152
        snd_ctl_card_info_alloca(&card_info);
 
153
        if (mixer_device_name)
 
154
                err = snd_mixer_get_hctl(mixer, mixer_device_name, &hctl);
 
155
        else
 
156
                err = -1;
 
157
        if (err >= 0) {
 
158
                ctl = snd_hctl_ctl(hctl);
 
159
                err = snd_ctl_card_info(ctl, card_info);
 
160
                if (err >= 0) {
 
161
                        card_name = snd_ctl_card_info_get_name(card_info);
 
162
                        mixer_name = snd_ctl_card_info_get_mixername(card_info);
 
163
                }
 
164
        }
 
165
 
 
166
        if (card_name)
 
167
                wattrset(mixer_widget.window, attr_mixer_active);
 
168
        else {
 
169
                wattrset(mixer_widget.window, attr_mixer_text);
 
170
                if (unplugged)
 
171
                        card_name = _("(unplugged)");
 
172
                else
 
173
                        card_name = "-";
 
174
        }
 
175
        display_string_in_field(1, info_items_left, card_name, info_items_width, ALIGN_LEFT);
 
176
 
 
177
        if (mixer_name)
 
178
                wattrset(mixer_widget.window, attr_mixer_active);
 
179
        else {
 
180
                wattrset(mixer_widget.window, attr_mixer_text);
 
181
                mixer_name = "-";
 
182
        }
 
183
        display_string_in_field(2, info_items_left, mixer_name, info_items_width, ALIGN_LEFT);
 
184
}
 
185
 
 
186
void display_view_mode(void)
 
187
{
 
188
        const char *modes[3] = {
 
189
                _("Playback"),
 
190
                _("Capture"),
 
191
                _("All"),
 
192
        };
 
193
        unsigned int widths[3];
 
194
        bool has_view_mode;
 
195
        int i;
 
196
 
 
197
        if (!has_info_items)
 
198
                return;
 
199
 
 
200
        has_view_mode = controls_count > 0 || are_there_any_controls();
 
201
        for (i = 0; i < 3; ++i)
 
202
                widths[i] = get_mbs_width(modes[i]);
 
203
        if (4 + widths[0] + 6 + widths[1] + 6 + widths[2] + 1 <= info_items_width) {
 
204
                wmove(mixer_widget.window, 3, info_items_left);
 
205
                wattrset(mixer_widget.window, attr_mixer_text);
 
206
                for (i = 0; i < 3; ++i) {
 
207
                        wprintw(mixer_widget.window, "F%c:", '3' + i);
 
208
                        if (has_view_mode && (int)view_mode == i) {
 
209
                                wattrset(mixer_widget.window, attr_mixer_active);
 
210
                                wprintw(mixer_widget.window, "[%s]", modes[i]);
 
211
                                wattrset(mixer_widget.window, attr_mixer_text);
 
212
                        } else {
 
213
                                wprintw(mixer_widget.window, " %s ", modes[i]);
 
214
                        }
 
215
                        if (i < 2)
 
216
                                waddch(mixer_widget.window, ' ');
 
217
                }
 
218
        } else {
 
219
                wattrset(mixer_widget.window, attr_mixer_active);
 
220
                display_string_in_field(3, info_items_left,
 
221
                                        has_view_mode ? modes[view_mode] : "",
 
222
                                        info_items_width, ALIGN_LEFT);
 
223
        }
 
224
}
 
225
 
 
226
static char *format_gain(long db)
 
227
{
 
228
        if (db != SND_CTL_TLV_DB_GAIN_MUTE)
 
229
                return casprintf("%.2f", db / 100.0);
 
230
        else
 
231
                return cstrdup(_("mute"));
 
232
}
 
233
 
 
234
static void display_focus_item_info(void)
 
235
{
 
236
        struct control *control;
 
237
        unsigned int index;
 
238
        char buf[64];
 
239
        long db, db2;
 
240
        int sw, sw2;
 
241
        char *dbs, *dbs2;
 
242
        char *value_info;
 
243
        char *item_info;
 
244
        int err;
 
245
 
 
246
        if (!has_info_items)
 
247
                return;
 
248
        wattrset(mixer_widget.window, attr_mixer_active);
 
249
        if (!controls_count || screen_too_small) {
 
250
                display_string_in_field(4, info_items_left, "", info_items_width, ALIGN_LEFT);
 
251
                return;
 
252
        }
 
253
        control = &controls[focus_control_index];
 
254
        value_info = NULL;
 
255
        if (control->flags & TYPE_ENUM) {
 
256
                err = snd_mixer_selem_get_enum_item(control->elem, ffs(control->enum_channel_bits) - 1, &index);
 
257
                if (err >= 0)
 
258
                        err = snd_mixer_selem_get_enum_item_name(control->elem, index, sizeof buf - 1, buf);
 
259
                if (err >= 0)
 
260
                        value_info = casprintf(" [%s]", buf);
 
261
        } else if (control->flags & (TYPE_PVOLUME | TYPE_CVOLUME)) {
 
262
                int (*get_vol_func)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, long *);
 
263
 
 
264
                if (control->flags & TYPE_PVOLUME)
 
265
                        get_vol_func = snd_mixer_selem_get_playback_dB;
 
266
                else
 
267
                        get_vol_func = snd_mixer_selem_get_capture_dB;
 
268
                if (!(control->flags & HAS_VOLUME_1)) {
 
269
                        err = get_vol_func(control->elem, control->volume_channels[0], &db);
 
270
                        if (err >= 0) {
 
271
                                dbs = format_gain(db);
 
272
                                value_info = casprintf(" [%s %s]", _("dB gain:"), dbs);
 
273
                                free(dbs);
 
274
                        }
 
275
                } else {
 
276
                        err = get_vol_func(control->elem, control->volume_channels[0], &db);
 
277
                        if (err >= 0)
 
278
                                err = get_vol_func(control->elem, control->volume_channels[1], &db2);
 
279
                        if (err >= 0) {
 
280
                                dbs = format_gain(db);
 
281
                                dbs2 = format_gain(db2);
 
282
                                value_info = casprintf(_(" [%s %s, %s]"), _("dB gain:"), dbs, dbs2);
 
283
                                free(dbs);
 
284
                                free(dbs2);
 
285
                        }
 
286
                }
 
287
        } else if (control->flags & TYPE_PSWITCH) {
 
288
                if (!(control->flags & HAS_PSWITCH_1)) {
 
289
                        err = snd_mixer_selem_get_playback_switch(control->elem, control->pswitch_channels[0], &sw);
 
290
                        if (err >= 0 && !sw)
 
291
                                value_info = casprintf(" [%s]", _("Off"));
 
292
                } else {
 
293
                        err = snd_mixer_selem_get_playback_switch(control->elem, control->pswitch_channels[0], &sw);
 
294
                        if (err >= 0)
 
295
                                err = snd_mixer_selem_get_playback_switch(control->elem, control->pswitch_channels[1], &sw2);
 
296
                        if (err >= 0 && (!sw || !sw2))
 
297
                                value_info = casprintf(" [%s, %s]", sw ? _("On") : _("Off"), sw2 ? _("On") : _("Off"));
 
298
                }
 
299
        } else if (control->flags & TYPE_CSWITCH) {
 
300
                if (!(control->flags & HAS_CSWITCH_1)) {
 
301
                        err = snd_mixer_selem_get_capture_switch(control->elem, control->cswitch_channels[0], &sw);
 
302
                        if (err >= 0 && !sw)
 
303
                                value_info = casprintf(" [%s]", _("Off"));
 
304
                } else {
 
305
                        err = snd_mixer_selem_get_capture_switch(control->elem, control->cswitch_channels[0], &sw);
 
306
                        if (err >= 0)
 
307
                                err = snd_mixer_selem_get_capture_switch(control->elem, control->cswitch_channels[1], &sw2);
 
308
                        if (err >= 0 && (!sw || !sw2))
 
309
                                value_info = casprintf(" [%s, %s]", sw ? _("On") : _("Off"), sw2 ? _("On") : _("Off"));
 
310
                }
 
311
        }
 
312
        item_info = casprintf("%s%s", control->name, value_info ? value_info : "");
 
313
        free(value_info);
 
314
        display_string_in_field(4, info_items_left, item_info, info_items_width, ALIGN_LEFT);
 
315
        free(item_info);
 
316
}
 
317
 
 
318
static void clear_controls_display(void)
 
319
{
 
320
        int i;
 
321
 
 
322
        wattrset(mixer_widget.window, attr_mixer_frame);
 
323
        for (i = 5; i < screen_lines - 1; ++i)
 
324
                mvwprintw(mixer_widget.window, i, 1, "%*s", screen_cols - 2, "");
 
325
}
 
326
 
 
327
static void center_string(int line, const char *s)
 
328
{
 
329
        int width = get_mbs_width(s);
 
330
        if (width <= screen_cols - 2)
 
331
                mvwaddstr(mixer_widget.window, line, (screen_cols - width) / 2, s);
 
332
}
 
333
 
 
334
static void display_unplugged(void)
 
335
{
 
336
        int lines, top, left;
 
337
        bool boojum;
 
338
 
 
339
        lines = screen_lines - 6;
 
340
        if (lines < 2)
 
341
                return;
 
342
        top = lines / 2;
 
343
        boojum = lines >= 10 && screen_cols >= 48;
 
344
        top -= boojum ? 5 : 1;
 
345
        if (top < 5)
 
346
                top = 5;
 
347
        if (boojum) {
 
348
                left = (screen_cols - 46) / 2;
 
349
                wattrset(mixer_widget.window, attr_mixer_text);
 
350
                mvwaddstr(mixer_widget.window, top + 0, left,    "In the midst of the word he was trying to say,");
 
351
                mvwaddstr(mixer_widget.window, top + 1, left + 2,  "In the midst of his laughter and glee,");
 
352
                mvwaddstr(mixer_widget.window, top + 2, left,    "He had softly and suddenly vanished away---");
 
353
                mvwaddstr(mixer_widget.window, top + 3, left + 2,  "For the Snark was a Boojum, you see.");
 
354
                mvwchgat(mixer_widget.window,  top + 3, left + 16, 3,          /* ^^^ */
 
355
                         attr_mixer_text | A_BOLD, PAIR_NUMBER(attr_mixer_text), NULL);
 
356
                mvwaddstr(mixer_widget.window, top + 5, left,    "(Lewis Carroll, \"The Hunting of the Snark\")");
 
357
                top += 8;
 
358
        }
 
359
        wattrset(mixer_widget.window, attr_errormsg);
 
360
        center_string(top, _("The sound device was unplugged."));
 
361
        center_string(top + 1, _("Press F6 to select another sound card."));
 
362
}
 
363
 
 
364
static void display_no_controls(void)
 
365
{
 
366
        int y;
 
367
        const char *msg;
 
368
 
 
369
        y = (screen_lines - 6) / 2 - 1;
 
370
        if (y < 5)
 
371
                y = 5;
 
372
        if (y >= screen_lines - 1)
 
373
                return;
 
374
        wattrset(mixer_widget.window, attr_infomsg);
 
375
        if (view_mode == VIEW_MODE_PLAYBACK && are_there_any_controls())
 
376
                msg = _("This sound device does not have any playback controls.");
 
377
        else if (view_mode == VIEW_MODE_CAPTURE && are_there_any_controls())
 
378
                msg = _("This sound device does not have any capture controls.");
 
379
        else
 
380
                msg = _("This sound device does not have any controls.");
 
381
        center_string(y, msg);
 
382
}
 
383
 
 
384
static void display_string_centered_in_control(int y, int col, const char *s, int width)
 
385
{
 
386
        int left, x;
 
387
 
 
388
        left = first_control_x + col * (control_width + 1);
 
389
        x = left + (control_width - width) / 2;
 
390
        display_string_in_field(y, x, s, width, ALIGN_CENTER);
 
391
}
 
392
 
 
393
static void display_control(unsigned int control_index)
 
394
{
 
395
        struct control *control;
 
396
        int col;
 
397
        int i, c;
 
398
        int left, frame_left;
 
399
        int bar_height, value;
 
400
        long volumes[2];
 
401
        long min, max;
 
402
        int switches[2];
 
403
        unsigned int index;
 
404
        const char *s;
 
405
        char buf[64];
 
406
        int err;
 
407
 
 
408
        control = &controls[control_index];
 
409
        col = control_index - first_visible_control_index;
 
410
        left = first_control_x + col * (control_width + 1);
 
411
        frame_left = left + (control_width - 4) / 2;
 
412
        if (control->flags & IS_ACTIVE)
 
413
                wattrset(mixer_widget.window, attr_ctl_frame);
 
414
        else
 
415
                wattrset(mixer_widget.window, attr_ctl_inactive);
 
416
        if (control->flags & (TYPE_PVOLUME | TYPE_CVOLUME)) {
 
417
                mvwaddch(mixer_widget.window, base_y - volume_height - 1, frame_left, ACS_ULCORNER);
 
418
                waddch(mixer_widget.window, ACS_HLINE);
 
419
                waddch(mixer_widget.window, ACS_HLINE);
 
420
                waddch(mixer_widget.window, ACS_URCORNER);
 
421
                for (i = 0; i < volume_height; ++i) {
 
422
                        mvwaddch(mixer_widget.window, base_y - i - 1, frame_left, ACS_VLINE);
 
423
                        mvwaddch(mixer_widget.window, base_y - i - 1, frame_left + 3, ACS_VLINE);
 
424
                }
 
425
                mvwaddch(mixer_widget.window, base_y, frame_left,
 
426
                         control->flags & TYPE_PSWITCH ? ACS_LTEE : ACS_LLCORNER);
 
427
                waddch(mixer_widget.window, ACS_HLINE);
 
428
                waddch(mixer_widget.window, ACS_HLINE);
 
429
                waddch(mixer_widget.window,
 
430
                       control->flags & TYPE_PSWITCH ? ACS_RTEE : ACS_LRCORNER);
 
431
        } else if (control->flags & TYPE_PSWITCH) {
 
432
                mvwaddch(mixer_widget.window, base_y, frame_left, ACS_ULCORNER);
 
433
                waddch(mixer_widget.window, ACS_HLINE);
 
434
                waddch(mixer_widget.window, ACS_HLINE);
 
435
                waddch(mixer_widget.window, ACS_URCORNER);
 
436
        }
 
437
        if (control->flags & TYPE_PSWITCH) {
 
438
                mvwaddch(mixer_widget.window, base_y + 1, frame_left, ACS_VLINE);
 
439
                mvwaddch(mixer_widget.window, base_y + 1, frame_left + 3, ACS_VLINE);
 
440
                mvwaddch(mixer_widget.window, base_y + 2, frame_left, ACS_LLCORNER);
 
441
                waddch(mixer_widget.window, ACS_HLINE);
 
442
                waddch(mixer_widget.window, ACS_HLINE);
 
443
                waddch(mixer_widget.window, ACS_LRCORNER);
 
444
        }
 
445
        if (control->flags & (TYPE_PVOLUME | TYPE_CVOLUME)) {
 
446
                int (*get_vol_func)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, long *);
 
447
 
 
448
                if (control->flags & TYPE_PVOLUME)
 
449
                        get_vol_func = snd_mixer_selem_get_playback_volume;
 
450
                else
 
451
                        get_vol_func = snd_mixer_selem_get_capture_volume;
 
452
                err = get_vol_func(control->elem, control->volume_channels[0], &volumes[0]);
 
453
                if (err >= 0 && (control->flags & HAS_VOLUME_1))
 
454
                        err = get_vol_func(control->elem, control->volume_channels[1], &volumes[1]);
 
455
                else
 
456
                        volumes[1] = volumes[0];
 
457
                if (err < 0)
 
458
                        return;
 
459
                if (control->flags & TYPE_PVOLUME)
 
460
                        err = snd_mixer_selem_get_playback_volume_range(control->elem, &min, &max);
 
461
                else
 
462
                        err = snd_mixer_selem_get_capture_volume_range(control->elem, &min, &max);
 
463
                if (err < 0)
 
464
                        return;
 
465
 
 
466
                if (control->flags & IS_ACTIVE)
 
467
                        wattrset(mixer_widget.window, 0);
 
468
                for (c = 0; c < 2; c++) {
 
469
                        bar_height = ((volumes[c] - min) * volume_height +
 
470
                                      max - min - 1) / (max - min);
 
471
                        for (i = 0; i < volume_height; ++i) {
 
472
                                chtype ch;
 
473
                                if (i + 1 > bar_height)
 
474
                                        ch = ' ' | (control->flags & IS_ACTIVE ?
 
475
                                                    attr_ctl_frame : 0);
 
476
                                else {
 
477
                                        ch = ACS_CKBOARD;
 
478
                                        if (!(control->flags & IS_ACTIVE))
 
479
                                                ;
 
480
#ifdef TRICOLOR_VOLUME_BAR
 
481
                                        else if (i > volume_height * 8 / 10)
 
482
                                                ch |= attr_ctl_bar_hi;
 
483
                                        else if (i > volume_height * 4 / 10)
 
484
                                                ch |= attr_ctl_bar_mi;
 
485
#endif
 
486
                                        else
 
487
                                                ch |= attr_ctl_bar_lo;
 
488
                                }
 
489
                                mvwaddch(mixer_widget.window, base_y - i - 1,
 
490
                                         frame_left + c + 1, ch);
 
491
                        }
 
492
                }
 
493
                if (control->flags & IS_ACTIVE)
 
494
                        wattrset(mixer_widget.window, attr_mixer_active);
 
495
                value = ((volumes[0] - min) * 100 + (max - min) / 2) / (max - min);
 
496
                if (!(control->flags & HAS_VOLUME_1)) {
 
497
                        sprintf(buf, "%d", value);
 
498
                        display_string_in_field(values_y, frame_left - 2, buf, 8, ALIGN_CENTER);
 
499
                } else {
 
500
                        mvwprintw(mixer_widget.window, values_y, frame_left - 2, "%3d", value);
 
501
                        if (control->flags & IS_ACTIVE)
 
502
                                wattrset(mixer_widget.window, attr_ctl_frame);
 
503
                        waddstr(mixer_widget.window, "<>");
 
504
                        if (control->flags & IS_ACTIVE)
 
505
                                wattrset(mixer_widget.window, attr_mixer_active);
 
506
                        value = ((volumes[1] - min) * 100 + (max - min) / 2) / (max - min);
 
507
                        wprintw(mixer_widget.window, "%-3d", value);
 
508
                }
 
509
        }
 
510
 
 
511
        if (control->flags & TYPE_PSWITCH) {
 
512
                err = snd_mixer_selem_get_playback_switch(control->elem, control->pswitch_channels[0], &switches[0]);
 
513
                if (err >= 0 && (control->flags & HAS_PSWITCH_1))
 
514
                        err = snd_mixer_selem_get_playback_switch(control->elem, control->pswitch_channels[1], &switches[1]);
 
515
                else
 
516
                        switches[1] = switches[0];
 
517
                if (err < 0)
 
518
                        return;
 
519
                if (control->flags & IS_ACTIVE)
 
520
                        wattrset(mixer_widget.window, 0);
 
521
                mvwaddch(mixer_widget.window, base_y + 1, frame_left + 1,
 
522
                         switches[0]
 
523
                         /* TRANSLATORS: playback on; one character */
 
524
                         ? _("O")[0] | (control->flags & IS_ACTIVE ? attr_ctl_nomute : 0)
 
525
                         /* TRANSLATORS: playback muted; one character */
 
526
                         : _("M")[0] | (control->flags & IS_ACTIVE ? attr_ctl_mute : 0));
 
527
                waddch(mixer_widget.window,
 
528
                       switches[1]
 
529
                       ? _("O")[0] | (control->flags & IS_ACTIVE ? attr_ctl_nomute : 0)
 
530
                       : _("M")[0] | (control->flags & IS_ACTIVE ? attr_ctl_mute : 0));
 
531
        }
 
532
 
 
533
        if (control->flags & TYPE_CSWITCH) {
 
534
                err = snd_mixer_selem_get_capture_switch(control->elem, control->cswitch_channels[0], &switches[0]);
 
535
                if (err >= 0 && (control->flags & HAS_CSWITCH_1))
 
536
                        err = snd_mixer_selem_get_capture_switch(control->elem, control->cswitch_channels[1], &switches[1]);
 
537
                else
 
538
                        switches[1] = switches[0];
 
539
                if (err < 0)
 
540
                        return;
 
541
                if (control->flags & IS_ACTIVE)
 
542
                        wattrset(mixer_widget.window, switches[0] ? attr_ctl_capture : attr_ctl_nocapture);
 
543
                /* TRANSLATORS: "left"; no more than two characters */
 
544
                display_string_in_field(cswitch_y - 1, frame_left - 2, switches[0] ? _("L") : "", 2, ALIGN_RIGHT);
 
545
                if (control->flags & IS_ACTIVE)
 
546
                        wattrset(mixer_widget.window, switches[1] ? attr_ctl_capture : attr_ctl_nocapture);
 
547
                /* TRANSLATORS: "right"; no more than two characters */
 
548
                display_string_in_field(cswitch_y - 1, frame_left + 4, switches[1] ? _("R") : "", 2, ALIGN_LEFT);
 
549
                /* TRANSLATORS: no more than eight characters */
 
550
                s = _("CAPTURE");
 
551
                if (switches[0] || switches[1]) {
 
552
                        if (control->flags & IS_ACTIVE)
 
553
                                wattrset(mixer_widget.window, attr_ctl_capture);
 
554
                        display_string_in_field(cswitch_y, frame_left - 2, s, 8, ALIGN_CENTER);
 
555
                } else {
 
556
                        i = get_mbs_width(s);
 
557
                        if (i > 8)
 
558
                                i = 8;
 
559
                        memset(buf, '-', i);
 
560
                        buf[i] = '\0';
 
561
                        if (control->flags & IS_ACTIVE)
 
562
                                wattrset(mixer_widget.window, attr_ctl_nocapture);
 
563
                        display_string_in_field(cswitch_y, frame_left - 2, buf, 8, ALIGN_CENTER);
 
564
                }
 
565
        }
 
566
 
 
567
        if (control->flags & TYPE_ENUM) {
 
568
                err = snd_mixer_selem_get_enum_item(control->elem, ffs(control->enum_channel_bits) - 1, &index);
 
569
                if (err < 0)
 
570
                        return;
 
571
                err = snd_mixer_selem_get_enum_item_name(control->elem, index, sizeof buf - 1, buf);
 
572
                if (err < 0)
 
573
                        return;
 
574
                if (control->flags & IS_ACTIVE)
 
575
                        wattrset(mixer_widget.window, attr_mixer_active);
 
576
                display_string_centered_in_control(base_y, col, buf, control_width);
 
577
        }
 
578
 
 
579
        if (control_index == focus_control_index) {
 
580
                i = first_control_x + col * (control_width + 1) + (control_width - control_name_width) / 2;
 
581
                wattrset(mixer_widget.window, attr_ctl_mark_focus);
 
582
                mvwaddch(mixer_widget.window, name_y, i - 1, '<');
 
583
                mvwaddch(mixer_widget.window, name_y, i + control_name_width, '>');
 
584
                if (control->flags & IS_ACTIVE)
 
585
                        wattrset(mixer_widget.window, attr_ctl_label_focus);
 
586
                else
 
587
                        wattrset(mixer_widget.window, attr_ctl_label_inactive);
 
588
        } else {
 
589
                if (control->flags & IS_ACTIVE)
 
590
                        wattrset(mixer_widget.window, attr_ctl_label);
 
591
                else
 
592
                        wattrset(mixer_widget.window, attr_ctl_label_inactive);
 
593
        }
 
594
        display_string_centered_in_control(name_y, col, control->name, control_name_width);
 
595
        if (channel_name_y > name_y) {
 
596
                if (control->flags & IS_MULTICH) {
 
597
                        switch (control->flags & MULTICH_MASK) {
 
598
                        case 0:
 
599
                        default:
 
600
                                s = _("Front");
 
601
                                break;
 
602
                        case 1:
 
603
                                s = _("Rear");
 
604
                                break;
 
605
                        case 2:
 
606
                                s = _("Center");
 
607
                                break;
 
608
                        case 3:
 
609
                                s = _("Woofer");
 
610
                                break;
 
611
                        case 4:
 
612
                                s = _("Side");
 
613
                                break;
 
614
                        }
 
615
                } else {
 
616
                        s = "";
 
617
                        wattrset(mixer_widget.window, attr_mixer_frame);
 
618
                }
 
619
                display_string_centered_in_control(channel_name_y, col, s,
 
620
                                                   control_name_width);
 
621
        }
 
622
}
 
623
 
 
624
static void display_scroll_indicators(void)
 
625
{
 
626
        int y0, y1, y;
 
627
        chtype left, right;
 
628
 
 
629
        if (screen_too_small)
 
630
                return;
 
631
        y0 = screen_lines * 3 / 8;
 
632
        y1 = screen_lines * 5 / 8;
 
633
        left = first_visible_control_index > 0 ? ACS_LARROW : ACS_VLINE;
 
634
        right = first_visible_control_index + visible_controls < controls_count
 
635
                ? ACS_RARROW : ACS_VLINE;
 
636
        wattrset(mixer_widget.window, attr_mixer_frame);
 
637
        for (y = y0; y <= y1; ++y) {
 
638
                mvwaddch(mixer_widget.window, y, 0, left);
 
639
                mvwaddch(mixer_widget.window, y, screen_cols - 1, right);
 
640
        }
 
641
}
 
642
 
 
643
void display_controls(void)
 
644
{
 
645
        unsigned int i;
 
646
 
 
647
        if (first_visible_control_index > controls_count - visible_controls)
 
648
                first_visible_control_index = controls_count - visible_controls;
 
649
        if (first_visible_control_index > focus_control_index)
 
650
                first_visible_control_index = focus_control_index;
 
651
        else if (first_visible_control_index < focus_control_index - visible_controls + 1 && visible_controls)
 
652
                first_visible_control_index = focus_control_index - visible_controls + 1;
 
653
 
 
654
        clear_controls_display();
 
655
 
 
656
        display_focus_item_info();
 
657
 
 
658
        if (controls_count > 0) {
 
659
                if (!screen_too_small)
 
660
                        for (i = 0; i < visible_controls; ++i)
 
661
                                display_control(first_visible_control_index + i);
 
662
        } else if (unplugged) {
 
663
                display_unplugged();
 
664
        } else if (mixer_device_name) {
 
665
                display_no_controls();
 
666
        }
 
667
        display_scroll_indicators();
 
668
        controls_changed = FALSE;
 
669
}
 
670
 
 
671
void compute_controls_layout(void)
 
672
{
 
673
        bool any_volume, any_pswitch, any_cswitch, any_multich;
 
674
        int max_width, name_len;
 
675
        int height, space;
 
676
        unsigned int i;
 
677
 
 
678
        if (controls_count == 0 || screen_too_small) {
 
679
                visible_controls = 0;
 
680
                return;
 
681
        }
 
682
 
 
683
        any_volume = FALSE;
 
684
        any_pswitch = FALSE;
 
685
        any_cswitch = FALSE;
 
686
        any_multich = FALSE;
 
687
        for (i = 0; i < controls_count; ++i) {
 
688
                if (controls[i].flags & (TYPE_PVOLUME | TYPE_CVOLUME))
 
689
                        any_volume = 1;
 
690
                if (controls[i].flags & TYPE_PSWITCH)
 
691
                        any_pswitch = 1;
 
692
                if (controls[i].flags & TYPE_CSWITCH)
 
693
                        any_cswitch = 1;
 
694
                if (controls[i].flags & IS_MULTICH)
 
695
                        any_multich = 1;
 
696
        }
 
697
 
 
698
        max_width = 8;
 
699
        for (i = 0; i < controls_count; ++i) {
 
700
                name_len = strlen(controls[i].name);
 
701
                if (name_len > max_width)
 
702
                        max_width = name_len;
 
703
        }
 
704
        max_width = (max_width + 1) & ~1;
 
705
 
 
706
        control_width = (screen_cols - 3 - (int)controls_count) / controls_count;
 
707
        if (control_width < 8)
 
708
                control_width = 8;
 
709
        if (control_width > max_width)
 
710
                control_width = max_width;
 
711
        if (control_width > screen_cols - 4)
 
712
                control_width = screen_cols - 4;
 
713
 
 
714
        visible_controls = (screen_cols - 3) / (control_width + 1);
 
715
        if (visible_controls > controls_count)
 
716
                visible_controls = controls_count;
 
717
 
 
718
        first_control_x = 2 + (screen_cols - 3 - visible_controls * (control_width + 1)) / 2;
 
719
 
 
720
        if (control_width < max_width)
 
721
                control_name_width = control_width;
 
722
        else
 
723
                control_name_width = max_width;
 
724
 
 
725
        height = 2;
 
726
        if (any_volume)
 
727
                height += 2;
 
728
        if (any_pswitch)
 
729
                height += 2;
 
730
        if (any_cswitch)
 
731
                height += 1;
 
732
        if (any_multich)
 
733
                height += 1;
 
734
        if (any_volume) {
 
735
                space = screen_lines - 6 - height;
 
736
                if (space <= 1)
 
737
                        volume_height = 1;
 
738
                else if (space <= 10)
 
739
                        volume_height = space;
 
740
                else
 
741
                        volume_height = 10 + (space - 10) / 2;
 
742
                height += volume_height;
 
743
        }
 
744
 
 
745
        space = screen_lines - 6 - height;
 
746
        channel_name_y = screen_lines - 2 - space / 2;
 
747
        name_y = channel_name_y - any_multich;
 
748
        values_y = name_y - any_volume;
 
749
        cswitch_y = values_y - any_cswitch;
 
750
        base_y = cswitch_y - 1 - 2 * any_pswitch;
 
751
}