mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-11-09 17:25:42 +01:00
10a2557d7e
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
763 lines
24 KiB
C
763 lines
24 KiB
C
/*
|
|
* mixer_display.c - handles displaying of mixer widget and controls
|
|
* Copyright (c) 1874 Lewis Carroll
|
|
* Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#define _C99_SOURCE /* lrint() */
|
|
#include "aconfig.h"
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <math.h>
|
|
#include CURSESINC
|
|
#include <alsa/asoundlib.h>
|
|
#include "gettext_curses.h"
|
|
#include "utils.h"
|
|
#include "mem.h"
|
|
#include "colors.h"
|
|
#include "widget.h"
|
|
#include "volume_mapping.h"
|
|
#include "mixer_widget.h"
|
|
#include "mixer_controls.h"
|
|
#include "mixer_display.h"
|
|
#include "mixer_clickable.h"
|
|
|
|
enum align {
|
|
ALIGN_LEFT,
|
|
ALIGN_RIGHT,
|
|
ALIGN_CENTER,
|
|
};
|
|
|
|
static bool screen_too_small;
|
|
static bool has_info_items;
|
|
|
|
static int info_items_left;
|
|
static int info_items_width;
|
|
|
|
static int visible_controls;
|
|
static int first_visible_control_index;
|
|
static int first_control_x;
|
|
static int control_width;
|
|
static int control_name_width;
|
|
|
|
static int base_y;
|
|
static int volume_height;
|
|
static int cswitch_y;
|
|
static int values_y;
|
|
static int name_y;
|
|
static int channel_name_y;
|
|
|
|
static void display_string_in_field(int y, int x, const char *s, int width, enum align align)
|
|
{
|
|
int string_width;
|
|
const char *s_end;
|
|
int spaces;
|
|
int cur_y, cur_x;
|
|
|
|
wmove(mixer_widget.window, y, x);
|
|
string_width = width;
|
|
s_end = mbs_at_width(s, &string_width, -1);
|
|
if (string_width >= width) {
|
|
waddnstr(mixer_widget.window, s, s_end - s);
|
|
} else {
|
|
if (align != ALIGN_LEFT) {
|
|
spaces = width - string_width;
|
|
if (align == ALIGN_CENTER)
|
|
spaces /= 2;
|
|
if (spaces > 0)
|
|
wprintw(mixer_widget.window, "%*s", spaces, "");
|
|
}
|
|
waddstr(mixer_widget.window, s);
|
|
if (align != ALIGN_RIGHT) {
|
|
getyx(mixer_widget.window, cur_y, cur_x);
|
|
if (cur_y == y) {
|
|
spaces = x + width - cur_x;
|
|
if (spaces > 0)
|
|
wprintw(mixer_widget.window, "%*s", spaces, "");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void init_mixer_layout(void)
|
|
{
|
|
const char *labels_left[4] = {
|
|
_("Card:"),
|
|
_("Chip:"),
|
|
_("View:"),
|
|
_("Item:"),
|
|
};
|
|
const char *labels_right[4] = {
|
|
_("F1: Help"),
|
|
_("F2: System information"),
|
|
_("F6: Select sound card"),
|
|
_("Esc: Exit"),
|
|
};
|
|
int label_width_left, label_width_right;
|
|
int right_x, i;
|
|
|
|
clickable_clear(0, 0, -1, -1);
|
|
screen_too_small = screen_lines < 14 || screen_cols < 12;
|
|
has_info_items = screen_lines >= 6;
|
|
if (!has_info_items)
|
|
return;
|
|
|
|
label_width_left = get_max_mbs_width(labels_left, 4);
|
|
label_width_right = get_max_mbs_width(labels_right, 4);
|
|
if (2 + label_width_left + 1 + 28 + label_width_right + 2 > screen_cols)
|
|
label_width_right = 0;
|
|
if (2 + label_width_left + 1 + 28 + label_width_right + 2 > screen_cols)
|
|
label_width_left = 0;
|
|
|
|
info_items_left = label_width_left ? 3 + label_width_left : 2;
|
|
right_x = screen_cols - label_width_right - 2;
|
|
info_items_width = right_x - info_items_left;
|
|
if (info_items_width < 1) {
|
|
has_info_items = FALSE;
|
|
return;
|
|
}
|
|
|
|
wattrset(mixer_widget.window, attrs.mixer_text);
|
|
if (label_width_left)
|
|
for (i = 0; i < 4; ++i)
|
|
display_string_in_field(1 + i, 2, labels_left[i],
|
|
label_width_left, ALIGN_RIGHT);
|
|
if (label_width_right)
|
|
for (i = 0; i < 4; ++i) {
|
|
display_string_in_field(1 + i, right_x, labels_right[i],
|
|
label_width_right, ALIGN_LEFT);
|
|
clickable_set(1 + i, right_x, 1 + i, right_x + label_width_right - 1,
|
|
CMD_MIXER_HELP + i, -1);
|
|
}
|
|
}
|
|
|
|
void display_card_info(void)
|
|
{
|
|
snd_hctl_t *hctl;
|
|
snd_ctl_t *ctl;
|
|
snd_ctl_card_info_t *card_info;
|
|
const char *card_name = NULL;
|
|
const char *mixer_name = NULL;
|
|
int err;
|
|
|
|
if (!has_info_items)
|
|
return;
|
|
|
|
snd_ctl_card_info_alloca(&card_info);
|
|
if (mixer_device_name)
|
|
err = snd_mixer_get_hctl(mixer, mixer_device_name, &hctl);
|
|
else
|
|
err = -1;
|
|
if (err >= 0) {
|
|
ctl = snd_hctl_ctl(hctl);
|
|
err = snd_ctl_card_info(ctl, card_info);
|
|
if (err >= 0) {
|
|
card_name = snd_ctl_card_info_get_name(card_info);
|
|
mixer_name = snd_ctl_card_info_get_mixername(card_info);
|
|
}
|
|
}
|
|
|
|
if (card_name)
|
|
wattrset(mixer_widget.window, attrs.mixer_active);
|
|
else {
|
|
wattrset(mixer_widget.window, attrs.mixer_text);
|
|
if (unplugged)
|
|
card_name = _("(unplugged)");
|
|
else
|
|
card_name = "-";
|
|
}
|
|
display_string_in_field(1, info_items_left, card_name, info_items_width, ALIGN_LEFT);
|
|
|
|
if (mixer_name)
|
|
wattrset(mixer_widget.window, attrs.mixer_active);
|
|
else {
|
|
wattrset(mixer_widget.window, attrs.mixer_text);
|
|
mixer_name = "-";
|
|
}
|
|
display_string_in_field(2, info_items_left, mixer_name, info_items_width, ALIGN_LEFT);
|
|
}
|
|
|
|
void display_view_mode(void)
|
|
{
|
|
const char *modes[3] = {
|
|
_("Playback"),
|
|
_("Capture"),
|
|
_("All"),
|
|
};
|
|
int widths[3];
|
|
bool has_view_mode;
|
|
int i;
|
|
|
|
clickable_clear(3, 0, 3, 30);
|
|
if (!has_info_items)
|
|
return;
|
|
|
|
has_view_mode = controls_count > 0 || are_there_any_controls();
|
|
for (i = 0; i < 3; ++i)
|
|
widths[i] = get_mbs_width(modes[i]);
|
|
if (4 + widths[0] + 6 + widths[1] + 6 + widths[2] + 1 <= info_items_width) {
|
|
wmove(mixer_widget.window, 3, info_items_left - 1);
|
|
wattrset(mixer_widget.window, attrs.mixer_text);
|
|
for (i = 0; i < 3; ++i) {
|
|
wprintw(mixer_widget.window, " F%c:", '3' + i);
|
|
if (has_view_mode && (int)view_mode == i) {
|
|
wattrset(mixer_widget.window, attrs.mixer_active);
|
|
wprintw(mixer_widget.window, "[%s]", modes[i]);
|
|
wattrset(mixer_widget.window, attrs.mixer_text);
|
|
} else {
|
|
wprintw(mixer_widget.window, " %s ", modes[i]);
|
|
}
|
|
clickable_set_relative(mixer_widget.window, 0, -(widths[i] + 5), 0, -1,
|
|
CMD_WITH_ARG(CMD_MIXER_SET_VIEW_MODE, i), -1);
|
|
}
|
|
} else {
|
|
wattrset(mixer_widget.window, attrs.mixer_active);
|
|
display_string_in_field(3, info_items_left,
|
|
has_view_mode ? modes[view_mode] : "",
|
|
info_items_width, ALIGN_LEFT);
|
|
}
|
|
}
|
|
|
|
static char *format_gain(long db)
|
|
{
|
|
if (db != SND_CTL_TLV_DB_GAIN_MUTE)
|
|
return casprintf("%.2f", db / 100.0);
|
|
else
|
|
return cstrdup(_("mute"));
|
|
}
|
|
|
|
static void display_focus_item_info(void)
|
|
{
|
|
struct control *control;
|
|
unsigned int index;
|
|
char buf[64];
|
|
long db, db2;
|
|
int sw, sw2;
|
|
char *dbs, *dbs2;
|
|
char *value_info;
|
|
char *item_info;
|
|
int err;
|
|
|
|
if (!has_info_items)
|
|
return;
|
|
wattrset(mixer_widget.window, attrs.mixer_active);
|
|
if (!controls_count || screen_too_small) {
|
|
display_string_in_field(4, info_items_left, "", info_items_width, ALIGN_LEFT);
|
|
return;
|
|
}
|
|
control = &controls[focus_control_index];
|
|
value_info = NULL;
|
|
if (control->flags & TYPE_ENUM) {
|
|
err = snd_mixer_selem_get_enum_item(control->elem, ffs(control->enum_channel_bits) - 1, &index);
|
|
if (err >= 0)
|
|
err = snd_mixer_selem_get_enum_item_name(control->elem, index, sizeof buf - 1, buf);
|
|
if (err >= 0)
|
|
value_info = casprintf(" [%s]", buf);
|
|
} else if (control->flags & (TYPE_PVOLUME | TYPE_CVOLUME)) {
|
|
int (*get_vol_func)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t, long *);
|
|
|
|
if (control->flags & TYPE_PVOLUME)
|
|
get_vol_func = snd_mixer_selem_get_playback_dB;
|
|
else
|
|
get_vol_func = snd_mixer_selem_get_capture_dB;
|
|
if (!(control->flags & HAS_VOLUME_1)) {
|
|
err = get_vol_func(control->elem, control->volume_channels[0], &db);
|
|
if (err >= 0) {
|
|
dbs = format_gain(db);
|
|
value_info = casprintf(" [%s %s]", _("dB gain:"), dbs);
|
|
free(dbs);
|
|
}
|
|
} else {
|
|
err = get_vol_func(control->elem, control->volume_channels[0], &db);
|
|
if (err >= 0)
|
|
err = get_vol_func(control->elem, control->volume_channels[1], &db2);
|
|
if (err >= 0) {
|
|
dbs = format_gain(db);
|
|
dbs2 = format_gain(db2);
|
|
value_info = casprintf(_(" [%s %s, %s]"), _("dB gain:"), dbs, dbs2);
|
|
free(dbs);
|
|
free(dbs2);
|
|
}
|
|
}
|
|
} else if (control->flags & TYPE_PSWITCH) {
|
|
if (!(control->flags & HAS_PSWITCH_1)) {
|
|
err = snd_mixer_selem_get_playback_switch(control->elem, control->pswitch_channels[0], &sw);
|
|
if (err >= 0 && !sw)
|
|
value_info = casprintf(" [%s]", _("Off"));
|
|
} else {
|
|
err = snd_mixer_selem_get_playback_switch(control->elem, control->pswitch_channels[0], &sw);
|
|
if (err >= 0)
|
|
err = snd_mixer_selem_get_playback_switch(control->elem, control->pswitch_channels[1], &sw2);
|
|
if (err >= 0 && (!sw || !sw2))
|
|
value_info = casprintf(" [%s, %s]", sw ? _("On") : _("Off"), sw2 ? _("On") : _("Off"));
|
|
}
|
|
} else if (control->flags & TYPE_CSWITCH) {
|
|
if (!(control->flags & HAS_CSWITCH_1)) {
|
|
err = snd_mixer_selem_get_capture_switch(control->elem, control->cswitch_channels[0], &sw);
|
|
if (err >= 0 && !sw)
|
|
value_info = casprintf(" [%s]", _("Off"));
|
|
} else {
|
|
err = snd_mixer_selem_get_capture_switch(control->elem, control->cswitch_channels[0], &sw);
|
|
if (err >= 0)
|
|
err = snd_mixer_selem_get_capture_switch(control->elem, control->cswitch_channels[1], &sw2);
|
|
if (err >= 0 && (!sw || !sw2))
|
|
value_info = casprintf(" [%s, %s]", sw ? _("On") : _("Off"), sw2 ? _("On") : _("Off"));
|
|
}
|
|
}
|
|
item_info = casprintf("%s%s", control->name, value_info ? value_info : "");
|
|
free(value_info);
|
|
display_string_in_field(4, info_items_left, item_info, info_items_width, ALIGN_LEFT);
|
|
free(item_info);
|
|
}
|
|
|
|
static void clear_controls_display(void)
|
|
{
|
|
int i;
|
|
|
|
clickable_clear(5, 0, -1, -1);
|
|
wattrset(mixer_widget.window, attrs.mixer_frame);
|
|
for (i = 5; i < screen_lines - 1; ++i)
|
|
mvwprintw(mixer_widget.window, i, 1, "%*s", screen_cols - 2, "");
|
|
}
|
|
|
|
static void center_string(int line, const char *s)
|
|
{
|
|
int width = get_mbs_width(s);
|
|
if (width <= screen_cols - 2)
|
|
mvwaddstr(mixer_widget.window, line, (screen_cols - width) / 2, s);
|
|
}
|
|
|
|
static void display_unplugged(void)
|
|
{
|
|
int lines, top, left;
|
|
bool boojum;
|
|
|
|
lines = screen_lines - 6;
|
|
if (lines < 2)
|
|
return;
|
|
top = lines / 2;
|
|
boojum = lines >= 10 && screen_cols >= 48;
|
|
top -= boojum ? 5 : 1;
|
|
if (top < 5)
|
|
top = 5;
|
|
if (boojum) {
|
|
left = (screen_cols - 46) / 2;
|
|
wattrset(mixer_widget.window, attrs.mixer_text);
|
|
mvwaddstr(mixer_widget.window, top + 0, left, "In the midst of the word he was trying to say,");
|
|
mvwaddstr(mixer_widget.window, top + 1, left + 2, "In the midst of his laughter and glee,");
|
|
mvwaddstr(mixer_widget.window, top + 2, left, "He had softly and suddenly vanished away---");
|
|
mvwaddstr(mixer_widget.window, top + 3, left + 2, "For the Snark was a Boojum, you see.");
|
|
mvwchgat(mixer_widget.window, top + 3, left + 16, 3, /* ^^^ */
|
|
attrs.mixer_text | A_BOLD, PAIR_NUMBER(attrs.mixer_text), NULL);
|
|
mvwaddstr(mixer_widget.window, top + 5, left, "(Lewis Carroll, \"The Hunting of the Snark\")");
|
|
top += 8;
|
|
}
|
|
wattrset(mixer_widget.window, attrs.errormsg);
|
|
center_string(top, _("The sound device was unplugged."));
|
|
center_string(top + 1, _("Press F6 to select another sound card."));
|
|
}
|
|
|
|
static void display_no_controls(void)
|
|
{
|
|
int y;
|
|
const char *msg;
|
|
|
|
y = (screen_lines - 6) / 2 - 1;
|
|
if (y < 5)
|
|
y = 5;
|
|
if (y >= screen_lines - 1)
|
|
return;
|
|
wattrset(mixer_widget.window, attrs.infomsg);
|
|
if (view_mode == VIEW_MODE_PLAYBACK && are_there_any_controls())
|
|
msg = _("This sound device does not have any playback controls.");
|
|
else if (view_mode == VIEW_MODE_CAPTURE && are_there_any_controls())
|
|
msg = _("This sound device does not have any capture controls.");
|
|
else
|
|
msg = _("This sound device does not have any controls.");
|
|
center_string(y, msg);
|
|
}
|
|
|
|
static void display_string_centered_in_control(int y, int col, const char *s, int width)
|
|
{
|
|
int left, x;
|
|
|
|
left = first_control_x + col * (control_width + 1);
|
|
x = left + (control_width - width) / 2;
|
|
display_string_in_field(y, x, s, width, ALIGN_CENTER);
|
|
}
|
|
|
|
static void display_control(unsigned int control_index)
|
|
{
|
|
struct control *control;
|
|
int col;
|
|
int i, c;
|
|
int left, frame_left;
|
|
int bar_height;
|
|
double volumes[2];
|
|
int switches[2];
|
|
unsigned int index;
|
|
const char *s;
|
|
char buf[64];
|
|
int err;
|
|
|
|
control = &controls[control_index];
|
|
col = control_index - first_visible_control_index;
|
|
left = first_control_x + col * (control_width + 1);
|
|
frame_left = left + (control_width - 4) / 2;
|
|
if (control->flags & IS_ACTIVE)
|
|
wattrset(mixer_widget.window, attrs.ctl_frame);
|
|
else
|
|
wattrset(mixer_widget.window, attrs.ctl_inactive);
|
|
if (control->flags & (TYPE_PVOLUME | TYPE_CVOLUME)) {
|
|
mvwaddch(mixer_widget.window, base_y - volume_height - 1, frame_left, ACS_ULCORNER);
|
|
waddch(mixer_widget.window, ACS_HLINE);
|
|
waddch(mixer_widget.window, ACS_HLINE);
|
|
waddch(mixer_widget.window, ACS_URCORNER);
|
|
mvwvline(mixer_widget.window, base_y - volume_height, frame_left, ACS_VLINE, volume_height);
|
|
mvwvline(mixer_widget.window, base_y - volume_height, frame_left + 3, ACS_VLINE, volume_height);
|
|
mvwaddch(mixer_widget.window, base_y, frame_left,
|
|
control->flags & TYPE_PSWITCH ? ACS_LTEE : ACS_LLCORNER);
|
|
waddch(mixer_widget.window, ACS_HLINE);
|
|
waddch(mixer_widget.window, ACS_HLINE);
|
|
waddch(mixer_widget.window,
|
|
control->flags & TYPE_PSWITCH ? ACS_RTEE : ACS_LRCORNER);
|
|
} else if (control->flags & TYPE_PSWITCH) {
|
|
mvwaddch(mixer_widget.window, base_y, frame_left, ACS_ULCORNER);
|
|
waddch(mixer_widget.window, ACS_HLINE);
|
|
waddch(mixer_widget.window, ACS_HLINE);
|
|
waddch(mixer_widget.window, ACS_URCORNER);
|
|
}
|
|
if (control->flags & TYPE_PSWITCH) {
|
|
mvwaddch(mixer_widget.window, base_y + 1, frame_left, ACS_VLINE);
|
|
mvwaddch(mixer_widget.window, base_y + 1, frame_left + 3, ACS_VLINE);
|
|
mvwaddch(mixer_widget.window, base_y + 2, frame_left, ACS_LLCORNER);
|
|
waddch(mixer_widget.window, ACS_HLINE);
|
|
waddch(mixer_widget.window, ACS_HLINE);
|
|
waddch(mixer_widget.window, ACS_LRCORNER);
|
|
}
|
|
if (control->flags & (TYPE_PVOLUME | TYPE_CVOLUME)) {
|
|
double (*get_vol_func)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t);
|
|
|
|
if (control->flags & TYPE_PVOLUME)
|
|
get_vol_func = get_normalized_playback_volume;
|
|
else
|
|
get_vol_func = get_normalized_capture_volume;
|
|
volumes[0] = get_vol_func(control->elem, control->volume_channels[0]);
|
|
if (control->flags & HAS_VOLUME_1)
|
|
volumes[1] = get_vol_func(control->elem, control->volume_channels[1]);
|
|
else
|
|
volumes[1] = volumes[0];
|
|
|
|
if (control->flags & IS_ACTIVE)
|
|
wattrset(mixer_widget.window, 0);
|
|
for (c = 0; c < 2; c++) {
|
|
bar_height = lrint(volumes[c] * volume_height);
|
|
for (i = 0; i < volume_height; ++i) {
|
|
chtype ch;
|
|
if (i + 1 > bar_height)
|
|
ch = ' ' | (control->flags & IS_ACTIVE ?
|
|
attrs.ctl_frame : 0);
|
|
else {
|
|
ch = ACS_CKBOARD;
|
|
if (!(control->flags & IS_ACTIVE))
|
|
;
|
|
#ifdef TRICOLOR_VOLUME_BAR
|
|
else if (i > volume_height * 8 / 10)
|
|
ch |= attrs.ctl_bar_hi;
|
|
else if (i > volume_height * 4 / 10)
|
|
ch |= attrs.ctl_bar_mi;
|
|
#endif
|
|
else
|
|
ch |= attrs.ctl_bar_lo;
|
|
}
|
|
mvwaddch(mixer_widget.window, base_y - i - 1,
|
|
frame_left + c + 1, ch);
|
|
}
|
|
}
|
|
clickable_set(base_y - volume_height, frame_left + 1, base_y, frame_left + 2,
|
|
CMD_MIXER_MOUSE_CLICK_VOLUME_BAR, control_index);
|
|
if (control->flags & IS_ACTIVE)
|
|
wattrset(mixer_widget.window, attrs.mixer_active);
|
|
if (!(control->flags & HAS_VOLUME_1)) {
|
|
sprintf(buf, "%d", (int)lrint(volumes[0] * 100));
|
|
display_string_in_field(values_y, frame_left - 2, buf, 8, ALIGN_CENTER);
|
|
} else {
|
|
mvwprintw(mixer_widget.window, values_y, frame_left - 2,
|
|
"%3d", (int)lrint(volumes[0] * 100));
|
|
if (control->flags & IS_ACTIVE)
|
|
wattrset(mixer_widget.window, attrs.ctl_frame);
|
|
waddstr(mixer_widget.window, "<>");
|
|
if (control->flags & IS_ACTIVE)
|
|
wattrset(mixer_widget.window, attrs.mixer_active);
|
|
wprintw(mixer_widget.window, "%-3d", (int)lrint(volumes[1] * 100));
|
|
}
|
|
}
|
|
|
|
if (control->flags & TYPE_PSWITCH) {
|
|
err = snd_mixer_selem_get_playback_switch(control->elem, control->pswitch_channels[0], &switches[0]);
|
|
if (err >= 0 && (control->flags & HAS_PSWITCH_1))
|
|
err = snd_mixer_selem_get_playback_switch(control->elem, control->pswitch_channels[1], &switches[1]);
|
|
else
|
|
switches[1] = switches[0];
|
|
if (err < 0)
|
|
return;
|
|
if (control->flags & IS_ACTIVE)
|
|
wattrset(mixer_widget.window, 0);
|
|
mvwaddch(mixer_widget.window, base_y + 1, frame_left + 1,
|
|
switches[0]
|
|
/* TRANSLATORS: playback on; one character */
|
|
? _("O")[0] | (control->flags & IS_ACTIVE ? attrs.ctl_nomute : 0)
|
|
/* TRANSLATORS: playback muted; one character */
|
|
: _("M")[0] | (control->flags & IS_ACTIVE ? attrs.ctl_mute : 0));
|
|
waddch(mixer_widget.window,
|
|
switches[1]
|
|
? _("O")[0] | (control->flags & IS_ACTIVE ? attrs.ctl_nomute : 0)
|
|
: _("M")[0] | (control->flags & IS_ACTIVE ? attrs.ctl_mute : 0));
|
|
clickable_set(base_y + 1, frame_left + 1, base_y + 1, frame_left + 2,
|
|
CMD_MIXER_MOUSE_CLICK_MUTE, control_index);
|
|
}
|
|
|
|
if (control->flags & TYPE_CSWITCH) {
|
|
err = snd_mixer_selem_get_capture_switch(control->elem, control->cswitch_channels[0], &switches[0]);
|
|
if (err >= 0 && (control->flags & HAS_CSWITCH_1))
|
|
err = snd_mixer_selem_get_capture_switch(control->elem, control->cswitch_channels[1], &switches[1]);
|
|
else
|
|
switches[1] = switches[0];
|
|
if (err < 0)
|
|
return;
|
|
if (control->flags & IS_ACTIVE)
|
|
wattrset(mixer_widget.window, switches[0] ? attrs.ctl_capture : attrs.ctl_nocapture);
|
|
/* TRANSLATORS: "left"; no more than two characters */
|
|
display_string_in_field(cswitch_y - 1, frame_left - 2, switches[0] ? _("L") : "", 2, ALIGN_RIGHT);
|
|
clickable_set(cswitch_y - 1, frame_left - 2, cswitch_y - 1, frame_left - 1,
|
|
CMD_WITH_ARG(CMD_MIXER_TOGGLE_CAPTURE, LEFT), control_index);
|
|
if (control->flags & IS_ACTIVE)
|
|
wattrset(mixer_widget.window, switches[1] ? attrs.ctl_capture : attrs.ctl_nocapture);
|
|
/* TRANSLATORS: "right"; no more than two characters */
|
|
display_string_in_field(cswitch_y - 1, frame_left + 4, switches[1] ? _("R") : "", 2, ALIGN_LEFT);
|
|
clickable_set(cswitch_y - 1, frame_left + 4, cswitch_y - 1, frame_left + 5,
|
|
CMD_WITH_ARG(CMD_MIXER_TOGGLE_CAPTURE, RIGHT), control_index);
|
|
/* TRANSLATORS: no more than eight characters */
|
|
s = _("CAPTURE");
|
|
if (switches[0] || switches[1]) {
|
|
if (control->flags & IS_ACTIVE)
|
|
wattrset(mixer_widget.window, attrs.ctl_capture);
|
|
display_string_in_field(cswitch_y, frame_left - 2, s, 8, ALIGN_CENTER);
|
|
} else {
|
|
i = get_mbs_width(s);
|
|
if (i > 8)
|
|
i = 8;
|
|
memset(buf, '-', i);
|
|
buf[i] = '\0';
|
|
if (control->flags & IS_ACTIVE)
|
|
wattrset(mixer_widget.window, attrs.ctl_nocapture);
|
|
display_string_in_field(cswitch_y, frame_left - 2, buf, 8, ALIGN_CENTER);
|
|
}
|
|
clickable_set(cswitch_y, frame_left - 2, cswitch_y, frame_left - 2 + 8,
|
|
CMD_WITH_ARG(CMD_MIXER_TOGGLE_CAPTURE, LEFT|RIGHT), control_index);
|
|
}
|
|
|
|
if (control->flags & TYPE_ENUM) {
|
|
err = snd_mixer_selem_get_enum_item(control->elem, ffs(control->enum_channel_bits) - 1, &index);
|
|
if (err < 0)
|
|
return;
|
|
err = snd_mixer_selem_get_enum_item_name(control->elem, index, sizeof buf - 1, buf);
|
|
if (err < 0)
|
|
return;
|
|
if (control->flags & IS_ACTIVE)
|
|
wattrset(mixer_widget.window, attrs.mixer_active);
|
|
display_string_centered_in_control(base_y, col, buf, control_width);
|
|
clickable_set_relative(mixer_widget.window, 0, -control_name_width, 0, -2,
|
|
CMD_MIXER_MOUSE_CLICK_CONTROL_ENUM, control_index);
|
|
}
|
|
|
|
if ((int)control_index == focus_control_index) {
|
|
i = first_control_x + col * (control_width + 1) + (control_width - control_name_width) / 2;
|
|
wattrset(mixer_widget.window, attrs.ctl_mark_focus);
|
|
mvwaddch(mixer_widget.window, name_y, i - 1, '<');
|
|
mvwaddch(mixer_widget.window, name_y, i + control_name_width, '>');
|
|
if (control->flags & IS_ACTIVE)
|
|
wattrset(mixer_widget.window, attrs.ctl_label_focus);
|
|
else
|
|
wattrset(mixer_widget.window, attrs.ctl_label_inactive);
|
|
} else {
|
|
if (control->flags & IS_ACTIVE)
|
|
wattrset(mixer_widget.window, attrs.ctl_label);
|
|
else
|
|
wattrset(mixer_widget.window, attrs.ctl_label_inactive);
|
|
}
|
|
display_string_centered_in_control(name_y, col, control->name, control_name_width);
|
|
clickable_set_relative(mixer_widget.window, -1, -control_name_width, 0, -2,
|
|
CMD_WITH_ARG(CMD_MIXER_FOCUS_CONTROL, control_index), -1);
|
|
if (channel_name_y > name_y) {
|
|
if (control->flags & IS_MULTICH) {
|
|
switch (control->flags & MULTICH_MASK) {
|
|
case 0:
|
|
default:
|
|
s = _("Front");
|
|
break;
|
|
case 1:
|
|
s = _("Rear");
|
|
break;
|
|
case 2:
|
|
s = _("Center");
|
|
break;
|
|
case 3:
|
|
s = _("Woofer");
|
|
break;
|
|
case 4:
|
|
s = _("Side");
|
|
break;
|
|
}
|
|
} else {
|
|
s = "";
|
|
wattrset(mixer_widget.window, attrs.mixer_frame);
|
|
}
|
|
display_string_centered_in_control(channel_name_y, col, s,
|
|
control_name_width);
|
|
}
|
|
}
|
|
|
|
static void display_scroll_indicators(void)
|
|
{
|
|
int y0, y1;
|
|
chtype left, right;
|
|
|
|
if (screen_too_small)
|
|
return;
|
|
y0 = screen_lines * 3 / 8;
|
|
y1 = screen_lines * 5 / 8;
|
|
left = first_visible_control_index > 0 ? ACS_LARROW : ACS_VLINE;
|
|
right = first_visible_control_index + visible_controls < (int)controls_count
|
|
? ACS_RARROW : ACS_VLINE;
|
|
wattrset(mixer_widget.window, attrs.mixer_frame);
|
|
mvwvline(mixer_widget.window, y0, 0, left, y1 - y0 + 1);
|
|
mvwvline(mixer_widget.window, y0, screen_cols -1, right, y1 - y0 + 1);
|
|
clickable_set(y0, 0, y1, 0,
|
|
CMD_WITH_ARG(CMD_MIXER_PREVIOUS, visible_controls), -1);
|
|
clickable_set(y0, screen_cols - 1, y1, screen_cols - 1,
|
|
CMD_WITH_ARG(CMD_MIXER_NEXT, visible_controls), -1);
|
|
}
|
|
|
|
void display_controls(void)
|
|
{
|
|
int i;
|
|
|
|
if (first_visible_control_index > (int)controls_count - visible_controls)
|
|
first_visible_control_index = controls_count - visible_controls;
|
|
if (first_visible_control_index > focus_control_index)
|
|
first_visible_control_index = focus_control_index;
|
|
else if (first_visible_control_index < focus_control_index - visible_controls + 1 && visible_controls)
|
|
first_visible_control_index = focus_control_index - visible_controls + 1;
|
|
|
|
clear_controls_display();
|
|
|
|
display_focus_item_info();
|
|
|
|
if (controls_count > 0) {
|
|
if (!screen_too_small)
|
|
for (i = 0; i < visible_controls; ++i)
|
|
display_control(first_visible_control_index + i);
|
|
} else if (unplugged) {
|
|
display_unplugged();
|
|
} else if (mixer_device_name) {
|
|
display_no_controls();
|
|
}
|
|
display_scroll_indicators();
|
|
}
|
|
|
|
void compute_controls_layout(void)
|
|
{
|
|
bool any_volume, any_pswitch, any_cswitch, any_multich;
|
|
int max_width, name_len;
|
|
int height, space;
|
|
unsigned int i;
|
|
|
|
if (controls_count == 0 || screen_too_small) {
|
|
visible_controls = 0;
|
|
return;
|
|
}
|
|
|
|
any_volume = FALSE;
|
|
any_pswitch = FALSE;
|
|
any_cswitch = FALSE;
|
|
any_multich = FALSE;
|
|
for (i = 0; i < controls_count; ++i) {
|
|
if (controls[i].flags & (TYPE_PVOLUME | TYPE_CVOLUME))
|
|
any_volume = 1;
|
|
if (controls[i].flags & TYPE_PSWITCH)
|
|
any_pswitch = 1;
|
|
if (controls[i].flags & TYPE_CSWITCH)
|
|
any_cswitch = 1;
|
|
if (controls[i].flags & IS_MULTICH)
|
|
any_multich = 1;
|
|
}
|
|
|
|
max_width = 8;
|
|
for (i = 0; i < controls_count; ++i) {
|
|
name_len = strlen(controls[i].name);
|
|
if (name_len > max_width)
|
|
max_width = name_len;
|
|
}
|
|
max_width = (max_width + 1) & ~1;
|
|
|
|
control_width = (screen_cols - 3 - (int)controls_count) / controls_count;
|
|
if (control_width < 8)
|
|
control_width = 8;
|
|
if (control_width > max_width)
|
|
control_width = max_width;
|
|
if (control_width > screen_cols - 4)
|
|
control_width = screen_cols - 4;
|
|
|
|
visible_controls = (screen_cols - 3) / (control_width + 1);
|
|
if (visible_controls > (int)controls_count)
|
|
visible_controls = controls_count;
|
|
|
|
first_control_x = 2 + (screen_cols - 3 - visible_controls * (control_width + 1)) / 2;
|
|
|
|
if (control_width < max_width)
|
|
control_name_width = control_width;
|
|
else
|
|
control_name_width = max_width;
|
|
|
|
height = 2;
|
|
if (any_volume)
|
|
height += 2;
|
|
if (any_pswitch)
|
|
height += 2;
|
|
if (any_cswitch)
|
|
height += 1;
|
|
if (any_multich)
|
|
height += 1;
|
|
if (any_volume) {
|
|
space = screen_lines - 6 - height;
|
|
if (space <= 1)
|
|
volume_height = 1;
|
|
else if (space <= 10)
|
|
volume_height = space;
|
|
else
|
|
volume_height = 10 + (space - 10) / 2;
|
|
height += volume_height;
|
|
}
|
|
|
|
space = screen_lines - 6 - height;
|
|
channel_name_y = screen_lines - 2 - space / 2;
|
|
name_y = channel_name_y - any_multich;
|
|
values_y = name_y - any_volume;
|
|
cswitch_y = values_y - any_cswitch;
|
|
base_y = cswitch_y - 1 - 2 * any_pswitch;
|
|
}
|