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