| /* |
| * 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" |
| |
| 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"), |
| }; |
| unsigned int label_width_left, label_width_right; |
| unsigned int right_x, i; |
| |
| 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, attr_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); |
| } |
| |
| 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, attr_mixer_active); |
| else { |
| wattrset(mixer_widget.window, attr_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, attr_mixer_active); |
| else { |
| wattrset(mixer_widget.window, attr_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"), |
| }; |
| unsigned int widths[3]; |
| bool has_view_mode; |
| int i; |
| |
| 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); |
| wattrset(mixer_widget.window, attr_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, attr_mixer_active); |
| wprintw(mixer_widget.window, "[%s]", modes[i]); |
| wattrset(mixer_widget.window, attr_mixer_text); |
| } else { |
| wprintw(mixer_widget.window, " %s ", modes[i]); |
| } |
| if (i < 2) |
| waddch(mixer_widget.window, ' '); |
| } |
| } else { |
| wattrset(mixer_widget.window, attr_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, attr_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; |
| |
| wattrset(mixer_widget.window, attr_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, attr_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, /* ^^^ */ |
| attr_mixer_text | A_BOLD, PAIR_NUMBER(attr_mixer_text), NULL); |
| mvwaddstr(mixer_widget.window, top + 5, left, "(Lewis Carroll, \"The Hunting of the Snark\")"); |
| top += 8; |
| } |
| wattrset(mixer_widget.window, attr_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, attr_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, attr_ctl_frame); |
| else |
| wattrset(mixer_widget.window, attr_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); |
| for (i = 0; i < volume_height; ++i) { |
| mvwaddch(mixer_widget.window, base_y - i - 1, frame_left, ACS_VLINE); |
| mvwaddch(mixer_widget.window, base_y - i - 1, frame_left + 3, ACS_VLINE); |
| } |
| 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 ? |
| attr_ctl_frame : 0); |
| else { |
| ch = ACS_CKBOARD; |
| if (!(control->flags & IS_ACTIVE)) |
| ; |
| #ifdef TRICOLOR_VOLUME_BAR |
| else if (i > volume_height * 8 / 10) |
| ch |= attr_ctl_bar_hi; |
| else if (i > volume_height * 4 / 10) |
| ch |= attr_ctl_bar_mi; |
| #endif |
| else |
| ch |= attr_ctl_bar_lo; |
| } |
| mvwaddch(mixer_widget.window, base_y - i - 1, |
| frame_left + c + 1, ch); |
| } |
| } |
| if (control->flags & IS_ACTIVE) |
| wattrset(mixer_widget.window, attr_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, attr_ctl_frame); |
| waddstr(mixer_widget.window, "<>"); |
| if (control->flags & IS_ACTIVE) |
| wattrset(mixer_widget.window, attr_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 ? attr_ctl_nomute : 0) |
| /* TRANSLATORS: playback muted; one character */ |
| : _("M")[0] | (control->flags & IS_ACTIVE ? attr_ctl_mute : 0)); |
| waddch(mixer_widget.window, |
| switches[1] |
| ? _("O")[0] | (control->flags & IS_ACTIVE ? attr_ctl_nomute : 0) |
| : _("M")[0] | (control->flags & IS_ACTIVE ? attr_ctl_mute : 0)); |
| } |
| |
| 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] ? attr_ctl_capture : attr_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); |
| if (control->flags & IS_ACTIVE) |
| wattrset(mixer_widget.window, switches[1] ? attr_ctl_capture : attr_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); |
| /* TRANSLATORS: no more than eight characters */ |
| s = _("CAPTURE"); |
| if (switches[0] || switches[1]) { |
| if (control->flags & IS_ACTIVE) |
| wattrset(mixer_widget.window, attr_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, attr_ctl_nocapture); |
| display_string_in_field(cswitch_y, frame_left - 2, buf, 8, ALIGN_CENTER); |
| } |
| } |
| |
| 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, attr_mixer_active); |
| display_string_centered_in_control(base_y, col, buf, control_width); |
| } |
| |
| if (control_index == focus_control_index) { |
| i = first_control_x + col * (control_width + 1) + (control_width - control_name_width) / 2; |
| wattrset(mixer_widget.window, attr_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, attr_ctl_label_focus); |
| else |
| wattrset(mixer_widget.window, attr_ctl_label_inactive); |
| } else { |
| if (control->flags & IS_ACTIVE) |
| wattrset(mixer_widget.window, attr_ctl_label); |
| else |
| wattrset(mixer_widget.window, attr_ctl_label_inactive); |
| } |
| display_string_centered_in_control(name_y, col, control->name, control_name_width); |
| 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, attr_mixer_frame); |
| } |
| display_string_centered_in_control(channel_name_y, col, s, |
| control_name_width); |
| } |
| } |
| |
| static void display_scroll_indicators(void) |
| { |
| int y0, y1, y; |
| 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 < controls_count |
| ? ACS_RARROW : ACS_VLINE; |
| wattrset(mixer_widget.window, attr_mixer_frame); |
| for (y = y0; y <= y1; ++y) { |
| mvwaddch(mixer_widget.window, y, 0, left); |
| mvwaddch(mixer_widget.window, y, screen_cols - 1, right); |
| } |
| } |
| |
| void display_controls(void) |
| { |
| unsigned int i; |
| |
| if (first_visible_control_index > 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(); |
| controls_changed = FALSE; |
| } |
| |
| 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 > 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; |
| } |