1*33b82b03Srillig /* $NetBSD: main.c,v 1.7 2024/11/03 10:43:27 rillig Exp $ */ 226802ffdSnia /*- 326802ffdSnia * Copyright (c) 2021 The NetBSD Foundation, Inc. 426802ffdSnia * All rights reserved. 526802ffdSnia * 626802ffdSnia * This code is derived from software contributed to The NetBSD Foundation 726802ffdSnia * by Nia Alarie. 826802ffdSnia * 926802ffdSnia * Redistribution and use in source and binary forms, with or without 1026802ffdSnia * modification, are permitted provided that the following conditions 1126802ffdSnia * are met: 1226802ffdSnia * 1. Redistributions of source code must retain the above copyright 1326802ffdSnia * notice, this list of conditions and the following disclaimer. 1426802ffdSnia * 2. Redistributions in binary form must reproduce the above copyright 1526802ffdSnia * notice, this list of conditions and the following disclaimer in the 1626802ffdSnia * documentation and/or other materials provided with the distribution. 1726802ffdSnia * 1826802ffdSnia * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 1926802ffdSnia * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2026802ffdSnia * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2126802ffdSnia * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2226802ffdSnia * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2326802ffdSnia * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2426802ffdSnia * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2526802ffdSnia * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2626802ffdSnia * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2726802ffdSnia * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2826802ffdSnia * POSSIBILITY OF SUCH DAMAGE. 2926802ffdSnia */ 3026802ffdSnia #include <sys/audioio.h> 3126802ffdSnia #include <sys/ioctl.h> 3226802ffdSnia #include <fcntl.h> 3326802ffdSnia #include <unistd.h> 3426802ffdSnia #include <signal.h> 3526802ffdSnia #include <paths.h> 3626802ffdSnia #include <curses.h> 3726802ffdSnia #include <stdlib.h> 3826802ffdSnia #include <err.h> 3926802ffdSnia #include "app.h" 4026802ffdSnia #include "draw.h" 4126802ffdSnia #include "parse.h" 4226802ffdSnia 4326802ffdSnia static void process_device_select(struct aiomixer *, unsigned int); 4426802ffdSnia static void open_device(struct aiomixer *, const char *); 4526802ffdSnia static void __dead usage(void); 4626802ffdSnia static int adjust_level(int, int); 4726802ffdSnia static int select_class(struct aiomixer *, unsigned int); 4826802ffdSnia static int select_control(struct aiomixer *, unsigned int); 4926802ffdSnia static void slide_control(struct aiomixer *, struct aiomixer_control *, bool); 5026802ffdSnia static int toggle_set(struct aiomixer *); 5126802ffdSnia static void step_up(struct aiomixer *); 5226802ffdSnia static void step_down(struct aiomixer *); 5326802ffdSnia static int read_key(struct aiomixer *, int); 5426802ffdSnia 5526802ffdSnia static void __dead 5626802ffdSnia usage(void) 5726802ffdSnia { 5826802ffdSnia fputs("aiomixer [-u] [-d device]\n", stderr); 5926802ffdSnia exit(1); 6026802ffdSnia } 6126802ffdSnia 6226802ffdSnia static int 6326802ffdSnia select_class(struct aiomixer *aio, unsigned int n) 6426802ffdSnia { 6526802ffdSnia struct aiomixer_class *class; 6626802ffdSnia unsigned i; 6726802ffdSnia 6826802ffdSnia if (n >= aio->numclasses) 6926802ffdSnia return -1; 7026802ffdSnia 7126802ffdSnia class = &aio->classes[n]; 7226802ffdSnia aio->widgets_resized = true; 7326802ffdSnia aio->class_scroll_y = 0; 7426802ffdSnia aio->curcontrol = 0; 7526802ffdSnia aio->curclass = n; 7626802ffdSnia for (i = 0; i < class->numcontrols; ++i) { 7726802ffdSnia class->controls[i].setindex = -1; 7826802ffdSnia draw_control(aio, &class->controls[i], false); 7926802ffdSnia } 8026802ffdSnia draw_classbar(aio); 8126802ffdSnia return 0; 8226802ffdSnia } 8326802ffdSnia 8426802ffdSnia static int 8526802ffdSnia select_control(struct aiomixer *aio, unsigned int n) 8626802ffdSnia { 8726802ffdSnia struct aiomixer_class *class; 8826802ffdSnia struct aiomixer_control *lastcontrol; 8926802ffdSnia struct aiomixer_control *control; 9026802ffdSnia 9126802ffdSnia class = &aio->classes[aio->curclass]; 9226802ffdSnia 9326802ffdSnia if (n >= class->numcontrols) 9426802ffdSnia return -1; 9526802ffdSnia 9626802ffdSnia lastcontrol = &class->controls[aio->curcontrol]; 9726802ffdSnia lastcontrol->setindex = -1; 9826802ffdSnia draw_control(aio, lastcontrol, false); 9926802ffdSnia 10026802ffdSnia control = &class->controls[n]; 10126802ffdSnia aio->curcontrol = n; 10226802ffdSnia control->setindex = 0; 10326802ffdSnia draw_control(aio, control, true); 10426802ffdSnia 10526802ffdSnia if (aio->class_scroll_y > control->widget_y) { 10626802ffdSnia aio->class_scroll_y = control->widget_y; 10726802ffdSnia aio->widgets_resized = true; 10826802ffdSnia } 10926802ffdSnia 11026802ffdSnia if ((control->widget_y + control->height) > 11126802ffdSnia ((getmaxy(stdscr) - 4) + aio->class_scroll_y)) { 11226802ffdSnia aio->class_scroll_y = control->widget_y; 11326802ffdSnia aio->widgets_resized = true; 11426802ffdSnia } 11526802ffdSnia return 0; 11626802ffdSnia } 11726802ffdSnia 11826802ffdSnia static int 11926802ffdSnia adjust_level(int level, int delta) 12026802ffdSnia { 12126802ffdSnia if (level > (AUDIO_MAX_GAIN - delta)) 12226802ffdSnia return AUDIO_MAX_GAIN; 12326802ffdSnia 12426802ffdSnia if (delta < 0 && level < (AUDIO_MIN_GAIN + (-delta))) 12526802ffdSnia return AUDIO_MIN_GAIN; 12626802ffdSnia 12726802ffdSnia return level + delta; 12826802ffdSnia } 12926802ffdSnia 13026802ffdSnia static void 13126802ffdSnia slide_control(struct aiomixer *aio, 13226802ffdSnia struct aiomixer_control *control, bool right) 13326802ffdSnia { 13426802ffdSnia struct mixer_devinfo *info = &control->info; 13526802ffdSnia struct mixer_ctrl value; 13626802ffdSnia unsigned char *level; 13726802ffdSnia int i, delta; 13826802ffdSnia int cur_index = 0; 13926802ffdSnia 14026802ffdSnia if (info->type != AUDIO_MIXER_SET) { 14126802ffdSnia value.dev = info->index; 14226802ffdSnia value.type = info->type; 14326802ffdSnia if (info->type == AUDIO_MIXER_VALUE) 14426802ffdSnia value.un.value.num_channels = info->un.v.num_channels; 14526802ffdSnia 14626802ffdSnia if (ioctl(aio->fd, AUDIO_MIXER_READ, &value) < 0) 14726802ffdSnia err(EXIT_FAILURE, "failed to read mixer control"); 14826802ffdSnia } 14926802ffdSnia 15026802ffdSnia switch (info->type) { 15126802ffdSnia case AUDIO_MIXER_VALUE: 152d257b0e8Snia if (info->un.v.delta != 0) { 15326802ffdSnia delta = right ? info->un.v.delta : -info->un.v.delta; 154d257b0e8Snia } else { 155d257b0e8Snia /* delta is 0 in qemu with sb(4) */ 156d257b0e8Snia delta = right ? 16 : -16; 157d257b0e8Snia } 15826802ffdSnia /* 15926802ffdSnia * work around strange problem where the level can be 16026802ffdSnia * increased but not decreased, seen with uaudio(4) 16126802ffdSnia */ 16226802ffdSnia if (delta < 16) 16326802ffdSnia delta *= 2; 16426802ffdSnia if (aio->channels_unlocked) { 16526802ffdSnia level = &value.un.value.level[control->setindex]; 16626802ffdSnia *level = (unsigned char)adjust_level(*level, delta); 16726802ffdSnia } else { 16826802ffdSnia for (i = 0; i < value.un.value.num_channels; ++i) { 16926802ffdSnia level = &value.un.value.level[i]; 17026802ffdSnia *level = (unsigned char)adjust_level(*level, delta); 17126802ffdSnia } 17226802ffdSnia } 17326802ffdSnia break; 17426802ffdSnia case AUDIO_MIXER_ENUM: 17526802ffdSnia for (i = 0; i < info->un.e.num_mem; ++i) { 17626802ffdSnia if (info->un.e.member[i].ord == value.un.ord) { 17726802ffdSnia cur_index = i; 17826802ffdSnia break; 17926802ffdSnia } 18026802ffdSnia } 18126802ffdSnia if (right) { 18226802ffdSnia value.un.ord = cur_index < (info->un.e.num_mem - 1) ? 18326802ffdSnia info->un.e.member[cur_index + 1].ord : 18426802ffdSnia info->un.e.member[0].ord; 18526802ffdSnia } else { 18626802ffdSnia value.un.ord = cur_index > 0 ? 18726802ffdSnia info->un.e.member[cur_index - 1].ord : 18826802ffdSnia info->un.e.member[control->info.un.e.num_mem - 1].ord; 18926802ffdSnia } 19026802ffdSnia break; 19126802ffdSnia case AUDIO_MIXER_SET: 19226802ffdSnia if (right) { 19326802ffdSnia control->setindex = 19426802ffdSnia control->setindex < (info->un.s.num_mem - 1) ? 19526802ffdSnia control->setindex + 1 : 0; 19626802ffdSnia } else { 19726802ffdSnia control->setindex = control->setindex > 0 ? 19826802ffdSnia control->setindex - 1 : 19926802ffdSnia control->info.un.s.num_mem - 1; 20026802ffdSnia } 20126802ffdSnia break; 20226802ffdSnia } 20326802ffdSnia 20426802ffdSnia if (info->type != AUDIO_MIXER_SET) { 20526802ffdSnia if (ioctl(aio->fd, AUDIO_MIXER_WRITE, &value) < 0) 20626802ffdSnia err(EXIT_FAILURE, "failed to adjust mixer control"); 20726802ffdSnia } 20826802ffdSnia 20926802ffdSnia draw_control(aio, control, true); 21026802ffdSnia } 21126802ffdSnia 21226802ffdSnia static int 21326802ffdSnia toggle_set(struct aiomixer *aio) 21426802ffdSnia { 21526802ffdSnia struct mixer_ctrl ctrl; 21626802ffdSnia struct aiomixer_class *class = &aio->classes[aio->curclass]; 21726802ffdSnia struct aiomixer_control *control = &class->controls[aio->curcontrol]; 21826802ffdSnia 21926802ffdSnia ctrl.dev = control->info.index; 22026802ffdSnia ctrl.type = control->info.type; 22126802ffdSnia 22226802ffdSnia if (control->info.type != AUDIO_MIXER_SET) 22326802ffdSnia return -1; 22426802ffdSnia 22526802ffdSnia if (ioctl(aio->fd, AUDIO_MIXER_READ, &ctrl) < 0) 22626802ffdSnia err(EXIT_FAILURE, "failed to read mixer control"); 22726802ffdSnia 22826802ffdSnia ctrl.un.mask ^= control->info.un.s.member[control->setindex].mask; 22926802ffdSnia 23026802ffdSnia if (ioctl(aio->fd, AUDIO_MIXER_WRITE, &ctrl) < 0) 23126802ffdSnia err(EXIT_FAILURE, "failed to read mixer control"); 23226802ffdSnia 23326802ffdSnia draw_control(aio, control, true); 23426802ffdSnia return 0; 23526802ffdSnia } 23626802ffdSnia 23726802ffdSnia static void 23826802ffdSnia step_up(struct aiomixer *aio) 23926802ffdSnia { 24026802ffdSnia struct aiomixer_class *class; 24126802ffdSnia struct aiomixer_control *control; 24226802ffdSnia 24326802ffdSnia class = &aio->classes[aio->curclass]; 24426802ffdSnia control = &class->controls[aio->curcontrol]; 24526802ffdSnia 24626802ffdSnia if (aio->channels_unlocked && 24726802ffdSnia control->info.type == AUDIO_MIXER_VALUE && 24826802ffdSnia control->setindex > 0) { 24926802ffdSnia control->setindex--; 25026802ffdSnia draw_control(aio, control, true); 25126802ffdSnia return; 25226802ffdSnia } 25326802ffdSnia select_control(aio, aio->curcontrol - 1); 25426802ffdSnia } 25526802ffdSnia 25626802ffdSnia static void 25726802ffdSnia step_down(struct aiomixer *aio) 25826802ffdSnia { 25926802ffdSnia struct aiomixer_class *class; 26026802ffdSnia struct aiomixer_control *control; 26126802ffdSnia 26226802ffdSnia class = &aio->classes[aio->curclass]; 26326802ffdSnia control = &class->controls[aio->curcontrol]; 26426802ffdSnia 26526802ffdSnia if (aio->channels_unlocked && 26626802ffdSnia control->info.type == AUDIO_MIXER_VALUE && 26726802ffdSnia control->setindex < (control->info.un.v.num_channels - 1)) { 26826802ffdSnia control->setindex++; 26926802ffdSnia draw_control(aio, control, true); 27026802ffdSnia return; 27126802ffdSnia } 27226802ffdSnia 27326802ffdSnia select_control(aio, (aio->curcontrol + 1) % class->numcontrols); 27426802ffdSnia } 27526802ffdSnia 27626802ffdSnia static int 27726802ffdSnia read_key(struct aiomixer *aio, int ch) 27826802ffdSnia { 27926802ffdSnia struct aiomixer_class *class; 28026802ffdSnia struct aiomixer_control *control; 28126802ffdSnia size_t i; 28226802ffdSnia 28326802ffdSnia switch (ch) { 28426802ffdSnia case KEY_RESIZE: 28526802ffdSnia class = &aio->classes[aio->curclass]; 28626802ffdSnia resize_widgets(aio); 28726802ffdSnia draw_header(aio); 28826802ffdSnia draw_classbar(aio); 28926802ffdSnia for (i = 0; i < class->numcontrols; ++i) { 29026802ffdSnia draw_control(aio, 29126802ffdSnia &class->controls[i], 29226802ffdSnia aio->state == STATE_CONTROL_SELECT ? 29326802ffdSnia (aio->curcontrol == i) : false); 29426802ffdSnia } 29526802ffdSnia break; 29626802ffdSnia case KEY_LEFT: 29726802ffdSnia case 'h': 29826802ffdSnia if (aio->state == STATE_CLASS_SELECT) { 29926802ffdSnia select_class(aio, aio->curclass > 0 ? 30026802ffdSnia aio->curclass - 1 : aio->numclasses - 1); 30126802ffdSnia } else if (aio->state == STATE_CONTROL_SELECT) { 30226802ffdSnia class = &aio->classes[aio->curclass]; 30326802ffdSnia slide_control(aio, 30426802ffdSnia &class->controls[aio->curcontrol], false); 30526802ffdSnia } 30626802ffdSnia break; 30726802ffdSnia case KEY_RIGHT: 30826802ffdSnia case 'l': 30926802ffdSnia if (aio->state == STATE_CLASS_SELECT) { 31026802ffdSnia select_class(aio, 31126802ffdSnia (aio->curclass + 1) % aio->numclasses); 31226802ffdSnia } else if (aio->state == STATE_CONTROL_SELECT) { 31326802ffdSnia class = &aio->classes[aio->curclass]; 31426802ffdSnia slide_control(aio, 31526802ffdSnia &class->controls[aio->curcontrol], true); 31626802ffdSnia } 31726802ffdSnia break; 31826802ffdSnia case KEY_UP: 31926802ffdSnia case 'k': 32026802ffdSnia if (aio->state == STATE_CONTROL_SELECT) { 32126802ffdSnia if (aio->curcontrol == 0) { 32226802ffdSnia class = &aio->classes[aio->curclass]; 32326802ffdSnia control = &class->controls[aio->curcontrol]; 32426802ffdSnia control->setindex = -1; 32526802ffdSnia aio->state = STATE_CLASS_SELECT; 32626802ffdSnia draw_control(aio, control, false); 32726802ffdSnia } else { 32826802ffdSnia step_up(aio); 32926802ffdSnia } 33026802ffdSnia } 33126802ffdSnia break; 33226802ffdSnia case KEY_DOWN: 33326802ffdSnia case 'j': 33426802ffdSnia if (aio->state == STATE_CLASS_SELECT) { 33526802ffdSnia class = &aio->classes[aio->curclass]; 33626802ffdSnia if (class->numcontrols > 0) { 33726802ffdSnia aio->state = STATE_CONTROL_SELECT; 33826802ffdSnia select_control(aio, 0); 33926802ffdSnia } 34026802ffdSnia } else if (aio->state == STATE_CONTROL_SELECT) { 34126802ffdSnia step_down(aio); 34226802ffdSnia } 34326802ffdSnia break; 34426802ffdSnia case '\n': 34526802ffdSnia case ' ': 34626802ffdSnia if (aio->state == STATE_CONTROL_SELECT) 34726802ffdSnia toggle_set(aio); 34826802ffdSnia break; 34926802ffdSnia case '1': 35026802ffdSnia select_class(aio, 0); 35126802ffdSnia break; 35226802ffdSnia case '2': 35326802ffdSnia select_class(aio, 1); 35426802ffdSnia break; 35526802ffdSnia case '3': 35626802ffdSnia select_class(aio, 2); 35726802ffdSnia break; 35826802ffdSnia case '4': 35926802ffdSnia select_class(aio, 3); 36026802ffdSnia break; 36126802ffdSnia case '5': 36226802ffdSnia select_class(aio, 4); 36326802ffdSnia break; 36426802ffdSnia case '6': 36526802ffdSnia select_class(aio, 5); 36626802ffdSnia break; 36726802ffdSnia case '7': 36826802ffdSnia select_class(aio, 6); 36926802ffdSnia break; 37026802ffdSnia case '8': 37126802ffdSnia select_class(aio, 7); 37226802ffdSnia break; 37326802ffdSnia case '9': 37426802ffdSnia select_class(aio, 8); 37526802ffdSnia break; 37626802ffdSnia case 'q': 37726802ffdSnia case '\e': 37826802ffdSnia if (aio->state == STATE_CONTROL_SELECT) { 37926802ffdSnia class = &aio->classes[aio->curclass]; 38026802ffdSnia control = &class->controls[aio->curcontrol]; 38126802ffdSnia aio->state = STATE_CLASS_SELECT; 38226802ffdSnia draw_control(aio, control, false); 38326802ffdSnia break; 38426802ffdSnia } 38526802ffdSnia return 1; 38626802ffdSnia case 'u': 38726802ffdSnia aio->channels_unlocked = !aio->channels_unlocked; 38826802ffdSnia if (aio->state == STATE_CONTROL_SELECT) { 38926802ffdSnia class = &aio->classes[aio->curclass]; 39026802ffdSnia control = &class->controls[aio->curcontrol]; 39126802ffdSnia if (control->info.type == AUDIO_MIXER_VALUE) 39226802ffdSnia draw_control(aio, control, true); 39326802ffdSnia } 39426802ffdSnia break; 39526802ffdSnia } 39626802ffdSnia 39726802ffdSnia draw_screen(aio); 39826802ffdSnia return 0; 39926802ffdSnia } 40026802ffdSnia 40126802ffdSnia static void 40226802ffdSnia process_device_select(struct aiomixer *aio, unsigned int num_devices) 40326802ffdSnia { 40426802ffdSnia unsigned int selected_device = 0; 40526802ffdSnia char device_path[16]; 40626802ffdSnia int ch; 40726802ffdSnia 40826802ffdSnia draw_mixer_select(num_devices, selected_device); 40926802ffdSnia 41026802ffdSnia while ((ch = getch()) != ERR) { 41126802ffdSnia switch (ch) { 41226802ffdSnia case '\n': 413c4b49835Snia clear(); 41426802ffdSnia (void)snprintf(device_path, sizeof(device_path), 41526802ffdSnia "/dev/mixer%d", selected_device); 41626802ffdSnia open_device(aio, device_path); 41726802ffdSnia return; 41826802ffdSnia case KEY_UP: 41926802ffdSnia case 'k': 42026802ffdSnia if (selected_device > 0) 42126802ffdSnia selected_device--; 42226802ffdSnia else 42326802ffdSnia selected_device = (num_devices - 1); 42426802ffdSnia break; 42526802ffdSnia case KEY_DOWN: 42626802ffdSnia case 'j': 42726802ffdSnia if (selected_device < (num_devices - 1)) 42826802ffdSnia selected_device++; 42926802ffdSnia else 43026802ffdSnia selected_device = 0; 43126802ffdSnia break; 43226802ffdSnia case '1': 43326802ffdSnia selected_device = 0; 43426802ffdSnia break; 43526802ffdSnia case '2': 43626802ffdSnia selected_device = 1; 43726802ffdSnia break; 43826802ffdSnia case '3': 43926802ffdSnia selected_device = 2; 44026802ffdSnia break; 44126802ffdSnia case '4': 44226802ffdSnia selected_device = 3; 44326802ffdSnia break; 44426802ffdSnia case '5': 44526802ffdSnia selected_device = 4; 44626802ffdSnia break; 44726802ffdSnia case '6': 44826802ffdSnia selected_device = 5; 44926802ffdSnia break; 45026802ffdSnia case '7': 45126802ffdSnia selected_device = 6; 45226802ffdSnia break; 45326802ffdSnia case '8': 45426802ffdSnia selected_device = 7; 45526802ffdSnia break; 45626802ffdSnia case '9': 45726802ffdSnia selected_device = 8; 45826802ffdSnia break; 45926802ffdSnia } 46026802ffdSnia draw_mixer_select(num_devices, selected_device); 46126802ffdSnia } 46226802ffdSnia } 46326802ffdSnia 46426802ffdSnia static void 46526802ffdSnia open_device(struct aiomixer *aio, const char *device) 46626802ffdSnia { 46726802ffdSnia int ch; 46826802ffdSnia 46926802ffdSnia if ((aio->fd = open(device, O_RDWR)) < 0) 47026802ffdSnia err(EXIT_FAILURE, "couldn't open mixer device"); 47126802ffdSnia 47226802ffdSnia if (ioctl(aio->fd, AUDIO_GETDEV, &aio->mixerdev) < 0) 47326802ffdSnia err(EXIT_FAILURE, "AUDIO_GETDEV failed"); 47426802ffdSnia 47526802ffdSnia aio->state = STATE_CLASS_SELECT; 47626802ffdSnia 47726802ffdSnia aiomixer_parse(aio); 47826802ffdSnia 47926802ffdSnia create_widgets(aio); 48026802ffdSnia 48126802ffdSnia draw_header(aio); 48226802ffdSnia select_class(aio, 0); 48326802ffdSnia draw_screen(aio); 48426802ffdSnia 48526802ffdSnia while ((ch = getch()) != ERR) { 48626802ffdSnia if (read_key(aio, ch) != 0) 48726802ffdSnia break; 48826802ffdSnia } 48926802ffdSnia } 49026802ffdSnia 491627850c8Schristos static __dead void 49226802ffdSnia on_signal(int dummy) 49326802ffdSnia { 49426802ffdSnia endwin(); 49526802ffdSnia exit(0); 49626802ffdSnia } 49726802ffdSnia 49826802ffdSnia int 49926802ffdSnia main(int argc, char **argv) 50026802ffdSnia { 50126802ffdSnia const char *mixer_device = NULL; 50226802ffdSnia struct aiomixer *aio; 50326802ffdSnia char mixer_path[32]; 50426802ffdSnia unsigned int mixer_count = 0; 50526802ffdSnia int i, fd; 50626802ffdSnia int ch; 5077956bcafSnia char *no_color = getenv("NO_COLOR"); 50826802ffdSnia 50926802ffdSnia if ((aio = malloc(sizeof(struct aiomixer))) == NULL) { 51026802ffdSnia err(EXIT_FAILURE, "malloc failed"); 51126802ffdSnia } 51226802ffdSnia 51326802ffdSnia while ((ch = getopt(argc, argv, "d:u")) != -1) { 51426802ffdSnia switch (ch) { 51526802ffdSnia case 'd': 51626802ffdSnia mixer_device = optarg; 51726802ffdSnia break; 51826802ffdSnia case 'u': 51926802ffdSnia aio->channels_unlocked = true; 52026802ffdSnia break; 52126802ffdSnia default: 52226802ffdSnia usage(); 52326802ffdSnia break; 52426802ffdSnia } 52526802ffdSnia } 52626802ffdSnia 52726802ffdSnia argc -= optind; 52826802ffdSnia argv += optind; 52926802ffdSnia 53026802ffdSnia if (initscr() == NULL) 53126802ffdSnia err(EXIT_FAILURE, "can't initialize curses"); 53226802ffdSnia 53326802ffdSnia (void)signal(SIGHUP, on_signal); 53426802ffdSnia (void)signal(SIGINT, on_signal); 53526802ffdSnia (void)signal(SIGTERM, on_signal); 53626802ffdSnia 53726802ffdSnia curs_set(0); 53826802ffdSnia keypad(stdscr, TRUE); 53926802ffdSnia cbreak(); 54026802ffdSnia noecho(); 54126802ffdSnia 5427956bcafSnia aio->use_colour = true; 5437956bcafSnia 5447956bcafSnia if (!has_colors()) 5457956bcafSnia aio->use_colour = false; 5467956bcafSnia 5477956bcafSnia if (no_color != NULL && no_color[0] != '\0') 5487956bcafSnia aio->use_colour = false; 5497956bcafSnia 5507956bcafSnia if (aio->use_colour) { 55126802ffdSnia start_color(); 5525d5b2a54Snia use_default_colors(); 55326802ffdSnia init_pair(COLOR_CONTROL_SELECTED, COLOR_BLUE, COLOR_BLACK); 55426802ffdSnia init_pair(COLOR_LEVELS, COLOR_GREEN, COLOR_BLACK); 55526802ffdSnia init_pair(COLOR_SET_SELECTED, COLOR_BLACK, COLOR_GREEN); 55626802ffdSnia init_pair(COLOR_ENUM_ON, COLOR_WHITE, COLOR_RED); 55726802ffdSnia init_pair(COLOR_ENUM_OFF, COLOR_WHITE, COLOR_BLUE); 55826802ffdSnia init_pair(COLOR_ENUM_MISC, COLOR_BLACK, COLOR_YELLOW); 55926802ffdSnia } 56026802ffdSnia 56126802ffdSnia if (mixer_device != NULL) { 56226802ffdSnia open_device(aio, mixer_device); 56326802ffdSnia } else { 56426802ffdSnia for (i = 0; i < 16; ++i) { 56526802ffdSnia (void)snprintf(mixer_path, sizeof(mixer_path), 56626802ffdSnia "/dev/mixer%d", i); 56726802ffdSnia fd = open(mixer_path, O_RDWR); 56826802ffdSnia if (fd == -1) 56926802ffdSnia break; 57026802ffdSnia close(fd); 57126802ffdSnia mixer_count++; 57226802ffdSnia } 57326802ffdSnia 57426802ffdSnia if (mixer_count > 1) { 57526802ffdSnia process_device_select(aio, mixer_count); 57626802ffdSnia } else { 57726802ffdSnia open_device(aio, _PATH_MIXER); 57826802ffdSnia } 57926802ffdSnia } 58026802ffdSnia 58126802ffdSnia endwin(); 58226802ffdSnia close(aio->fd); 58326802ffdSnia free(aio); 58426802ffdSnia 58526802ffdSnia return 0; 58626802ffdSnia } 587