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 | ||||
| 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 | ||||
|    includes PulseAudio volume management and hand-written NUT and MPD clients, | ||||
|    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 | ||||
|  - 'input-switch' likewise switches the input source of external displays | ||||
|  - '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
 | ||||
| 
 | ||||
| 	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
 | ||||
| 	struct sink *sinks;                 ///< 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 | ||||
| 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; | ||||
| 	struct app_context *ctx = userdata; | ||||
| 
 | ||||
| 	// Assuming replies cannot overlap
 | ||||
| 	if (ctx->reset_sinks) | ||||
| 	{ | ||||
| 		forget_sinks (ctx); | ||||
| 		ctx->reset_sinks = false; | ||||
| 	} | ||||
| 	if (!info || eol) | ||||
| 	{ | ||||
| 		// 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; | ||||
| 
 | ||||
| 		poller_idle_set (&ctx->redraw_event); | ||||
| 		ctx->reset_sinks = true; | ||||
| 		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); | ||||
| } | ||||
| 
 | ||||
| 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 | ||||
| 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 | ||||
| 		(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 | ||||
| forget_sink_inputs (struct app_context *ctx) | ||||
| { | ||||
| @ -326,14 +312,35 @@ forget_sink_inputs (struct app_context *ctx) | ||||
| 	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 | ||||
| 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 | ||||
| 		(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); | ||||
| } | ||||
| 
 | ||||
| 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 | ||||
| on_event (pa_context *context, pa_subscription_event_type_t event, | ||||
| 	uint32_t index, void *userdata) | ||||
| @ -368,8 +382,7 @@ on_event (pa_context *context, pa_subscription_event_type_t event, | ||||
| 		update_sink_inputs (ctx); | ||||
| 		break; | ||||
| 	case PA_SUBSCRIPTION_EVENT_SERVER: | ||||
| 		pa_operation_unref (pa_context_get_server_info (context, | ||||
| 			on_server_info, userdata)); | ||||
| 		update_server_info (ctx); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -401,11 +414,13 @@ on_context_state_change (pa_context *context, void *userdata) | ||||
| 		ctx->context = NULL; | ||||
| 
 | ||||
| 		forget_sinks (ctx); | ||||
| 		forget_sink_inputs (ctx); | ||||
| 		cstr_set (&ctx->default_sink, NULL); | ||||
| 
 | ||||
| 		// Retry after an arbitrary delay of 5 seconds
 | ||||
| 		poller_timer_set (&ctx->make_context, 5000); | ||||
| 		return; | ||||
| 
 | ||||
| 	case PA_CONTEXT_READY: | ||||
| 		ctx->failed = false; | ||||
| 		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_SERVER, on_subscribe_finish, userdata)); | ||||
| 
 | ||||
| 		ctx->reset_sinks = true; | ||||
| 		ctx->reset_inputs = true; | ||||
| 
 | ||||
| 		update_sinks (ctx); | ||||
| 		update_sink_inputs (ctx); | ||||
| 
 | ||||
| 		pa_operation_unref (pa_context_get_server_info (context, | ||||
| 			on_server_info, userdata)); | ||||
| 		update_server_info (ctx); | ||||
| 	default: | ||||
| 		return; | ||||
| 	} | ||||
| @ -520,7 +536,8 @@ on_redraw (struct app_context *ctx) | ||||
| 	printf ("\x1b[H");   // Cursor to home
 | ||||
| 	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
 | ||||
| 	if (ctx->failed) | ||||
| 	{ | ||||
| @ -682,7 +699,11 @@ g_key_handlers[] = | ||||
| 
 | ||||
| 	{ "k",       ACTION_UP       }, | ||||
| 	{ "j",       ACTION_DOWN     }, | ||||
| 	{ "\x10",    ACTION_UP       }, | ||||
| 	{ "\x0e",    ACTION_DOWN     }, | ||||
| 	{ "\r",      ACTION_SELECT   }, | ||||
| 	{ "+",       ACTION_VOLUP    }, | ||||
| 	{ "-",       ACTION_VOLDOWN  }, | ||||
| 	{ "\x1b[5~", ACTION_VOLUP    }, | ||||
| 	{ "\x1b[6~", ACTION_VOLDOWN  }, | ||||
| 	{ "m",       ACTION_MUTE     }, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user