Commit Diff


commit - cb550f104ae6a6230a56885cf3423ad366048c87
commit + c9699bb019e4dc003c99044938d318c7f3416fc9
blob - 0b19dfd9b61bdf2cc9abd839cb844107acf17d44
blob + 4b859156e6c7cfb00ea69c97e7e7a5d9e58854f2
--- ctlitem.c
+++ ctlitem.c
@@ -25,6 +25,7 @@ extern struct app_state s;
 typedef enum
 {
 	PROP_LEVEL = 1,
+	PROP_DEVICE,
 	N_PROPS
 } SiomixerCtlItemProperty;
 
@@ -87,6 +88,8 @@ siomixer_ctl_item_class_init(SiomixerCtlItemClass *kla
 
 	properties[PROP_LEVEL] =
 	    g_param_spec_int("level", "Level", "Volume level", 0, 255, 0, G_PARAM_READWRITE);
+	properties[PROP_DEVICE] =
+	    g_param_spec_string("device", "Device", "Device identifier", "None", G_PARAM_READWRITE);
 
 	g_object_class_install_properties(object_class, N_PROPS, properties);
 
@@ -112,10 +115,17 @@ siomixer_ctl_item_get_property(GObject *o, guint prope
     GValue *value, GParamSpec *pspec)
 {
 	SiomixerCtlItem *self = SIOMIXER_CTL_ITEM(o);
+	char *dev;
 	switch ((SiomixerCtlItemProperty) property_id) {
 	case PROP_LEVEL:
 		g_value_set_int(value, self->level);
 		break;
+	case PROP_DEVICE:
+		asprintf(&dev, "%s: %s", self->desc.node1.name,
+		    strlen(self->desc.display) ? self->desc.display : "Unknown");
+		g_value_set_string(value, dev);
+		free(dev);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
 		break;
@@ -173,7 +183,7 @@ siomixer_ctl_item_to_widget(gpointer o, gpointer user_
 	char *group = NULL;
 	char *unit = NULL;
 
-	/* Scales only make sense for numeric values */
+	/* The filter makes sure we only see SIOCTL_NUM */
 	g_assert(item->desc.type == SIOCTL_NUM);
 
 	GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
@@ -193,12 +203,12 @@ siomixer_ctl_item_to_widget(gpointer o, gpointer user_
 	gtk_widget_set_vexpand(gscale, TRUE);
 	gtk_scale_set_draw_value(GTK_SCALE(gscale), TRUE);
 	gtk_scale_set_format_value_func(GTK_SCALE(gscale), _format_percent, NULL, NULL);
+	gtk_widget_set_size_request(gscale, -1, 100);
 
 	g_signal_connect(item, "notify::level", G_CALLBACK(_on_scale_value_changed), item);
 	GtkAdjustment *a = gtk_range_get_adjustment(GTK_RANGE(gscale)); 
 	item->binding = g_object_bind_property(item, "level", a, "value",
 	    G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
-	
 	gtk_box_append(GTK_BOX(box), gscale);
 
 	free(label);
@@ -248,3 +258,26 @@ siomixer_ctl_item_get_addr(SiomixerCtlItem *self)
 {
 	return self->desc.addr;
 }
+
+gboolean
+siomixer_ctl_item_scale_filter(gpointer item, gpointer user_data)
+{
+	SiomixerCtlItem *i = SIOMIXER_CTL_ITEM(item); 
+	if (i->desc.type == SIOCTL_NUM)
+		return TRUE;
+	else
+		return FALSE;
+}
+
+gboolean
+siomixer_ctl_item_server_devices_filter(gpointer item, gpointer user_data)
+{
+	SiomixerCtlItem *i = SIOMIXER_CTL_ITEM(item); 
+	if (i->desc.type != SIOCTL_SEL)
+		return FALSE;
+	if (strcmp(i->desc.node0.name, "server") != 0)
+		return FALSE;
+	if (strcmp(i->desc.func, "device") != 0)
+		return FALSE;
+	return TRUE;
+}
blob - d58370f9ea152367409c8d967026c3332a20a958
blob + 64384887d0259fd15c424b3c70c7679c2c538bd7
--- ctlitem.h
+++ ctlitem.h
@@ -34,4 +34,7 @@ int siomixer_ctl_item_get_level(SiomixerCtlItem *);
 int siomixer_ctl_item_get_mute(SiomixerCtlItem *);
 unsigned siomixer_ctl_item_get_addr(SiomixerCtlItem *);
 
+gboolean siomixer_ctl_item_scale_filter(gpointer, gpointer);
+gboolean siomixer_ctl_item_server_devices_filter(gpointer, gpointer);
+
 #endif /* CTLITEM_H */
blob - 283c55070cc28b52ecc6143aa87240e658b3476d
blob + 9d5f9cf215218bfe88579e043a8e8eee844f6a68
--- siomixer.c
+++ siomixer.c
@@ -18,6 +18,7 @@
 #include <poll.h>
 #include <fcntl.h>
 
+#include <gdk/gdk.h>
 #include <gtk/gtk.h>
 #include <sndio.h>
 
@@ -58,8 +59,7 @@ void onctl(void *, unsigned, unsigned);
 void
 ondesc(void *arg, struct sioctl_desc *d, int curval)
 {
-	gint i, cmp = -1;
-	guint n_items;
+	gint i, n_items, cmp = -1;
 	SiomixerCtlItem *item = NULL;
 
 	if (d == NULL)
@@ -72,8 +72,10 @@ ondesc(void *arg, struct sioctl_desc *d, int curval)
 			g_list_store_remove(G_LIST_STORE(s.controls), i);
 	}
 
+	printf("%d: %s/%s:%s[%d]=%s/%d\n", d->type, d->group, d->node0.name, d->func, d->node0.unit, d->node1.name, curval);
 	switch (d->type) {
 	case SIOCTL_NUM:
+	case SIOCTL_SEL:
 	case SIOCTL_NONE:
 		/*
 		 * find the right position to insert or merge ctl
@@ -98,10 +100,8 @@ ondesc(void *arg, struct sioctl_desc *d, int curval)
 
 	switch (d->type) {
 	case SIOCTL_NUM:
+	case SIOCTL_SEL:
 		if (item == NULL) {
-			if (d->type == SIOCTL_NONE) {
-				g_error("should never happen\n");
-			}
 			item = siomixer_ctl_item_new(d);
 			g_list_store_insert(G_LIST_STORE(s.controls), i, item);
 
@@ -111,11 +111,13 @@ ondesc(void *arg, struct sioctl_desc *d, int curval)
 		siomixer_ctl_item_set_level(item, curval);
 		break;
 	case SIOCTL_NONE:
-		if (item != NULL) {
+		if (item != NULL)
 			g_list_store_remove(G_LIST_STORE(s.controls), i);
-			n_items = g_list_model_get_n_items(G_LIST_MODEL(s.controls));
-		}
 		break;
+	default:
+		/* Should never be reachable */
+		g_error("Invalid SIOCTL type.");
+		break;
 	}
 }
 
@@ -195,25 +197,80 @@ nextent(struct info *i, int mono)
 #endif
 
 static void
+_on_selected_changed(GObject *obj, GParamSpec *pspec, gpointer user_data)
+{
+	GtkDropDown *dropdown = GTK_DROP_DOWN(obj);
+	SiomixerCtlItem *item = gtk_drop_down_get_selected_item(dropdown);
+	if (!item)
+		return;
+	sioctl_setval(s.hdl, siomixer_ctl_item_get_addr(item), 1);
+}
+
+static void
 activate (GtkApplication *app, gpointer user_data)
 {
 	GtkWidget *window;
+	GtkWidget *vbox;
 	GtkWidget *scroll;
 	GtkWidget *flowbox;
 
+	GtkWidget *frame;
+	GtkWidget *hbox;
+	GtkWidget *label;
+	GtkWidget *dropdown;
+
 	window = gtk_application_window_new (app);
-	gtk_window_set_title(GTK_WINDOW (window), "OpenMixer");
+	gtk_window_set_title(GTK_WINDOW (window), "Mixer");
 	gtk_window_set_default_size(GTK_WINDOW (window), -1, 400);
 
+	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
+	gtk_widget_set_hexpand(hbox, FALSE);
+	gtk_widget_set_margin_top(hbox, 10);
+	gtk_widget_set_margin_start(hbox, 10);
+	gtk_widget_set_margin_bottom(hbox, 10);
+	gtk_widget_set_margin_end(hbox, 10);
+
+	frame = gtk_frame_new(NULL);
+	gtk_widget_set_margin_top(frame, 10);
+	gtk_widget_set_margin_start(frame, 10);
+	gtk_widget_set_margin_bottom(frame, 10);
+	gtk_widget_set_margin_end(frame, 10);
+
+	label = gtk_label_new("Device:");
+	gtk_box_append(GTK_BOX(hbox), label);
+
+	GtkFilterListModel *dropdown_filter_model =
+	    gtk_filter_list_model_new(G_LIST_MODEL(s.controls), s.dropdownfilter);
+	dropdown = gtk_drop_down_new(G_LIST_MODEL(dropdown_filter_model), NULL);
+	gtk_widget_set_hexpand(dropdown, FALSE);
+	gtk_drop_down_set_expression(GTK_DROP_DOWN(dropdown),
+	    gtk_property_expression_new(SIOMIXER_TYPE_CTL_ITEM, NULL, "device"));
+	gtk_widget_set_margin_top(dropdown, 5);
+	gtk_widget_set_margin_start(dropdown, 10);
+	gtk_widget_set_margin_bottom(dropdown, 5);
+	gtk_widget_set_margin_end(dropdown, 10);
+	/* XXX: Find and highlight selected */
+	g_signal_connect(dropdown, "notify::selected-item", G_CALLBACK(_on_selected_changed), NULL);
+	gtk_box_append(GTK_BOX(hbox), dropdown);
+	gtk_frame_set_child(GTK_FRAME(frame), hbox);
+
 	flowbox = gtk_flow_box_new();
-	gtk_flow_box_bind_model(GTK_FLOW_BOX(flowbox), G_LIST_MODEL(s.controls),
+	GtkFilterListModel *filter_model =
+	    gtk_filter_list_model_new(G_LIST_MODEL(s.controls), s.scalefilter);
+	gtk_flow_box_bind_model(GTK_FLOW_BOX(flowbox), G_LIST_MODEL(filter_model),
 	    siomixer_ctl_item_to_widget, NULL, NULL);
+
 	gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(flowbox), GTK_SELECTION_NONE);
 
 	scroll = gtk_scrolled_window_new();
 	gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), flowbox);
 
-	gtk_window_set_child(GTK_WINDOW(window), scroll);
+	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
+	gtk_box_append(GTK_BOX(vbox), frame);
+	gtk_box_append(GTK_BOX(vbox), scroll);
+	gtk_widget_set_size_request(vbox, -1, 400);
+
+	gtk_window_set_child(GTK_WINDOW(window), vbox);
 	gtk_window_present(GTK_WINDOW (window));
 }
 
@@ -253,6 +310,7 @@ sio_fd_check(GSource *source)
 static gboolean
 sio_fd_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
 {
+	/* Needs to exist but our actual callback happens in sioctl_revents() */
 	return TRUE;
 }
 
@@ -272,6 +330,12 @@ main (int argc, char **argv)
 		g_error("%s: can't open control device\n", devname);
 
 	s.controls = g_list_store_new(SIOMIXER_TYPE_CTL_ITEM);
+	s.scalefilter =
+	    GTK_FILTER(gtk_custom_filter_new(siomixer_ctl_item_scale_filter,
+	    NULL, NULL));
+	s.dropdownfilter =
+	    GTK_FILTER(gtk_custom_filter_new(siomixer_ctl_item_server_devices_filter,
+	    NULL, NULL));
 
 	/* Initial state */
 	if (!sioctl_ondesc(s.hdl, ondesc, NULL))
blob - b8cf9ebbd7c2ac7348ef83bcbe251e4a68c65c01
blob + 09bf2656186e4a01e732b6330e0127f53434b860
--- siomixer.h
+++ siomixer.h
@@ -22,4 +22,6 @@ struct app_state {
 	int nfds;
 	struct info *infolist;
 	GListStore *controls;
+	GtkFilter *scalefilter;
+	GtkFilter *dropdownfilter;
 };