static void render() { if (about_is_show) { return; } else if (visualization_isopen) { show_visualization(0, 0); show_processbar<79>(24, 0); return; } show_list(); show_processbar<80>(20, 0); show_help(21, 0); show_volume(21, 40); }
int main(int argc, char *const *argv) { static const char *context_name = "i3blocks-pulse-volume"; static const char *usage_str = "Usage: %s [-h] [-d] [-s INDEX] [-m FUNC]\n" "Options:\n" " -s INDEX: pulseaudio sink index on which to wait for " "changes (default: 0)\n" " -m FUNC : function used to compute the displayed volume value\n" " in there are multiple channels (eg. left/right):\n" " * avg: use average volume of all channels (default)\n" " * min: use minimum volume of all channels\n" " * max: use maximum volume of all channels\n" " -d : use decibel notation instead of 0-100 percentage; " "the sink may\n" " not support this feature\n"; options_t options; options.calculator = pa_cvolume_avg; options.use_decibel = 0; options.observed_index = 0; sink_info_data_t sink_info_data; sink_info_data.sink_ready = 0; sink_info_data.sink_changed = 0; pa_mainloop *pa_ml = NULL; pa_mainloop_api *pa_ml_api = NULL; pa_operation *pa_op = NULL; pa_context *pa_ctx = NULL; enum state_t state = FIRST_SINK_INFO; int pa_ready = CONN_WAIT; int opt; while ((opt = getopt(argc, argv, "m:s:dh")) != -1) { if (opt == 'm') { if (strcmp(optarg, "min") == 0) { options.calculator = pa_cvolume_min; } else if (strcmp(optarg, "max") == 0) { options.calculator = pa_cvolume_max; } else if (strcmp(optarg, "avg") == 0) { options.calculator = pa_cvolume_avg; } else { fprintf(stderr, usage_str, argv[0]); return 1; } } else if (opt == 's') { // Parse observed sink index errno = 0; char *endptr; uint32_t *oind = &options.observed_index; *oind = strtoul(optarg, &endptr, 10); if ((errno == ERANGE) || (errno != 0 && *oind == 0) || endptr == optarg) { fprintf(stderr, "%s: invalid sink index: %s\n", argv[0], optarg); fprintf(stderr, usage_str, argv[0]); return 1; } } else if (opt == 'd') { options.use_decibel = 1; } else if (opt == 'h') { fprintf(stderr, usage_str, argv[0]); return 0; } else { fprintf(stderr, usage_str, argv[0]); return 1; } } // Needed to filter out sink in callbacks sink_info_data.observed_index = options.observed_index; if ((pa_ml = pa_mainloop_new()) == NULL) { fprintf(stderr, "error: failed to allocate pulseaudio mainloop.\n"); return 2; } if ((pa_ml_api = pa_mainloop_get_api(pa_ml)) == NULL) { fprintf(stderr, "error: failed to allocate pulseaudio mainloop API.\n"); return 3; } if ((pa_ctx = pa_context_new(pa_ml_api, context_name)) == NULL) { fprintf(stderr, "error: failed to allocate pulseaudio context.\n"); return 4; } if (pa_context_connect(pa_ctx, NULL, PA_CONTEXT_NOFAIL, NULL) < 0) { fprintf(stderr, "error: failed to connect to pulseaudio context: %s\n", pa_strerror(pa_context_errno(pa_ctx))); return 5; } pa_context_set_state_callback(pa_ctx, state_cb, &pa_ready); pa_context_set_subscribe_callback(pa_ctx, subscribe_cb, &sink_info_data); while (1) { if (pa_ready == CONN_WAIT) { pa_mainloop_iterate(pa_ml, 1, NULL); continue; } if (pa_ready == CONN_FAILED) { pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); fprintf(stderr, "error: failed to connect to pulseaudio context.\n"); return 6; } // Main loop automaton switch (state) { case FIRST_SINK_INFO: // First run pa_op = pa_context_get_sink_info_by_index(pa_ctx, options.observed_index, sink_info_cb, &sink_info_data); state = SUBSCRIBE; break; case SUBSCRIBE: if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) { pa_operation_unref(pa_op); if (!sink_info_data.sink_ready) { fprintf(stderr, "error: sink %u does not exist.\n", options.observed_index); pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); return 7; } if (options.use_decibel && !sink_info_data.sink.can_decibel) { fprintf(stderr, "error: sink %u does not support decibel; " "try without `-d`.\n", options.observed_index); pa_context_disconnect(pa_ctx); pa_context_unref(pa_ctx); pa_mainloop_free(pa_ml); return 8; } // Show volume once at start show_volume(&sink_info_data.sink, &options); // Subsequent runs: wait for changes pa_op = pa_context_subscribe(pa_ctx, PA_SUBSCRIPTION_MASK_SINK, NULL, &sink_info_data); state = SUBSCRIBED; } break; case SUBSCRIBED: if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) { pa_operation_unref(pa_op); state = WAIT_FOR_CHANGE; } break; case WAIT_FOR_CHANGE: if (sink_info_data.sink_changed) { sink_info_data.sink_changed = 0; pa_op = pa_context_get_sink_info_by_index(pa_ctx, options.observed_index, sink_info_cb, &sink_info_data); state = SHOW_CHANGE; } break; case SHOW_CHANGE: if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) { pa_operation_unref(pa_op); show_volume(&sink_info_data.sink, &options); state = WAIT_FOR_CHANGE; } break; default: return 7; } pa_mainloop_iterate(pa_ml, 1, NULL); } }
static error_t parse_opt (int key, char *arg, struct argp_state *state) { int s,d; struct arguments *arguments = state->input; switch (key) { case 's': show_type(); show_zcd(); show_fnc(); show_ycm(); show_fblk(); show_volume(); show_mute(); break; case 'm': mute(); break; case 'u': unmute(); break; case 'l': set_volume(atoi(arg)); break; case 'z': set_zcd(atoi(arg)); break; case 'f': set_fnc(atoi(arg)); break; case 'y': set_ycm(atoi(arg)); break; case 'v': if ( parse_route_arg(arg,&s,&d) < 0 ) { return ARGP_ERR_UNKNOWN; } set_video(s,d); break; case 'a': if ( parse_route_arg(arg,&s,&d) < 0 ) { return ARGP_ERR_UNKNOWN; } set_audio(s,d); break; default: return ARGP_ERR_UNKNOWN; } return 0; }