#include "pulse.h" #include "../util.h" #include #include #include #include #include #define CONTEXT_NAME "quick-text-bar-audio" #define CONTEXT_CONNECTING 0 #define CONTEXT_CONNECTED 1 #define CONTEXT_FAILED 2 static pa_threaded_mainloop *mainloop; static pa_mainloop_api *mainloop_api; static pa_context *context; static int volume; static int mute; static void sink_info_callback(pa_context *ctx, const pa_sink_info *info, int eol, void *user_data) { if (info) { pa_volume_t arg = pa_cvolume_avg(&info->volume); float float_volume = ((float)arg / (float)PA_VOLUME_NORM) * 100.0f; int new_volume = round(float_volume); if (mute != info->mute || volume != new_volume) { mute = info->mute; volume = new_volume; qtb_signal_modules(1); } } } static void server_info_callback(pa_context *ctx, const pa_server_info *info, void *user_data) { pa_operation *op = pa_context_get_sink_info_by_name( ctx, info->default_sink_name, &sink_info_callback, NULL); pa_operation_unref(op); } static void subscribe_callback(pa_context *ctx, pa_subscription_event_type_t type, uint32_t idx, void *user_data) { unsigned int facility = type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK; pa_operation *op = NULL; switch (facility) { case PA_SUBSCRIPTION_EVENT_SINK: op = pa_context_get_sink_info_by_index(ctx, idx, &sink_info_callback, NULL); break; default: break; } if (op) { pa_operation_unref(op); } } static void state_callback(pa_context *ctx, void *user_data) { pa_operation *op; switch (pa_context_get_state(ctx)) { case PA_CONTEXT_READY: op = pa_context_get_server_info(ctx, &server_info_callback, NULL); pa_operation_unref(op); pa_context_set_subscribe_callback(ctx, &subscribe_callback, NULL); op = pa_context_subscribe(ctx, PA_SUBSCRIPTION_MASK_SINK, NULL, NULL); pa_operation_unref(op); break; case PA_CONTEXT_FAILED: volume = -1; qtb_signal_modules(1); break; default: break; } } void pulse_module_init() { mainloop = pa_threaded_mainloop_new(); if (!mainloop) { qtb_log("could not create mainloop"); qtb_die(); } mainloop_api = pa_threaded_mainloop_get_api(mainloop); if (!mainloop_api) { qtb_log("could not get api"); qtb_die(); } context = pa_context_new(mainloop_api, CONTEXT_NAME); if (!context) { qtb_log("could not create context"); qtb_die(); } pa_context_set_state_callback( context, (pa_context_notify_cb_t)&state_callback, NULL); if (pa_context_connect(context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0) { qtb_log("could not connect"); qtb_die(); } if (pa_threaded_mainloop_start(mainloop) < 0) { qtb_log("could not start mainloop"); qtb_die(); } } static const char *get_volume_icon() { if (mute) { return "ﱝ"; } else if (volume > 50) { return "墳"; } else if (volume > 0) { return "奔"; } return "奄"; } char *pulse_module_poll() { char *output = qtb_calloc(32, 1); if (volume < 0) { /* not connected */ sprintf(output, "ﱝ ??%%"); pa_threaded_mainloop_stop( mainloop); /* free the old mainloop and reset this module */ pa_threaded_mainloop_free(mainloop); pulse_module_init(); } else { const char *volume_icon = get_volume_icon(); sprintf(output, "%s%3d%%", volume_icon, volume); } return output; }