diff options
Diffstat (limited to 'voss_devctl_gtk.c')
| -rw-r--r-- | voss_devctl_gtk.c | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/voss_devctl_gtk.c b/voss_devctl_gtk.c new file mode 100644 index 0000000..27a85b2 --- /dev/null +++ b/voss_devctl_gtk.c @@ -0,0 +1,310 @@ +#include <gtk/gtk.h> + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/soundcard.h> +#include <sys/sysctl.h> + +#include <stdbool.h> +#include <string.h> +#include <fcntl.h> +#include <assert.h> + +#include "virtual_oss.h" + +#define OSS_DEV "/dev/dsp0" +#define VOSS_DEV "/dev/dsp.ctl" + +enum model_columns { + COL_TEXT, + COL_VALUE, + _NUM_COLS +}; + +enum dev_types { + DEVTYPE_PLAY, + DEVTYPE_REC, + _NUM_DEVTYPES +}; + +char voss_opts[2] = { + [DEVTYPE_PLAY] = 'P', + [DEVTYPE_REC] = 'R' +}; + +struct device_selection_cb_data { + GtkWidget *combo; + int type; +}; + + +static void +voss_cmd_from_selections(GtkWidget *combo, int type) +{ + GtkTreeModel *model; + GtkTreeIter iter; + gchar *oss_devnode; + int voss_dsp_fd; + char options[VIRTUAL_OSS_OPTIONS_MAX]; + + if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(combo), &iter)) { + model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); + gtk_tree_model_get(model, &iter, COL_VALUE, &oss_devnode, -1); + + if ((voss_dsp_fd = open(VOSS_DEV, O_WRONLY)) > 0) { + snprintf(options, VIRTUAL_OSS_OPTIONS_MAX, + "-%c %s", voss_opts[type], oss_devnode); + ioctl(voss_dsp_fd, VIRTUAL_OSS_ADD_OPTIONS, &options); + close(voss_dsp_fd); + } + + g_free(oss_devnode); + } +} + + +static void +cb_toggle_enabled(GtkToggleButton *button, gpointer user_data) +{ + struct device_selection_cb_data *data = user_data; + gboolean checked = gtk_toggle_button_get_active(button); + + assert(data->type < _NUM_DEVTYPES); + + /* disable the combo when checked */ + gtk_widget_set_sensitive(data->combo, !checked); + + char options[VIRTUAL_OSS_OPTIONS_MAX]; + int voss_dsp_fd; + + /* if disabled, set to /dev/null */ + if (checked) { + if ((voss_dsp_fd = open(VOSS_DEV, O_WRONLY)) > 0) { + snprintf(options, VIRTUAL_OSS_OPTIONS_MAX, + "-%c /dev/null", voss_opts[data->type]); + ioctl(voss_dsp_fd, VIRTUAL_OSS_ADD_OPTIONS, &options); + close(voss_dsp_fd); + } + } + else { + voss_cmd_from_selections(GTK_WIDGET(data->combo), data->type); + } +} + + +static void +cb_dev_selected(GtkComboBox *combo, gpointer user_data) +{ + int type = GPOINTER_TO_INT(user_data); + printf("%d\n", type); + assert(type == DEVTYPE_PLAY || type == DEVTYPE_REC); + voss_cmd_from_selections(GTK_WIDGET(combo), type); +} + + +static void +cb_window_realize(GtkWidget *widget, gpointer user_data) +{ + (void)user_data; //unused + GdkWindow *window = gtk_widget_get_window(widget); + if (GDK_IS_WINDOW(window)) { + gdk_window_set_type_hint(window, GDK_WINDOW_TYPE_HINT_UTILITY); + } +} + + +static void +combo_select_by_value(GtkWidget *combo, const char *val_target) +{ + GtkTreeModel *model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); + GtkTreeIter iter; + + gboolean valid = gtk_tree_model_get_iter_first(model, &iter); + while (valid) { + gchar *val = NULL; + + gtk_tree_model_get(model, &iter, COL_VALUE, &val, -1); + + if (g_strcmp0(val, val_target) == 0) { + gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), &iter); + g_free(val); + break; + } + + g_free(val); + valid = gtk_tree_model_iter_next(model, &iter); + } +} + + +static void +setup_device_selection(GtkListStore *store, GtkWidget *combo, + GtkWidget *check, int type) +{ + assert(type < _NUM_DEVTYPES); + + struct oss_sysinfo si; + int oss_dsp_fd; + + /* initialize oss global information */ + if ((oss_dsp_fd = open(OSS_DEV, O_WRONLY)) == -1) { + printf("Failed to open device %s: %s\n", + OSS_DEV, strerror(errno)); + } + if (ioctl(oss_dsp_fd, SNDCTL_SYSINFO, &si) < 0) { + printf("Cannot get OSS system info: %s\n", + strerror(errno)); + } + + int flags = (type == DEVTYPE_REC) ? PCM_CAP_INPUT : PCM_CAP_OUTPUT; + + for (int i = 0; i < si.numaudios; i++) { + struct oss_audioinfo ai = { .dev = i }; + + if (ioctl(oss_dsp_fd, SNDCTL_AUDIOINFO, &ai) < 0) { + printf("OSS: could not get device %d info: %s\n", + i, strerror(errno)); + continue; + } + + if (!ai.enabled) { + continue; + } + + if (ai.caps & (flags | PCM_CAP_VIRTUAL)) { + gtk_list_store_insert_with_values(store, NULL, -1, COL_TEXT, ai.name, + COL_VALUE, ai.devnode, -1); + } + } + + close(oss_dsp_fd); + + struct virtual_oss_system_info vsi; + int voss_dsp_fd; + char options[VIRTUAL_OSS_OPTIONS_MAX]; + + /* the same for Virtual OSS */ + if ((voss_dsp_fd = open(VOSS_DEV, O_WRONLY)) == -1) { + printf("Failed to open device %s: %s\n", + VOSS_DEV, strerror(errno)); + } + if (ioctl(voss_dsp_fd, VIRTUAL_OSS_GET_SYSTEM_INFO, &vsi) < 0) { + printf("Cannot get Virtual OSS system info: %s\n", + strerror(errno)); + } + + close(voss_dsp_fd); + + char current_dev[OSS_DEVNODE_SIZE]; + if (type == DEVTYPE_PLAY) { + strncpy(current_dev, vsi.tx_device_name, OSS_DEVNODE_SIZE); + } + else if (type == DEVTYPE_REC) { + strncpy(current_dev, vsi.rx_device_name, OSS_DEVNODE_SIZE); + } + + bool active = true; + if (strncmp("/dev/null", current_dev, OSS_DEVNODE_SIZE)) { + combo_select_by_value(combo, current_dev); + } + else { + active = false; + } + + /* if no match and not already /dev/null, revert to /dev/null */ + if (active && gtk_combo_box_get_active(GTK_COMBO_BOX(combo)) < 0) { + active = false; + if ((voss_dsp_fd = open(VOSS_DEV, O_WRONLY)) > 0) { + snprintf(options, VIRTUAL_OSS_OPTIONS_MAX, + "-%c /dev/null", voss_opts[type]); + ioctl(voss_dsp_fd, VIRTUAL_OSS_ADD_OPTIONS, &options); + close(voss_dsp_fd); + } + } + + if (!active) { + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE); + gtk_widget_set_sensitive(combo, FALSE); + gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); + } + + struct device_selection_cb_data *data = g_new(struct device_selection_cb_data, 1); + data->combo = combo; + data->type = type; + + GtkCellRenderer *render = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), render, TRUE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), render, + "text", COL_TEXT, NULL); + + g_signal_connect(combo, "changed", G_CALLBACK(cb_dev_selected), + GINT_TO_POINTER(type)); + g_signal_connect(check, "toggled", G_CALLBACK(cb_toggle_enabled), data); +} + + +static void +cb_init(GtkApplication* app, gpointer user_data) +{ + GtkWidget *window, *vbox_main, + *hbox_play, *combo_play, *check_play, + *hbox_rec, *combo_rec, *check_rec; + GtkListStore *store_play, *store_rec; + + (void)user_data; + + window = gtk_application_window_new(app); + gtk_window_set_title(GTK_WINDOW(window), "Virtual OSS Device Selector"); + gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER_ALWAYS); + gtk_window_set_resizable(GTK_WINDOW(window), FALSE); + + g_signal_connect(window, "realize", G_CALLBACK(cb_window_realize), NULL); + + store_play = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); + combo_play = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store_play)); + check_play = gtk_check_button_new_with_label("Disabled"); + setup_device_selection(store_play, combo_play, check_play, DEVTYPE_PLAY); + + store_rec = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); + combo_rec = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store_rec)); + check_rec = gtk_check_button_new_with_label("Disabled"); + setup_device_selection(store_rec, combo_rec, check_rec, DEVTYPE_REC); + + hbox_play = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10); + gtk_box_pack_start(GTK_BOX(hbox_play), combo_play, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(hbox_play), check_play, FALSE, FALSE, 0); + + hbox_rec = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10); + gtk_box_pack_start(GTK_BOX(hbox_rec), combo_rec, TRUE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(hbox_rec), check_rec, FALSE, FALSE, 0); + + vbox_main = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10); + gtk_box_pack_start(GTK_BOX(vbox_main), hbox_play, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox_main), hbox_rec, FALSE, FALSE, 0); + + gtk_widget_set_margin_start(vbox_main, 12); + gtk_widget_set_margin_end(vbox_main, 12); + gtk_widget_set_margin_top(vbox_main, 12); + gtk_widget_set_margin_bottom(vbox_main, 12); + + gtk_container_add(GTK_CONTAINER(window), vbox_main); + + gtk_widget_show_all(window); +} + + +int +main(int argc, char **argv) +{ + GtkApplication *app; + int ret; + + app = gtk_application_new("org.freebsd.voss-devctl", + G_APPLICATION_DEFAULT_FLAGS); + + g_signal_connect(app, "activate", G_CALLBACK(cb_init), NULL); + ret = g_application_run(G_APPLICATION(app), argc, argv); + g_object_unref(app); + + return ret; +} |
