commit - cb550f104ae6a6230a56885cf3423ad366048c87
commit + c9699bb019e4dc003c99044938d318c7f3416fc9
blob - 0b19dfd9b61bdf2cc9abd839cb844107acf17d44
blob + 4b859156e6c7cfb00ea69c97e7e7a5d9e58854f2
--- ctlitem.c
+++ ctlitem.c
typedef enum
{
PROP_LEVEL = 1,
+ PROP_DEVICE,
N_PROPS
} SiomixerCtlItemProperty;
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);
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;
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);
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);
{
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
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
#include <poll.h>
#include <fcntl.h>
+#include <gdk/gdk.h>
#include <gtk/gtk.h>
#include <sndio.h>
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)
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
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);
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;
}
}
#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));
}
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;
}
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
int nfds;
struct info *infolist;
GListStore *controls;
+ GtkFilter *scalefilter;
+ GtkFilter *dropdownfilter;
};