commit c9699bb019e4dc003c99044938d318c7f3416fc9 from: Tobias Heider date: Mon Feb 16 22:44:47 2026 UTC Add device selection dropdown and many minor fixes 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 #include +#include #include #include @@ -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; };