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