commit 092a63203be736454ac80eab935c05c8d8386a4d from: Tobias Heider date: Sun Feb 15 20:41:50 2026 UTC Rewrite with ListModel backed FlowBox This required making everything a little more GLib and object oriented commit - 2ae45b1c436f5e25cf4aad9f95f7640eb4affcac commit + 092a63203be736454ac80eab935c05c8d8386a4d blob - 3e6d7786253c41479580428aa687557a1c0c7e68 blob + 62b9f2b960db4a229fc83813d5be8b043832d0e5 --- Makefile +++ Makefile @@ -2,9 +2,9 @@ PROG= siomixer 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 @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2026 Tobias Heider - * Copyright (c) 2014-2020 Alexandre Ratchov - * - * 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 -#include - -#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 @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2026 Tobias Heider + * + * 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 +#include + +#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 @@ -16,11 +16,12 @@ */ #include -#include #include #include +#include +#include "ctlitem.h" #include "siomixer.h" struct app_state s = { 0 }; @@ -43,63 +44,13 @@ static GSourceFuncs sioctl_source_funcs = { 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. @@ -107,98 +58,86 @@ cmpdesc(struct sioctl_desc *d1, struct sioctl_desc *d2 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 */ @@ -271,36 +210,29 @@ nextent(struct info *i, int mono) } 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)); } @@ -310,6 +242,7 @@ activate (GtkApplication *app, gpointer user_data) void onctl(void *arg, unsigned addr, unsigned val) { +#if 0 struct info *i; /* Update infolist */ @@ -332,6 +265,7 @@ onctl(void *arg, unsigned addr, unsigned val) gtk_range_set_value(audiowidget_get_gtkrange(i->widget), i->curval); } +#endif } static gboolean @@ -371,6 +305,7 @@ main (int argc, char **argv) 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 @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2026 Tobias Heider + * + * 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 @@ -14,30 +14,16 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include + 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 { @@ -45,7 +31,9 @@ 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