commit - 2ae45b1c436f5e25cf4aad9f95f7640eb4affcac
commit + 092a63203be736454ac80eab935c05c8d8386a4d
blob - 3e6d7786253c41479580428aa687557a1c0c7e68
blob + 62b9f2b960db4a229fc83813d5be8b043832d0e5
--- Makefile
+++ Makefile
WARNINGS= yes
BINDIR?= /usr/local/bin
MANDIR?= /usr/local/man/man
-CFLAGS+= $$(pkg-config --cflags gtk4) -g -O0 -Wall
+CFLAGS+= $$(pkg-config --cflags gtk4) -g -O0 -Wall -fno-omit-frame-pointer -Wimplicit-fallthrough
LDADD+= -lutil -lsndio $$(pkg-config --libs gtk4)
-SRCS= siomixer.c audiowidget.c
+SRCS= siomixer.c ctlitem.c
VERSION= 0.1
blob - fb314cce0616924d8b46bda7c46d3cb66cef6bdf (mode 644)
blob + /dev/null
--- audiowidget.c
+++ /dev/null
-/*
- * Copyright (c) 2026 Tobias Heider <tobhe@openbsd.org>
- * Copyright (c) 2014-2020 Alexandre Ratchov <alex@caoua.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sndio.h>
-#include <gtk/gtk.h>
-
-#include "siomixer.h"
-
-extern struct app_state s;
-
-struct info *_nextpar(struct info *);
-static gboolean _on_debounce_timeout(gpointer);
-static void _on_scale_value_changed(GtkRange *, gpointer);
-static char *_format_percent(GtkScale *, double, gpointer);
-
-/*
- * find the next parameter with the same group, name, func
- */
-struct info *
-_nextpar(struct info *i)
-{
- char *str, *group, *func;
- int unit;
-
- group = i->desc.group;
- func = i->desc.func;
- str = i->desc.node0.name;
- unit = i->desc.node0.unit;
- for (i = i->next; i != NULL; i = i->next) {
- if (strcmp(i->desc.group, group) != 0 ||
- strcmp(i->desc.node0.name, str) != 0 ||
- strcmp(i->desc.func, func) != 0)
- break;
- /* XXX: need to check for -1 ? */
- if (i->desc.node0.unit != unit)
- return i;
- }
- return NULL;
-}
-
-static gboolean
-_on_debounce_timeout(gpointer user_data)
-{
- struct info *i = user_data;
- double value = gtk_range_get_value(audiowidget_get_gtkrange(i->widget));
- unsigned int uvalue = (unsigned int)(value + 0.5);
-
- for (; i != NULL; i = _nextpar(i)) {
- sioctl_setval(s.hdl, i->ctladdr, uvalue);
- i->timeout = 0;
- }
- return G_SOURCE_REMOVE; // one-shot
-}
-
-static void
-_on_scale_value_changed(GtkRange *range, gpointer user_data)
-{
- struct info *i = user_data;
-
- /* Rate limit */
- if (i->timeout != 0)
- return;
- i->timeout = g_timeout_add(150, _on_debounce_timeout, i);
-}
-
-/* Handle conversion from 0-255 to % */
-static char *
-_format_percent(GtkScale *scale, double value, gpointer user_data)
-{
- GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(scale));
- double min = gtk_adjustment_get_lower(adj);
- double max = gtk_adjustment_get_upper(adj);
- double percent = (value - min) / (max - min) * 100.0;
-
- // printf("(%f - %f) / (%f - %f) * 100.0 = %f\n", value, min, max, min, percent);
-
- return g_strdup_printf("%.0f%%", percent);
-}
-
-GtkRange *
-audiowidget_get_gtkrange(AudioWidget *a)
-{
- return GTK_RANGE(a->scale);
-}
-
-GtkWidget *
-audiowidget_get_gtkwidget(AudioWidget *a)
-{
- return a->box;
-}
-
-AudioWidget *
-audiowidget_new(struct info *i)
-{
- char *label = NULL;
- char *group = NULL;
- AudioWidget *a;
-
- switch (i->desc.type) {
- case SIOCTL_NUM:
- /* Only numeric switches make sense as scales */
- break;
- default:
- /* do nothing */
- return NULL;
- }
- // printf("%s %s %d\n", i->desc.group, i->desc.func, i->curval);
-
- a = calloc(1, sizeof(AudioWidget));
- if (a == NULL)
- return NULL;
-
- if (i->desc.group[0] != '\0')
- asprintf(&group, "%s/", i->desc.group);
- asprintf(&label, "%s%s.%s", group ? group : "", i->desc.node0.name, i->desc.func);
-
- GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
- g_object_ref_sink(box);
-
- GtkWidget *l = gtk_label_new(label);
- gtk_widget_set_vexpand(l, FALSE);
- gtk_box_append(GTK_BOX(box), l);
-
- GtkWidget *gscale = gtk_scale_new_with_range(GTK_ORIENTATION_VERTICAL, 0, i->desc.maxval, 1);
- gtk_range_set_inverted(GTK_RANGE(gscale), TRUE);
- 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);
- g_signal_connect(gscale, "value-changed", G_CALLBACK(_on_scale_value_changed), i);
- gtk_range_set_value(GTK_RANGE(gscale), i->curval);
- gtk_box_append(GTK_BOX(box), gscale);
-
- free(label);
- free(group);
-
- a->box = box;
- a->scale = gscale;
-
- return a;
-}
-
-void
-audiowidget_free(AudioWidget *a)
-{
- /*
- * sink and label are owned by box and
- * should get freed with it
- */
- g_object_unref(a->box);
- free(a);
-}
blob - /dev/null
blob + 562f126da682607f64c39783beb9bb8087db7e62 (mode 644)
--- /dev/null
+++ ctlitem.c
+/*
+ * Copyright (c) 2026 Tobias Heider <tobhe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <gtk/gtk.h>
+#include <sndio.h>
+
+#include "ctlitem.h"
+#include "siomixer.h"
+
+extern struct app_state s;
+
+typedef enum
+{
+ PROP_LEVEL = 1,
+ PROP_MUTE,
+ N_PROPS
+} SiomixerCtlItemProperty;
+
+static GParamSpec *properties[N_PROPS] = { NULL, };
+
+struct _SiomixerCtlItem {
+ GObject parent_instance;
+ char *group;
+ char *name;
+ unsigned ctladdr;
+ unsigned timeout;
+ int maxlevel;
+ int level;
+ int mute;
+};
+G_DEFINE_TYPE (SiomixerCtlItem, siomixer_ctl_item, G_TYPE_OBJECT)
+
+static void siomixer_ctl_item_init(SiomixerCtlItem *);
+static void siomixer_ctl_item_class_init(SiomixerCtlItemClass *);
+
+static void siomixer_ctl_item_set_property(GObject *, guint,
+ const GValue *, GParamSpec *);
+static void siomixer_ctl_item_get_property(GObject *, guint,
+ GValue *, GParamSpec *);
+
+static char *_format_percent(GtkScale *, double, gpointer);
+static void siomixer_ctl_item_init(SiomixerCtlItem *);
+static void siomixer_ctl_item_class_init(SiomixerCtlItemClass *);
+static void siomixer_ctl_item_finalize(GObject *);
+
+static gboolean _on_debounce_timeout(gpointer);
+static void _on_scale_value_changed(GObject *, gpointer);
+
+/* Handle conversion from 0-255 to % */
+static char *
+_format_percent(GtkScale *scale, double value, gpointer user_data)
+{
+ GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(scale));
+ double min = gtk_adjustment_get_lower(adj);
+ double max = gtk_adjustment_get_upper(adj);
+ double percent = (value - min) / (max - min) * 100.0;
+
+ return g_strdup_printf("%.0f%%", percent);
+}
+
+static void
+siomixer_ctl_item_init(SiomixerCtlItem *self)
+{
+ self->name = NULL;
+ self->group = NULL;
+ self->ctladdr = 0;
+ self->timeout = 0;
+ self->maxlevel = -1;
+ self->level = -1;
+ self->mute = -1;
+}
+
+static void
+siomixer_ctl_item_class_init(SiomixerCtlItemClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
+
+ object_class->set_property = siomixer_ctl_item_set_property;
+ object_class->get_property = siomixer_ctl_item_get_property;
+ object_class->finalize = siomixer_ctl_item_finalize;
+
+ properties[PROP_LEVEL] =
+ g_param_spec_int("level", "Level", "Volume level", 0, 255, 0, G_PARAM_READWRITE);
+ properties[PROP_MUTE] =
+ g_param_spec_int("mute", "Mute", "Volume mute", -1, 1, 0, G_PARAM_READWRITE);
+
+ g_object_class_install_properties(object_class, N_PROPS, properties);
+
+}
+
+static void
+siomixer_ctl_item_set_property(GObject *o, guint property_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SiomixerCtlItem *self = SIOMIXER_CTL_ITEM(o);
+ switch ((SiomixerCtlItemProperty) property_id) {
+ case PROP_LEVEL:
+ self->level = g_value_get_int (value);
+ break;
+ case PROP_MUTE:
+ self->mute = g_value_get_int (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
+ break;
+ }
+}
+
+static void
+siomixer_ctl_item_get_property(GObject *o, guint property_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SiomixerCtlItem *self = SIOMIXER_CTL_ITEM(o);
+ switch ((SiomixerCtlItemProperty) property_id) {
+ case PROP_LEVEL:
+ g_value_set_int(value, self->level);
+ break;
+ case PROP_MUTE:
+ g_value_set_int(value, self->mute);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (o, property_id, pspec);
+ break;
+ }
+}
+
+static void
+siomixer_ctl_item_finalize(GObject *o)
+{
+ SiomixerCtlItem *self = SIOMIXER_CTL_ITEM(o);
+ free(self->name);
+ free(self->group);
+ G_OBJECT_CLASS(siomixer_ctl_item_parent_class)->finalize(o);
+}
+
+static gboolean
+_on_debounce_timeout(gpointer user_data)
+{
+ SiomixerCtlItem *item = SIOMIXER_CTL_ITEM(user_data);
+
+ sioctl_setval(s.hdl, item->ctladdr, item->level);
+ item->timeout = 0;
+ return G_SOURCE_REMOVE;
+}
+
+static void
+_on_scale_value_changed(GObject *o, gpointer user_data)
+{
+ SiomixerCtlItem *item = SIOMIXER_CTL_ITEM(o);
+
+ /* Rate limit */
+ if (item->timeout != 0)
+ return;
+ item->timeout = g_timeout_add(150, _on_debounce_timeout, o);
+}
+
+GtkWidget *
+siomixer_ctl_item_to_widget(gpointer o, gpointer user_data)
+{
+ SiomixerCtlItem *item = SIOMIXER_CTL_ITEM(o);
+ char *label = NULL;
+ char *group = NULL;
+
+ /* We only care about scales */
+ if (item->level == -1)
+ return NULL;
+
+ GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
+ if (strlen(item->group))
+ asprintf(&group, "%s/", item->group);
+ asprintf(&label, "%s%s", group ? group : "", item->name);
+
+ GtkWidget *l = gtk_label_new(label);
+ gtk_widget_set_vexpand(l, FALSE);
+ gtk_box_append(GTK_BOX(box), l);
+
+ GtkWidget *gscale = gtk_scale_new_with_range(GTK_ORIENTATION_VERTICAL, 0, item->maxlevel, 1);
+ gtk_range_set_inverted(GTK_RANGE(gscale), TRUE);
+ 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);
+
+ g_signal_connect(item, "notify::level", G_CALLBACK(_on_scale_value_changed), item);
+ GtkAdjustment *a = gtk_range_get_adjustment(GTK_RANGE(gscale));
+ g_object_bind_property(item, "level", a, "value",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+ gtk_box_append(GTK_BOX(box), gscale);
+
+ free(label);
+ free(group);
+
+ return box;
+}
+
+SiomixerCtlItem *
+siomixer_ctl_item_new(char *group, char *name, unsigned addr)
+{
+ SiomixerCtlItem *item = g_object_new(SIOMIXER_TYPE_CTL_ITEM, NULL);
+ item->group = strdup(group);
+ item->name = strdup(name);
+ item->ctladdr = addr;
+ return item;
+}
+
+int
+siomixer_ctl_item_cmpdesc(struct sioctl_desc *d, SiomixerCtlItem *self)
+{
+ int res;
+
+ res = strcmp(d->group, self->group);
+ if (res != 0)
+ return res;
+ res = strcmp(d->node0.name, self->name);
+ if (res != 0)
+ return res;
+ return res;
+}
+
+void
+siomixer_ctl_item_set_level(SiomixerCtlItem *self, int level)
+{
+ self->level = level;
+ g_object_notify_by_pspec(G_OBJECT(self),
+ properties[PROP_LEVEL]);
+}
+
+void
+siomixer_ctl_item_set_maxlevel(SiomixerCtlItem *self, int level)
+{
+ self->maxlevel = level;
+}
+
+void
+siomixer_ctl_item_set_mute(SiomixerCtlItem *self, int mute)
+{
+ self->mute = mute;
+ g_object_notify_by_pspec(G_OBJECT(self),
+ properties[PROP_MUTE]);
+}
+
+int
+siomixer_ctl_item_get_level(SiomixerCtlItem *self)
+{
+ return self->level;
+}
+
+int
+siomixer_ctl_item_get_maxlevel(SiomixerCtlItem *self)
+{
+ return self->maxlevel;
+}
+
+int
+siomixer_ctl_item_get_mute(SiomixerCtlItem *self)
+{
+ return self->mute;
+}
blob - 7cbbe889753607cde67e5baef9e8732ecfe034c5
blob + 81716996c756b1f80953d68971779eedfdcc952c
--- siomixer.c
+++ siomixer.c
*/
#include <poll.h>
-#include <sndio.h>
#include <fcntl.h>
#include <gtk/gtk.h>
+#include <sndio.h>
+#include "ctlitem.h"
#include "siomixer.h"
struct app_state s = { 0 };
NULL, NULL,
};
-int cmpdesc(struct sioctl_desc *, struct sioctl_desc *);
-int isdiag(struct info *);
-struct info *vecent(struct info *, char *, int);
struct info *nextfunc(struct info *);
struct info *firstent(struct info *, char *);
struct info *nextent(struct info *, int);
void ondesc(void *, struct sioctl_desc *, int);
void onctl(void *, unsigned, unsigned);
-static void _info_free(struct info *);
-static void
-_info_free(struct info *i)
-{
- if (i == NULL)
- return;
- if (i->widget)
- audiowidget_free(i->widget);
- if (i->timeout)
- g_source_remove(i->timeout);
- free(i);
-}
-
/*
- * compare two sioctl_desc structures, used to sort infolist
- */
-int
-cmpdesc(struct sioctl_desc *d1, struct sioctl_desc *d2)
-{
- int res;
-
- res = strcmp(d1->group, d2->group);
- if (res != 0)
- return res;
- res = strcmp(d1->node0.name, d2->node0.name);
- if (res != 0)
- return res;
- res = d1->type - d2->type;
- if (res != 0)
- return res;
- res = strcmp(d1->func, d2->func);
- if (res != 0)
- return res;
- res = d1->node0.unit - d2->node0.unit;
- if (d1->type == SIOCTL_SEL ||
- d1->type == SIOCTL_VEC ||
- d1->type == SIOCTL_LIST) {
- if (res != 0)
- return res;
- res = strcmp(d1->node1.name, d2->node1.name);
- if (res != 0)
- return res;
- res = d1->node1.unit - d2->node1.unit;
- }
- return res;
-}
-
-/*
* register a new knob/button, called from the poll() loop. this may be
* called when label string changes, in which case we update the
* existing label widget rather than inserting a new one.
void
ondesc(void *arg, struct sioctl_desc *d, int curval)
{
- struct info *i, **pi;
- int cmp;
+ gint i, cmp = -1;
+ guint n_items;
+ SiomixerCtlItem *item = NULL;
if (d == NULL)
return;
- if (s.flowbox)
- gtk_flow_box_remove_all(GTK_FLOW_BOX(s.flowbox));
+ /* We don't handle units for now */
+ if (d->node0.unit > 0)
+ return;
- /*
- * delete control
- */
- for (pi = &s.infolist; (i = *pi) != NULL; pi = &i->next) {
- if (d->addr == i->desc.addr) {
- *pi = i->next;
- _info_free(i);
- break;
- }
- }
-
switch (d->type) {
- case SIOCTL_NUM:
case SIOCTL_SW:
- case SIOCTL_VEC:
- case SIOCTL_LIST:
- case SIOCTL_SEL:
+ case SIOCTL_NUM:
+ case SIOCTL_NONE:
+ printf("%d: %s %s %s %d\n", d->type, d->group, d->node0.name, d->func, d->node0.unit);
/*
- * find the right position to insert the new widget
+ * find the right position to insert or merge ctl
*/
- for (pi = &s.infolist; (i = *pi) != NULL; pi = &i->next) {
- cmp = cmpdesc(d, &i->desc);
- if (cmp <= 0)
+ n_items = g_list_model_get_n_items(G_LIST_MODEL(s.store));
+ printf("nitems %d\n", n_items);
+ for (i = 0; i < n_items; i++) {
+ item = g_list_model_get_item(G_LIST_MODEL(s.store), i);
+ cmp = siomixer_ctl_item_cmpdesc(d, item);
+ printf("cmp=%d\n", cmp);
+ if (cmp == 0) {
+ /* Update */
+ printf("found at %d\n", i);
break;
- }
- i = malloc(sizeof(struct info));
- if (i == NULL) {
- perror("malloc");
- exit(1);
- }
- i->desc = *d;
- printf("new node: %s %s %s %d\n", i->desc.group, i->desc.node0.name, i->desc.func, i->desc.node0.unit);
- i->ctladdr = d->addr;
- i->curval = i->newval = curval;
- i->mode = MODE_IGNORE;
- i->next = *pi;
- i->timeout = 0;
- i->widget = NULL;
- *pi = i;
- break;
- default:
- break;
- }
-
- /* Reconstruct flow_box */
- if (s.flowbox) {
- for (i = s.infolist; i != NULL; i = nextfunc(i)) {
- if (i->widget == NULL)
- i->widget = audiowidget_new(i);
- if (i->widget) {
- gtk_flow_box_append(GTK_FLOW_BOX(s.flowbox),
- audiowidget_get_gtkwidget(i->widget));
+ } else if (cmp < 0) {
+ item = NULL;
+ break;
}
+ item = NULL;
}
+ if (item == NULL) {
+ printf("alloc new...\n");
+ item = siomixer_ctl_item_new(d->group, d->node0.name, d->addr);
+ g_list_store_insert(G_LIST_STORE(s.store), i, item);
+ }
+ break;
+ default:
+ printf("default: %s/%s.%s %d\n", d->group, d->node0.name, d->func, d->type);
+ return;
}
-}
-/*
- * return true of the vector entry is diagonal
- */
-int
-isdiag(struct info *e)
-{
- if (e->desc.node0.unit < 0 || e->desc.node1.unit < 0)
- return 1;
- return e->desc.node1.unit == e->desc.node0.unit;
-}
-
-/*
- * find the selector or vector entry with the given name and channels
- */
-struct info *
-vecent(struct info *i, char *vstr, int vunit)
-{
- while (i != NULL) {
- if ((strcmp(i->desc.node1.name, vstr) == 0) &&
- (vunit < 0 || i->desc.node1.unit == vunit))
- break;
- i = i->next;
+ switch (d->type) {
+ case SIOCTL_SW:
+ case SIOCTL_NUM:
+ if (strcmp(d->func, "level") == 0) {
+ siomixer_ctl_item_set_level(item, curval);
+ siomixer_ctl_item_set_maxlevel(item, d->maxval);
+ } if (strcmp(d->func, "mute") == 0) {
+ siomixer_ctl_item_set_mute(item, curval);
+ }
+ break;
+ case SIOCTL_NONE:
+ if (strcmp(d->func, "level") == 0) {
+ siomixer_ctl_item_set_level(item, -1);
+ } else if (strcmp(d->func, "mute") == 0) {
+ siomixer_ctl_item_set_mute(item, -1);
+ }
+ /* All controls unused -> free */
+ if ((siomixer_ctl_item_get_level(item) == -1) &&
+ (siomixer_ctl_item_get_mute(item) == -1)) {
+ printf(">>> delete at %d\n", i);
+ g_list_store_remove(G_LIST_STORE(s.store), i);
+ printf(">>> after delete at %d\n", i);
+ }
+ break;
+ case SIOCTL_VEC:
+ case SIOCTL_LIST:
+ case SIOCTL_SEL:
+ default:
+ printf("unhandled: %s %s %s %d\n", d->group, d->node0.name, d->func, d->node0.unit);
+ break;
}
- return i;
}
+#if 0
/*
* skip all parameters with the same group, name, and func
*/
}
return NULL;
}
+#endif
static void
activate (GtkApplication *app, gpointer user_data)
{
- struct info *i;
+ printf(">>> %s: __entry__ \n", __func__);
GtkWidget *window;
GtkWidget *scroll;
+ GtkWidget *flowbox;
window = gtk_application_window_new (app);
gtk_window_set_title(GTK_WINDOW (window), "OpenMixer");
gtk_window_set_default_size(GTK_WINDOW (window), -1, 400);
+ flowbox = gtk_flow_box_new();
+ gtk_flow_box_bind_model(GTK_FLOW_BOX(flowbox), G_LIST_MODEL(s.store),
+ 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);
- s.flowbox = gtk_flow_box_new();
- gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(s.flowbox), GTK_SELECTION_NONE);
-
- /* Build initial list of widgets and append to flow box */
- for (i = s.infolist; i != NULL; i = nextfunc(i)) {
- if (i->widget == NULL)
- i->widget = audiowidget_new(i);
- if (i->widget)
- gtk_flow_box_append(GTK_FLOW_BOX(s.flowbox),
- audiowidget_get_gtkwidget(i->widget));
- }
-
- gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scroll), s.flowbox);
-
gtk_window_set_child(GTK_WINDOW(window), scroll);
-
gtk_window_present(GTK_WINDOW (window));
}
void
onctl(void *arg, unsigned addr, unsigned val)
{
+#if 0
struct info *i;
/* Update infolist */
gtk_range_set_value(audiowidget_get_gtkrange(i->widget),
i->curval);
}
+#endif
}
static gboolean
fprintf(stderr, "%s: can't open control device\n", devname);
exit(1);
}
+ s.store = g_list_store_new(SIOMIXER_TYPE_CTL_ITEM);
/* Initial state */
if (!sioctl_ondesc(s.hdl, ondesc, NULL)) {
blob - /dev/null
blob + 4650de767eed318e09849ee2ce78c2b4778ffd41 (mode 644)
--- /dev/null
+++ ctlitem.h
+/*
+ * Copyright (c) 2026 Tobias Heider <tobhe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef CTLITEM_H
+#define CTLITEM_H
+
+G_BEGIN_DECLS
+
+#define SIOMIXER_TYPE_CTL_ITEM siomixer_ctl_item_get_type()
+G_DECLARE_FINAL_TYPE(SiomixerCtlItem, siomixer_ctl_item, SIOMIXER, CTL_ITEM, GObject)
+
+GtkWidget *siomixer_ctl_item_to_widget(gpointer, gpointer);
+
+SiomixerCtlItem * siomixer_ctl_item_new(char *, char *, unsigned);
+int siomixer_ctl_item_cmpdesc(struct sioctl_desc *, SiomixerCtlItem *);
+
+void siomixer_ctl_item_set_level(SiomixerCtlItem *, int);
+void siomixer_ctl_item_set_maxlevel(SiomixerCtlItem *, int);
+void siomixer_ctl_item_set_mute(SiomixerCtlItem *, int);
+
+int siomixer_ctl_item_get_level(SiomixerCtlItem *);
+int siomixer_ctl_item_get_maxlevel(SiomixerCtlItem *);
+int siomixer_ctl_item_get_mute(SiomixerCtlItem *);
+
+#endif /* CTLITEM_H */
blob - 7e42d7f50d932615ce037e3c74d143164df47487
blob + e6205d8df618198cb51d55ac3008362d9565a8ee
--- siomixer.h
+++ siomixer.h
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <glib-object.h>
+
typedef struct _audiowidget AudioWidget;
-struct info {
- struct info *next;
- struct sioctl_desc desc;
- unsigned ctladdr;
-#define MODE_IGNORE 0 /* ignore this value */
-#define MODE_PRINT 1 /* print-only, don't change value */
-#define MODE_SET 2 /* set to newval value */
-#define MODE_ADD 3 /* increase current value by newval */
-#define MODE_SUB 4 /* decrease current value by newval */
-#define MODE_TOGGLE 5 /* toggle current value */
- unsigned mode;
- int curval, newval;
- unsigned timeout;
- AudioWidget *widget;
-};
-
struct app_state {
struct sioctl_hdl *hdl;
struct pollfd *pfds;
int nfds;
struct info *infolist;
- GtkWidget *flowbox;
+ GListStore *store;
};
struct _audiowidget {
GtkWidget *scale;
};
-AudioWidget *audiowidget_new(struct info *);
GtkWidget *audiowidget_get_gtkwidget(AudioWidget *);
GtkRange *audiowidget_get_gtkrange(AudioWidget *);
+#if 0
+AudioWidget *audiowidget_new(struct info *);
void audiowidget_free(AudioWidget *);
+#endif