From c02b6a4aae66f57df4972a79e9d7cac46d0f4a2e Mon Sep 17 00:00:00 2001 From: Christos Margiolis Date: Sat, 14 Jan 2023 21:43:03 +0200 Subject: [PATCH] use mixer(3) and fix style(9) --- Makefile | 2 +- mixertui.c | 1164 ++++++++++++++++++++++++---------------------------- 2 files changed, 535 insertions(+), 631 deletions(-) diff --git a/Makefile b/Makefile index ab3c0ce..348eea7 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ OBJECTS= ${SOURCES:.c=.o} # BASE ncurses CFLAGS= -Wall -Wextra -I/usr/local/include LDFLAGS= -L/usr/lib -lncursesw -ltinfow \ - -L/usr/local/lib -lsysctlmibinfo2 -lbsddialog + -L/usr/local/lib -lsysctlmibinfo2 -lbsddialog -lmixer RM= rm -f diff --git a/mixertui.c b/mixertui.c index 9009b87..71035e7 100644 --- a/mixertui.c +++ b/mixertui.c @@ -2,6 +2,8 @@ * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2020-2022 Alfonso Sabato Siciliano + * Copyright (c) 2023 Alfonso Sabato Siciliano + * Christos Margiolis * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,27 +28,32 @@ */ #include -#include #include -#include -#include -#include -#include +#include #include -#include +#include #include #include -#include #include #include +#include +#include +#include +#include + #define VERSION "1.5-devel" #define LINES_HEADER 7 #define COLS_HEADER 45 #define HIDE_HEADER (COLS < COLS_HEADER || LINES < LINES_HEADER + 2) +#define ITEM_NAME 10 +#define MAXDEVICES 100 +#define DEV_NAME 6 +#define DEV_DESC 50 + #define GET_COLOR(bg, fg) (COLOR_PAIR(bg * 8 + fg + 1)) /* Foreground on BLACK background */ #define WHITE GET_COLOR(COLOR_WHITE, COLOR_BLACK) @@ -54,102 +61,78 @@ #define CYAN GET_COLOR(COLOR_CYAN, COLOR_BLACK) #define BLACK GET_COLOR(COLOR_BLACK, COLOR_BLACK) -static struct bsddialog_conf conf; -static struct bsddialog_theme theme; +#define POSITION(view, dev) (view == ALL ? abspos(dev) : relpos(dev)) -enum viewmode { PLAYBACK, CAPTURE, ALL, NODEVICE }; - -#define ITEM_NAME 10 -struct item { - int id; - unsigned int abspos; - unsigned int relpos; -#define POSITION(view, item) (view == ALL ? item->abspos : item->relpos) - char name[ITEM_NAME + 1]; - bool lmute; - unsigned int lvolume; - bool rmute; - unsigned int rvolume; - bool is_playback; +enum { + C_VOL = 0, + C_MUTE, }; -#define MAXDEVICES 100 -#define DEV_NAME 6 -#define DEV_DESC 50 -struct device { - int fd; - int unit; - char name[DEV_NAME]; - char desc[DEV_DESC]; - bool is_default; - unsigned int nitems; - unsigned int nplaybacks; - unsigned int ncaptures; - struct item items[SOUND_MIXER_NRDEVICES]; - int curr_item; +enum viewmode { + PLAYBACK, + CAPTURE, + ALL, + NODEVICE }; -void usage(void); +static struct bsddialog_conf conf; +static struct bsddialog_theme theme; +static int nplaybacks; +static int ncaptures; + +static void usage(void); /* View Layer */ -int init_tui(bool enable_color); -void set_popup_theme(int colorpair); -void draw_text(const char* text, int y, int x, bool bold, int colorpair); -void draw_title_and_borders(void); -void draw_header(enum viewmode view, struct device *dev); -int get_prev_item(enum viewmode view, struct device *dev, int start_item); -int get_next_item(enum viewmode view, struct device *dev, int start_item); -#define GET_FIRST_ITEM(view, dev) get_next_item(view, dev, -1) -void draw_item(enum viewmode view, int first, struct item *item, int last, - bool is_selected); -void draw_items(enum viewmode view, struct device *dev, int *first, int *last); +static int init_tui(bool); +static void set_popup_theme(int); +static void draw_text(const char *, int, int, bool, int); +static void draw_title_and_borders(void); +static void draw_header(enum viewmode, struct mixer *); +static void draw_item(enum viewmode, int, struct mix_dev *, int, bool); +static void draw_items(enum viewmode, struct mixer *, int *, int *); /* Popup */ -void error_popup(const char *error, bool show_errno); -void sysctlinfo_error_popup(void); -void help_popup(void); -int select_device_popup(char *mixer); -void sysctl_popup(const char *title, const char *root); +static void error_popup(const char *, bool); +static void sysctlinfo_error_popup(void); +static void help_popup(void); +static int select_device_popup(void); +static void sysctl_popup(const char *, const char *); /* Model Layer */ -int get_default_device_index(void); -bool set_default_device(struct device *dev); -bool get_device(int dev_unit, struct device *dev); -bool set_volume(struct device *dev); -int get_ndevices(char *mixer); -void save_profile(char *mixer); -int load_profile(char *filename); - -void usage(void) +static void save_profile(void); +static int load_profile(char *); +/* Utility functions */ +static int relpos(struct mix_dev *); +static int abspos(struct mix_dev *); +static void count_playbacks_and_captures(struct mixer *); +static int dev_matches_view(struct mix_dev *, enum viewmode); +static struct mix_dev *get_dev(struct mixer *, enum viewmode, int); +static int devno_to_index(struct mix_dev *); +/* Mixer controls */ +static void initctls(struct mixer *); +static int mod_volume(struct mix_dev *, void *); +static int mod_mute(struct mix_dev *, void *); + +int +main(int argc, char *argv[]) { - printf("usage: mixertui -h | -v\n"); - printf(" mixertui -P \n"); - printf(" mixertui [-c] [-d ] [-m ] [-p ] "\ - "[-V p|c|a]\n"); -} - -/* The main() is the Controller Layer */ -int main(int argc, char *argv[argc]) -{ - int input, dev_unit, first, last; - bool loop = true, enable_color = true; - char mixer[MAXPATHLEN] = "/dev/mixer"; + struct mixer *m; + mix_ctl_t *cp; + char name[MAXPATHLEN] = "/dev/mixer"; + char buf[MAXPATHLEN]; char *profilefile = NULL; char sysctl_pcm[15]; - struct device dev; + int input, dev_unit, first, last; + bool loop = true, enable_color = true; enum viewmode view = PLAYBACK; - dev_unit = get_default_device_index(); + dev_unit = mixer_get_dunit(); while ((input = getopt(argc, argv, "cd:hm:P:p:V:v")) != -1) { switch (input) { case 'c': enable_color = false; break; - case 'd': - dev_unit = atoi(optarg); - break; case 'h': usage(); printf("\n"); printf(" -c\t\t Disable color\n"); - printf(" -d \t Open device with number\n"); printf(" -h\t\t Display this help\n"); printf(" -m \t Use to get devices\n"); printf(" -P \t Set and quit (EXPERIMENTAL)\n"); @@ -159,315 +142,197 @@ int main(int argc, char *argv[argc]) printf("\n"); printf("Press F1 inside MixerTUI for runtime features.\n"); printf("See \'man mixertui\' for more information.\n"); - return 0; + return (0); case 'm': - strcpy(mixer, optarg); + strcpy(name, optarg); break; case 'P': - return load_profile(optarg); + return (load_profile(optarg)); case 'p': profilefile = optarg; break; case 'V': - if(strcmp(optarg, "playback") == 0 || strcmp(optarg, "p") == 0) + if (strcmp(optarg, "playback") == 0 || + strcmp(optarg, "p") == 0) view = PLAYBACK; - else if(strcmp(optarg, "capture") == 0 || strcmp(optarg, "c") == 0) + else if (strcmp(optarg, "capture") == 0 || + strcmp(optarg, "c") == 0) view = CAPTURE; - else if(strcmp(optarg, "all") == 0 || strcmp(optarg, "a") == 0) + else if (strcmp(optarg, "all") == 0 || + strcmp(optarg, "a") == 0) view = ALL; else { usage(); - return 1; + return (1); } break; case 'v': printf("MixerTUI %s\n", VERSION); - return 0; + return (0); + case '?': /* FALLTHROUGH */ default: usage(); - return 1; + return (1); } } argc -= optind; argv += optind; - if(init_tui(enable_color) != 0) { + if (init_tui(enable_color) != 0) { printf("Cannot init curses TUI\n"); - return 1; + return (1); } - draw_title_and_borders(); - set_popup_theme(GET_COLOR(COLOR_WHITE, COLOR_BLUE)); - if(profilefile != NULL) { + if (profilefile != NULL) load_profile(profilefile); - dev_unit = get_default_device_index(); - } - if(get_device(dev_unit, &dev)) - dev.curr_item = GET_FIRST_ITEM(view, &dev); - else - view = NODEVICE; + if ((m = mixer_open(name)) == NULL) { + error_popup("Cannot open mixer.", true); + endwin(); + return (1); + } + initctls(m); + count_playbacks_and_captures(m); - draw_header(view, &dev); first = last = 0; - draw_items(view, &dev, &first, &last); + set_popup_theme(GET_COLOR(COLOR_WHITE, COLOR_BLUE)); + draw_title_and_borders(); + draw_items(view, m, &first, &last); + draw_header(view, m); - while(loop) { - input = getch(); - switch(input) { - case 'Q': + while (loop) { + switch ((input = getch())) { + case 'Q': /* FALLTHROUGH */ case 'q': - case 27: /* Esc */ + case 27: /* Esc */ loop = false; break; case KEY_F(1): - case '?': + case '?': /* FALLTHROUGH */ case 'H': case 'h': help_popup(); - draw_header(view, &dev); - draw_items(view, &dev, &first, &last); break; case KEY_F(2): sysctl_popup(" Sound Driver ", "hw.snd"); - draw_header(view, &dev); - draw_items(view, &dev, &first, &last); break; - case '\t': /* TAB */ - if(view == NODEVICE) + case '\t': + if (view == NODEVICE) break; - if(view == PLAYBACK) { + if (view == PLAYBACK) view = CAPTURE; - dev.curr_item = GET_FIRST_ITEM(view, &dev); - } else if(view == CAPTURE) { + else if(view == CAPTURE) view = ALL; - if(dev.curr_item < 0) - dev.curr_item = GET_FIRST_ITEM(view, &dev); - } else { /* view == ALL */ + else /* view == ALL */ view = PLAYBACK; - if(dev.curr_item >= 0 && - !dev.items[dev.curr_item].is_playback) - dev.curr_item = GET_FIRST_ITEM(view, &dev); - } - draw_header(view, &dev); - draw_items(view, &dev, &first, &last); break; case KEY_F(3): - if(view == NODEVICE || view == PLAYBACK) + if (view == NODEVICE || view == PLAYBACK) break; view = PLAYBACK; - if(dev.curr_item < 0 || !dev.items[dev.curr_item].is_playback) - dev.curr_item = GET_FIRST_ITEM(view, &dev); - draw_header(view, &dev); - draw_items(view, &dev, &first, &last); break; case KEY_F(4): - if(view == NODEVICE || view == CAPTURE) + if (view == NODEVICE || view == CAPTURE) break; view = CAPTURE; - if(dev.curr_item < 0 || dev.items[dev.curr_item].is_playback) - dev.curr_item = GET_FIRST_ITEM(view, &dev); - draw_header(view, &dev); - draw_items(view, &dev, &first, &last); break; case KEY_F(5): - if(view == NODEVICE || view == ALL) + if (view == NODEVICE || view == ALL) break; view = ALL; - if(dev.curr_item < 0) - dev.curr_item = GET_FIRST_ITEM(view, &dev); - draw_header(view, &dev); - draw_items(view, &dev, &first, &last); break; case KEY_F(6): - if((dev_unit = select_device_popup(mixer)) >= 0) { - if(view != NODEVICE && dev_unit == dev.unit) { - draw_header(view, &dev); - draw_items(view, &dev, &first, &last); - break; - } - if(view != NODEVICE) - close(dev.fd); - if(get_device(dev_unit, &dev)) { - first = last = 0; - view = (view == NODEVICE) ? PLAYBACK : view; - dev.curr_item = GET_FIRST_ITEM(view, &dev); - } else { - view = NODEVICE; - } - } - draw_header(view, &dev); - draw_items(view, &dev, &first, &last); + dev_unit = select_device_popup(); + if (view != NODEVICE && dev_unit == m->unit) + break; + if (view != NODEVICE) + mixer_close(m); + snprintf(buf, sizeof(buf), "/dev/mixer%d", dev_unit); + if ((m = mixer_open(buf)) != NULL) { + initctls(m); + count_playbacks_and_captures(m); + first = last = 0; + view = (view == NODEVICE) ? PLAYBACK : view; + } else + view = NODEVICE; break; case KEY_F(7): - if(view == NODEVICE) + if (view == NODEVICE) break; - sprintf(sysctl_pcm, "dev.pcm.%d", dev.unit); + sprintf(sysctl_pcm, "dev.pcm.%d", m->unit); sysctl_popup(" Device info ", sysctl_pcm); - draw_header(view, &dev); - draw_items(view, &dev, &first, &last); break; case KEY_F(8): - if(view == NODEVICE) + if (view == NODEVICE) break; - if(!set_default_device(&dev)) - draw_items(view, &dev, &first, &last); - draw_header(view, &dev); + dev_unit = m->unit; + (void)mixer_set_dunit(m, dev_unit); break; - case 'b': + case 'b': /* FALLTHROUGH */ case 'B': - if(view == NODEVICE || dev.curr_item < 0) - break; - dev.items[dev.curr_item].lvolume = - (dev.items[dev.curr_item].lvolume + - dev.items[dev.curr_item].rvolume) / 2; - dev.items[dev.curr_item].rvolume = - dev.items[dev.curr_item].lvolume; - loop = set_volume(&dev); - draw_item(view, first, &dev.items[dev.curr_item], last, true); - break; - case 'M': - case 'm': - case '<': - case '>': - if(view == NODEVICE || dev.curr_item < 0) - break; - if(input != '>') - dev.items[dev.curr_item].lmute = - !dev.items[dev.curr_item].lmute; - if(input != '<') - dev.items[dev.curr_item].rmute = - !dev.items[dev.curr_item].rmute; - loop = set_volume(&dev); - draw_item(view, first, &dev.items[dev.curr_item], last, true); - break; - case '9': - case '8': - case '7': - case '6': - case '5': - case '4': - case '3': - case '2': - case '1': - case '0': - if(view == NODEVICE || dev.curr_item < 0) - break; - dev.items[dev.curr_item].lvolume = (input - 48) * 10; - dev.items[dev.curr_item].rvolume = (input - 48) * 10; - loop = set_volume(&dev); - draw_item(view, first, &dev.items[dev.curr_item], last, true); - break; + case '0' ... '9': case KEY_END: case KEY_HOME: - if(view == NODEVICE || dev.curr_item < 0) - break; - dev.items[dev.curr_item].lvolume = (input == KEY_END) ? 0 : 100; - dev.items[dev.curr_item].rvolume = (input == KEY_END) ? 0 : 100; - loop = set_volume(&dev); - draw_item(view, first, &dev.items[dev.curr_item], last, true); - break; case KEY_UP: case '+': case 's': case 'a': case 'd': - if(view == NODEVICE || dev.curr_item < 0) - break; - if(dev.items[dev.curr_item].lvolume < 100 && input != 'd') - dev.items[dev.curr_item].lvolume++; - if(dev.items[dev.curr_item].rvolume < 100 && input != 'a') - dev.items[dev.curr_item].rvolume++; - loop = set_volume(&dev); - draw_item(view, first, &dev.items[dev.curr_item], last, true); - break; case KEY_DOWN: case '-': case 'x': case 'z': case 'c': - if(view == NODEVICE || dev.curr_item < 0) - break; - if(dev.items[dev.curr_item].lvolume > 0 && input != 'c') - dev.items[dev.curr_item].lvolume--; - if(dev.items[dev.curr_item].rvolume > 0 && input != 'z') - dev.items[dev.curr_item].rvolume--; - loop = set_volume(&dev); - draw_item(view, first, &dev.items[dev.curr_item], last, true); - break; case KEY_NPAGE: - if(view == NODEVICE || dev.curr_item < 0) + case KEY_PPAGE: + if (view == NODEVICE) break; - if(dev.items[dev.curr_item].lvolume < 10) - dev.items[dev.curr_item].lvolume = 0; - else - dev.items[dev.curr_item].lvolume -= 10; - if(dev.items[dev.curr_item].rvolume < 10) - dev.items[dev.curr_item].rvolume = 0; - else - dev.items[dev.curr_item].rvolume -= 10; - loop = set_volume(&dev); - draw_item(view, first, &dev.items[dev.curr_item], last, true); + cp = mixer_get_ctl(m->dev, C_VOL); + cp->mod(cp->parent_dev, &input); break; - case KEY_PPAGE: - if(view == NODEVICE || dev.curr_item < 0) + case 'M': /* FALLTHROUGH */ + case 'm': + if (view == NODEVICE) break; - dev.items[dev.curr_item].lvolume += 10; - if(dev.items[dev.curr_item].lvolume > 100) - dev.items[dev.curr_item].lvolume = 100; - dev.items[dev.curr_item].rvolume += 10; - if(dev.items[dev.curr_item].rvolume > 100) - dev.items[dev.curr_item].rvolume = 100; - loop = set_volume(&dev); - draw_item(view, first, &dev.items[dev.curr_item], last, true); + cp = mixer_get_ctl(m->dev, C_MUTE); + cp->mod(cp->parent_dev, &input); break; case KEY_LEFT: - if(view == NODEVICE || dev.curr_item <= 0 || - get_prev_item(view, &dev, dev.curr_item) < 0) + if (view == NODEVICE || get_dev(m, view, -1) == NULL) break; - draw_item(view, first, &dev.items[dev.curr_item], last, false); - dev.curr_item = get_prev_item(view, &dev, dev.curr_item); - draw_header(view, &dev); - if((int)POSITION(view, (&dev.items[dev.curr_item])) < first) - draw_items(view, &dev, &first, &last); - else - draw_item(view, first, &dev.items[dev.curr_item], last, true); - break; + m->dev = get_dev(m, view, -1); break; case KEY_RIGHT: - if(view == NODEVICE || dev.curr_item < 0 || - get_next_item(view, &dev, dev.curr_item) < 0) + if (view == NODEVICE || get_dev(m, view, +1) == NULL) break; - draw_item(view, first, &dev.items[dev.curr_item], last, false); - dev.curr_item = get_next_item(view, &dev, dev.curr_item); - draw_header(view, &dev); - if((int)POSITION(view, (&dev.items[dev.curr_item])) >= last) - draw_items(view, &dev, &first, &last); - else - draw_item(view, first, &dev.items[dev.curr_item], last, true); + m->dev = get_dev(m, view, +1); break; - case 'p': + case 'p': /* FALLTHROUGH */ case 'P': - if(view == NODEVICE) + if (view == NODEVICE) break; - save_profile(mixer); - draw_header(view, &dev); - draw_items(view, &dev, &first, &last); + save_profile(); break; - case KEY_RESIZE: - case 'r': - case 'R': - draw_header(view, &dev); - draw_items(view, &dev, &first, &last); } + draw_items(view, m, &first, &last); + draw_header(view, m); } endwin(); - return 0; + return (0); +} + +static void +usage(void) +{ + printf("usage: mixertui -h | -v\n"); + printf(" mixertui -P \n"); + printf(" mixertui [-c] [-m ] [-p ] "\ + "[-V p|c|a]\n"); } -/* View */ -void set_popup_theme(int colorpair) +static void +set_popup_theme(int colorpair) { bsddialog_initconf(&conf); conf.shadow = false; @@ -504,12 +369,13 @@ void set_popup_theme(int colorpair) bsddialog_set_theme(&theme); } -int init_tui(bool enable_color) +static int +init_tui(bool enable_color) { int c, i, j, error; - if(initscr() == NULL) - return -1; + if (initscr() == NULL) + return (-1); error = 0; error += keypad(stdscr, TRUE); @@ -530,10 +396,11 @@ int init_tui(bool enable_color) } } - return error; + return (error); } -void draw_text(const char* text, int y, int x, bool bold, int colorpair) +static void +draw_text(const char* text, int y, int x, bool bold, int colorpair) { attron(colorpair | (bold ? A_BOLD : 0)); @@ -541,7 +408,8 @@ void draw_text(const char* text, int y, int x, bool bold, int colorpair) attroff(colorpair | (bold ? A_BOLD : 0)); } -void draw_title_and_borders(void) +static void +draw_title_and_borders(void) { const char *title = " MixerTUI "; @@ -552,9 +420,9 @@ void draw_title_and_borders(void) draw_text(title, 0, (COLS/2) - (strlen(title)/2), true, YELLOW); } -void draw_header(enum viewmode view, struct device *dev) +static void +draw_header(enum viewmode view, struct mixer *m) { - int i, j; char left[5][43] = { "Device", "Chip", @@ -569,21 +437,22 @@ void draw_header(enum viewmode view, struct device *dev) "F7: Device info ", "Esc: Exit " }; + int i, j; - if(HIDE_HEADER) + if (HIDE_HEADER) return; - for(i=1; i < LINES_HEADER; i++) - for(j=1; j < COLS-1; j++) + for (i = 1; i < LINES_HEADER; i++) + for (j = 1; j < COLS-1; j++) draw_text(" ", i, j, true, BLACK); - for(i=0; i < 5; i++) + for (i = 0; i < 5; i++) draw_text(left[i], i+1, 2, true, CYAN); if (view != NODEVICE) { - draw_text(dev->name, 1, 9, true, YELLOW); - draw_text(dev->desc, 2, 9, true, YELLOW); - if(dev->is_default) + draw_text(m->mi.name, 1, 9, true, YELLOW); + draw_text(m->ci.longname, 2, 9, true, YELLOW); + if (m->f_default) draw_text("Default", 3, 9, true, YELLOW); else draw_text("F8: Set as default", 3, 9, true, CYAN); @@ -595,100 +464,70 @@ void draw_header(enum viewmode view, struct device *dev) else if (view == ALL) draw_text("[All]", 4, 39, true, YELLOW); - if(dev->curr_item >= 0) - draw_text(dev->items[dev->curr_item].name, 5, 9, true, YELLOW); + if (m->dev->devno >= 0) + draw_text(m->dev->name, 5, 9, true, YELLOW); } /* right side*/ - if((int)(strlen(right[0]) + strlen(left[3]) + 3) > COLS) + if ((int)(strlen(right[0]) + strlen(left[3]) + 3) > COLS) return; - for(i=0; i < 5; i++) + for (i = 0; i < 5; i++) draw_text(right[i], i+1, COLS - strlen(right[i]) -1, true, CYAN); } -int get_prev_item(enum viewmode view, struct device *dev, int start_item) -{ - int i; - - for(i=start_item-1; i>= 0; i--) { - if(view == PLAYBACK && dev->items[i].is_playback) - return i; - if(view == CAPTURE && (dev->items[i].is_playback == false)) - return i; - if(view == ALL) - return i; - } - - return -1; -} - -int get_next_item(enum viewmode view, struct device *dev, int start_item) +static void +draw_item(enum viewmode view, int first, struct mix_dev *dev, int last, + bool is_selected) { - unsigned int i; - - for(i=start_item+1; i< dev->nitems; i++) { - if(view == PLAYBACK && dev->items[i].is_playback) - return i; - if(view == CAPTURE && (dev->items[i].is_playback == false)) - return i; - if(view == ALL) - return i; - } - - return -1; -} - -void draw_item(enum viewmode view, int first, struct item *item, int last, - bool is_selected) -{ - int y, x, i, pos, top, barH, barW, color_bar; WINDOW *bar; + int y, x, i, pos, top, barh, barw, color_bar; - if(LINES < 4) + if (LINES < 4) return; /* responsive no error */ - pos = POSITION(view, item); + pos = POSITION(view, dev); y = LINES - 2; - x = ((COLS -3) - ((ITEM_NAME +1) * (last - first))) / 2; + x = 0; + x = ((COLS - 3) - ((ITEM_NAME + 1) * (last - first))) / 2; x = x < 0 ? 0 : x; pos = pos - first; - x += pos * (ITEM_NAME +1) + 1; + x += pos * (ITEM_NAME + 1) + 1; - if((x + ITEM_NAME + 1) >= (COLS -1)) + if ((x + ITEM_NAME + 1) >= (COLS - 1)) return; /* responsive no error */ /* label */ attron(is_selected ? (YELLOW | A_BOLD) : CYAN); - mvprintw(y-1, x + 2, "%3d %-3d", item->lvolume, item->rvolume); - if(item->lmute) - mvprintw(y-1, x + 2, " M"); - if(item->rmute) - mvprintw(y-1, x + 7, "M "); + mvprintw(y - 1, x + 1, "%.2f %-.2f", dev->vol.left, dev->vol.right); + if (MIX_ISMUTE(dev->parent_mixer, dev->devno)) { + mvprintw(y - 1, x + 1, " M "); + mvprintw(y - 1, x + 6, " M "); + } attroff(is_selected ? (YELLOW | A_BOLD) : CYAN); - draw_text("<>", y-1, x + 5, true, is_selected ? CYAN : WHITE); + draw_text("<>", y - 1, x + 5, true, is_selected ? CYAN : WHITE); - for(i=0; iname, y, - x + 1 + ((ITEM_NAME - strlen(item->name))/2), + draw_text(dev->name, y, + x + 1 + ((ITEM_NAME - strlen(dev->name)) / 2), true, GET_COLOR(COLOR_YELLOW, COLOR_CYAN)); draw_text(">", y, x + 1 + ITEM_NAME, true, YELLOW); } else { draw_text(" ", y, x, false, BLACK); - draw_text(item->name, y, - x + 1 + ((ITEM_NAME - strlen(item->name))/2), + draw_text(dev->name, y, + x + 1 + ((ITEM_NAME - strlen(dev->name)) / 2), false, GET_COLOR(COLOR_BLACK, COLOR_CYAN)); draw_text(" ", y, x + 1 + ITEM_NAME, false, BLACK); } /* bar */ top = (COLS < COLS_HEADER) ? 2 : LINES_HEADER; - if((y - top - 1 ) < 5) - return; /* responsive no error */ - bar = newwin(y - top - 1, 4, top, x + (ITEM_NAME/2) - 1); + if ((y - top - 1) < 5) + return; /* responsive no error */ + bar = newwin(y - top - 1, 4, top, x + (ITEM_NAME / 2) - 1); wbkgd(bar, BLACK); wattron(bar, is_selected ? (YELLOW | A_BOLD) : CYAN); @@ -696,18 +535,18 @@ void draw_item(enum viewmode view, int first, struct item *item, int last, wattroff(bar, is_selected ? (YELLOW | A_BOLD) : CYAN); color_bar = GET_COLOR(COLOR_GREEN, COLOR_GREEN); - getmaxyx(bar, barH, barW); - for(i=1; i < barH-1; i++) { - if (i > (barH-2)/2) + getmaxyx(bar, barh, barw); + for (i = 1; i < barh - 1; i++) { + if (i > (barh - 2) / 2) color_bar = GET_COLOR(COLOR_WHITE, COLOR_WHITE); - if (i > (barH-2)*0.8) + if (i > (barh - 2) * 0.8) color_bar = GET_COLOR(COLOR_RED, COLOR_RED); wattron(bar, color_bar); - wmove(bar, barH-1-i, 1); - if(item->lvolume > i * (100/(float)barH)) + wmove(bar, barh - 1 - i, 1); + if(dev->vol.left > i * (MIX_VOLMAX / (float)barh)) waddch(bar, '*'); - wmove(bar, barH-1-i, 2); - if(item->rvolume > i * (100/(float)barH)) + wmove(bar, barh - 1 - i, 2); + if(dev->vol.right > i * (MIX_VOLMAX / (float)barh)) waddch(bar, '*'); wattroff(bar, color_bar); } @@ -716,8 +555,10 @@ void draw_item(enum viewmode view, int first, struct item *item, int last, delwin(bar); } -void draw_items(enum viewmode view, struct device *dev, int *first, int *last) +static void +draw_items(enum viewmode view, struct mixer *m, int *first, int *last) { + struct mix_dev *dp; int to_show, nitems, selected, pos; unsigned int i; @@ -725,57 +566,61 @@ void draw_items(enum viewmode view, struct device *dev, int *first, int *last) clrtobot(); draw_title_and_borders(); - if(view == NODEVICE) { + if (view == NODEVICE) { refresh(); return; } - if(view == ALL) { - nitems = dev->nitems; - selected = dev->curr_item; + if (view == ALL) { + nitems = m->ndev; + selected = devno_to_index(m->dev); } else { - nitems = (view == PLAYBACK) ? dev->nplaybacks : dev->ncaptures; - selected = dev->curr_item < 0 ? -1 : dev->items[dev->curr_item].relpos; + nitems = (view == PLAYBACK) ? nplaybacks : ncaptures; + selected = relpos(m->dev); } to_show = (COLS - 3) / (ITEM_NAME + 1); to_show = nitems < to_show ? nitems : to_show; - if(nitems <= to_show) { + if (nitems <= to_show) { *first = 0; } else { - if(selected < *first) + if (selected < *first) *first = selected; - else if(selected > *first + to_show - 1) + else if (selected > *first + to_show - 1) *first = selected - to_show + 1; } *last = *first + to_show; - if(*first > 0) - for(i=0; i<10; i++) - draw_text("<", LINES/2 - 5 + i, 0, true, CYAN); - if(nitems > *last) - for(i=0; i<10; i++) - draw_text(">", LINES/2 - 5 + i, COLS-1, true, CYAN); + if (*first > 0) + for (i = 0; i < 10; i++) + draw_text("<", LINES / 2 - 5 + i, 0, true, CYAN); + if (nitems > *last) + for (i = 0; i < 10; i++) + draw_text(">", LINES / 2 - 5 + i, COLS - 1, true, CYAN); refresh(); - for(i=0; i < dev->nitems; i++) { - if(view == CAPTURE && dev->items[i].is_playback) + TAILQ_FOREACH(dp, &m->devs, devs) { + if (view == CAPTURE && !MIX_ISREC(m, dp->devno)) continue; - if(view == PLAYBACK && !dev->items[i].is_playback) + if (view == PLAYBACK && MIX_ISREC(m, dp->devno)) continue; - pos = (view == ALL) ? i : dev->items[i].relpos; - if(pos < *first || pos >= *last) + pos = (view == ALL) ? abspos(dp) : relpos(dp); + if (pos < *first || pos >= *last) continue; - draw_item(view, *first, &dev->items[i], *last, false); + draw_item(view, *first, dp, *last, false); } - if(dev->curr_item >= 0) - draw_item(view, *first, &dev->items[dev->curr_item], *last, true); + /* + * Use `dp` as a temporary variable to make sure we don't assign NULL + * to m->dev and break things. + */ + draw_item(view, *first, m->dev, *last, true); } /* Popup */ -void error_popup(const char *error, bool show_errno) +static void +error_popup(const char *error, bool show_errno) { char *text; @@ -790,7 +635,8 @@ void error_popup(const char *error, bool show_errno) set_popup_theme(GET_COLOR(COLOR_WHITE, COLOR_BLUE)); } -void sysctlinfo_error_popup(void) +static void +sysctlinfo_error_popup(void) { const char *msg="Make sure that you have loaded the\n" \ "sysctlinfo kernel module, by doing:\n\n" \ @@ -802,7 +648,8 @@ void sysctlinfo_error_popup(void) error_popup(msg, false); } -void help_popup(void) +static void +help_popup(void) { const char *text = \ " Version " VERSION "\n" \ @@ -838,19 +685,20 @@ void help_popup(void) bsddialog_msgbox(&conf, text, 0, 0); } -int select_device_popup(char *mixer) +static int +select_device_popup(void) { - int i, ndevices, selected, button; + struct bsddialog_menuitem item[MAXDEVICES]; char oidname[DEV_NAME + 12]; char pcm[MAXDEVICES][DEV_NAME]; char desc[MAXDEVICES][DEV_DESC]; size_t desclen; - struct bsddialog_menuitem item[MAXDEVICES]; + int i, ndevices, selected, button; - if((ndevices = get_ndevices(mixer)) < 0) - return - 1; + if ((ndevices = mixer_get_nmixers()) < 0) + return (-1); - for(i=0; i < ndevices; i++){ + for (i = 0; i < ndevices; i++){ item[i].prefix = ""; item[i].on = false; item[i].depth = 0; @@ -873,16 +721,17 @@ int select_device_popup(char *mixer) return (button == BSDDIALOG_OK ? selected : -1); } -void sysctl_popup(const char *title, const char *root) +static void +sysctl_popup(const char *title, const char *root) { - const char *pname; struct sysctlmif_object_list *list; struct sysctlmif_object *obj; - size_t valuesize; - char value[MAXPATHLEN]; + const char *pname; char *text, *current, *old; + char value[MAXPATHLEN]; + size_t valuesize; - if((list = sysctlmif_grouplistbyname(root)) == NULL) { + if ((list = sysctlmif_grouplistbyname(root)) == NULL) { sysctlinfo_error_popup(); return; } @@ -890,21 +739,21 @@ void sysctl_popup(const char *title, const char *root) asprintf(&old, ""); SLIST_FOREACH(obj, list, object_link) { valuesize = MAXPATHLEN; - if(sysctl(obj->id, obj->idlevel, value, &valuesize, NULL, 0) != 0) + if (sysctl(obj->id, obj->idlevel, value, &valuesize, NULL, 0) != 0) continue; - if(obj->type == CTLTYPE_STRING && strlen(value) == 0) + if (obj->type == CTLTYPE_STRING && strlen(value) == 0) continue; - pname = &obj->name[strlen(root)+1]; + pname = &obj->name[strlen(root) + 1]; switch (obj->type) { case CTLTYPE_INT: asprintf(¤t, "%s\n%s: %d\n\n", - obj->desc, pname, *((int*)value)); + obj->desc, pname, *((int *)value)); break; case CTLTYPE_UINT: asprintf(¤t, "%s\n%s: %u\n\n", - obj->desc, pname, *((unsigned int*)value)); + obj->desc, pname, *((unsigned int *)value)); break; case CTLTYPE_STRING: value[valuesize] = '\0'; @@ -923,161 +772,22 @@ void sysctl_popup(const char *title, const char *root) free(text); } -/* Model */ -int get_ndevices(char *mixer) -{ - int fd; - struct oss_sysinfo si; - - if ((fd = open(mixer, O_RDWR)) == -1) { - error_popup(mixer, true); - return -1; - } - - if (ioctl(fd, SNDCTL_SYSINFO, &si) == -1) { - error_popup("SNDCTL_SYSINFO", true); - return -1; - } - - close(fd); - - return si.nummixers; -} - -int get_default_device_index(void) -{ - int unit; - size_t size = sizeof(int); - - if(sysctlbyname("hw.snd.default_unit", &unit, &size, NULL, 0) !=0) - return -1; - - return unit; -} - -bool set_default_device(struct device *dev) -{ - size_t size = sizeof(int); - char error[40]; - - if (sysctlbyname("hw.snd.default_unit", NULL, 0, &dev->unit, size) < 0) { - sprintf(error, "sysctl hw.snd.default_unit=%d", dev->unit); - error_popup(error, true); - return false; - } - - dev->is_default = true; - - return true; -} - -bool get_device(int dev_unit, struct device *dev) -{ - int i, fd, volume, devmask, recmask; - char pcm[DEV_NAME], oidname[30], desc[DEV_DESC], mixer[30]; - size_t desclen = DEV_DESC; - const char *names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES; - - sprintf(mixer, "/dev/mixer%d", dev_unit); - if ((fd = open(mixer, O_RDWR)) == -1) { - error_popup(mixer, true); - return false; - } - - /* - * SNDCTL_DSP_GET_RECSRC_NAMES is implemented, - * SNDCTL_DSP_GET_PLAYTGT_NAMES is not fully implemented, - * so use the old interface - */ - if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) { - error_popup("SOUND_MIXER_READ_DEVMASK", true); - return false; - } - if (ioctl(fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1) { - error_popup("SOUND_MIXER_READ_RECMASK", true); - return false; - } - - dev->fd=fd; - dev->unit = dev_unit; - - sprintf(pcm, "pcm%d", dev_unit); - strcpy(dev->name, pcm); - - sprintf(oidname, "dev.pcm.%d.%%desc", dev_unit); - sysctlbyname(oidname, desc, &desclen, NULL, 0); - strncpy(dev->desc, desc, desclen); - dev->desc[desclen] = '\0'; - - dev->is_default = dev_unit == get_default_device_index() ? true : false; - - dev->nitems = 0; - dev->nplaybacks = 0; - dev->ncaptures = 0; - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - if (!((1 << i) & devmask)) - continue; - dev->items[dev->nitems].id=i; - strncpy(dev->items[dev->nitems].name, names[i], ITEM_NAME); - dev->items[dev->nitems].name[ITEM_NAME] = '\0'; - - if (ioctl(fd, MIXER_READ(i), &volume) == -1) - volume = 0; - dev->items[dev->nitems].lvolume = volume & 0x7f; - dev->items[dev->nitems].rvolume = (volume >> 8) & 0x7f; - dev->items[dev->nitems].lmute = false; - dev->items[dev->nitems].rmute = false; - - if ((1 << i) & recmask) { - dev->items[dev->nitems].is_playback = false; - dev->items[dev->nitems].relpos = dev->ncaptures; - dev->ncaptures++; - } else { - dev->items[dev->nitems].is_playback = true; - dev->items[dev->nitems].relpos = dev->nplaybacks; - dev->nplaybacks++; - } - - dev->items[dev->nitems].abspos = dev->nitems; - dev->nitems++; - } - - return true; -} - -bool set_volume(struct device *dev) -{ - int volume; - struct item *item = &dev->items[dev->curr_item]; - - /* - * SNDCTL_MIX_EXTINFO and MIXT_ONOFF are not implemented, - * mute is handled in-software - */ - volume = item->lmute ? 0 : item->lvolume; - volume = volume | (item->rmute ? 0 : item->rvolume << 8); - if (ioctl(dev->fd, MIXER_WRITE(item->id), &volume) == -1) { - error_popup("Cannot change volume, RESTART.", true); - return false; - } - - return true; -} - -void save_profile(char *mixer) +static void +save_profile(void) { + struct mixer *m; + struct mix_dev *dp; + struct sysctlmif_object_list *list; + struct sysctlmif_object *obj; + FILE *fp; + char buf[MAXPATHLEN]; char cwd[MAXPATHLEN], filename[MAXPATHLEN], value[BUFSIZ]; char *text; size_t valuesize; - FILE *fp; - struct device dev; int i, ndevs; - unsigned int j; time_t clock; - struct sysctlmif_object_list *list; - struct sysctlmif_object *obj; - if(time(&clock) < 0) { + if (time(&clock) < 0) { error_popup("Cannot save the profile", true); return; } @@ -1092,52 +802,56 @@ void save_profile(char *mixer) fprintf(fp, "### MixerTUI profile - %s", ctime(&clock)); fprintf(fp, "version %s\n", VERSION); - ndevs = get_ndevices(mixer); - for(i=0; iname, m->ci.longname); + fputs("# VOLUME PCM NAME ID LVOL LMUTE RVOL RMUTE\n", fp); + TAILQ_FOREACH(dp, &m->devs, devs) { + fprintf(fp, "volume %d %s %i %.2f %.2f %s\n", + m->unit, + dp->name, + dp->devno, + dp->vol.left, + dp->vol.right, + MIX_ISMUTE(m, dp->devno) ? "mute" : "unmute"); } } fputs("\n## sysctl objects\n", fp); - if((list = sysctlmif_grouplistbyname("hw.snd")) == NULL) { + if ((list = sysctlmif_grouplistbyname("hw.snd")) == NULL) { sysctlinfo_error_popup(); return; } SLIST_CONCAT(list, sysctlmif_grouplistbyname("dev.pcm"), - sysctlmif_object, object_link); + sysctlmif_object, object_link); SLIST_FOREACH(obj, list, object_link) { - if(!(obj->flags & CTLFLAG_WR)) + if (!(obj->flags & CTLFLAG_WR)) continue; valuesize = BUFSIZ; - if(sysctl(obj->id, obj->idlevel, value, &valuesize, NULL, 0) != 0) + if (sysctl(obj->id, obj->idlevel, value, &valuesize, NULL, 0) != 0) continue; - if(obj->type == CTLTYPE_STRING && strlen(value) == 0) + if (obj->type == CTLTYPE_STRING && strlen(value) == 0) continue; - if(!(obj->flags & CTLFLAG_ANYBODY)) + if (!(obj->flags & CTLFLAG_ANYBODY)) fprintf(fp, "# "); - if(obj->type == CTLTYPE_INT) - fprintf(fp, "sysctl %s %d\n", obj->name, *((int*)value)); - else if(obj->type == CTLTYPE_UINT) - fprintf(fp, "sysctl %s %u\n",obj->name, *((unsigned int*)value)); - else if(obj->type == CTLTYPE_STRING) + if (obj->type == CTLTYPE_INT) { + fprintf(fp, "sysctl %s %d\n", + obj->name, *((int *)value)); + } else if (obj->type == CTLTYPE_UINT) { + fprintf(fp, "sysctl %s %u\n", + obj->name, *((unsigned int *)value)); + } else if (obj->type == CTLTYPE_STRING) fprintf(fp, "sysctl %s %s\n", obj->name, value); } sysctlmif_freelist(list); - fclose(fp); conf.title = " Info "; @@ -1151,68 +865,68 @@ void save_profile(char *mixer) free(text); } -int load_profile(char *filename) +static int +load_profile(char *filename) { + struct mixer *m; + struct mix_dev *dp; + struct sysctlmif_object *obj; FILE *fp; - int pcm, iditem, valueint; - unsigned int i, lvol, rvol, newvaluesize, valueuint; char line[BUFSIZ], option[10], sysctlname[MAXPATHLEN], *parse; - char nameitem[ITEM_NAME + 1], valuestr[BUFSIZ], lmute[7], rmute[7]; + char nameitem[ITEM_NAME + 1], valuestr[BUFSIZ], mute[7]; + char buf[MAXPATHLEN]; + float lvol, rvol; + int pcm, iditem, valueint; + unsigned int newvaluesize, valueuint; void *newvalue; - struct sysctlmif_object *obj; - struct device dev; - if((fp = fopen(filename, "r")) == NULL) { + if ((fp = fopen(filename, "r")) == NULL) { error_popup("Cannot load the profile:", true); - return 1; + return (1); } - while(fgets(line, BUFSIZ, fp) != NULL) { - if(line[0] == '#' || line[0] == '\n') + while (fgets(line, BUFSIZ, fp) != NULL) { + if (line[0] == '#' || line[0] == '\n') continue; sscanf(line, "%s", option); parse = strchr(line, ' ') + 1; - if(strcmp(option, "version") == 0) { - ; // nothing for now - } - else if(strcmp(option, "volume") == 0) { - sscanf(parse, "%d %s %i %u %s %u %s", - &pcm, nameitem, &iditem, &lvol, lmute, &rvol, rmute); - if(get_device(pcm, &dev) == false) - return 1; - for(i=0; idevs, devs) { + if(iditem == dp->devno){ + m->dev = dp; + dp->vol.left = lvol; + dp->vol.right = rvol; + if (mixer_set_vol(m, dp->vol) < 0) + return (1); + if (strcmp(mute, "mute") == 0 && + mixer_set_mute(m, MIX_MUTE) < 0) + return (1); + break; } } - close(dev.fd); - } - else if(strcmp(option, "sysctl") == 0) { + mixer_close(m); + } else if (strcmp(option, "sysctl") == 0) { sscanf(parse, "%s %s", sysctlname, valuestr); - if((obj = sysctlmif_objectbyname(sysctlname)) == NULL) { + if ((obj = sysctlmif_objectbyname(sysctlname)) == NULL) { sysctlinfo_error_popup(); - return 1; + return (1); } - if(obj->type == CTLTYPE_INT) { + if (obj->type == CTLTYPE_INT) { newvaluesize = sizeof(int); valueint = (int)strtoll(valuestr, NULL, 0); newvalue = &valueint; - } - else if(obj->type == CTLTYPE_UINT) { + } else if (obj->type == CTLTYPE_UINT) { newvaluesize = sizeof(unsigned int); valueuint = (u_int)strtoull(valuestr, NULL, 0); newvalue = &valueuint; - } - else if (obj->type == CTLTYPE_STRING) { + } else if (obj->type == CTLTYPE_STRING) { newvaluesize = strlen(valuestr); newvalue = valuestr; } @@ -1220,8 +934,198 @@ int load_profile(char *filename) sysctlmif_freeobject(obj); } } - fclose(fp); - return 0; + return (0); +} + +static int +abspos(struct mix_dev *d) +{ + return (devno_to_index(d)); +} + +static int +relpos(struct mix_dev *d) +{ + struct mixer *m; + struct mix_dev *dp; + int pos = 0, flag; + + m = d->parent_mixer; + flag = MIX_ISREC(m, d->devno); + TAILQ_FOREACH(dp, &m->devs, devs) { + if (flag != MIX_ISREC(m, dp->devno)) + continue; + if (dp->devno == d->devno) + break; + pos++; + } + + return (pos); +} + +static void +count_playbacks_and_captures(struct mixer *m) +{ + struct mix_dev *dp; + + nplaybacks = ncaptures = 0; + TAILQ_FOREACH(dp, &m->devs, devs) { + if (MIX_ISREC(m, dp->devno)) + ncaptures++; + else + nplaybacks++; + } +} + +static int +dev_matches_view(struct mix_dev *d, enum viewmode view) +{ + struct mixer *m; + + m = d->parent_mixer; + if (view == PLAYBACK && !MIX_ISREC(m, d->devno)) + return (1); + if (view == CAPTURE && MIX_ISREC(m, d->devno)) + return (1); + if (view == ALL) + return (1); + + return (0); +} + +static struct mix_dev * +get_dev(struct mixer *m, enum viewmode view, int v) +{ + struct mix_dev *dp; + + if (v > 0) { + dp = TAILQ_NEXT(m->dev, devs); + TAILQ_FOREACH_FROM(dp, &m->devs, devs) { + if (dev_matches_view(dp,view)) + return (dp); + } + return (TAILQ_FIRST(&m->devs)); + } else { + dp = TAILQ_PREV(m->dev, mix_devhead, devs); + TAILQ_FOREACH_REVERSE_FROM(dp, &m->devs, mix_devhead, devs) { + if (dev_matches_view(dp, view)) + return (dp); + } + return (TAILQ_LAST(&m->devs, mix_devhead)); + } +} + +static int +devno_to_index(struct mix_dev *d) +{ + struct mixer *m; + struct mix_dev *dp; + int i = 0; + + m = d->parent_mixer; + TAILQ_FOREACH(dp, &m->devs, devs) { + if (dp->devno == d->devno) + break; + i++; + } + + return (i); +} + +static void +initctls(struct mixer *m) +{ + struct mix_dev *dp; + int rc = 0; + + TAILQ_FOREACH(dp, &m->devs, devs) { + rc += mixer_add_ctl(dp, C_VOL, "volume", mod_volume, NULL); + rc += mixer_add_ctl(dp, C_MUTE, "mute", mod_mute, NULL); + } + if (rc) { + mixer_close(m); + error_popup("Cannot create controls, RESTART.", true); + } +} + +static int +mod_volume(struct mix_dev *d, void *p) +{ + struct mixer *m; + mix_volume_t v; + int *key; + + m = d->parent_mixer; + key = p; + v = d->vol; + + switch (*key) { + case 'b': /* FALLTHROUGH */ + case 'B': + v.left = v.right = (d->vol.left + d->vol.right) / 2; + break; + case '0' ... '9': + v.left = v.right = MIX_VOLNORM((*key - 48) * 10); + break; + case KEY_END: + v.left = v.right = MIX_VOLMIN; + break; + case KEY_HOME: + v.left = v.right = MIX_VOLMAX; + break; + case KEY_UP: /* FALLTHROUGH */ + case '+': + case 's': + case 'a': + case 'd': + v.left += 0.01f; + v.right += 0.01f; + break; + case KEY_DOWN: /* FALLTHROUGH */ + case '-': + case 'x': + case 'z': + case 'c': + v.left -= 0.01f; + v.right -= 0.01f; + break; + case KEY_NPAGE: + v.left -= 0.1f; + v.right -= 0.1f; + break; + case KEY_PPAGE: + v.left += 0.1f; + v.right += 0.1f; + break; + } + if (v.left < MIX_VOLMIN) + v.left = MIX_VOLMIN; + else if (v.left > MIX_VOLMAX) + v.left = MIX_VOLMAX; + if (v.right < MIX_VOLMIN) + v.right = MIX_VOLMIN; + else if (v.right > MIX_VOLMAX) + v.right = MIX_VOLMAX; + if (mixer_set_vol(m, v) < 0) { + error_popup("Cannot change volume, RESTART.", true); + return (-1); + } + + return (0); +} + +static int +mod_mute(struct mix_dev *d, void *p __unused) +{ + struct mixer *m; + + m = d->parent_mixer; + if (mixer_set_mute(m, MIX_TOGGLEMUTE) < 0) { + error_popup("Cannot toggle mute, RESTART.", true); + return (-1); + } + + return (0); } -- 2.38.1