ncmixerc.c (8015B)
1 #include <sys/types.h> 2 #include <sys/socket.h> 3 #include <sys/un.h> 4 5 #include <curses.h> 6 #include <err.h> 7 #include <errno.h> 8 #include <math.h> 9 #include <signal.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <string.h> 13 #include <unistd.h> 14 15 #include "proto.h" 16 #include "util.h" 17 18 #define LEN(x) (sizeof (x) / sizeof *(x)) 19 #undef MIN 20 #define MIN(x, y) ((x) < (y) ? (x) : (y)) 21 #undef MAX 22 #define SA struct sockaddr 23 #define MAX(x, y) ((x) > (y) ? (x) : (y)) 24 #define ISODD(x) ((x) & 1) 25 #define CONTROL(c) ((c) ^ 0x40) 26 #define CTL_SOCK_PATH "/tmp/mixerctl" 27 #define TIMEO_MS 40 28 29 #ifdef DEBUG 30 #define DEBUG_FD 8 31 #define DPRINTF(x...) dprintf(DEBUG_FD, x) 32 #define DPRINTF_D(x) dprintf(DEBUG_FD, #x "=%d\n", x) 33 #define DPRINTF_F(x) dprintf(DEBUG_FD, #x "=%0.2f\n", x) 34 #else 35 #define DPRINTF 36 #define DPRINTF_D(x) 37 #define DPRINTF_F(x) 38 #endif /* DEBUG */ 39 40 float xf_pos = 0.0f; 41 float step = 0.0625f; 42 float level_step = 0.125f; 43 float ch0_level = 1.0f; 44 float ch1_level = 1.0f; 45 int mon0_on; 46 int mon1_on; 47 int in0_on; 48 int in1_on; 49 int out0_on; 50 int out1_on; 51 int air_on; 52 int sock; 53 54 void 55 curses_init(void) 56 { 57 char *term; 58 59 if (initscr() == NULL) { 60 term = getenv("TERM"); 61 if (term != NULL) 62 errx(1, "error opening terminal: %s", term); 63 else 64 errx(1, "failed to initialize curses"); 65 } 66 cbreak(); 67 noecho(); 68 nonl(); 69 intrflush(stdscr, FALSE); 70 curs_set(FALSE); 71 timeout(TIMEO_MS); 72 } 73 74 void 75 curses_exit(void) 76 { 77 endwin(); 78 } 79 80 void 81 tx_msg(struct msg *msg) 82 { 83 int n; 84 85 do { 86 n = write(sock, msg, sizeof(*msg)); 87 } while (n == -1 && errno == EINTR); 88 if (n == -1) { 89 curses_exit(); 90 errx(1, "lost connection to daemon"); 91 } 92 } 93 94 void 95 rx_msg(struct msg *msg) 96 { 97 int n; 98 99 do { 100 n = read(sock, msg, sizeof(*msg)); 101 } while (n == -1 && errno == EINTR); 102 if (n == 0 || n == -1) { 103 curses_exit(); 104 errx(1, "lost connection to daemon"); 105 } 106 } 107 108 void 109 get_xf_pos(float *xf_pos) 110 { 111 struct msg msg; 112 113 msg.type = GET_XF; 114 tx_msg(&msg); 115 rx_msg(&msg); 116 *xf_pos = msg.m_xf_pos; 117 } 118 119 void 120 set_xf_pos(float xf_pos) 121 { 122 struct msg msg; 123 124 msg.type = SET_XF; 125 msg.m_xf_pos = xf_pos; 126 tx_msg(&msg); 127 } 128 129 void 130 get_mon_on(int index, int *on) 131 { 132 struct msg msg; 133 134 msg.type = GET_MON; 135 msg.m_mon_index = index; 136 tx_msg(&msg); 137 rx_msg(&msg); 138 *on = msg.m_mon_on; 139 } 140 141 void 142 set_mon_on(int index, int on) 143 { 144 struct msg msg; 145 146 msg.type = SET_MON; 147 msg.m_mon_index = index; 148 msg.m_mon_on = on; 149 tx_msg(&msg); 150 } 151 152 void 153 get_vol_level(int chan, float *level) 154 { 155 struct msg msg; 156 157 msg.type = GET_VOL; 158 msg.m_vol_chan = chan; 159 tx_msg(&msg); 160 rx_msg(&msg); 161 *level = msg.m_vol_level; 162 } 163 164 void 165 set_vol_level(int chan, float level) 166 { 167 struct msg msg; 168 169 msg.type = SET_VOL; 170 msg.m_vol_chan = chan; 171 msg.m_vol_level = level; 172 tx_msg(&msg); 173 } 174 175 void 176 get_input_on(int index, int *on) 177 { 178 struct msg msg; 179 180 msg.type = GET_IN; 181 msg.m_input_index = index; 182 tx_msg(&msg); 183 rx_msg(&msg); 184 *on = msg.m_input_on; 185 } 186 187 void 188 get_output_on(int index, int *on) 189 { 190 struct msg msg; 191 192 msg.type = GET_OUT; 193 msg.m_output_index = index; 194 tx_msg(&msg); 195 rx_msg(&msg); 196 *on = msg.m_output_on; 197 } 198 199 void 200 get_air_on(int *on) 201 { 202 struct msg msg; 203 204 msg.type = GET_AIR; 205 tx_msg(&msg); 206 rx_msg(&msg); 207 *on = msg.m_air_on; 208 } 209 210 void 211 set_air_on(int on) 212 { 213 struct msg msg; 214 215 msg.type = SET_AIR; 216 msg.m_air_on = on; 217 tx_msg(&msg); 218 } 219 220 void 221 kb_handler(void) 222 { 223 int c; 224 225 c = getch(); 226 DPRINTF_D(c); 227 switch (c) { 228 case 'h': 229 get_xf_pos(&xf_pos); 230 xf_pos = MAX(xf_pos - step, -1.0f); 231 set_xf_pos(xf_pos); 232 break; 233 case 'l': 234 get_xf_pos(&xf_pos); 235 xf_pos = MIN(xf_pos + step, 1.0f); 236 set_xf_pos(xf_pos); 237 break; 238 case 'j': 239 get_xf_pos(&xf_pos); 240 step = MAX(step / 2, 0.015625f); 241 set_xf_pos(xf_pos); 242 break; 243 case 'k': 244 get_xf_pos(&xf_pos); 245 step = MIN(step * 2, 0.25f); 246 set_xf_pos(xf_pos); 247 break; 248 case CONTROL('H'): 249 get_xf_pos(&xf_pos); 250 if (xf_pos > 0.) 251 xf_pos = 0.f; 252 else 253 xf_pos = -1.f; 254 set_xf_pos(xf_pos); 255 break; 256 case CONTROL('L'): 257 get_xf_pos(&xf_pos); 258 if (xf_pos < 0.) 259 xf_pos = 0.f; 260 else 261 xf_pos = 1.f; 262 set_xf_pos(xf_pos); 263 break; 264 case '1': 265 get_mon_on(0, &mon0_on); 266 mon0_on = !mon0_on; 267 set_mon_on(0, mon0_on); 268 break; 269 case '2': 270 get_mon_on(1, &mon1_on); 271 mon1_on = !mon1_on; 272 set_mon_on(1, mon1_on); 273 break; 274 case 'q': 275 get_vol_level(0, &ch0_level); 276 ch0_level = MAX(ch0_level - level_step, 0.0f); 277 set_vol_level(0, ch0_level); 278 break; 279 case 'w': 280 get_vol_level(0, &ch0_level); 281 ch0_level = MIN(ch0_level + level_step, 1.0f); 282 set_vol_level(0, ch0_level); 283 break; 284 case 'o': 285 get_vol_level(1, &ch1_level); 286 ch1_level = MAX(ch1_level - level_step, 0.0f); 287 set_vol_level(1, ch1_level); 288 break; 289 case 'p': 290 get_vol_level(1, &ch1_level); 291 ch1_level = MIN(ch1_level + level_step, 1.0f); 292 set_vol_level(1, ch1_level); 293 break; 294 case CONTROL('A'): 295 get_air_on(&air_on); 296 air_on = !air_on; 297 set_air_on(air_on); 298 break; 299 case 'x': 300 curses_exit(); 301 exit(0); 302 } 303 } 304 305 void 306 draw_status(void) 307 { 308 int offs; 309 310 offs = ISODD(COLS) ? 2 : 3; 311 if (COLS < 2 + strlen("[CH0: ] [MON: ] [CH1: ] [MON: ]") + offs) { 312 move(getcury(stdscr), 0); 313 printw("\n"); 314 return; 315 } 316 317 get_input_on(0, &in0_on); 318 get_input_on(1, &in1_on); 319 get_mon_on(0, &mon0_on); 320 get_mon_on(1, &mon1_on); 321 322 move(getcury(stdscr), 2); 323 printw("[CH0:%c] [MON:%c]", 324 in0_on ? '*' : ' ', 325 mon0_on ? '*' : ' '); 326 move(getcury(stdscr), COLS - strlen("[MON: ] [CH1: ]") - offs); 327 printw("[CH1:%c] [MON:%c]", 328 in1_on ? '*' : ' ', 329 mon1_on ? '*' : ' '); 330 printw("\n"); 331 } 332 333 void 334 draw_levels(void) 335 { 336 int start, end, len, nsteps, i; 337 338 start = 2; 339 end = ISODD(COLS) ? COLS - 2 : COLS - 3; 340 len = 1.0f / level_step; 341 342 get_vol_level(0, &ch0_level); 343 get_vol_level(1, &ch1_level); 344 345 move(getcury(stdscr), start); 346 nsteps = ch0_level / level_step; 347 for (i = 0; i < nsteps; i++) 348 printw("|"); 349 nsteps = ch1_level / level_step; 350 move(getcury(stdscr), end - len); 351 for (i = 0; i < nsteps; i++) 352 printw("|"); 353 printw("\n"); 354 } 355 356 void 357 draw_xfader(void) 358 { 359 int start, end, len, center, pos, i; 360 361 if (COLS < strlen(" -|x|- ")) 362 return; 363 364 get_xf_pos(&xf_pos); 365 366 start = 2; 367 end = ISODD(COLS) ? COLS - 2 : COLS - 3; 368 len = end - start; 369 center = start + (len / 2); 370 if (xf_pos <= 0.0f) 371 pos = center - ceil((len / 2) * fabsf(xf_pos)); 372 else 373 pos = center + ceil((len / 2) * fabsf(xf_pos)); 374 375 move(getcury(stdscr), center - strlen("[step: 0.000000") / 2); 376 printw("step: %0.6f\n", step); 377 move(getcury(stdscr), center - strlen("[pos: +0.000000") / 2); 378 printw("pos: %+0.6f\n", xf_pos); 379 printw("\n"); 380 381 move(getcury(stdscr), start); 382 for (i = 0; i < len; i++) 383 printw("-"); 384 move(getcury(stdscr), center); 385 printw("+"); 386 move(getcury(stdscr), pos - 1); 387 if (pos == center) 388 printw("|x|"); 389 else 390 printw("|||"); 391 move(getcury(stdscr), end + 1); 392 printw("\n"); 393 } 394 395 void 396 draw_outputs(void) 397 { 398 int start, end, len, center; 399 400 start = 2; 401 end = ISODD(COLS) ? COLS - 2 : COLS - 3; 402 len = end - start; 403 center = start + (len / 2); 404 405 get_output_on(0, &out0_on); 406 get_output_on(1, &out1_on); 407 get_air_on(&air_on); 408 409 move(getcury(stdscr), center - strlen(" master: off") / 2); 410 printw(" master: %s\n", out0_on ? "on" : "off"); 411 move(getcury(stdscr), center - strlen("monitor: off") / 2); 412 printw("monitor: %s\n", out1_on ? "on" : "off"); 413 move(getcury(stdscr), center - strlen(" air: off") / 2); 414 printw(" onair: %s\n", air_on ? "on" : "off"); 415 } 416 417 void 418 draw(void) 419 { 420 erase(); 421 draw_status(); 422 draw_xfader(); 423 draw_levels(); 424 draw_outputs(); 425 refresh(); 426 } 427 428 void 429 loop(void) 430 { 431 for (;;) { 432 draw(); 433 kb_handler(); 434 } 435 } 436 437 int 438 client_connect(char *name) 439 { 440 struct sockaddr_un sun; 441 socklen_t len; 442 int fd; 443 444 fd = socket(AF_UNIX, SOCK_SEQPACKET, 0); 445 if (fd == -1) 446 err(1, "socket"); 447 sun.sun_family = AF_UNIX; 448 strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 449 len = sizeof(sun); 450 if (connect(fd, (SA *)&sun, len) == -1) 451 errx(1, "cannot connect to daemon"); 452 return fd; 453 } 454 455 int 456 main(void) 457 { 458 if (isatty(0) == 0 || isatty(1) == 0) 459 errx(1, "stdin or stdout is not a tty"); 460 signal(SIGPIPE, SIG_IGN); 461 sock = client_connect(CTL_SOCK_PATH); 462 curses_init(); 463 loop(); 464 curses_exit(); 465 return 0; 466 }