Home | History | Annotate | Line # | Download | only in aiomixer
      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