paswitch: avoid information duplication
And miscellaneous cleanup.
This commit is contained in:
parent
bdacb48fb9
commit
1f36351ab7
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
||||||
Copyright (c) 2015 - 2016, Přemysl Janouch <p@janouch.name>
|
Copyright (c) 2015 - 2018, Přemysl Janouch <p@janouch.name>
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
purpose with or without fee is hereby granted.
|
purpose with or without fee is hereby granted.
|
||||||
|
|
|
@ -8,6 +8,8 @@ to other people as well:
|
||||||
- 'wmstatus' does literally everything my i3 doesn't but I'd like it to. It
|
- 'wmstatus' does literally everything my i3 doesn't but I'd like it to. It
|
||||||
includes PulseAudio volume management and hand-written NUT and MPD clients,
|
includes PulseAudio volume management and hand-written NUT and MPD clients,
|
||||||
all in the name of liberation from GPL-licensed software of course
|
all in the name of liberation from GPL-licensed software of course
|
||||||
|
- 'paswitch' displays a list of all PulseAudio sinks and ports and allows
|
||||||
|
switching between them, moving all playing inputs
|
||||||
- 'brightness' allows me to change the brightness of w/e display device I have
|
- 'brightness' allows me to change the brightness of w/e display device I have
|
||||||
- 'input-switch' likewise switches the input source of external displays
|
- 'input-switch' likewise switches the input source of external displays
|
||||||
- 'fancontrol-ng' is a clone of fancontrol that can handle errors on resume
|
- 'fancontrol-ng' is a clone of fancontrol that can handle errors on resume
|
||||||
|
|
107
paswitch.c
107
paswitch.c
|
@ -154,6 +154,9 @@ struct app_context
|
||||||
pa_context *context; ///< PulseAudio connection context
|
pa_context *context; ///< PulseAudio connection context
|
||||||
|
|
||||||
bool failed; ///< General PulseAudio failure
|
bool failed; ///< General PulseAudio failure
|
||||||
|
bool reset_sinks; ///< Flag for info callback
|
||||||
|
bool reset_inputs; ///< Flag for info callback
|
||||||
|
|
||||||
char *default_sink; ///< Name of the default sink
|
char *default_sink; ///< Name of the default sink
|
||||||
struct sink *sinks; ///< PulseAudio sinks
|
struct sink *sinks; ///< PulseAudio sinks
|
||||||
struct sink *sinks_tail; ///< Tail of PulseAudio sinks
|
struct sink *sinks_tail; ///< Tail of PulseAudio sinks
|
||||||
|
@ -234,7 +237,13 @@ make_inputs_status (struct app_context *ctx, struct sink *sink)
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
||||||
#define DEFAULT_SINK "@DEFAULT_SINK@"
|
static void
|
||||||
|
forget_sinks (struct app_context *ctx)
|
||||||
|
{
|
||||||
|
LIST_FOR_EACH (struct sink, iter, ctx->sinks)
|
||||||
|
sink_destroy (iter);
|
||||||
|
ctx->sinks = ctx->sinks_tail = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_sink_info (pa_context *context, const pa_sink_info *info, int eol,
|
on_sink_info (pa_context *context, const pa_sink_info *info, int eol,
|
||||||
|
@ -242,6 +251,13 @@ on_sink_info (pa_context *context, const pa_sink_info *info, int eol,
|
||||||
{
|
{
|
||||||
(void) context;
|
(void) context;
|
||||||
struct app_context *ctx = userdata;
|
struct app_context *ctx = userdata;
|
||||||
|
|
||||||
|
// Assuming replies cannot overlap
|
||||||
|
if (ctx->reset_sinks)
|
||||||
|
{
|
||||||
|
forget_sinks (ctx);
|
||||||
|
ctx->reset_sinks = false;
|
||||||
|
}
|
||||||
if (!info || eol)
|
if (!info || eol)
|
||||||
{
|
{
|
||||||
// TODO: handle the case of when sinks disappear
|
// TODO: handle the case of when sinks disappear
|
||||||
|
@ -249,6 +265,7 @@ on_sink_info (pa_context *context, const pa_sink_info *info, int eol,
|
||||||
ctx->selected_sink = ctx->sinks->index;
|
ctx->selected_sink = ctx->sinks->index;
|
||||||
|
|
||||||
poller_idle_set (&ctx->redraw_event);
|
poller_idle_set (&ctx->redraw_event);
|
||||||
|
ctx->reset_sinks = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,44 +297,13 @@ on_sink_info (pa_context *context, const pa_sink_info *info, int eol,
|
||||||
LIST_APPEND_WITH_TAIL (ctx->sinks, ctx->sinks_tail, sink);
|
LIST_APPEND_WITH_TAIL (ctx->sinks, ctx->sinks_tail, sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
forget_sinks (struct app_context *ctx)
|
|
||||||
{
|
|
||||||
LIST_FOR_EACH (struct sink, iter, ctx->sinks)
|
|
||||||
sink_destroy (iter);
|
|
||||||
ctx->sinks = ctx->sinks_tail = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
update_sinks (struct app_context *ctx)
|
update_sinks (struct app_context *ctx)
|
||||||
{
|
{
|
||||||
// It shouldn't matter much if we interrupt this operation in the middle
|
|
||||||
// since we request new information right away. At least so long as
|
|
||||||
// replies can't overlap. Though even then we're safe, at least.
|
|
||||||
forget_sinks (ctx);
|
|
||||||
|
|
||||||
pa_operation_unref (pa_context_get_sink_info_list
|
pa_operation_unref (pa_context_get_sink_info_list
|
||||||
(ctx->context, on_sink_info, ctx));
|
(ctx->context, on_sink_info, ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
on_sink_input_info (pa_context *context, const struct pa_sink_input_info *info,
|
|
||||||
int eol, void *userdata)
|
|
||||||
{
|
|
||||||
(void) context;
|
|
||||||
struct app_context *ctx = userdata;
|
|
||||||
if (!info || eol)
|
|
||||||
{
|
|
||||||
poller_idle_set (&ctx->redraw_event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct sink_input *input = sink_input_new ();
|
|
||||||
input->sink = info->sink;
|
|
||||||
input->index = info->index;
|
|
||||||
LIST_APPEND_WITH_TAIL (ctx->inputs, ctx->inputs_tail, input);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
forget_sink_inputs (struct app_context *ctx)
|
forget_sink_inputs (struct app_context *ctx)
|
||||||
{
|
{
|
||||||
|
@ -326,14 +312,35 @@ forget_sink_inputs (struct app_context *ctx)
|
||||||
ctx->inputs = ctx->inputs_tail = NULL;
|
ctx->inputs = ctx->inputs_tail = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_sink_input_info (pa_context *context, const struct pa_sink_input_info *info,
|
||||||
|
int eol, void *userdata)
|
||||||
|
{
|
||||||
|
(void) context;
|
||||||
|
struct app_context *ctx = userdata;
|
||||||
|
|
||||||
|
// Assuming replies cannot overlap
|
||||||
|
if (ctx->reset_inputs)
|
||||||
|
{
|
||||||
|
forget_sink_inputs (ctx);
|
||||||
|
ctx->reset_inputs = false;
|
||||||
|
}
|
||||||
|
if (!info || eol)
|
||||||
|
{
|
||||||
|
poller_idle_set (&ctx->redraw_event);
|
||||||
|
ctx->reset_inputs = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sink_input *input = sink_input_new ();
|
||||||
|
input->sink = info->sink;
|
||||||
|
input->index = info->index;
|
||||||
|
LIST_APPEND_WITH_TAIL (ctx->inputs, ctx->inputs_tail, input);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
update_sink_inputs (struct app_context *ctx)
|
update_sink_inputs (struct app_context *ctx)
|
||||||
{
|
{
|
||||||
// It shouldn't matter much if we interrupt this operation in the middle
|
|
||||||
// since we request new information right away. At least so long as
|
|
||||||
// replies can't overlap. Though even then we're safe, at least.
|
|
||||||
forget_sink_inputs (ctx);
|
|
||||||
|
|
||||||
pa_operation_unref (pa_context_get_sink_input_info_list
|
pa_operation_unref (pa_context_get_sink_input_info_list
|
||||||
(ctx->context, on_sink_input_info, ctx));
|
(ctx->context, on_sink_input_info, ctx));
|
||||||
}
|
}
|
||||||
|
@ -351,6 +358,13 @@ on_server_info (pa_context *context, const struct pa_server_info *info,
|
||||||
cstr_set (&ctx->default_sink, NULL);
|
cstr_set (&ctx->default_sink, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
update_server_info (struct app_context *ctx)
|
||||||
|
{
|
||||||
|
pa_operation_unref (pa_context_get_server_info (ctx->context,
|
||||||
|
on_server_info, ctx));
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
on_event (pa_context *context, pa_subscription_event_type_t event,
|
on_event (pa_context *context, pa_subscription_event_type_t event,
|
||||||
uint32_t index, void *userdata)
|
uint32_t index, void *userdata)
|
||||||
|
@ -368,8 +382,7 @@ on_event (pa_context *context, pa_subscription_event_type_t event,
|
||||||
update_sink_inputs (ctx);
|
update_sink_inputs (ctx);
|
||||||
break;
|
break;
|
||||||
case PA_SUBSCRIPTION_EVENT_SERVER:
|
case PA_SUBSCRIPTION_EVENT_SERVER:
|
||||||
pa_operation_unref (pa_context_get_server_info (context,
|
update_server_info (ctx);
|
||||||
on_server_info, userdata));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,11 +414,13 @@ on_context_state_change (pa_context *context, void *userdata)
|
||||||
ctx->context = NULL;
|
ctx->context = NULL;
|
||||||
|
|
||||||
forget_sinks (ctx);
|
forget_sinks (ctx);
|
||||||
|
forget_sink_inputs (ctx);
|
||||||
cstr_set (&ctx->default_sink, NULL);
|
cstr_set (&ctx->default_sink, NULL);
|
||||||
|
|
||||||
// Retry after an arbitrary delay of 5 seconds
|
// Retry after an arbitrary delay of 5 seconds
|
||||||
poller_timer_set (&ctx->make_context, 5000);
|
poller_timer_set (&ctx->make_context, 5000);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case PA_CONTEXT_READY:
|
case PA_CONTEXT_READY:
|
||||||
ctx->failed = false;
|
ctx->failed = false;
|
||||||
poller_idle_set (&ctx->redraw_event);
|
poller_idle_set (&ctx->redraw_event);
|
||||||
|
@ -415,11 +430,12 @@ on_context_state_change (pa_context *context, void *userdata)
|
||||||
PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SINK_INPUT |
|
PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SINK_INPUT |
|
||||||
PA_SUBSCRIPTION_MASK_SERVER, on_subscribe_finish, userdata));
|
PA_SUBSCRIPTION_MASK_SERVER, on_subscribe_finish, userdata));
|
||||||
|
|
||||||
|
ctx->reset_sinks = true;
|
||||||
|
ctx->reset_inputs = true;
|
||||||
|
|
||||||
update_sinks (ctx);
|
update_sinks (ctx);
|
||||||
update_sink_inputs (ctx);
|
update_sink_inputs (ctx);
|
||||||
|
update_server_info (ctx);
|
||||||
pa_operation_unref (pa_context_get_server_info (context,
|
|
||||||
on_server_info, userdata));
|
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -520,7 +536,8 @@ on_redraw (struct app_context *ctx)
|
||||||
printf ("\x1b[H"); // Cursor to home
|
printf ("\x1b[H"); // Cursor to home
|
||||||
printf ("\x1b[2J"); // Clear the whole screen
|
printf ("\x1b[2J"); // Clear the whole screen
|
||||||
|
|
||||||
// TODO: see if we can reduce flickering. Buffering doesn't help much.
|
// TODO: see if we can reduce flickering in rxvt-unicode.
|
||||||
|
// Buffering doesn't help, we have to do something more sophisticated.
|
||||||
// TODO: try not to write more lines than g_terminal_lines for starters
|
// TODO: try not to write more lines than g_terminal_lines for starters
|
||||||
if (ctx->failed)
|
if (ctx->failed)
|
||||||
{
|
{
|
||||||
|
@ -682,7 +699,11 @@ g_key_handlers[] =
|
||||||
|
|
||||||
{ "k", ACTION_UP },
|
{ "k", ACTION_UP },
|
||||||
{ "j", ACTION_DOWN },
|
{ "j", ACTION_DOWN },
|
||||||
|
{ "\x10", ACTION_UP },
|
||||||
|
{ "\x0e", ACTION_DOWN },
|
||||||
{ "\r", ACTION_SELECT },
|
{ "\r", ACTION_SELECT },
|
||||||
|
{ "+", ACTION_VOLUP },
|
||||||
|
{ "-", ACTION_VOLDOWN },
|
||||||
{ "\x1b[5~", ACTION_VOLUP },
|
{ "\x1b[5~", ACTION_VOLUP },
|
||||||
{ "\x1b[6~", ACTION_VOLDOWN },
|
{ "\x1b[6~", ACTION_VOLDOWN },
|
||||||
{ "m", ACTION_MUTE },
|
{ "m", ACTION_MUTE },
|
||||||
|
|
Loading…
Reference in New Issue