1/* 2 * mixer_controls.c - handles mixer controls and mapping from selems 3 * Copyright (c) 1998,1999 Tim Janik 4 * Jaroslav Kysela <perex@perex.cz> 5 * Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de> 6 * 7 * This program is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation, either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21#include "aconfig.h" 22#include <stdlib.h> 23#include <string.h> 24#include <assert.h> 25#include CURSESINC 26#include <alsa/asoundlib.h> 27#include "utils.h" 28#include "mem.h" 29#include "mixer_display.h" 30#include "mixer_widget.h" 31#include "mixer_controls.h" 32 33struct control *controls; 34unsigned int controls_count; 35 36static const snd_mixer_selem_channel_id_t supported_channels[] = { 37 SND_MIXER_SCHN_FRONT_LEFT, 38 SND_MIXER_SCHN_FRONT_RIGHT, 39 SND_MIXER_SCHN_REAR_LEFT, 40 SND_MIXER_SCHN_REAR_RIGHT, 41 SND_MIXER_SCHN_FRONT_CENTER, 42 SND_MIXER_SCHN_WOOFER, 43 SND_MIXER_SCHN_SIDE_LEFT, 44 SND_MIXER_SCHN_SIDE_RIGHT, 45}; 46#define LAST_SUPPORTED_CHANNEL SND_MIXER_SCHN_SIDE_RIGHT 47 48static const snd_mixer_selem_channel_id_t control_channels[][2] = { 49 { SND_MIXER_SCHN_FRONT_LEFT, SND_MIXER_SCHN_FRONT_RIGHT }, 50 { SND_MIXER_SCHN_REAR_LEFT, SND_MIXER_SCHN_REAR_RIGHT }, 51 { SND_MIXER_SCHN_FRONT_CENTER, SND_MIXER_SCHN_UNKNOWN }, 52 { SND_MIXER_SCHN_WOOFER, SND_MIXER_SCHN_UNKNOWN }, 53 { SND_MIXER_SCHN_SIDE_LEFT, SND_MIXER_SCHN_SIDE_RIGHT }, 54}; 55 56bool are_there_any_controls(void) 57{ 58 snd_mixer_elem_t *elem; 59 unsigned int i; 60 61 for (elem = snd_mixer_first_elem(mixer); 62 elem; 63 elem = snd_mixer_elem_next(elem)) { 64 if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_SIMPLE) 65 continue; 66 if (snd_mixer_selem_is_enumerated(elem)) 67 return TRUE; 68 if (snd_mixer_selem_has_playback_volume_joined(elem) || 69 snd_mixer_selem_has_capture_volume_joined(elem) || 70 snd_mixer_selem_has_playback_switch_joined(elem) || 71 snd_mixer_selem_has_capture_switch_joined(elem)) 72 return TRUE; 73 for (i = 0; i < ARRAY_SIZE(supported_channels); ++i) 74 if (snd_mixer_selem_has_playback_channel(elem, supported_channels[i]) || 75 snd_mixer_selem_has_capture_channel(elem, supported_channels[i])) 76 return TRUE; 77 } 78 return FALSE; 79} 80 81static bool has_more_than_front_capture_channels(snd_mixer_elem_t *elem) 82{ 83 unsigned int i; 84 85 for (i = 2; i < ARRAY_SIZE(supported_channels); ++i) 86 if (snd_mixer_selem_has_capture_channel(elem, supported_channels[i])) 87 return TRUE; 88 return FALSE; 89} 90 91static bool has_any_control_channel(snd_mixer_elem_t *elem, 92 const snd_mixer_selem_channel_id_t channels[2], 93 int (*has_channel)(snd_mixer_elem_t *, snd_mixer_selem_channel_id_t)) 94{ 95 return has_channel(elem, channels[0]) || 96 (channels[1] != SND_MIXER_SCHN_UNKNOWN && has_channel(elem, channels[1])); 97} 98 99static bool has_merged_cswitch(snd_mixer_elem_t *elem) 100{ 101 bool pvol, psw; 102 unsigned int i; 103 104 pvol = snd_mixer_selem_has_playback_volume(elem); 105 psw = snd_mixer_selem_has_playback_switch(elem); 106 if ((pvol || psw) && 107 snd_mixer_selem_has_capture_switch(elem) && 108 !snd_mixer_selem_has_capture_volume(elem)) { 109 if (snd_mixer_selem_has_capture_switch_joined(elem)) 110 return TRUE; 111 else if (((pvol && snd_mixer_selem_has_playback_volume_joined(elem)) || 112 (psw && snd_mixer_selem_has_playback_switch_joined(elem))) && 113 has_more_than_front_capture_channels(elem)) 114 return FALSE; 115 for (i = 0; i < ARRAY_SIZE(control_channels); ++i) { 116 if (has_any_control_channel(elem, control_channels[i], snd_mixer_selem_has_capture_channel) && 117 !has_any_control_channel(elem, control_channels[i], snd_mixer_selem_has_playback_channel)) 118 return FALSE; 119 } 120 return TRUE; 121 } 122 return FALSE; 123} 124 125static unsigned int get_playback_controls_count(snd_mixer_elem_t *elem) 126{ 127 unsigned int count = 0; 128 unsigned int i; 129 int has_vol, has_sw; 130 131 has_vol = snd_mixer_selem_has_playback_volume(elem); 132 has_sw = snd_mixer_selem_has_playback_switch(elem); 133 if (!has_vol && !has_sw) 134 return 0; 135 if ((!has_vol || snd_mixer_selem_has_playback_volume_joined(elem)) && 136 (!has_sw || snd_mixer_selem_has_playback_switch_joined(elem))) 137 return 1; 138 for (i = 0; i < ARRAY_SIZE(control_channels); ++i) { 139 if (snd_mixer_selem_has_playback_channel(elem, control_channels[i][0]) || 140 (control_channels[i][1] != SND_MIXER_SCHN_UNKNOWN && 141 snd_mixer_selem_has_playback_channel(elem, control_channels[i][1]))) 142 ++count; 143 } 144 return count; 145} 146 147static unsigned int get_capture_controls_count(snd_mixer_elem_t *elem) 148{ 149 unsigned int count = 0; 150 unsigned int i; 151 int has_vol, has_sw; 152 153 has_vol = snd_mixer_selem_has_capture_volume(elem); 154 has_sw = snd_mixer_selem_has_capture_switch(elem); 155 if ((!has_vol && !has_sw) || 156 (view_mode == VIEW_MODE_ALL && has_merged_cswitch(elem))) 157 return 0; 158 if ((!has_vol || snd_mixer_selem_has_capture_volume_joined(elem)) && 159 (!has_sw || snd_mixer_selem_has_capture_switch_joined(elem))) 160 return 1; 161 for (i = 0; i < ARRAY_SIZE(control_channels); ++i) { 162 if (snd_mixer_selem_has_capture_channel(elem, control_channels[i][0]) || 163 (control_channels[i][1] != SND_MIXER_SCHN_UNKNOWN && 164 snd_mixer_selem_has_capture_channel(elem, control_channels[i][1]))) 165 ++count; 166 } 167 return count; 168} 169 170static unsigned int get_controls_count_for_elem(snd_mixer_elem_t *elem) 171{ 172 unsigned int p, c; 173 174 if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_SIMPLE) 175 return 0; 176 if (snd_mixer_selem_is_enumerated(elem)) { 177 switch (view_mode) { 178 case VIEW_MODE_PLAYBACK: 179 return snd_mixer_selem_is_enum_capture(elem) ? 0 : 1; 180 case VIEW_MODE_CAPTURE: 181 return snd_mixer_selem_is_enum_capture(elem) ? 1 : 0; 182 case VIEW_MODE_ALL: 183 default: 184 return 1; 185 } 186 } 187 switch (view_mode) { 188 case VIEW_MODE_PLAYBACK: 189 return get_playback_controls_count(elem); 190 case VIEW_MODE_CAPTURE: 191 return get_capture_controls_count(elem); 192 case VIEW_MODE_ALL: 193 default: 194 p = get_playback_controls_count(elem); 195 c = get_capture_controls_count(elem); 196 return has_merged_cswitch(elem) ? p : p + c; 197 } 198} 199 200static void create_name(struct control *control) 201{ 202 unsigned int index; 203 char *s; 204 205 index = snd_mixer_selem_get_index(control->elem); 206 if (index > 0) 207 control->name = casprintf("%s %u", snd_mixer_selem_get_name(control->elem), index); 208 else 209 control->name = cstrdup(snd_mixer_selem_get_name(control->elem)); 210 211 while ((s = strstr(control->name, "IEC958")) != NULL) 212 memcpy(s, "S/PDIF", 6); 213} 214 215static unsigned int create_controls_for_elem(snd_mixer_elem_t *elem, struct control *control) 216{ 217 unsigned int count = 0; 218 unsigned int i; 219 unsigned int enum_index; 220 struct control *front_control = NULL; 221 bool has_pvol, has_psw; 222 bool has_cvol, has_csw; 223 bool has_channel[LAST_SUPPORTED_CHANNEL + 1]; 224 bool merged_cswitch; 225 bool has_ch0, has_ch1; 226 227 if (snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_SIMPLE) 228 return 0; 229 if (snd_mixer_selem_is_enumerated(elem)) { 230 if ((view_mode == VIEW_MODE_PLAYBACK && snd_mixer_selem_is_enum_capture(elem)) || 231 (view_mode == VIEW_MODE_CAPTURE && !snd_mixer_selem_is_enum_capture(elem))) 232 return 0; 233 control->elem = elem; 234 control->flags = TYPE_ENUM; 235 control->enum_channel_bits = 0; 236 for (i = 0; i <= SND_MIXER_SCHN_LAST; ++i) 237 if (snd_mixer_selem_get_enum_item(control->elem, (snd_mixer_selem_channel_id_t)i, &enum_index) >= 0) 238 control->enum_channel_bits |= 1 << i; 239 if (snd_mixer_selem_is_active(control->elem)) 240 control->flags |= IS_ACTIVE; 241 create_name(control); 242 return 1; 243 } 244 has_pvol = snd_mixer_selem_has_playback_volume(elem); 245 has_psw = snd_mixer_selem_has_playback_switch(elem); 246 has_cvol = snd_mixer_selem_has_capture_volume(elem); 247 has_csw = snd_mixer_selem_has_capture_switch(elem); 248 merged_cswitch = view_mode == VIEW_MODE_ALL && has_merged_cswitch(elem); 249 if (view_mode != VIEW_MODE_CAPTURE && (has_pvol || has_psw)) { 250 if ((!has_pvol || snd_mixer_selem_has_playback_volume_joined(elem)) && 251 (!has_psw || snd_mixer_selem_has_playback_switch_joined(elem))) { 252 control->elem = elem; 253 if (has_pvol) { 254 control->flags |= TYPE_PVOLUME | HAS_VOLUME_0; 255 control->volume_channels[0] = 0; 256 } 257 if (has_psw) { 258 control->flags |= TYPE_PSWITCH | HAS_PSWITCH_0; 259 control->pswitch_channels[0] = 0; 260 } 261 if (merged_cswitch) { 262 control->flags |= TYPE_CSWITCH; 263 if (snd_mixer_selem_has_capture_switch_joined(elem)) { 264 control->flags |= HAS_CSWITCH_0; 265 control->cswitch_channels[0] = 0; 266 } else { 267 if (snd_mixer_selem_has_capture_channel(elem, control_channels[0][0])) { 268 control->flags |= HAS_CSWITCH_0; 269 control->cswitch_channels[0] = control_channels[0][0]; 270 } 271 if (control_channels[0][1] != SND_MIXER_SCHN_UNKNOWN && 272 snd_mixer_selem_has_capture_channel(elem, control_channels[0][1])) { 273 control->flags |= HAS_CSWITCH_1; 274 control->cswitch_channels[1] = control_channels[0][1]; 275 } 276 } 277 if ((control->flags & (HAS_CSWITCH_0 | HAS_CSWITCH_1)) == HAS_CSWITCH_1) { 278 control->flags ^= HAS_CSWITCH_0 | HAS_CSWITCH_1; 279 control->cswitch_channels[0] = control->cswitch_channels[1]; 280 } 281 } 282 if (snd_mixer_selem_is_active(control->elem)) 283 control->flags |= IS_ACTIVE; 284 create_name(control); 285 ++control; 286 ++count; 287 } else { 288 for (i = 0; i < ARRAY_SIZE(supported_channels); ++i) 289 has_channel[supported_channels[i]] = 290 snd_mixer_selem_has_playback_channel(elem, supported_channels[i]); 291 for (i = 0; i < ARRAY_SIZE(control_channels); ++i) { 292 has_ch0 = has_channel[control_channels[i][0]]; 293 has_ch1 = control_channels[i][1] != SND_MIXER_SCHN_UNKNOWN && 294 has_channel[control_channels[i][1]]; 295 if (!has_ch0 && !has_ch1) 296 continue; 297 control->elem = elem; 298 if (has_pvol) { 299 control->flags |= TYPE_PVOLUME; 300 if (snd_mixer_selem_has_playback_volume_joined(elem)) { 301 control->flags |= HAS_VOLUME_0; 302 control->volume_channels[0] = 0; 303 } else { 304 if (has_ch0) { 305 control->flags |= HAS_VOLUME_0; 306 control->volume_channels[0] = control_channels[i][0]; 307 } 308 if (has_ch1) { 309 control->flags |= HAS_VOLUME_1; 310 control->volume_channels[1] = control_channels[i][1]; 311 } 312 } 313 } 314 if (has_psw) { 315 control->flags |= TYPE_PSWITCH; 316 if (snd_mixer_selem_has_playback_switch_joined(elem)) { 317 control->flags |= HAS_PSWITCH_0; 318 control->pswitch_channels[0] = 0; 319 } else { 320 if (has_ch0) { 321 control->flags |= HAS_PSWITCH_0; 322 control->pswitch_channels[0] = control_channels[i][0]; 323 } 324 if (has_ch1) { 325 control->flags |= HAS_PSWITCH_1; 326 control->pswitch_channels[1] = control_channels[i][1]; 327 } 328 } 329 } 330 if (merged_cswitch) { 331 control->flags |= TYPE_CSWITCH; 332 if (snd_mixer_selem_has_capture_switch_joined(elem)) { 333 control->flags |= HAS_CSWITCH_0; 334 control->cswitch_channels[0] = 0; 335 } else { 336 if (snd_mixer_selem_has_capture_channel(elem, control_channels[i][0])) { 337 control->flags |= HAS_CSWITCH_0; 338 control->cswitch_channels[0] = control_channels[i][0]; 339 } 340 if (control_channels[i][1] != SND_MIXER_SCHN_UNKNOWN && 341 snd_mixer_selem_has_capture_channel(elem, control_channels[i][1])) { 342 control->flags |= HAS_CSWITCH_1; 343 control->cswitch_channels[1] = control_channels[i][1]; 344 } 345 } 346 } 347 if ((control->flags & (HAS_VOLUME_0 | HAS_VOLUME_1)) == HAS_VOLUME_1) { 348 control->flags ^= HAS_VOLUME_0 | HAS_VOLUME_1; 349 control->volume_channels[0] = control->volume_channels[1]; 350 } 351 if ((control->flags & (HAS_PSWITCH_0 | HAS_PSWITCH_1)) == HAS_PSWITCH_1) { 352 control->flags ^= HAS_PSWITCH_0 | HAS_PSWITCH_1; 353 control->pswitch_channels[0] = control->pswitch_channels[1]; 354 } 355 if ((control->flags & (HAS_CSWITCH_0 | HAS_CSWITCH_1)) == HAS_CSWITCH_1) { 356 control->flags ^= HAS_CSWITCH_0 | HAS_CSWITCH_1; 357 control->cswitch_channels[0] = control->cswitch_channels[1]; 358 } 359 if (snd_mixer_selem_is_active(control->elem)) 360 control->flags |= IS_ACTIVE; 361 create_name(control); 362 if (i == 0) 363 front_control = control; 364 else { 365 front_control->flags |= IS_MULTICH | 0; 366 control->flags |= IS_MULTICH | i; 367 } 368 ++control; 369 ++count; 370 } 371 } 372 } 373 if (view_mode != VIEW_MODE_PLAYBACK && (has_cvol || has_csw) && !merged_cswitch) { 374 if ((!has_cvol || snd_mixer_selem_has_capture_volume_joined(elem)) && 375 (!has_csw || snd_mixer_selem_has_capture_switch_joined(elem))) { 376 control->elem = elem; 377 if (has_cvol) { 378 control->flags |= TYPE_CVOLUME | HAS_VOLUME_0; 379 control->volume_channels[0] = 0; 380 } 381 if (has_csw) { 382 control->flags |= TYPE_CSWITCH | HAS_CSWITCH_0; 383 control->cswitch_channels[0] = 0; 384 } 385 if (snd_mixer_selem_is_active(control->elem)) 386 control->flags |= IS_ACTIVE; 387 create_name(control); 388 ++control; 389 ++count; 390 } else { 391 for (i = 0; i < ARRAY_SIZE(supported_channels); ++i) 392 has_channel[supported_channels[i]] = 393 snd_mixer_selem_has_capture_channel(elem, supported_channels[i]); 394 for (i = 0; i < ARRAY_SIZE(control_channels); ++i) { 395 has_ch0 = has_channel[control_channels[i][0]]; 396 has_ch1 = control_channels[i][1] != SND_MIXER_SCHN_UNKNOWN && 397 has_channel[control_channels[i][1]]; 398 if (!has_ch0 && !has_ch1) 399 continue; 400 control->elem = elem; 401 if (has_cvol) { 402 control->flags |= TYPE_CVOLUME; 403 if (snd_mixer_selem_has_capture_volume_joined(elem)) { 404 control->flags |= HAS_VOLUME_0; 405 control->volume_channels[0] = 0; 406 } else { 407 if (has_ch0) { 408 control->flags |= HAS_VOLUME_0; 409 control->volume_channels[0] = control_channels[i][0]; 410 } 411 if (has_ch1) { 412 control->flags |= HAS_VOLUME_1; 413 control->volume_channels[1] = control_channels[i][1]; 414 } 415 } 416 } 417 if (has_csw) { 418 control->flags |= TYPE_CSWITCH; 419 if (snd_mixer_selem_has_capture_switch_joined(elem)) { 420 control->flags |= HAS_CSWITCH_0; 421 control->cswitch_channels[0] = 0; 422 } else { 423 if (has_ch0) { 424 control->flags |= HAS_CSWITCH_0; 425 control->cswitch_channels[0] = control_channels[i][0]; 426 } 427 if (has_ch1) { 428 control->flags |= HAS_CSWITCH_1; 429 control->cswitch_channels[1] = control_channels[i][1]; 430 } 431 } 432 } 433 if ((control->flags & (HAS_VOLUME_0 | HAS_VOLUME_1)) == HAS_VOLUME_1) { 434 control->flags ^= HAS_VOLUME_0 | HAS_VOLUME_1; 435 control->volume_channels[0] = control->volume_channels[1]; 436 } 437 if ((control->flags & (HAS_CSWITCH_0 | HAS_CSWITCH_1)) == HAS_CSWITCH_1) { 438 control->flags ^= HAS_CSWITCH_0 | HAS_CSWITCH_1; 439 control->cswitch_channels[0] = control->cswitch_channels[1]; 440 } 441 if (snd_mixer_selem_is_active(control->elem)) 442 control->flags |= IS_ACTIVE; 443 create_name(control); 444 if (i == 0) 445 front_control = control; 446 else { 447 front_control->flags |= IS_MULTICH | 0; 448 control->flags |= IS_MULTICH | i; 449 } 450 ++control; 451 ++count; 452 } 453 } 454 } 455 return count; 456} 457 458static void search_for_focus_control(void) 459{ 460 snd_mixer_elem_t *elem; 461 unsigned int i; 462 463 elem = snd_mixer_find_selem(mixer, current_selem_id); 464 if (elem) 465 for (i = 0; i < controls_count; ++i) 466 if (controls[i].elem == elem) { 467 focus_control_index = i; 468 for (;;) { 469 ++i; 470 if (i >= controls_count || controls[i].elem != elem) 471 return; 472 if (controls[i].flags == current_control_flags) { 473 focus_control_index = i; 474 return; 475 } 476 } 477 } 478 focus_control_index = 0; 479} 480 481void free_controls(void) 482{ 483 unsigned int i; 484 485 for (i = 0; i < controls_count; ++i) 486 free(controls[i].name); 487 free(controls); 488 controls = NULL; 489 controls_count = 0; 490} 491 492void create_controls(void) 493{ 494 snd_mixer_elem_t *elem; 495 struct control *control; 496 497 free_controls(); 498 499 for (elem = snd_mixer_first_elem(mixer); 500 elem; 501 elem = snd_mixer_elem_next(elem)) 502 controls_count += get_controls_count_for_elem(elem); 503 504 if (controls_count > 0) { 505 controls = ccalloc(controls_count, sizeof *controls); 506 control = controls; 507 for (elem = snd_mixer_first_elem(mixer); 508 elem; 509 elem = snd_mixer_elem_next(elem)) 510 control += create_controls_for_elem(elem, control); 511 assert(control == controls + controls_count); 512 } 513 514 compute_controls_layout(); 515 display_view_mode(); 516 517 search_for_focus_control(); 518 refocus_control(); 519} 520