1 1.7 rillig /* $NetBSD: main.c,v 1.7 2024/11/03 10:43:27 rillig Exp $ */ 2 1.1 nia /*- 3 1.1 nia * Copyright (c) 2021 The NetBSD Foundation, Inc. 4 1.1 nia * All rights reserved. 5 1.1 nia * 6 1.1 nia * This code is derived from software contributed to The NetBSD Foundation 7 1.1 nia * by Nia Alarie. 8 1.1 nia * 9 1.1 nia * Redistribution and use in source and binary forms, with or without 10 1.1 nia * modification, are permitted provided that the following conditions 11 1.1 nia * are met: 12 1.1 nia * 1. Redistributions of source code must retain the above copyright 13 1.1 nia * notice, this list of conditions and the following disclaimer. 14 1.1 nia * 2. Redistributions in binary form must reproduce the above copyright 15 1.1 nia * notice, this list of conditions and the following disclaimer in the 16 1.1 nia * documentation and/or other materials provided with the distribution. 17 1.1 nia * 18 1.1 nia * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 19 1.1 nia * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 1.1 nia * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 1.1 nia * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 22 1.1 nia * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 1.1 nia * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 1.1 nia * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 1.1 nia * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 1.1 nia * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 1.1 nia * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 1.1 nia * POSSIBILITY OF SUCH DAMAGE. 29 1.1 nia */ 30 1.1 nia #include <sys/audioio.h> 31 1.1 nia #include <sys/ioctl.h> 32 1.1 nia #include <fcntl.h> 33 1.1 nia #include <unistd.h> 34 1.1 nia #include <signal.h> 35 1.1 nia #include <paths.h> 36 1.1 nia #include <curses.h> 37 1.1 nia #include <stdlib.h> 38 1.1 nia #include <err.h> 39 1.1 nia #include "app.h" 40 1.1 nia #include "draw.h" 41 1.1 nia #include "parse.h" 42 1.1 nia 43 1.1 nia static void process_device_select(struct aiomixer *, unsigned int); 44 1.1 nia static void open_device(struct aiomixer *, const char *); 45 1.1 nia static void __dead usage(void); 46 1.1 nia static int adjust_level(int, int); 47 1.1 nia static int select_class(struct aiomixer *, unsigned int); 48 1.1 nia static int select_control(struct aiomixer *, unsigned int); 49 1.1 nia static void slide_control(struct aiomixer *, struct aiomixer_control *, bool); 50 1.1 nia static int toggle_set(struct aiomixer *); 51 1.1 nia static void step_up(struct aiomixer *); 52 1.1 nia static void step_down(struct aiomixer *); 53 1.1 nia static int read_key(struct aiomixer *, int); 54 1.1 nia 55 1.1 nia static void __dead 56 1.1 nia usage(void) 57 1.1 nia { 58 1.1 nia fputs("aiomixer [-u] [-d device]\n", stderr); 59 1.1 nia exit(1); 60 1.1 nia } 61 1.1 nia 62 1.1 nia static int 63 1.1 nia select_class(struct aiomixer *aio, unsigned int n) 64 1.1 nia { 65 1.1 nia struct aiomixer_class *class; 66 1.1 nia unsigned i; 67 1.1 nia 68 1.1 nia if (n >= aio->numclasses) 69 1.1 nia return -1; 70 1.1 nia 71 1.1 nia class = &aio->classes[n]; 72 1.1 nia aio->widgets_resized = true; 73 1.1 nia aio->class_scroll_y = 0; 74 1.1 nia aio->curcontrol = 0; 75 1.1 nia aio->curclass = n; 76 1.1 nia for (i = 0; i < class->numcontrols; ++i) { 77 1.1 nia class->controls[i].setindex = -1; 78 1.1 nia draw_control(aio, &class->controls[i], false); 79 1.1 nia } 80 1.1 nia draw_classbar(aio); 81 1.1 nia return 0; 82 1.1 nia } 83 1.1 nia 84 1.1 nia static int 85 1.1 nia select_control(struct aiomixer *aio, unsigned int n) 86 1.1 nia { 87 1.1 nia struct aiomixer_class *class; 88 1.1 nia struct aiomixer_control *lastcontrol; 89 1.1 nia struct aiomixer_control *control; 90 1.1 nia 91 1.1 nia class = &aio->classes[aio->curclass]; 92 1.1 nia 93 1.1 nia if (n >= class->numcontrols) 94 1.1 nia return -1; 95 1.1 nia 96 1.1 nia lastcontrol = &class->controls[aio->curcontrol]; 97 1.1 nia lastcontrol->setindex = -1; 98 1.1 nia draw_control(aio, lastcontrol, false); 99 1.1 nia 100 1.1 nia control = &class->controls[n]; 101 1.1 nia aio->curcontrol = n; 102 1.1 nia control->setindex = 0; 103 1.1 nia draw_control(aio, control, true); 104 1.1 nia 105 1.1 nia if (aio->class_scroll_y > control->widget_y) { 106 1.1 nia aio->class_scroll_y = control->widget_y; 107 1.1 nia aio->widgets_resized = true; 108 1.1 nia } 109 1.1 nia 110 1.1 nia if ((control->widget_y + control->height) > 111 1.1 nia ((getmaxy(stdscr) - 4) + aio->class_scroll_y)) { 112 1.1 nia aio->class_scroll_y = control->widget_y; 113 1.1 nia aio->widgets_resized = true; 114 1.1 nia } 115 1.1 nia return 0; 116 1.1 nia } 117 1.1 nia 118 1.1 nia static int 119 1.1 nia adjust_level(int level, int delta) 120 1.1 nia { 121 1.1 nia if (level > (AUDIO_MAX_GAIN - delta)) 122 1.1 nia return AUDIO_MAX_GAIN; 123 1.1 nia 124 1.1 nia if (delta < 0 && level < (AUDIO_MIN_GAIN + (-delta))) 125 1.1 nia return AUDIO_MIN_GAIN; 126 1.1 nia 127 1.1 nia return level + delta; 128 1.1 nia } 129 1.1 nia 130 1.1 nia static void 131 1.1 nia slide_control(struct aiomixer *aio, 132 1.1 nia struct aiomixer_control *control, bool right) 133 1.1 nia { 134 1.1 nia struct mixer_devinfo *info = &control->info; 135 1.1 nia struct mixer_ctrl value; 136 1.1 nia unsigned char *level; 137 1.1 nia int i, delta; 138 1.1 nia int cur_index = 0; 139 1.1 nia 140 1.1 nia if (info->type != AUDIO_MIXER_SET) { 141 1.1 nia value.dev = info->index; 142 1.1 nia value.type = info->type; 143 1.1 nia if (info->type == AUDIO_MIXER_VALUE) 144 1.1 nia value.un.value.num_channels = info->un.v.num_channels; 145 1.1 nia 146 1.1 nia if (ioctl(aio->fd, AUDIO_MIXER_READ, &value) < 0) 147 1.1 nia err(EXIT_FAILURE, "failed to read mixer control"); 148 1.1 nia } 149 1.1 nia 150 1.1 nia switch (info->type) { 151 1.1 nia case AUDIO_MIXER_VALUE: 152 1.6 nia if (info->un.v.delta != 0) { 153 1.6 nia delta = right ? info->un.v.delta : -info->un.v.delta; 154 1.6 nia } else { 155 1.6 nia /* delta is 0 in qemu with sb(4) */ 156 1.6 nia delta = right ? 16 : -16; 157 1.6 nia } 158 1.1 nia /* 159 1.1 nia * work around strange problem where the level can be 160 1.1 nia * increased but not decreased, seen with uaudio(4) 161 1.1 nia */ 162 1.1 nia if (delta < 16) 163 1.1 nia delta *= 2; 164 1.1 nia if (aio->channels_unlocked) { 165 1.1 nia level = &value.un.value.level[control->setindex]; 166 1.1 nia *level = (unsigned char)adjust_level(*level, delta); 167 1.1 nia } else { 168 1.1 nia for (i = 0; i < value.un.value.num_channels; ++i) { 169 1.1 nia level = &value.un.value.level[i]; 170 1.1 nia *level = (unsigned char)adjust_level(*level, delta); 171 1.1 nia } 172 1.1 nia } 173 1.1 nia break; 174 1.1 nia case AUDIO_MIXER_ENUM: 175 1.1 nia for (i = 0; i < info->un.e.num_mem; ++i) { 176 1.1 nia if (info->un.e.member[i].ord == value.un.ord) { 177 1.1 nia cur_index = i; 178 1.1 nia break; 179 1.1 nia } 180 1.1 nia } 181 1.1 nia if (right) { 182 1.1 nia value.un.ord = cur_index < (info->un.e.num_mem - 1) ? 183 1.1 nia info->un.e.member[cur_index + 1].ord : 184 1.1 nia info->un.e.member[0].ord; 185 1.1 nia } else { 186 1.1 nia value.un.ord = cur_index > 0 ? 187 1.1 nia info->un.e.member[cur_index - 1].ord : 188 1.1 nia info->un.e.member[control->info.un.e.num_mem - 1].ord; 189 1.1 nia } 190 1.1 nia break; 191 1.1 nia case AUDIO_MIXER_SET: 192 1.1 nia if (right) { 193 1.1 nia control->setindex = 194 1.1 nia control->setindex < (info->un.s.num_mem - 1) ? 195 1.1 nia control->setindex + 1 : 0; 196 1.1 nia } else { 197 1.1 nia control->setindex = control->setindex > 0 ? 198 1.1 nia control->setindex - 1 : 199 1.1 nia control->info.un.s.num_mem - 1; 200 1.1 nia } 201 1.1 nia break; 202 1.1 nia } 203 1.1 nia 204 1.1 nia if (info->type != AUDIO_MIXER_SET) { 205 1.1 nia if (ioctl(aio->fd, AUDIO_MIXER_WRITE, &value) < 0) 206 1.1 nia err(EXIT_FAILURE, "failed to adjust mixer control"); 207 1.1 nia } 208 1.1 nia 209 1.1 nia draw_control(aio, control, true); 210 1.1 nia } 211 1.1 nia 212 1.1 nia static int 213 1.1 nia toggle_set(struct aiomixer *aio) 214 1.1 nia { 215 1.1 nia struct mixer_ctrl ctrl; 216 1.1 nia struct aiomixer_class *class = &aio->classes[aio->curclass]; 217 1.1 nia struct aiomixer_control *control = &class->controls[aio->curcontrol]; 218 1.1 nia 219 1.1 nia ctrl.dev = control->info.index; 220 1.1 nia ctrl.type = control->info.type; 221 1.1 nia 222 1.1 nia if (control->info.type != AUDIO_MIXER_SET) 223 1.1 nia return -1; 224 1.1 nia 225 1.1 nia if (ioctl(aio->fd, AUDIO_MIXER_READ, &ctrl) < 0) 226 1.1 nia err(EXIT_FAILURE, "failed to read mixer control"); 227 1.1 nia 228 1.1 nia ctrl.un.mask ^= control->info.un.s.member[control->setindex].mask; 229 1.1 nia 230 1.1 nia if (ioctl(aio->fd, AUDIO_MIXER_WRITE, &ctrl) < 0) 231 1.1 nia err(EXIT_FAILURE, "failed to read mixer control"); 232 1.1 nia 233 1.1 nia draw_control(aio, control, true); 234 1.1 nia return 0; 235 1.1 nia } 236 1.1 nia 237 1.1 nia static void 238 1.1 nia step_up(struct aiomixer *aio) 239 1.1 nia { 240 1.1 nia struct aiomixer_class *class; 241 1.1 nia struct aiomixer_control *control; 242 1.1 nia 243 1.1 nia class = &aio->classes[aio->curclass]; 244 1.1 nia control = &class->controls[aio->curcontrol]; 245 1.1 nia 246 1.1 nia if (aio->channels_unlocked && 247 1.1 nia control->info.type == AUDIO_MIXER_VALUE && 248 1.1 nia control->setindex > 0) { 249 1.1 nia control->setindex--; 250 1.1 nia draw_control(aio, control, true); 251 1.1 nia return; 252 1.1 nia } 253 1.1 nia select_control(aio, aio->curcontrol - 1); 254 1.1 nia } 255 1.1 nia 256 1.1 nia static void 257 1.1 nia step_down(struct aiomixer *aio) 258 1.1 nia { 259 1.1 nia struct aiomixer_class *class; 260 1.1 nia struct aiomixer_control *control; 261 1.1 nia 262 1.1 nia class = &aio->classes[aio->curclass]; 263 1.1 nia control = &class->controls[aio->curcontrol]; 264 1.1 nia 265 1.1 nia if (aio->channels_unlocked && 266 1.1 nia control->info.type == AUDIO_MIXER_VALUE && 267 1.1 nia control->setindex < (control->info.un.v.num_channels - 1)) { 268 1.1 nia control->setindex++; 269 1.1 nia draw_control(aio, control, true); 270 1.1 nia return; 271 1.1 nia } 272 1.1 nia 273 1.1 nia select_control(aio, (aio->curcontrol + 1) % class->numcontrols); 274 1.1 nia } 275 1.1 nia 276 1.1 nia static int 277 1.1 nia read_key(struct aiomixer *aio, int ch) 278 1.1 nia { 279 1.1 nia struct aiomixer_class *class; 280 1.1 nia struct aiomixer_control *control; 281 1.1 nia size_t i; 282 1.1 nia 283 1.1 nia switch (ch) { 284 1.1 nia case KEY_RESIZE: 285 1.1 nia class = &aio->classes[aio->curclass]; 286 1.1 nia resize_widgets(aio); 287 1.1 nia draw_header(aio); 288 1.1 nia draw_classbar(aio); 289 1.1 nia for (i = 0; i < class->numcontrols; ++i) { 290 1.1 nia draw_control(aio, 291 1.1 nia &class->controls[i], 292 1.1 nia aio->state == STATE_CONTROL_SELECT ? 293 1.1 nia (aio->curcontrol == i) : false); 294 1.1 nia } 295 1.1 nia break; 296 1.1 nia case KEY_LEFT: 297 1.1 nia case 'h': 298 1.1 nia if (aio->state == STATE_CLASS_SELECT) { 299 1.1 nia select_class(aio, aio->curclass > 0 ? 300 1.1 nia aio->curclass - 1 : aio->numclasses - 1); 301 1.1 nia } else if (aio->state == STATE_CONTROL_SELECT) { 302 1.1 nia class = &aio->classes[aio->curclass]; 303 1.1 nia slide_control(aio, 304 1.1 nia &class->controls[aio->curcontrol], false); 305 1.1 nia } 306 1.1 nia break; 307 1.1 nia case KEY_RIGHT: 308 1.1 nia case 'l': 309 1.1 nia if (aio->state == STATE_CLASS_SELECT) { 310 1.1 nia select_class(aio, 311 1.1 nia (aio->curclass + 1) % aio->numclasses); 312 1.1 nia } else if (aio->state == STATE_CONTROL_SELECT) { 313 1.1 nia class = &aio->classes[aio->curclass]; 314 1.1 nia slide_control(aio, 315 1.1 nia &class->controls[aio->curcontrol], true); 316 1.1 nia } 317 1.1 nia break; 318 1.1 nia case KEY_UP: 319 1.1 nia case 'k': 320 1.1 nia if (aio->state == STATE_CONTROL_SELECT) { 321 1.1 nia if (aio->curcontrol == 0) { 322 1.1 nia class = &aio->classes[aio->curclass]; 323 1.1 nia control = &class->controls[aio->curcontrol]; 324 1.1 nia control->setindex = -1; 325 1.1 nia aio->state = STATE_CLASS_SELECT; 326 1.1 nia draw_control(aio, control, false); 327 1.1 nia } else { 328 1.1 nia step_up(aio); 329 1.1 nia } 330 1.1 nia } 331 1.1 nia break; 332 1.1 nia case KEY_DOWN: 333 1.1 nia case 'j': 334 1.1 nia if (aio->state == STATE_CLASS_SELECT) { 335 1.1 nia class = &aio->classes[aio->curclass]; 336 1.1 nia if (class->numcontrols > 0) { 337 1.1 nia aio->state = STATE_CONTROL_SELECT; 338 1.1 nia select_control(aio, 0); 339 1.1 nia } 340 1.1 nia } else if (aio->state == STATE_CONTROL_SELECT) { 341 1.1 nia step_down(aio); 342 1.1 nia } 343 1.1 nia break; 344 1.1 nia case '\n': 345 1.1 nia case ' ': 346 1.1 nia if (aio->state == STATE_CONTROL_SELECT) 347 1.1 nia toggle_set(aio); 348 1.1 nia break; 349 1.1 nia case '1': 350 1.1 nia select_class(aio, 0); 351 1.1 nia break; 352 1.1 nia case '2': 353 1.1 nia select_class(aio, 1); 354 1.1 nia break; 355 1.1 nia case '3': 356 1.1 nia select_class(aio, 2); 357 1.1 nia break; 358 1.1 nia case '4': 359 1.1 nia select_class(aio, 3); 360 1.1 nia break; 361 1.1 nia case '5': 362 1.1 nia select_class(aio, 4); 363 1.1 nia break; 364 1.1 nia case '6': 365 1.1 nia select_class(aio, 5); 366 1.1 nia break; 367 1.1 nia case '7': 368 1.1 nia select_class(aio, 6); 369 1.1 nia break; 370 1.1 nia case '8': 371 1.1 nia select_class(aio, 7); 372 1.1 nia break; 373 1.1 nia case '9': 374 1.1 nia select_class(aio, 8); 375 1.1 nia break; 376 1.1 nia case 'q': 377 1.1 nia case '\e': 378 1.1 nia if (aio->state == STATE_CONTROL_SELECT) { 379 1.1 nia class = &aio->classes[aio->curclass]; 380 1.1 nia control = &class->controls[aio->curcontrol]; 381 1.1 nia aio->state = STATE_CLASS_SELECT; 382 1.1 nia draw_control(aio, control, false); 383 1.1 nia break; 384 1.1 nia } 385 1.1 nia return 1; 386 1.1 nia case 'u': 387 1.1 nia aio->channels_unlocked = !aio->channels_unlocked; 388 1.1 nia if (aio->state == STATE_CONTROL_SELECT) { 389 1.1 nia class = &aio->classes[aio->curclass]; 390 1.1 nia control = &class->controls[aio->curcontrol]; 391 1.1 nia if (control->info.type == AUDIO_MIXER_VALUE) 392 1.1 nia draw_control(aio, control, true); 393 1.1 nia } 394 1.1 nia break; 395 1.1 nia } 396 1.1 nia 397 1.1 nia draw_screen(aio); 398 1.1 nia return 0; 399 1.1 nia } 400 1.1 nia 401 1.1 nia static void 402 1.1 nia process_device_select(struct aiomixer *aio, unsigned int num_devices) 403 1.1 nia { 404 1.1 nia unsigned int selected_device = 0; 405 1.1 nia char device_path[16]; 406 1.1 nia int ch; 407 1.1 nia 408 1.1 nia draw_mixer_select(num_devices, selected_device); 409 1.1 nia 410 1.1 nia while ((ch = getch()) != ERR) { 411 1.1 nia switch (ch) { 412 1.1 nia case '\n': 413 1.3 nia clear(); 414 1.1 nia (void)snprintf(device_path, sizeof(device_path), 415 1.1 nia "/dev/mixer%d", selected_device); 416 1.1 nia open_device(aio, device_path); 417 1.1 nia return; 418 1.1 nia case KEY_UP: 419 1.1 nia case 'k': 420 1.1 nia if (selected_device > 0) 421 1.1 nia selected_device--; 422 1.1 nia else 423 1.1 nia selected_device = (num_devices - 1); 424 1.1 nia break; 425 1.1 nia case KEY_DOWN: 426 1.1 nia case 'j': 427 1.1 nia if (selected_device < (num_devices - 1)) 428 1.1 nia selected_device++; 429 1.1 nia else 430 1.1 nia selected_device = 0; 431 1.1 nia break; 432 1.1 nia case '1': 433 1.1 nia selected_device = 0; 434 1.1 nia break; 435 1.1 nia case '2': 436 1.1 nia selected_device = 1; 437 1.1 nia break; 438 1.1 nia case '3': 439 1.1 nia selected_device = 2; 440 1.1 nia break; 441 1.1 nia case '4': 442 1.1 nia selected_device = 3; 443 1.1 nia break; 444 1.1 nia case '5': 445 1.1 nia selected_device = 4; 446 1.1 nia break; 447 1.1 nia case '6': 448 1.1 nia selected_device = 5; 449 1.1 nia break; 450 1.1 nia case '7': 451 1.1 nia selected_device = 6; 452 1.1 nia break; 453 1.1 nia case '8': 454 1.1 nia selected_device = 7; 455 1.1 nia break; 456 1.1 nia case '9': 457 1.1 nia selected_device = 8; 458 1.1 nia break; 459 1.1 nia } 460 1.1 nia draw_mixer_select(num_devices, selected_device); 461 1.1 nia } 462 1.1 nia } 463 1.1 nia 464 1.1 nia static void 465 1.1 nia open_device(struct aiomixer *aio, const char *device) 466 1.1 nia { 467 1.1 nia int ch; 468 1.1 nia 469 1.1 nia if ((aio->fd = open(device, O_RDWR)) < 0) 470 1.1 nia err(EXIT_FAILURE, "couldn't open mixer device"); 471 1.1 nia 472 1.1 nia if (ioctl(aio->fd, AUDIO_GETDEV, &aio->mixerdev) < 0) 473 1.1 nia err(EXIT_FAILURE, "AUDIO_GETDEV failed"); 474 1.1 nia 475 1.1 nia aio->state = STATE_CLASS_SELECT; 476 1.1 nia 477 1.1 nia aiomixer_parse(aio); 478 1.1 nia 479 1.1 nia create_widgets(aio); 480 1.1 nia 481 1.1 nia draw_header(aio); 482 1.1 nia select_class(aio, 0); 483 1.1 nia draw_screen(aio); 484 1.1 nia 485 1.1 nia while ((ch = getch()) != ERR) { 486 1.1 nia if (read_key(aio, ch) != 0) 487 1.1 nia break; 488 1.1 nia } 489 1.1 nia } 490 1.1 nia 491 1.2 christos static __dead void 492 1.1 nia on_signal(int dummy) 493 1.1 nia { 494 1.1 nia endwin(); 495 1.1 nia exit(0); 496 1.1 nia } 497 1.1 nia 498 1.1 nia int 499 1.1 nia main(int argc, char **argv) 500 1.1 nia { 501 1.1 nia const char *mixer_device = NULL; 502 1.1 nia struct aiomixer *aio; 503 1.1 nia char mixer_path[32]; 504 1.1 nia unsigned int mixer_count = 0; 505 1.1 nia int i, fd; 506 1.1 nia int ch; 507 1.5 nia char *no_color = getenv("NO_COLOR"); 508 1.1 nia 509 1.1 nia if ((aio = malloc(sizeof(struct aiomixer))) == NULL) { 510 1.1 nia err(EXIT_FAILURE, "malloc failed"); 511 1.1 nia } 512 1.1 nia 513 1.1 nia while ((ch = getopt(argc, argv, "d:u")) != -1) { 514 1.1 nia switch (ch) { 515 1.1 nia case 'd': 516 1.1 nia mixer_device = optarg; 517 1.1 nia break; 518 1.1 nia case 'u': 519 1.1 nia aio->channels_unlocked = true; 520 1.1 nia break; 521 1.1 nia default: 522 1.1 nia usage(); 523 1.1 nia break; 524 1.1 nia } 525 1.1 nia } 526 1.1 nia 527 1.1 nia argc -= optind; 528 1.1 nia argv += optind; 529 1.1 nia 530 1.1 nia if (initscr() == NULL) 531 1.1 nia err(EXIT_FAILURE, "can't initialize curses"); 532 1.1 nia 533 1.1 nia (void)signal(SIGHUP, on_signal); 534 1.1 nia (void)signal(SIGINT, on_signal); 535 1.1 nia (void)signal(SIGTERM, on_signal); 536 1.1 nia 537 1.1 nia curs_set(0); 538 1.1 nia keypad(stdscr, TRUE); 539 1.1 nia cbreak(); 540 1.1 nia noecho(); 541 1.1 nia 542 1.5 nia aio->use_colour = true; 543 1.5 nia 544 1.5 nia if (!has_colors()) 545 1.5 nia aio->use_colour = false; 546 1.5 nia 547 1.5 nia if (no_color != NULL && no_color[0] != '\0') 548 1.5 nia aio->use_colour = false; 549 1.5 nia 550 1.5 nia if (aio->use_colour) { 551 1.1 nia start_color(); 552 1.4 nia use_default_colors(); 553 1.1 nia init_pair(COLOR_CONTROL_SELECTED, COLOR_BLUE, COLOR_BLACK); 554 1.1 nia init_pair(COLOR_LEVELS, COLOR_GREEN, COLOR_BLACK); 555 1.1 nia init_pair(COLOR_SET_SELECTED, COLOR_BLACK, COLOR_GREEN); 556 1.1 nia init_pair(COLOR_ENUM_ON, COLOR_WHITE, COLOR_RED); 557 1.1 nia init_pair(COLOR_ENUM_OFF, COLOR_WHITE, COLOR_BLUE); 558 1.1 nia init_pair(COLOR_ENUM_MISC, COLOR_BLACK, COLOR_YELLOW); 559 1.1 nia } 560 1.1 nia 561 1.1 nia if (mixer_device != NULL) { 562 1.1 nia open_device(aio, mixer_device); 563 1.1 nia } else { 564 1.1 nia for (i = 0; i < 16; ++i) { 565 1.1 nia (void)snprintf(mixer_path, sizeof(mixer_path), 566 1.1 nia "/dev/mixer%d", i); 567 1.1 nia fd = open(mixer_path, O_RDWR); 568 1.1 nia if (fd == -1) 569 1.1 nia break; 570 1.1 nia close(fd); 571 1.1 nia mixer_count++; 572 1.1 nia } 573 1.1 nia 574 1.1 nia if (mixer_count > 1) { 575 1.1 nia process_device_select(aio, mixer_count); 576 1.1 nia } else { 577 1.1 nia open_device(aio, _PATH_MIXER); 578 1.1 nia } 579 1.1 nia } 580 1.1 nia 581 1.1 nia endwin(); 582 1.1 nia close(aio->fd); 583 1.1 nia free(aio); 584 1.1 nia 585 1.1 nia return 0; 586 1.1 nia } 587