Commit cdce3a82 authored by minus's avatar minus

Watch sink inputs by name

parent d33657fb
......@@ -9,7 +9,7 @@ alsa = dependency('alsa')
glib = dependency('glib-2.0')
executable('panelcontrol',
'panelcontrol.c',
['panelcontrol.c', 'pa.c', 'pa_sink_input_watch_list.c'],
dependencies : [pa, alsa, glib],
install : true)
......
#include <stdio.h>
#include <pulse/pulseaudio.h>
#include <pulse/glib-mainloop.h>
#include "panelcontrol.h"
#include "pa.h"
#include "pa_sink_input_watch_list.h"
static pa_glib_mainloop *xpa_main_loop;
static void xpa_on_sink_input_info(pa_context *ctx,
const pa_sink_input_info *info, int eol, void *userdata) {
if (!info || info->volume.channels < 1) return;
fprintf(stderr, "pa sink input: idx=%u name=%s volume=%u sink=%u mute=%i\n",
info->index, info->name, info->volume.values[0],
info->sink, info->mute);
struct sink_input_watch *w = sink_inputs_get(info->name);
if (w) {
sink_input_watch_add_index(w, info->index);
sink_input_watch_update(w, info->volume.values[0], info->mute != 0);
}
}
static void xpa_on_subscribe(pa_context *ctx,
pa_subscription_event_type_t type, uint32_t idx, void *userdata) {
int etype = type & PA_SUBSCRIPTION_EVENT_TYPE_MASK;
const char *type_str = etype == PA_SUBSCRIPTION_EVENT_NEW ? "new" :
etype == PA_SUBSCRIPTION_EVENT_CHANGE ? "changed" :
etype == PA_SUBSCRIPTION_EVENT_REMOVE ? "removed" : "???";
if (etype == PA_SUBSCRIPTION_EVENT_REMOVE) {
sink_inputs_remove_index(idx);
} else {
pa_operation *op = pa_context_get_sink_input_info(ctx, idx,
&xpa_on_sink_input_info, NULL);
pa_operation_unref(op);
}
fprintf(stderr, "pa subscribe: idx=%u %s\n", idx, type_str);
}
static void xpa_on_connect(pa_context *ctx, void *userdata) {
if (pa_context_get_state(ctx) == PA_CONTEXT_READY) {
// subscribe to PA sink input changes (volume, output, mute, new source)
pa_context_set_subscribe_callback(ctx, &xpa_on_subscribe, NULL);
pa_operation_unref(pa_context_subscribe(ctx, PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, NULL));
// initialize watched sink inputs by listing all
pa_operation_unref(pa_context_get_sink_input_info_list(ctx, &xpa_on_sink_input_info, NULL));
}
}
void pa() {
// connect to PA
xpa_main_loop = pa_glib_mainloop_new(g_main_context_default());
pa_mainloop_api *api = pa_glib_mainloop_get_api(xpa_main_loop);
pa_context *ctx = pa_context_new(api, CLIENT_NAME);
pa_context_connect(ctx, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL);
pa_context_set_state_callback(ctx, &xpa_on_connect, NULL);
// -> update seq with that info (encoder LEDs and buttons)
// subscribe to seq changes
}
void pa();
#include <string.h>
#include "pa_sink_input_watch_list.h"
static struct sink_input_watch_list {
size_t len;
struct sink_input_watch *items;
} sink_inputs = {0};
void sink_input_watch_add_index(struct sink_input_watch *w, uint32_t pa_index) {
for (size_t i = 0; i < w->indices.len;) {
if (w->indices.items[i] == pa_index) {
return;
}
}
w->indices.len++;
w->indices.items = realloc(w->indices.items,
sizeof(uint32_t) * w->indices.len);
w->indices.items[w->indices.len - 1] = pa_index;
}
void sink_input_watch_remove_index(struct sink_input_watch *w, uint32_t pa_index) {
if (w->indices.len == 0) return;
for (size_t i = 0; i < w->indices.len;) {
if (w->indices.items[i] == pa_index) {
memmove(&w->indices.items[i], &w->indices.items[i + 1],
w->indices.len - i - 1);
w->indices.len--;
w->indices.items = realloc(w->indices.items,
sizeof(uint32_t) * w->indices.len);
} else {
i++;
}
}
}
void sink_input_watch_update(struct sink_input_watch *w, pa_volume_t volume, bool mute) {
w->volume = volume;
w->mute = mute;
if (w->volume_changed) {
w->volume_changed(w);
}
}
struct sink_input_watch *sink_inputs_add_sink(const char* sink_name) {
sink_inputs.len++;
sink_inputs.items = realloc(sink_inputs.items,
sizeof(struct sink_input_watch) * sink_inputs.len);
struct sink_input_watch *item = &sink_inputs.items[sink_inputs.len - 1];
item->name = strdup(sink_name);
item->volume = PA_VOLUME_MUTED;
item->mute = true;
item->indices.len = 0;
item->indices.items = NULL;
item->volume_changed = NULL;
item->userdata = NULL;
return item;
}
struct sink_input_watch *sink_inputs_get(const char *sink_name) {
for (size_t i = 0; i < sink_inputs.len; i++) {
if (strcmp(sink_inputs.items[i].name, sink_name) == 0) {
return &sink_inputs.items[i];
}
}
return NULL;
}
void sink_inputs_remove_index(uint32_t pa_index) {
for (size_t i = 0; i < sink_inputs.len; i++) {
sink_input_watch_remove_index(&sink_inputs.items[i], pa_index);
}
}
#include <pulse/pulseaudio.h>
#include <stdbool.h>
struct sink_input_watch {
const char *name;
pa_volume_t volume;
bool mute;
struct indices_list {
size_t len;
uint32_t *items;
} indices;
void (*volume_changed)(struct sink_input_watch *);
void *userdata;
};
void sink_input_watch_add_index(struct sink_input_watch *w, uint32_t pa_index);
void sink_input_watch_remove_index(struct sink_input_watch *w, uint32_t pa_index);
void sink_input_watch_update(struct sink_input_watch *w, pa_volume_t volume, bool mute);
struct sink_input_watch *sink_inputs_add_sink(const char* sink_name);
struct sink_input_watch *sink_inputs_get(const char *sink_name);
void sink_inputs_remove_index(uint32_t pa_index);
......@@ -3,111 +3,9 @@
#include <pulse/glib-mainloop.h>
#include <glib.h>
static const char *CLIENT_NAME = "panelcontrol";
static const int button_notes[] = {
// top row
89,
90,
40,
41,
42,
43,
44,
45,
// bottom row
87,
88,
91,
92,
86,
93,
94,
95,
// layer buttons
84,
85,
};
enum button {
// top row
TOP1 = 89,
TOP2 = 90,
TOP3 = 40,
TOP4 = 41,
TOP5 = 42,
TOP6 = 43,
TOP7 = 44,
TOP8 = 45,
// bottom row
BOTTOM1 = 87,
BOTTOM2 = 88,
BOTTOM3 = 91,
BOTTOM4 = 92,
BOTTOM5 = 86,
BOTTOM6 = 93,
BOTTOM7 = 94,
BOTTOM8 = 95,
// layer buttons
LAYERA = 84,
LAYERB = 85,
};
static pa_glib_mainloop *xpa_main_loop;
struct sink_input {
const char *name;
pa_cvolume volume;
uint32_t *indices;
};
static struct sink_input *sink_inputs = NULL;
static void xpa_on_sink_input_info(pa_context *ctx,
const pa_sink_input_info *info, int eol, void *userdata) {
if (!info) return;
fprintf(stderr, "pa sink input: name=%s volume=%ui sink=%ui mute=%i\n",
info->name, info->volume.values[0], info->sink, info->mute);
}
static void xpa_on_subscribe(pa_context *ctx,
pa_subscription_event_type_t type, uint32_t idx, void *userdata) {
int etype = type & PA_SUBSCRIPTION_EVENT_TYPE_MASK;
const char *type_str = etype == PA_SUBSCRIPTION_EVENT_NEW ? "new" :
etype == PA_SUBSCRIPTION_EVENT_CHANGE ? "changed" :
etype == PA_SUBSCRIPTION_EVENT_REMOVE ? "removed" : "???";
if (etype == PA_SUBSCRIPTION_EVENT_REMOVE) {
for ...
} else {
pa_operation *op = pa_context_get_sink_input_info(ctx, idx,
&xpa_on_sink_input_info, NULL);
pa_operation_unref(op);
}
fprintf(stderr, "pa subscribe: %s\n", type_str);
}
void xpa_on_connect(pa_context *ctx, void *userdata) {
if (pa_context_get_state(ctx) == PA_CONTEXT_READY) {
// subscribe to PA sink input changes (volume, output, mute, new source)
pa_context_set_subscribe_callback(ctx, &xpa_on_subscribe, NULL);
pa_context_subscribe(ctx, PA_SUBSCRIPTION_MASK_SINK_INPUT, NULL, NULL);
}
}
void pa() {
// connect to PA
xpa_main_loop = pa_glib_mainloop_new(g_main_context_default());
pa_mainloop_api *api = pa_glib_mainloop_get_api(xpa_main_loop);
pa_context *ctx = pa_context_new(api, CLIENT_NAME);
pa_context_connect(ctx, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL);
pa_context_set_state_callback(ctx, &xpa_on_connect, NULL);
// -> update seq with that info (encoder LEDs and buttons)
// subscribe to seq changes
}
#include "panelcontrol.h"
#include "pa.h"
#include "pa_sink_input_watch_list.h"
gboolean on_alsa_readable(GIOChannel *source, GIOCondition condition, gpointer data) {
fprintf(stdout, "source=%p cond=%i data=%p\n", (void *)source, condition, data);
......@@ -177,22 +75,35 @@ snd_seq_t *alsa(int source) {
return seq;
}
void toggle_mute(enum button button, void *userdata) {
const char *name = (const char *)userdata;
//xpa_sink_inputs_apply_by_name(name, &xpa_sink_input_set_volume, volume);
}
typedef void (*action_fn)(enum button, void *);
struct button_map_entry {
enum button button;
action_fn action;
void *userdata;
};
static const struct button_map_entry button_map[] = {
{.button = TOP1, .action = &toggle_mute, .userdata = "mpd pulse"}
};
//void toggle_mute(enum button button, void *userdata) {
// const char *name = (const char *)userdata;
// //xpa_sink_inputs_apply_by_name(name, &xpa_sink_input_set_volume, volume);
//}
//
//typedef void (*button_action_fn)(enum button, void *);
//
//struct button_map_entry {
// enum button button;
// button_action_fn action;
// void *userdata;
//};
//
//static const struct button_map_entry button_map[] = {
// {.button = TOP1, .sink_input = NULL, .action = &toggle_mute, .userdata = "mpd pulse"},
//};
//
//
//typedef void (*encoder_action_fn)(enum button, void *);
//
//struct encoder_map_entry {
// enum encoder encoder;
// encoder_action_fn action;
// void *userdata;
//}
//
//static const struct encoder_map_entry encoder_map[] = {
// {.encoder = ENC1, .sink_input = NULL, .action = &change_volume},
//};
int main(int argc, char *argv[]) {
if (argc != 2) {
......@@ -200,9 +111,22 @@ int main(int argc, char *argv[]) {
exit(1);
}
int source = atoi(argv[1]);
struct sink_input_watch *mpd = sink_inputs_add_sink("mpd pulse");
pa();
snd_seq_t *seq = alsa(source);
// create PA watch object for sink name "mpd pulse"
// with a callback function on change (info, sinks)
// register this object with PA
// has a reference to the ALSA button to change the light
// ALSA button/encoder mapping contains a reference to the PA watch
// object, as well as the button/encoder number and a callback.
// on change from MIDI side, the callback is executed and passed the
// MIDI action as well as the mapping struct with the PA watch reference
GMainLoop *main_loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run(main_loop);
g_main_loop_unref(main_loop);
......
static const char *CLIENT_NAME = "panelcontrol";
enum button {
// top row
TOP1 = 89,
TOP2 = 90,
TOP3 = 40,
TOP4 = 41,
TOP5 = 42,
TOP6 = 43,
TOP7 = 44,
TOP8 = 45,
// bottom row
BOTTOM1 = 87,
BOTTOM2 = 88,
BOTTOM3 = 91,
BOTTOM4 = 92,
BOTTOM5 = 86,
BOTTOM6 = 93,
BOTTOM7 = 94,
BOTTOM8 = 95,
// layer buttons
LAYERA = 84,
LAYERB = 85,
};
enum encoder {
ENC1 = 16,
ENC2 = 17,
ENC3 = 18,
ENC4 = 19,
ENC5 = 20,
ENC6 = 21,
ENC7 = 22,
ENC8 = 23,
};
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment