• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2   This file is part of PulseAudio.
3 
4   Copyright 2004-2006 Lennart Poettering
5 
6   PulseAudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as published
8   by the Free Software Foundation; either version 2.1 of the License,
9   or (at your option) any later version.
10 
11   PulseAudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15 
16   You should have received a copy of the GNU Lesser General Public License
17   along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18 ***/
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #include <signal.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <assert.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <getopt.h>
32 #include <locale.h>
33 #include <ctype.h>
34 
35 #ifdef SNDFILE_ENABLE
36 #include <sndfile.h>
37 #endif
38 
39 #include <pulse/pulseaudio.h>
40 #include <pulse/ext-device-restore.h>
41 #include <pulse/xmalloc.h>
42 
43 #include <pulsecore/i18n.h>
44 #include <pulsecore/json.h>
45 #include <pulsecore/macro.h>
46 #include <pulsecore/core-util.h>
47 #include <pulsecore/log.h>
48 
49 #ifdef SNDFILE_ENABLE
50 #include <pulsecore/sndfile-util.h>
51 #endif
52 
53 static pa_context *context = NULL;
54 static pa_mainloop_api *mainloop_api = NULL;
55 
56 static char
57     *list_type = NULL,
58     *sample_name = NULL,
59     *sink_name = NULL,
60     *source_name = NULL,
61     *module_name = NULL,
62     *module_args = NULL,
63     *card_name = NULL,
64     *profile_name = NULL,
65     *port_name = NULL,
66     *formats = NULL,
67     *object_path = NULL,
68     *message = NULL,
69     *message_args = NULL;
70 
71 static uint32_t
72     sink_input_idx = PA_INVALID_INDEX,
73     source_output_idx = PA_INVALID_INDEX,
74     sink_idx = PA_INVALID_INDEX;
75 
76 static bool short_list_format = false;
77 static uint32_t module_index;
78 static int32_t latency_offset;
79 static bool suspend;
80 static pa_cvolume volume;
81 static enum volume_flags {
82     VOL_UINT     = 0,
83     VOL_PERCENT  = 1,
84     VOL_LINEAR   = 2,
85     VOL_DECIBEL  = 3,
86     VOL_ABSOLUTE = 0 << 4,
87     VOL_RELATIVE = 1 << 4,
88 } volume_flags;
89 
90 static enum mute_flags {
91     INVALID_MUTE = -1,
92     UNMUTE = 0,
93     MUTE = 1,
94     TOGGLE_MUTE = 2
95 } mute = INVALID_MUTE;
96 
97 static pa_proplist *proplist = NULL;
98 
99 #ifdef SNDFILE_ENABLE
100 static SNDFILE *sndfile = NULL;
101 static pa_stream *sample_stream = NULL;
102 static pa_sample_spec sample_spec;
103 static pa_channel_map channel_map;
104 static size_t sample_length = 0;
105 #endif
106 
107 /* This variable tracks the number of ongoing asynchronous operations. When a
108  * new operation begins, this is incremented simply with actions++, and when
109  * an operation finishes, this is decremented with the complete_action()
110  * function, which shuts down the program if actions reaches zero. */
111 static int actions = 0;
112 
113 static bool nl = false;
114 static pa_json_encoder *list_encoder = NULL;
115 static pa_json_encoder *json_encoder = NULL;
116 
117 static enum {
118     NONE,
119     EXIT,
120     STAT,
121     INFO,
122     UPLOAD_SAMPLE,
123     PLAY_SAMPLE,
124     REMOVE_SAMPLE,
125     LIST,
126     MOVE_SINK_INPUT,
127     MOVE_SOURCE_OUTPUT,
128     LOAD_MODULE,
129     UNLOAD_MODULE,
130     SUSPEND_SINK,
131     SUSPEND_SOURCE,
132     SET_CARD_PROFILE,
133     SET_SINK_PORT,
134     GET_DEFAULT_SINK,
135     SET_DEFAULT_SINK,
136     SET_SOURCE_PORT,
137     GET_DEFAULT_SOURCE,
138     SET_DEFAULT_SOURCE,
139     GET_SINK_VOLUME,
140     SET_SINK_VOLUME,
141     GET_SOURCE_VOLUME,
142     SET_SOURCE_VOLUME,
143     SET_SINK_INPUT_VOLUME,
144     SET_SOURCE_OUTPUT_VOLUME,
145     GET_SINK_MUTE,
146     SET_SINK_MUTE,
147     GET_SOURCE_MUTE,
148     SET_SOURCE_MUTE,
149     SET_SINK_INPUT_MUTE,
150     SET_SOURCE_OUTPUT_MUTE,
151     SET_SINK_FORMATS,
152     SET_PORT_LATENCY_OFFSET,
153     SEND_MESSAGE,
154     SUBSCRIBE
155 } action = NONE;
156 
157 static enum {
158     TEXT,
159     JSON
160 } format = TEXT;
161 
quit(int ret)162 static void quit(int ret) {
163     pa_assert(mainloop_api);
164     mainloop_api->quit(mainloop_api, ret);
165 }
166 
context_drain_complete(pa_context * c,void * userdata)167 static void context_drain_complete(pa_context *c, void *userdata) {
168     pa_context_disconnect(c);
169 }
170 
drain(void)171 static void drain(void) {
172     pa_operation *o;
173 
174     if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
175         pa_context_disconnect(context);
176     else
177         pa_operation_unref(o);
178 }
179 
complete_action(void)180 static void complete_action(void) {
181     pa_assert(actions > 0);
182 
183     if (!(--actions))
184         drain();
185 }
186 
stat_callback(pa_context * c,const pa_stat_info * i,void * userdata)187 static void stat_callback(pa_context *c, const pa_stat_info *i, void *userdata) {
188     char s[PA_BYTES_SNPRINT_MAX];
189     if (!i) {
190         pa_log(_("Failed to get statistics: %s"), pa_strerror(pa_context_errno(c)));
191         quit(1);
192         return;
193     }
194 
195     if (format == JSON) {
196         printf("{\"current\":{\"blocks\":%u,\"size\":%u},"
197                "\"lifetime\":{\"blocks\":%u,\"size\":%u},"
198                "\"sample_cache_size\":%u}",
199                i->memblock_total,
200                i->memblock_total_size,
201                i->memblock_allocated,
202                i->memblock_allocated_size,
203                i->scache_size);
204     } else {
205         pa_bytes_snprint(s, sizeof(s), i->memblock_total_size);
206         printf(ngettext("Currently in use: %u block containing %s bytes total.\n",
207                         "Currently in use: %u blocks containing %s bytes total.\n",
208                         i->memblock_total),
209             i->memblock_total, s);
210 
211         pa_bytes_snprint(s, sizeof(s), i->memblock_allocated_size);
212         printf(ngettext("Allocated during whole lifetime: %u block containing %s bytes total.\n",
213                         "Allocated during whole lifetime: %u blocks containing %s bytes total.\n",
214                         i->memblock_allocated),
215             i->memblock_allocated, s);
216 
217         pa_bytes_snprint(s, sizeof(s), i->scache_size);
218         printf(_("Sample cache size: %s\n"), s);
219     }
220 
221     complete_action();
222 }
223 
get_default_sink(pa_context * c,const pa_server_info * i,void * userdata)224 static void get_default_sink(pa_context *c, const pa_server_info *i, void *userdata) {
225     if (!i) {
226         pa_log(_("Failed to get server information: %s"), pa_strerror(pa_context_errno(c)));
227         quit(1);
228         return;
229     }
230 
231     printf(_("%s\n"), i->default_sink_name);
232 
233     complete_action();
234 }
235 
get_default_source(pa_context * c,const pa_server_info * i,void * userdata)236 static void get_default_source(pa_context *c, const pa_server_info *i, void *userdata) {
237     if (!i) {
238         pa_log(_("Failed to get server information: %s"), pa_strerror(pa_context_errno(c)));
239         quit(1);
240         return;
241     }
242 
243     printf(_("%s\n"), i->default_source_name);
244 
245     complete_action();
246 }
247 
get_server_info_callback(pa_context * c,const pa_server_info * i,void * useerdata)248 static void get_server_info_callback(pa_context *c, const pa_server_info *i, void *useerdata) {
249     char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
250 
251     if (!i) {
252         pa_log(_("Failed to get server information: %s"), pa_strerror(pa_context_errno(c)));
253         quit(1);
254         return;
255     }
256 
257     pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec);
258     pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map);
259 
260     if (format == JSON) {
261         char* tile_size = pa_sprintf_malloc("%zu", pa_context_get_tile_size(c, NULL));
262         char* cookie = pa_sprintf_malloc("%04x:%04x", i->cookie >> 16, i->cookie & 0xFFFFU);
263         pa_json_encoder *encoder = pa_json_encoder_new();
264         pa_json_encoder_begin_element_object(encoder);
265         pa_json_encoder_add_member_string(encoder, "server_string", pa_context_get_server(c));
266         pa_json_encoder_add_member_int(encoder, "library_protocol_version", pa_context_get_protocol_version(c));
267         pa_json_encoder_add_member_int(encoder, "server_protocol_version", pa_context_get_server_protocol_version(c));
268         pa_json_encoder_add_member_string(encoder, "is_local", pa_yes_no_localised(pa_context_is_local(c)));
269         pa_json_encoder_add_member_int(encoder, "client_index", pa_context_get_index(c));
270         pa_json_encoder_add_member_string(encoder, "tile_size", tile_size);
271         pa_json_encoder_add_member_string(encoder, "user_name", i->user_name);
272         pa_json_encoder_add_member_string(encoder, "host_name", i->host_name);
273         pa_json_encoder_add_member_string(encoder, "server_name", i->server_name);
274         pa_json_encoder_add_member_string(encoder, "server_version", i->server_version);
275         pa_json_encoder_add_member_string(encoder, "default_sample_specification", ss);
276         pa_json_encoder_add_member_string(encoder, "default_channel_map", cm);
277         pa_json_encoder_add_member_string(encoder, "default_sink_name", i->default_sink_name);
278         pa_json_encoder_add_member_string(encoder, "default_source_name", i->default_source_name);
279         pa_json_encoder_add_member_string(encoder, "cookie", cookie);
280         pa_json_encoder_end_object(encoder);
281 
282         char* json_str = pa_json_encoder_to_string_free(encoder);
283         printf("%s", json_str);
284         pa_xfree(json_str);
285         pa_xfree(tile_size);
286         pa_xfree(cookie);
287     } else {
288         printf(_("Server String: %s\n"
289              "Library Protocol Version: %u\n"
290              "Server Protocol Version: %u\n"
291              "Is Local: %s\n"
292              "Client Index: %u\n"
293              "Tile Size: %zu\n"),
294              pa_context_get_server(c),
295              pa_context_get_protocol_version(c),
296              pa_context_get_server_protocol_version(c),
297              pa_yes_no_localised(pa_context_is_local(c)),
298              pa_context_get_index(c),
299              pa_context_get_tile_size(c, NULL));
300 
301         printf(_("User Name: %s\n"
302                 "Host Name: %s\n"
303                 "Server Name: %s\n"
304                 "Server Version: %s\n"
305                 "Default Sample Specification: %s\n"
306                 "Default Channel Map: %s\n"
307                 "Default Sink: %s\n"
308                 "Default Source: %s\n"
309                 "Cookie: %04x:%04x\n"),
310             i->user_name,
311             i->host_name,
312             i->server_name,
313             i->server_version,
314             ss,
315             cm,
316             i->default_sink_name,
317             i->default_source_name,
318             i->cookie >> 16,
319             i->cookie & 0xFFFFU);
320     }
321 
322     complete_action();
323 }
324 
get_available_str(int available)325 static const char* get_available_str(int available) {
326     switch (available) {
327         case PA_PORT_AVAILABLE_UNKNOWN: return _("availability unknown");
328         case PA_PORT_AVAILABLE_YES: return _("available");
329         case PA_PORT_AVAILABLE_NO: return _("not available");
330     }
331 
332     pa_assert_not_reached();
333 }
334 
get_device_port_type(unsigned int type)335 static const char* get_device_port_type(unsigned int type) {
336     static char buf[32];
337     switch (type) {
338     case PA_DEVICE_PORT_TYPE_UNKNOWN: return _("Unknown");
339     case PA_DEVICE_PORT_TYPE_AUX: return _("Aux");
340     case PA_DEVICE_PORT_TYPE_SPEAKER: return _("Speaker");
341     case PA_DEVICE_PORT_TYPE_HEADPHONES: return _("Headphones");
342     case PA_DEVICE_PORT_TYPE_LINE: return _("Line");
343     case PA_DEVICE_PORT_TYPE_MIC: return _("Mic");
344     case PA_DEVICE_PORT_TYPE_HEADSET: return _("Headset");
345     case PA_DEVICE_PORT_TYPE_HANDSET: return _("Handset");
346     case PA_DEVICE_PORT_TYPE_EARPIECE: return _("Earpiece");
347     case PA_DEVICE_PORT_TYPE_SPDIF: return _("SPDIF");
348     case PA_DEVICE_PORT_TYPE_HDMI: return _("HDMI");
349     case PA_DEVICE_PORT_TYPE_TV: return _("TV");
350     case PA_DEVICE_PORT_TYPE_RADIO: return _("Radio");
351     case PA_DEVICE_PORT_TYPE_VIDEO: return _("Video");
352     case PA_DEVICE_PORT_TYPE_USB: return _("USB");
353     case PA_DEVICE_PORT_TYPE_BLUETOOTH: return _("Bluetooth");
354     case PA_DEVICE_PORT_TYPE_PORTABLE: return _("Portable");
355     case PA_DEVICE_PORT_TYPE_HANDSFREE: return _("Handsfree");
356     case PA_DEVICE_PORT_TYPE_CAR: return _("Car");
357     case PA_DEVICE_PORT_TYPE_HIFI: return _("HiFi");
358     case PA_DEVICE_PORT_TYPE_PHONE: return _("Phone");
359     case PA_DEVICE_PORT_TYPE_NETWORK: return _("Network");
360     case PA_DEVICE_PORT_TYPE_ANALOG: return _("Analog");
361     }
362     snprintf(buf, sizeof(buf), "%s-%u", _("Unknown"), type);
363     return buf;
364 }
365 
pa_proplist_to_json_object(const pa_proplist * p)366 char* pa_proplist_to_json_object(const pa_proplist *p) {
367     const char *key;
368     void *state = NULL;
369     pa_json_encoder *encoder;
370 
371     pa_assert(p);
372 
373     encoder = pa_json_encoder_new();
374     pa_json_encoder_begin_element_object(encoder);
375     while (true) {
376         key = pa_proplist_iterate(p, &state);
377         if (!key) break;
378 
379         const char *v;
380 
381         if ((v = pa_proplist_gets(p, key))) {
382             pa_json_encoder_add_member_string(encoder, key, v);
383         } else {
384             const void *value;
385             size_t nbytes;
386             char *c;
387             char* hex_str;
388 
389             pa_assert_se(pa_proplist_get(p, key, &value, &nbytes) == 0);
390             c = pa_xmalloc(nbytes*2+1);
391             pa_hexstr((const uint8_t*) value, nbytes, c, nbytes*2+1);
392 
393             hex_str = pa_sprintf_malloc("hex:%s", c);
394             pa_json_encoder_add_member_string(encoder, key, hex_str);
395             pa_xfree(c);
396             pa_xfree(hex_str);
397         }
398     }
399     pa_json_encoder_end_object(encoder);
400 
401     return pa_json_encoder_to_string_free(encoder);
402 }
403 
pa_sink_ports_to_json_array(pa_sink_port_info ** ports)404 static const char* pa_sink_ports_to_json_array(pa_sink_port_info **ports) {
405     pa_json_encoder *encoder = pa_json_encoder_new();
406     if (!ports) {
407         pa_json_encoder_begin_element_array(encoder);
408         pa_json_encoder_end_array(encoder);
409         return pa_json_encoder_to_string_free(encoder);
410     }
411 
412     pa_sink_port_info **p;
413 
414     pa_json_encoder_begin_element_array(encoder);
415     for (p = ports; *p; p++) {
416         pa_json_encoder *sink_port_encoder = pa_json_encoder_new();
417         pa_json_encoder_begin_element_object(sink_port_encoder);
418         pa_json_encoder_add_member_string(sink_port_encoder, "name", (*p)->name);
419         pa_json_encoder_add_member_string(sink_port_encoder, "description", (*p)->description);
420         pa_json_encoder_add_member_string(sink_port_encoder, "type", get_device_port_type((*p)->type));
421         pa_json_encoder_add_member_int(sink_port_encoder, "priority", (*p)->priority);
422         pa_json_encoder_add_member_string(sink_port_encoder, "availability_group", (*p)->availability_group);
423         pa_json_encoder_add_member_string(sink_port_encoder, "availability", get_available_str((*p)->available));
424         pa_json_encoder_end_object(sink_port_encoder);
425 
426         char* sink_port_str = pa_json_encoder_to_string_free(sink_port_encoder);
427         pa_json_encoder_add_element_raw_json(encoder, sink_port_str);
428         pa_xfree(sink_port_str);
429     }
430     pa_json_encoder_end_array(encoder);
431 
432     return pa_json_encoder_to_string_free(encoder);
433 }
434 
pa_source_ports_to_json_array(pa_source_port_info ** ports)435 static const char* pa_source_ports_to_json_array(pa_source_port_info **ports) {
436     pa_json_encoder *encoder = pa_json_encoder_new();
437     if (!ports) {
438         pa_json_encoder_begin_element_array(encoder);
439         pa_json_encoder_end_array(encoder);
440         return pa_json_encoder_to_string_free(encoder);
441     }
442 
443     pa_source_port_info **p;
444 
445     pa_json_encoder_begin_element_array(encoder);
446     for (p = ports; *p; p++) {
447         pa_json_encoder *source_port_encoder = pa_json_encoder_new();
448         pa_json_encoder_begin_element_object(source_port_encoder);
449         pa_json_encoder_add_member_string(source_port_encoder, "name", (*p)->name);
450         pa_json_encoder_add_member_string(source_port_encoder, "description", (*p)->description);
451         pa_json_encoder_add_member_string(source_port_encoder, "type", get_device_port_type((*p)->type));
452         pa_json_encoder_add_member_int(source_port_encoder, "priority", (*p)->priority);
453         pa_json_encoder_add_member_string(source_port_encoder, "availability_group", (*p)->availability_group);
454         pa_json_encoder_add_member_string(source_port_encoder, "availability", get_available_str((*p)->available));
455         pa_json_encoder_end_object(source_port_encoder);
456 
457         char* source_port_str = pa_json_encoder_to_string_free(source_port_encoder);
458         pa_json_encoder_add_element_raw_json(encoder, source_port_str);
459         pa_xfree(source_port_str);
460     }
461     pa_json_encoder_end_array(encoder);
462 
463     return pa_json_encoder_to_string_free(encoder);
464 }
465 
pa_format_infos_to_json_array(pa_format_info ** formats,uint8_t n_formats)466 static const char* pa_format_infos_to_json_array(pa_format_info **formats, uint8_t n_formats) {
467     pa_json_encoder *encoder = pa_json_encoder_new();
468     if (!formats) {
469         pa_json_encoder_begin_element_array(encoder);
470         pa_json_encoder_end_array(encoder);
471         return pa_json_encoder_to_string_free(encoder);
472     }
473 
474     char f[PA_FORMAT_INFO_SNPRINT_MAX];
475     uint8_t i;
476 
477     pa_json_encoder_begin_element_array(encoder);
478     for (i = 0; i < n_formats; i++) {
479         pa_json_encoder_add_element_string(encoder, pa_format_info_snprint(f, sizeof(f), formats[i]));
480     }
481     pa_json_encoder_end_array(encoder);
482 
483     return pa_json_encoder_to_string_free(encoder);
484 }
485 
pa_volume_to_json_object(pa_volume_t v,int print_dB)486 const char* pa_volume_to_json_object(pa_volume_t v, int print_dB) {
487     pa_json_encoder *encoder = pa_json_encoder_new();
488     if (!PA_VOLUME_IS_VALID(v)) {
489         pa_json_encoder_begin_element_object(encoder);
490         pa_json_encoder_add_member_string(encoder, "error", _("(invalid)"));
491         pa_json_encoder_end_object(encoder);
492         return pa_json_encoder_to_string_free(encoder);
493     }
494 
495     char dB[PA_SW_VOLUME_SNPRINT_DB_MAX];
496     char* value_percent = pa_sprintf_malloc("%u%%", (unsigned)(((uint64_t)v * 100 + (uint64_t)PA_VOLUME_NORM / 2) / (uint64_t)PA_VOLUME_NORM));
497     pa_json_encoder_begin_element_object(encoder);
498     pa_json_encoder_add_member_int(encoder, "value", v);
499     pa_json_encoder_add_member_string(encoder, "value_percent", value_percent);
500     pa_json_encoder_add_member_string(encoder, "db", print_dB ? pa_sw_volume_snprint_dB(dB, sizeof(dB), v) : NULL);
501     pa_json_encoder_end_object(encoder);
502     pa_xfree(value_percent);
503 
504     return pa_json_encoder_to_string_free(encoder);
505 }
506 
pa_cvolume_to_json_object(const pa_cvolume * c,const pa_channel_map * map,int print_dB)507 const char* pa_cvolume_to_json_object(const pa_cvolume *c, const pa_channel_map *map, int print_dB) {
508     pa_json_encoder *encoder = pa_json_encoder_new();
509     if (!pa_cvolume_valid(c)) {
510         pa_json_encoder_begin_element_object(encoder);
511         pa_json_encoder_add_member_string(encoder, "error", _("(invalid)"));
512         pa_json_encoder_end_object(encoder);
513         return pa_json_encoder_to_string_free(encoder);
514     }
515 
516     pa_assert(!map || (map->channels == c->channels));
517     pa_assert(!map || pa_channel_map_valid(map));
518 
519     pa_json_encoder_begin_element_object(encoder);
520     for (unsigned channel = 0; channel < c->channels; channel++) {
521         char channel_position[32];
522         if (map)
523             pa_snprintf(channel_position, sizeof(channel_position), "%s", pa_channel_position_to_string(map->map[channel]));
524         else
525             pa_snprintf(channel_position, sizeof(channel_position), "%u", channel);
526 
527         pa_json_encoder_add_member_raw_json(encoder,
528             channel_position,
529             pa_volume_to_json_object(c->values[channel], print_dB));
530     }
531     pa_json_encoder_end_object(encoder);
532 
533     return pa_json_encoder_to_string_free(encoder);
534 }
535 
pa_json_encoder_end_array_handler(const char * name)536 static void pa_json_encoder_end_array_handler(const char *name) {
537     pa_assert(json_encoder != NULL);
538 
539     pa_json_encoder_end_array(json_encoder);
540     char* json_str = pa_json_encoder_to_string_free(json_encoder);
541     if (list_encoder != NULL) {
542         pa_json_encoder_add_member_raw_json(list_encoder, name, json_str);
543     } else {
544         printf("%s", json_str);
545     }
546     pa_xfree(json_str);
547 
548     json_encoder = NULL;
549 }
550 
get_sink_info_callback(pa_context * c,const pa_sink_info * i,int is_last,void * userdata)551 static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
552 
553     static const char *state_table[] = {
554         [1+PA_SINK_INVALID_STATE] = "n/a",
555         [1+PA_SINK_RUNNING] = "RUNNING",
556         [1+PA_SINK_IDLE] = "IDLE",
557         [1+PA_SINK_SUSPENDED] = "SUSPENDED"
558     };
559 
560     char
561         s[PA_SAMPLE_SPEC_SNPRINT_MAX],
562         cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX],
563         v[PA_VOLUME_SNPRINT_VERBOSE_MAX],
564         cm[PA_CHANNEL_MAP_SNPRINT_MAX],
565         f[PA_FORMAT_INFO_SNPRINT_MAX];
566     char *pl;
567 
568     if (format == JSON && json_encoder == NULL) {
569         json_encoder = pa_json_encoder_new();
570         pa_json_encoder_begin_element_array(json_encoder);
571     }
572 
573     if (is_last < 0) {
574         pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
575         quit(1);
576         return;
577     }
578 
579     if (is_last) {
580         if (format == JSON) {
581             pa_json_encoder_end_array_handler("sinks");
582         }
583         complete_action();
584         return;
585     }
586 
587     pa_assert(i);
588 
589     if (nl && !short_list_format && format == TEXT)
590         printf("\n");
591     nl = true;
592 
593     char *sample_spec = pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
594     if (short_list_format) {
595         if (format == JSON) {
596             pa_json_encoder *encoder = pa_json_encoder_new();
597             pa_json_encoder_begin_element_object(encoder);
598             pa_json_encoder_add_member_int(encoder, "index", i->index);
599             pa_json_encoder_add_member_string(encoder, "name", i->name);
600             pa_json_encoder_add_member_string(encoder, "driver", i->driver);
601             pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
602             pa_json_encoder_add_member_string(encoder, "state", state_table[1+i->state]);
603             pa_json_encoder_end_object(encoder);
604 
605             char* json_str = pa_json_encoder_to_string_free(encoder);
606             pa_json_encoder_add_element_raw_json(json_encoder, json_str);
607             pa_xfree(json_str);
608         } else {
609             printf("%u\t%s\t%s\t%s\t%s\n",
610                i->index,
611                i->name,
612                pa_strnull(i->driver),
613                sample_spec,
614                state_table[1+i->state]);
615         }
616         return;
617     }
618 
619     char *channel_map = pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map);
620     float volume_balance = pa_cvolume_get_balance(&i->volume, &i->channel_map);
621 
622     if (format == JSON) {
623         pa_json_encoder *latency_encoder = pa_json_encoder_new();
624         pa_json_encoder_begin_element_object(latency_encoder);
625         pa_json_encoder_add_member_double(latency_encoder, "actual", (double) i->latency, 2);
626         pa_json_encoder_add_member_double(latency_encoder, "configured", (double) i->configured_latency, 2);
627         pa_json_encoder_end_object(latency_encoder);
628         char* latency_json_str = pa_json_encoder_to_string_free(latency_encoder);
629 
630         pa_json_encoder *flags_encoder = pa_json_encoder_new();
631         pa_json_encoder_begin_element_array(flags_encoder);
632         if (i->flags & PA_SINK_HARDWARE) pa_json_encoder_add_element_string(flags_encoder, "HARDWARE");
633         if (i->flags & PA_SINK_NETWORK) pa_json_encoder_add_element_string(flags_encoder, "NETWORK");
634         if (i->flags & PA_SINK_HW_MUTE_CTRL) pa_json_encoder_add_element_string(flags_encoder, "HW_MUTE_CTRL");
635         if (i->flags & PA_SINK_HW_VOLUME_CTRL) pa_json_encoder_add_element_string(flags_encoder, "HW_VOLUME_CTRL");
636         if (i->flags & PA_SINK_DECIBEL_VOLUME) pa_json_encoder_add_element_string(flags_encoder, "DECIBEL_VOLUME");
637         if (i->flags & PA_SINK_LATENCY) pa_json_encoder_add_element_string(flags_encoder, "LATENCY");
638         if (i->flags & PA_SINK_SET_FORMATS) pa_json_encoder_add_element_string(flags_encoder, "SET_FORMATS");
639         pa_json_encoder_end_array(flags_encoder);
640         char* flags_json_str = pa_json_encoder_to_string_free(flags_encoder);
641 
642         pa_json_encoder *encoder = pa_json_encoder_new();
643         pa_json_encoder_begin_element_object(encoder);
644         pa_json_encoder_add_member_int(encoder, "index", i->index);
645         pa_json_encoder_add_member_string(encoder, "state", state_table[1+i->state]);
646         pa_json_encoder_add_member_string(encoder, "name", i->name);
647         pa_json_encoder_add_member_string(encoder, "description", i->description);
648         pa_json_encoder_add_member_string(encoder, "driver", i->driver);
649         pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
650         pa_json_encoder_add_member_string(encoder, "channel_map", channel_map);
651         pa_json_encoder_add_member_int(encoder, "owner_module", i->owner_module);
652         pa_json_encoder_add_member_bool(encoder, "mute", i->mute);
653         pa_json_encoder_add_member_raw_json(encoder, "volume", pa_cvolume_to_json_object(&i->volume, &i->channel_map, i->flags & PA_SINK_DECIBEL_VOLUME));
654         pa_json_encoder_add_member_double(encoder, "balance", volume_balance, 2);
655         pa_json_encoder_add_member_raw_json(encoder, "base_volume", pa_volume_to_json_object(i->base_volume, i->flags & PA_SINK_DECIBEL_VOLUME));
656         pa_json_encoder_add_member_string(encoder, "monitor_source", i->monitor_source_name);
657         pa_json_encoder_add_member_raw_json(encoder, "latency", latency_json_str);
658         pa_json_encoder_add_member_raw_json(encoder, "flags", flags_json_str);
659         pa_json_encoder_add_member_raw_json(encoder, "properties", pl = pa_proplist_to_json_object(i->proplist));
660         pa_json_encoder_add_member_raw_json(encoder, "ports", pa_sink_ports_to_json_array(i->ports));
661         i->active_port ? pa_json_encoder_add_member_string(encoder, "active_port", i->active_port->name): pa_json_encoder_add_member_null(encoder, "active_port");
662         pa_json_encoder_add_member_raw_json(encoder, "formats", pa_format_infos_to_json_array(i->formats, i->n_formats));
663         pa_json_encoder_end_object(encoder);
664 
665         char* json_str = pa_json_encoder_to_string_free(encoder);
666         pa_json_encoder_add_element_raw_json(json_encoder, json_str);
667         pa_xfree(json_str);
668         pa_xfree(latency_json_str);
669         pa_xfree(flags_json_str);
670     } else {
671         printf(_("Sink #%u\n"
672              "\tState: %s\n"
673              "\tName: %s\n"
674              "\tDescription: %s\n"
675              "\tDriver: %s\n"
676              "\tSample Specification: %s\n"
677              "\tChannel Map: %s\n"
678              "\tOwner Module: %u\n"
679              "\tMute: %s\n"
680              "\tVolume: %s\n"
681              "\t        balance %0.2f\n"
682              "\tBase Volume: %s\n"
683              "\tMonitor Source: %s\n"
684              "\tLatency: %0.0f usec, configured %0.0f usec\n"
685              "\tFlags: %s%s%s%s%s%s%s\n"
686              "\tProperties:\n\t\t%s\n"),
687            i->index,
688            state_table[1+i->state],
689            i->name,
690            pa_strnull(i->description),
691            pa_strnull(i->driver),
692            sample_spec,
693            channel_map,
694            i->owner_module,
695            pa_yes_no_localised(i->mute),
696            pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, i->flags & PA_SINK_DECIBEL_VOLUME),
697            volume_balance,
698            pa_volume_snprint_verbose(v, sizeof(v), i->base_volume, i->flags & PA_SINK_DECIBEL_VOLUME),
699            pa_strnull(i->monitor_source_name),
700            (double) i->latency, (double) i->configured_latency,
701            i->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
702            i->flags & PA_SINK_NETWORK ? "NETWORK " : "",
703            i->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
704            i->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
705            i->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
706            i->flags & PA_SINK_LATENCY ? "LATENCY " : "",
707            i->flags & PA_SINK_SET_FORMATS ? "SET_FORMATS " : "",
708            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
709 
710         if (i->ports) {
711             pa_sink_port_info **p;
712 
713             printf(_("\tPorts:\n"));
714             for (p = i->ports; *p; p++)
715                 printf(_("\t\t%s: %s (type: %s, priority: %u%s%s, %s)\n"),
716                         (*p)->name, (*p)->description, get_device_port_type((*p)->type),
717                         (*p)->priority, (*p)->availability_group ? _(", availability group: ") : "",
718                         (*p)->availability_group ?: "", get_available_str((*p)->available));
719         }
720 
721         if (i->active_port)
722             printf(_("\tActive Port: %s\n"),
723                 i->active_port->name);
724 
725         if (i->formats) {
726             uint8_t j;
727 
728             printf(_("\tFormats:\n"));
729             for (j = 0; j < i->n_formats; j++)
730                 printf("\t\t%s\n", pa_format_info_snprint(f, sizeof(f), i->formats[j]));
731         }
732     }
733 
734     pa_xfree(pl);
735 }
736 
get_source_info_callback(pa_context * c,const pa_source_info * i,int is_last,void * userdata)737 static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
738 
739     static const char *state_table[] = {
740         [1+PA_SOURCE_INVALID_STATE] = "n/a",
741         [1+PA_SOURCE_RUNNING] = "RUNNING",
742         [1+PA_SOURCE_IDLE] = "IDLE",
743         [1+PA_SOURCE_SUSPENDED] = "SUSPENDED"
744     };
745 
746     char
747         s[PA_SAMPLE_SPEC_SNPRINT_MAX],
748         cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX],
749         v[PA_VOLUME_SNPRINT_VERBOSE_MAX],
750         cm[PA_CHANNEL_MAP_SNPRINT_MAX],
751         f[PA_FORMAT_INFO_SNPRINT_MAX];
752     char *pl;
753 
754     if (format == JSON && json_encoder == NULL) {
755         json_encoder = pa_json_encoder_new();
756         pa_json_encoder_begin_element_array(json_encoder);
757     }
758 
759     if (is_last < 0) {
760         pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
761         quit(1);
762         return;
763     }
764 
765     if (is_last) {
766         if (format == JSON) {
767             pa_json_encoder_end_array_handler("sources");
768         }
769         complete_action();
770         return;
771     }
772 
773     pa_assert(i);
774 
775     if (nl && !short_list_format && format == TEXT)
776         printf("\n");
777     nl = true;
778 
779     char *sample_spec = pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
780     if (short_list_format) {
781         if (format == JSON) {
782             pa_json_encoder *encoder = pa_json_encoder_new();
783             pa_json_encoder_begin_element_object(encoder);
784             pa_json_encoder_add_member_int(encoder, "index", i->index);
785             pa_json_encoder_add_member_string(encoder, "name", i->name);
786             pa_json_encoder_add_member_string(encoder, "driver", i->driver);
787             pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
788             pa_json_encoder_add_member_string(encoder, "state", state_table[1+i->state]);
789             pa_json_encoder_end_object(encoder);
790 
791             char* json_str = pa_json_encoder_to_string_free(encoder);
792             pa_json_encoder_add_element_raw_json(json_encoder, json_str);
793             pa_xfree(json_str);
794         } else {
795             printf("%u\t%s\t%s\t%s\t%s\n",
796                i->index,
797                i->name,
798                pa_strnull(i->driver),
799                sample_spec,
800                state_table[1+i->state]);
801         }
802         return;
803     }
804 
805     char *channel_map = pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map);
806     float volume_balance = pa_cvolume_get_balance(&i->volume, &i->channel_map);
807 
808     if (format == JSON) {
809         pa_json_encoder *latency_encoder = pa_json_encoder_new();
810         pa_json_encoder_begin_element_object(latency_encoder);
811         pa_json_encoder_add_member_double(latency_encoder, "actual", (double) i->latency, 2);
812         pa_json_encoder_add_member_double(latency_encoder, "configured", (double) i->configured_latency, 2);
813         pa_json_encoder_end_object(latency_encoder);
814         char* latency_json_str = pa_json_encoder_to_string_free(latency_encoder);
815 
816         pa_json_encoder *flags_encoder = pa_json_encoder_new();
817         pa_json_encoder_begin_element_array(flags_encoder);
818         if (i->flags & PA_SOURCE_HARDWARE) pa_json_encoder_add_element_string(flags_encoder, "HARDWARE");
819         if (i->flags & PA_SOURCE_NETWORK) pa_json_encoder_add_element_string(flags_encoder, "NETWORK");
820         if (i->flags & PA_SOURCE_HW_MUTE_CTRL) pa_json_encoder_add_element_string(flags_encoder, "HW_MUTE_CTRL");
821         if (i->flags & PA_SOURCE_HW_VOLUME_CTRL) pa_json_encoder_add_element_string(flags_encoder, "HW_VOLUME_CTRL");
822         if (i->flags & PA_SOURCE_DECIBEL_VOLUME) pa_json_encoder_add_element_string(flags_encoder, "DECIBEL_VOLUME");
823         if (i->flags & PA_SOURCE_LATENCY) pa_json_encoder_add_element_string(flags_encoder, "LATENCY");
824         pa_json_encoder_end_array(flags_encoder);
825         char* flags_json_str = pa_json_encoder_to_string_free(flags_encoder);
826 
827         pa_json_encoder *encoder = pa_json_encoder_new();
828         pa_json_encoder_begin_element_object(encoder);
829         pa_json_encoder_add_member_int(encoder, "index", i->index);
830         pa_json_encoder_add_member_string(encoder, "state", state_table[1+i->state]);
831         pa_json_encoder_add_member_string(encoder, "name", i->name);
832         pa_json_encoder_add_member_string(encoder, "description", i->description);
833         pa_json_encoder_add_member_string(encoder, "driver", i->driver);
834         pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
835         pa_json_encoder_add_member_string(encoder, "channel_map", channel_map);
836         pa_json_encoder_add_member_int(encoder, "owner_module", i->owner_module);
837         pa_json_encoder_add_member_bool(encoder, "mute", i->mute);
838         pa_json_encoder_add_member_raw_json(encoder, "volume", pa_cvolume_to_json_object(&i->volume, &i->channel_map, i->flags & PA_SINK_DECIBEL_VOLUME));
839         pa_json_encoder_add_member_double(encoder, "balance", volume_balance, 2);
840         pa_json_encoder_add_member_raw_json(encoder, "base_volume", pa_volume_to_json_object(i->base_volume, i->flags & PA_SINK_DECIBEL_VOLUME));
841         pa_json_encoder_add_member_string(encoder, "monitor_source", i->monitor_of_sink_name);
842         pa_json_encoder_add_member_raw_json(encoder, "latency", latency_json_str);
843         pa_json_encoder_add_member_raw_json(encoder, "flags", flags_json_str);
844         pa_json_encoder_add_member_raw_json(encoder, "properties", pl = pa_proplist_to_json_object(i->proplist));
845         pa_json_encoder_add_member_raw_json(encoder, "ports", pa_source_ports_to_json_array(i->ports));
846         i->active_port ? pa_json_encoder_add_member_string(encoder, "active_port", i->active_port->name) : pa_json_encoder_add_member_null(encoder, "active_port");
847         pa_json_encoder_add_member_raw_json(encoder, "formats", pa_format_infos_to_json_array(i->formats, i->n_formats));
848         pa_json_encoder_end_object(encoder);
849 
850         char* json_str = pa_json_encoder_to_string_free(encoder);
851         pa_json_encoder_add_element_raw_json(json_encoder, json_str);
852         pa_xfree(json_str);
853         pa_xfree(latency_json_str);
854         pa_xfree(flags_json_str);
855     } else {
856         printf(_("Source #%u\n"
857                 "\tState: %s\n"
858                 "\tName: %s\n"
859                 "\tDescription: %s\n"
860                 "\tDriver: %s\n"
861                 "\tSample Specification: %s\n"
862                 "\tChannel Map: %s\n"
863                 "\tOwner Module: %u\n"
864                 "\tMute: %s\n"
865                 "\tVolume: %s\n"
866                 "\t        balance %0.2f\n"
867                 "\tBase Volume: %s\n"
868                 "\tMonitor of Sink: %s\n"
869                 "\tLatency: %0.0f usec, configured %0.0f usec\n"
870                 "\tFlags: %s%s%s%s%s%s\n"
871                 "\tProperties:\n\t\t%s\n"),
872             i->index,
873             state_table[1+i->state],
874             i->name,
875             pa_strnull(i->description),
876             pa_strnull(i->driver),
877             sample_spec,
878             channel_map,
879             i->owner_module,
880             pa_yes_no_localised(i->mute),
881             pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, i->flags & PA_SOURCE_DECIBEL_VOLUME),
882             volume_balance,
883             pa_volume_snprint_verbose(v, sizeof(v), i->base_volume, i->flags & PA_SOURCE_DECIBEL_VOLUME),
884             i->monitor_of_sink_name ? i->monitor_of_sink_name : _("n/a"),
885             (double) i->latency, (double) i->configured_latency,
886             i->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "",
887             i->flags & PA_SOURCE_NETWORK ? "NETWORK " : "",
888             i->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
889             i->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
890             i->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
891             i->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
892             pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
893 
894         if (i->ports) {
895             pa_source_port_info **p;
896 
897             printf(_("\tPorts:\n"));
898             for (p = i->ports; *p; p++)
899                 printf(_("\t\t%s: %s (type: %s, priority: %u%s%s, %s)\n"),
900                         (*p)->name, (*p)->description, get_device_port_type((*p)->type),
901                         (*p)->priority, (*p)->availability_group ? _(", availability group: ") : "",
902                         (*p)->availability_group ?: "", get_available_str((*p)->available));
903         }
904 
905         if (i->active_port)
906             printf(_("\tActive Port: %s\n"),
907                 i->active_port->name);
908 
909         if (i->formats) {
910             uint8_t j;
911 
912             printf(_("\tFormats:\n"));
913             for (j = 0; j < i->n_formats; j++)
914                 printf("\t\t%s\n", pa_format_info_snprint(f, sizeof(f), i->formats[j]));
915         }
916     }
917 
918     pa_xfree(pl);
919 }
920 
get_module_info_callback(pa_context * c,const pa_module_info * i,int is_last,void * userdata)921 static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
922     char t[32];
923     char *pl;
924 
925     if (format == JSON && json_encoder == NULL) {
926         json_encoder = pa_json_encoder_new();
927         pa_json_encoder_begin_element_array(json_encoder);
928     }
929 
930     if (is_last < 0) {
931         pa_log(_("Failed to get module information: %s"), pa_strerror(pa_context_errno(c)));
932         quit(1);
933         return;
934     }
935 
936     if (is_last) {
937         if (format == JSON) {
938             pa_json_encoder_end_array_handler("modules");
939         }
940         complete_action();
941         return;
942     }
943 
944     pa_assert(i);
945 
946     if (nl && !short_list_format && format == TEXT)
947         printf("\n");
948     nl = true;
949 
950     pa_snprintf(t, sizeof(t), "%u", i->n_used);
951 
952     if (short_list_format) {
953         if (format == JSON) {
954             pa_json_encoder *encoder = pa_json_encoder_new();
955             pa_json_encoder_begin_element_object(encoder);
956             pa_json_encoder_add_member_string(encoder, "name", i->name);
957             pa_json_encoder_add_member_string(encoder, "argument", i->argument);
958             pa_json_encoder_end_object(encoder);
959 
960             char* json_str = pa_json_encoder_to_string_free(encoder);
961             pa_json_encoder_add_element_raw_json(json_encoder, json_str);
962             pa_xfree(json_str);
963         } else {
964             printf("%u\t%s\t%s\t\n", i->index, i->name, i->argument ? i->argument : "");
965         }
966         return;
967     }
968 
969     char *n_used = i->n_used != PA_INVALID_INDEX ? t : _("n/a");
970     if (format == JSON) {
971         pa_json_encoder *encoder = pa_json_encoder_new();
972         pa_json_encoder_begin_element_object(encoder);
973         pa_json_encoder_add_member_string(encoder, "name", i->name);
974         pa_json_encoder_add_member_string(encoder, "argument", i->argument);
975         pa_json_encoder_add_member_string(encoder, "usage_counter", n_used);
976         pa_json_encoder_add_member_raw_json(encoder, "properties", pl = pa_proplist_to_json_object(i->proplist));
977         pa_json_encoder_end_object(encoder);
978 
979         char* json_str = pa_json_encoder_to_string_free(encoder);
980         pa_json_encoder_add_element_raw_json(json_encoder, json_str);
981         pa_xfree(json_str);
982     } else {
983         printf(_("Module #%u\n"
984                 "\tName: %s\n"
985                 "\tArgument: %s\n"
986                 "\tUsage counter: %s\n"
987                 "\tProperties:\n\t\t%s\n"),
988             i->index,
989             i->name,
990             i->argument ? i->argument : "",
991             n_used,
992             pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
993 
994     }
995 
996     pa_xfree(pl);
997 }
998 
get_client_info_callback(pa_context * c,const pa_client_info * i,int is_last,void * userdata)999 static void get_client_info_callback(pa_context *c, const pa_client_info *i, int is_last, void *userdata) {
1000     char t[32];
1001     char *pl;
1002 
1003     if (format == JSON && json_encoder == NULL) {
1004         json_encoder = pa_json_encoder_new();
1005         pa_json_encoder_begin_element_array(json_encoder);
1006     }
1007 
1008     if (is_last < 0) {
1009         pa_log(_("Failed to get client information: %s"), pa_strerror(pa_context_errno(c)));
1010         quit(1);
1011         return;
1012     }
1013 
1014     if (is_last) {
1015         if (format == JSON) {
1016             pa_json_encoder_end_array_handler("clients");
1017         }
1018         complete_action();
1019         return;
1020     }
1021 
1022     pa_assert(i);
1023 
1024     if (nl && !short_list_format && format == TEXT)
1025         printf("\n");
1026     nl = true;
1027 
1028     pa_snprintf(t, sizeof(t), "%u", i->owner_module);
1029 
1030     if (short_list_format) {
1031         if (format == JSON) {
1032             pa_json_encoder *encoder = pa_json_encoder_new();
1033             pa_json_encoder_begin_element_object(encoder);
1034             pa_json_encoder_add_member_int(encoder, "index", i->index);
1035             pa_json_encoder_add_member_string(encoder, "driver", i->driver);
1036             pa_json_encoder_add_member_string(encoder, PA_PROP_APPLICATION_PROCESS_BINARY, pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
1037             pa_json_encoder_end_object(encoder);
1038 
1039             char* json_str = pa_json_encoder_to_string_free(encoder);
1040             pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1041             pa_xfree(json_str);
1042         } else {
1043             printf("%u\t%s\t%s\n",
1044                i->index,
1045                pa_strnull(i->driver),
1046                pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_PROCESS_BINARY)));
1047         }
1048         return;
1049     } else {
1050         if (format == JSON) {
1051             pa_json_encoder *encoder = pa_json_encoder_new();
1052             pa_json_encoder_begin_element_object(encoder);
1053             pa_json_encoder_add_member_int(encoder, "index", i->index);
1054             i->driver ? pa_json_encoder_add_member_string(encoder, "driver", i->driver) : pa_json_encoder_add_member_null(encoder, "driver");
1055             i->owner_module != PA_INVALID_INDEX ? pa_json_encoder_add_member_string(encoder, "owner_module", t) : pa_json_encoder_add_member_null(encoder, "owner_module");
1056             pa_json_encoder_add_member_raw_json(encoder, "properties", pl = pa_proplist_to_json_object(i->proplist));
1057             pa_json_encoder_end_object(encoder);
1058 
1059             char* json_str = pa_json_encoder_to_string_free(encoder);
1060             pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1061             pa_xfree(json_str);
1062         } else {
1063             printf(_("Client #%u\n"
1064                 "\tDriver: %s\n"
1065                 "\tOwner Module: %s\n"
1066                 "\tProperties:\n\t\t%s\n"),
1067                 i->index,
1068                 pa_strnull(i->driver),
1069                 i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
1070                 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
1071         }
1072     }
1073 
1074     pa_xfree(pl);
1075 }
1076 
pa_card_profile_info_2_to_json_object(pa_card_profile_info2 ** profiles2)1077 const char* pa_card_profile_info_2_to_json_object(pa_card_profile_info2 **profiles2) {
1078     pa_json_encoder *encoder = pa_json_encoder_new();
1079     if (!profiles2) {
1080         pa_json_encoder_begin_element_object(encoder);
1081         pa_json_encoder_end_object(encoder);
1082         return pa_json_encoder_to_string_free(encoder);
1083     }
1084 
1085     pa_card_profile_info2 **p;
1086 
1087     pa_json_encoder_begin_element_object(encoder);
1088     for (p = profiles2; *p; p++) {
1089         pa_json_encoder *info_json_2_encoder = pa_json_encoder_new();
1090         pa_json_encoder_begin_element_object(info_json_2_encoder);
1091         pa_json_encoder_add_member_string(info_json_2_encoder, "description", (*p)->description);
1092         pa_json_encoder_add_member_int(info_json_2_encoder, "sinks", (*p)->n_sinks);
1093         pa_json_encoder_add_member_int(info_json_2_encoder, "sources", (*p)->n_sources);
1094         pa_json_encoder_add_member_int(info_json_2_encoder, "priority", (*p)->priority);
1095         pa_json_encoder_add_member_bool(info_json_2_encoder, "available", (*p)->available);
1096         pa_json_encoder_end_object(info_json_2_encoder);
1097 
1098         char *info_json_2_str = pa_json_encoder_to_string_free(info_json_2_encoder);
1099         pa_json_encoder_add_member_raw_json(encoder, (*p)->name, info_json_2_str);
1100         pa_xfree(info_json_2_str);
1101     }
1102     pa_json_encoder_end_object(encoder);
1103 
1104     return pa_json_encoder_to_string_free(encoder);
1105 }
1106 
pa_card_profile_info_to_json_array(pa_card_profile_info ** info)1107 const char* pa_card_profile_info_to_json_array(pa_card_profile_info **info) {
1108     pa_json_encoder *encoder = pa_json_encoder_new();
1109     if (!info) {
1110         pa_json_encoder_begin_element_array(encoder);
1111         pa_json_encoder_end_array(encoder);
1112         return pa_json_encoder_to_string_free(encoder);
1113     }
1114 
1115     pa_card_profile_info **p;
1116 
1117     pa_json_encoder_begin_element_array(encoder);
1118     for (p = info; *p; p++) {
1119         pa_json_encoder_add_element_string(encoder, (*p)->name);
1120     }
1121     pa_json_encoder_end_array(encoder);
1122 
1123     return pa_json_encoder_to_string_free(encoder);
1124 }
1125 
pa_card_port_info_to_json_object(pa_card_port_info ** info)1126 const char* pa_card_port_info_to_json_object(pa_card_port_info **info) {
1127     pa_json_encoder *encoder = pa_json_encoder_new();
1128     if (!info) {
1129         pa_json_encoder_begin_element_object(encoder);
1130         pa_json_encoder_end_object(encoder);
1131         return pa_json_encoder_to_string_free(encoder);
1132     }
1133 
1134     pa_card_port_info **p;
1135     char *pl;
1136 
1137     pa_json_encoder_begin_element_object(encoder);
1138     for (p = info; *p; p++) {
1139         pa_card_profile_info **pr = (*p)->profiles;
1140 
1141         char* latency_offset_str = pa_sprintf_malloc("%"PRId64" usec", (*p)->latency_offset);
1142         pa_json_encoder *port_info_encoder = pa_json_encoder_new();
1143         pa_json_encoder_begin_element_object(port_info_encoder);
1144         pa_json_encoder_add_member_string(port_info_encoder, "description", (*p)->description);
1145         pa_json_encoder_add_member_string(port_info_encoder, "type", get_device_port_type((*p)->type));
1146         pa_json_encoder_add_member_int(port_info_encoder, "priority", (*p)->priority);
1147         pa_json_encoder_add_member_string(port_info_encoder, "latency_offset", latency_offset_str);
1148         pa_json_encoder_add_member_string(port_info_encoder, "availability_group", (*p)->availability_group);
1149         pa_json_encoder_add_member_string(port_info_encoder, "availability", get_available_str((*p)->available));
1150         pa_json_encoder_add_member_raw_json(port_info_encoder, "properties", pl = pa_proplist_to_json_object((*p)->proplist));
1151         pa_json_encoder_add_member_raw_json(port_info_encoder, "profiles", pa_card_profile_info_to_json_array(pr));
1152         pa_json_encoder_end_object(port_info_encoder);
1153 
1154         char *port_info_str = pa_json_encoder_to_string_free(port_info_encoder);
1155         pa_json_encoder_add_member_raw_json(encoder, (*p)->name, port_info_str);
1156         pa_xfree(port_info_str);
1157         pa_xfree(latency_offset_str);
1158         pa_xfree(pl);
1159     }
1160     pa_json_encoder_end_object(encoder);
1161 
1162     return pa_json_encoder_to_string_free(encoder);
1163 }
1164 
get_card_info_callback(pa_context * c,const pa_card_info * i,int is_last,void * userdata)1165 static void get_card_info_callback(pa_context *c, const pa_card_info *i, int is_last, void *userdata) {
1166     char t[32];
1167     char *pl;
1168 
1169     if (format == JSON && json_encoder == NULL) {
1170         json_encoder = pa_json_encoder_new();
1171         pa_json_encoder_begin_element_array(json_encoder);
1172     }
1173 
1174     if (is_last < 0) {
1175         pa_log(_("Failed to get card information: %s"), pa_strerror(pa_context_errno(c)));
1176         complete_action();
1177         return;
1178     }
1179 
1180     if (is_last) {
1181         if (format == JSON) {
1182             pa_json_encoder_end_array_handler("cards");
1183         }
1184         complete_action();
1185         return;
1186     }
1187 
1188     pa_assert(i);
1189 
1190     if (nl && !short_list_format && format == TEXT)
1191         printf("\n");
1192     nl = true;
1193 
1194     pa_snprintf(t, sizeof(t), "%u", i->owner_module);
1195 
1196     if (short_list_format) {
1197         if (format == JSON) {
1198             pa_json_encoder *encoder = pa_json_encoder_new();
1199             pa_json_encoder_begin_element_object(encoder);
1200             pa_json_encoder_add_member_int(encoder, "index", i->index);
1201             pa_json_encoder_add_member_string(encoder, "name", i->name);
1202             pa_json_encoder_add_member_string(encoder, "driver", i->driver);
1203             pa_json_encoder_end_object(encoder);
1204 
1205             char* json_str = pa_json_encoder_to_string_free(encoder);
1206             pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1207             pa_xfree(json_str);
1208         } else {
1209             printf("%u\t%s\t%s\n", i->index, i->name, pa_strnull(i->driver));
1210         }
1211         return;
1212     }
1213 
1214     if (format == JSON) {
1215         pa_json_encoder *encoder = pa_json_encoder_new();
1216         pa_json_encoder_begin_element_object(encoder);
1217         pa_json_encoder_add_member_int(encoder, "index", i->index);
1218         pa_json_encoder_add_member_string(encoder, "name", i->name);
1219         pa_json_encoder_add_member_string(encoder, "driver", i->driver);
1220         i->owner_module != PA_INVALID_INDEX ? pa_json_encoder_add_member_string(encoder, "owner_module", t) : pa_json_encoder_add_member_null(encoder, "owner_module");
1221         pa_json_encoder_add_member_raw_json(encoder, "properties", pl = pa_proplist_to_json_object(i->proplist));
1222         pa_json_encoder_add_member_raw_json(encoder, "profiles", i->n_profiles > 0 ? pa_card_profile_info_2_to_json_object(i->profiles2) : "{}");
1223         i->active_profile ? pa_json_encoder_add_member_string(encoder, "active_profile", i->active_profile->name) : pa_json_encoder_add_member_null(encoder, "active_profile");
1224         pa_json_encoder_add_member_raw_json(encoder, "ports", pa_card_port_info_to_json_object(i->ports));
1225         pa_json_encoder_end_object(encoder);
1226 
1227         char* json_str = pa_json_encoder_to_string_free(encoder);
1228         pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1229         pa_xfree(json_str);
1230     } else {
1231         printf(_("Card #%u\n"
1232              "\tName: %s\n"
1233              "\tDriver: %s\n"
1234              "\tOwner Module: %s\n"
1235              "\tProperties:\n\t\t%s\n"),
1236             i->index,
1237             i->name,
1238             pa_strnull(i->driver),
1239             i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
1240             pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
1241 
1242         if (i->n_profiles > 0) {
1243             pa_card_profile_info2 **p;
1244 
1245             printf(_("\tProfiles:\n"));
1246             for (p = i->profiles2; *p; p++)
1247                 printf(_("\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"), (*p)->name,
1248                     (*p)->description, (*p)->n_sinks, (*p)->n_sources, (*p)->priority, pa_yes_no_localised((*p)->available));
1249         }
1250 
1251         if (i->active_profile)
1252             printf(_("\tActive Profile: %s\n"),
1253                 i->active_profile->name);
1254 
1255         if (i->ports) {
1256             pa_card_port_info **p;
1257 
1258             printf(_("\tPorts:\n"));
1259             for (p = i->ports; *p; p++) {
1260                 pa_card_profile_info **pr = (*p)->profiles;
1261                 printf(_("\t\t%s: %s (type: %s, priority: %u, latency offset: %" PRId64 " usec%s%s, %s)\n"), (*p)->name,
1262                     (*p)->description, get_device_port_type((*p)->type), (*p)->priority, (*p)->latency_offset,
1263                     (*p)->availability_group ? _(", availability group: ") : "", (*p)->availability_group ?: "",
1264                     get_available_str((*p)->available));
1265 
1266                 if (!pa_proplist_isempty((*p)->proplist)) {
1267                     pa_xfree(pl);
1268                     printf(_("\t\t\tProperties:\n\t\t\t\t%s\n"), pl = pa_proplist_to_string_sep((*p)->proplist, "\n\t\t\t\t"));
1269                 }
1270 
1271                 if (pr) {
1272                     printf(_("\t\t\tPart of profile(s): %s"), pa_strnull((*pr)->name));
1273                     pr++;
1274                     while (*pr) {
1275                         printf(", %s", pa_strnull((*pr)->name));
1276                         pr++;
1277                     }
1278                     printf("\n");
1279                 }
1280             }
1281         }
1282     }
1283 
1284     pa_xfree(pl);
1285 }
1286 
get_sink_input_info_callback(pa_context * c,const pa_sink_input_info * i,int is_last,void * userdata)1287 static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
1288     char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], f[PA_FORMAT_INFO_SNPRINT_MAX];
1289     char *pl;
1290 
1291     if (format == JSON && json_encoder == NULL) {
1292         json_encoder = pa_json_encoder_new();
1293         pa_json_encoder_begin_element_array(json_encoder);
1294     }
1295 
1296     if (is_last < 0) {
1297         pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
1298         quit(1);
1299         return;
1300     }
1301 
1302     if (is_last) {
1303         if (format == JSON) {
1304             pa_json_encoder_end_array_handler("sink_inputs");
1305         }
1306         complete_action();
1307         return;
1308     }
1309 
1310     pa_assert(i);
1311 
1312     if (nl && !short_list_format && format == TEXT)
1313         printf("\n");
1314     nl = true;
1315 
1316     pa_snprintf(t, sizeof(t), "%u", i->owner_module);
1317     pa_snprintf(k, sizeof(k), "%u", i->client);
1318 
1319     char *sample_spec = pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
1320     if (short_list_format) {
1321         if (format == JSON) {
1322             pa_json_encoder *encoder = pa_json_encoder_new();
1323             pa_json_encoder_begin_element_object(encoder);
1324             pa_json_encoder_add_member_int(encoder, "index", i->index);
1325             pa_json_encoder_add_member_int(encoder, "sink", i->sink);
1326             i->client != PA_INVALID_INDEX ? pa_json_encoder_add_member_string(encoder, "client", k) : pa_json_encoder_add_member_null(encoder, "client");
1327             pa_json_encoder_add_member_string(encoder, "driver", i->driver);
1328             pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
1329             pa_json_encoder_end_object(encoder);
1330 
1331             char* json_str = pa_json_encoder_to_string_free(encoder);
1332             pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1333             pa_xfree(json_str);
1334         } else {
1335             printf("%u\t%u\t%s\t%s\t%s\n",
1336                i->index,
1337                i->sink,
1338                i->client != PA_INVALID_INDEX ? k : "-",
1339                pa_strnull(i->driver),
1340                sample_spec);
1341         }
1342         return;
1343     }
1344 
1345     char *channel_map = pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map);
1346     char *format_info = pa_format_info_snprint(f, sizeof(f), i->format);
1347     float balance = pa_cvolume_get_balance(&i->volume, &i->channel_map);
1348     if (format == JSON) {
1349         pa_json_encoder *encoder = pa_json_encoder_new();
1350         pa_json_encoder_begin_element_object(encoder);
1351         pa_json_encoder_add_member_int(encoder, "index", i->index);
1352         pa_json_encoder_add_member_string(encoder, "driver", i->driver);
1353         i->owner_module != PA_INVALID_INDEX ? pa_json_encoder_add_member_string(encoder, "owner_module", t) : pa_json_encoder_add_member_null(encoder, "owner_module");
1354         i->client != PA_INVALID_INDEX ? pa_json_encoder_add_member_string(encoder, "client", k) : pa_json_encoder_add_member_null(encoder, "client");
1355         pa_json_encoder_add_member_int(encoder, "sink", i->sink);
1356         pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
1357         pa_json_encoder_add_member_string(encoder, "channel_map", channel_map);
1358         pa_json_encoder_add_member_string(encoder, "format", format_info);
1359         pa_json_encoder_add_member_bool(encoder, "corked", i->corked);
1360         pa_json_encoder_add_member_bool(encoder, "mute", i->mute);
1361         pa_json_encoder_add_member_raw_json(encoder, "volume", pa_cvolume_to_json_object(&i->volume, &i->channel_map, true));
1362         pa_json_encoder_add_member_double(encoder, "balance", balance, 2);
1363         pa_json_encoder_add_member_double(encoder, "buffer_latency_usec", (double) i->buffer_usec, 2);
1364         pa_json_encoder_add_member_double(encoder, "sink_latency_usec", (double) i->sink_usec, 2);
1365         pa_json_encoder_add_member_string(encoder, "resample_method", i->resample_method);
1366         pa_json_encoder_add_member_raw_json(encoder, "properties", pl = pa_proplist_to_json_object(i->proplist));
1367         pa_json_encoder_end_object(encoder);
1368 
1369         char* json_str = pa_json_encoder_to_string_free(encoder);
1370         pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1371         pa_xfree(json_str);
1372     } else {
1373         printf(_("Sink Input #%u\n"
1374              "\tDriver: %s\n"
1375              "\tOwner Module: %s\n"
1376              "\tClient: %s\n"
1377              "\tSink: %u\n"
1378              "\tSample Specification: %s\n"
1379              "\tChannel Map: %s\n"
1380              "\tFormat: %s\n"
1381              "\tCorked: %s\n"
1382              "\tMute: %s\n"
1383              "\tVolume: %s\n"
1384              "\t        balance %0.2f\n"
1385              "\tBuffer Latency: %0.0f usec\n"
1386              "\tSink Latency: %0.0f usec\n"
1387              "\tResample method: %s\n"
1388              "\tProperties:\n\t\t%s\n"),
1389            i->index,
1390            pa_strnull(i->driver),
1391            i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
1392            i->client != PA_INVALID_INDEX ? k : _("n/a"),
1393            i->sink,
1394            sample_spec,
1395            channel_map,
1396            format_info,
1397            pa_yes_no_localised(i->corked),
1398            pa_yes_no_localised(i->mute),
1399            pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, true),
1400            balance,
1401            (double) i->buffer_usec,
1402            (double) i->sink_usec,
1403            i->resample_method ? i->resample_method : _("n/a"),
1404            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
1405     }
1406 
1407     pa_xfree(pl);
1408 }
1409 
get_source_output_info_callback(pa_context * c,const pa_source_output_info * i,int is_last,void * userdata)1410 static void get_source_output_info_callback(pa_context *c, const pa_source_output_info *i, int is_last, void *userdata) {
1411     char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], f[PA_FORMAT_INFO_SNPRINT_MAX];
1412     char *pl;
1413 
1414     if (format == JSON && json_encoder == NULL) {
1415         json_encoder = pa_json_encoder_new();
1416         pa_json_encoder_begin_element_array(json_encoder);
1417     }
1418 
1419     if (is_last < 0) {
1420         pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
1421         quit(1);
1422         return;
1423     }
1424 
1425     if (is_last) {
1426         if (format == JSON) {
1427             pa_json_encoder_end_array_handler("source_outputs");
1428         }
1429         complete_action();
1430         return;
1431     }
1432 
1433     pa_assert(i);
1434 
1435     if (nl && !short_list_format && format == TEXT)
1436         printf("\n");
1437     nl = true;
1438 
1439     pa_snprintf(t, sizeof(t), "%u", i->owner_module);
1440     pa_snprintf(k, sizeof(k), "%u", i->client);
1441 
1442     char *sample_spec = pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
1443     if (short_list_format) {
1444         if (format == JSON) {
1445             pa_json_encoder *encoder = pa_json_encoder_new();
1446             pa_json_encoder_begin_element_object(encoder);
1447             pa_json_encoder_add_member_int(encoder, "index", i->index);
1448             pa_json_encoder_add_member_int(encoder, "source", i->source);
1449             i->client != PA_INVALID_INDEX ? pa_json_encoder_add_member_string(encoder, "client", k) : pa_json_encoder_add_member_null(encoder, "client");
1450             pa_json_encoder_add_member_string(encoder, "driver", i->driver);
1451             pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
1452             pa_json_encoder_end_object(encoder);
1453 
1454             char* json_str = pa_json_encoder_to_string_free(encoder);
1455             pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1456             pa_xfree(json_str);
1457         } else {
1458             printf("%u\t%u\t%s\t%s\t%s\n",
1459                i->index,
1460                i->source,
1461                i->client != PA_INVALID_INDEX ? k : "-",
1462                pa_strnull(i->driver),
1463                sample_spec);
1464         }
1465         return;
1466     }
1467 
1468     char *channel_map = pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map);
1469     char *format_info = pa_format_info_snprint(f, sizeof(f), i->format);
1470     float balance = pa_cvolume_get_balance(&i->volume, &i->channel_map);
1471     if (format == JSON) {
1472         pa_json_encoder *encoder = pa_json_encoder_new();
1473         pa_json_encoder_begin_element_object(encoder);
1474         pa_json_encoder_add_member_int(encoder, "index", i->index);
1475         pa_json_encoder_add_member_string(encoder, "driver", i->driver);
1476         i->owner_module != PA_INVALID_INDEX ? pa_json_encoder_add_member_string(encoder, "owner_module", t) : pa_json_encoder_add_member_null(encoder, "owner_module");
1477         i->client != PA_INVALID_INDEX ? pa_json_encoder_add_member_string(encoder, "client", k) : pa_json_encoder_add_member_null(encoder, "client");
1478         pa_json_encoder_add_member_int(encoder, "source", i->source);
1479         pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
1480         pa_json_encoder_add_member_string(encoder, "channel_map", channel_map);
1481         pa_json_encoder_add_member_string(encoder, "format", format_info);
1482         pa_json_encoder_add_member_bool(encoder, "corked", i->corked);
1483         pa_json_encoder_add_member_bool(encoder, "mute", i->mute);
1484         pa_json_encoder_add_member_raw_json(encoder, "volume", pa_cvolume_to_json_object(&i->volume, &i->channel_map, true));
1485         pa_json_encoder_add_member_double(encoder, "balance", balance, 2);
1486         pa_json_encoder_add_member_double(encoder, "buffer_latency_usec", (double) i->buffer_usec, 2);
1487         pa_json_encoder_add_member_double(encoder, "source_latency_usec", (double) i->source_usec, 2);
1488         pa_json_encoder_add_member_string(encoder, "resample_method", i->resample_method);
1489         pa_json_encoder_add_member_raw_json(encoder, "properties", pl = pa_proplist_to_json_object(i->proplist));
1490         pa_json_encoder_end_object(encoder);
1491 
1492         char* json_str = pa_json_encoder_to_string_free(encoder);
1493         pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1494         pa_xfree(json_str);
1495     } else {
1496         printf(_("Source Output #%u\n"
1497              "\tDriver: %s\n"
1498              "\tOwner Module: %s\n"
1499              "\tClient: %s\n"
1500              "\tSource: %u\n"
1501              "\tSample Specification: %s\n"
1502              "\tChannel Map: %s\n"
1503              "\tFormat: %s\n"
1504              "\tCorked: %s\n"
1505              "\tMute: %s\n"
1506              "\tVolume: %s\n"
1507              "\t        balance %0.2f\n"
1508              "\tBuffer Latency: %0.0f usec\n"
1509              "\tSource Latency: %0.0f usec\n"
1510              "\tResample method: %s\n"
1511              "\tProperties:\n\t\t%s\n"),
1512            i->index,
1513            pa_strnull(i->driver),
1514            i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
1515            i->client != PA_INVALID_INDEX ? k : _("n/a"),
1516            i->source,
1517            sample_spec,
1518            channel_map,
1519            format_info,
1520            pa_yes_no_localised(i->corked),
1521            pa_yes_no_localised(i->mute),
1522            pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, true),
1523            balance,
1524            (double) i->buffer_usec,
1525            (double) i->source_usec,
1526            i->resample_method ? i->resample_method : _("n/a"),
1527            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
1528     }
1529 
1530     pa_xfree(pl);
1531 }
1532 
get_sample_info_callback(pa_context * c,const pa_sample_info * i,int is_last,void * userdata)1533 static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) {
1534     char t[PA_BYTES_SNPRINT_MAX], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
1535     char *pl;
1536 
1537     if (format == JSON && json_encoder == NULL) {
1538         json_encoder = pa_json_encoder_new();
1539         pa_json_encoder_begin_element_array(json_encoder);
1540     }
1541 
1542     if (is_last < 0) {
1543         pa_log(_("Failed to get sample information: %s"), pa_strerror(pa_context_errno(c)));
1544         quit(1);
1545         return;
1546     }
1547 
1548     if (is_last) {
1549         if (format == JSON) {
1550             pa_json_encoder_end_array_handler("samples");
1551         }
1552         complete_action();
1553         return;
1554     }
1555 
1556     pa_assert(i);
1557 
1558     if (nl && !short_list_format && format == TEXT)
1559         printf("\n");
1560     nl = true;
1561 
1562     pa_bytes_snprint(t, sizeof(t), i->bytes);
1563 
1564     char *sample_spec = pa_sample_spec_valid(&i->sample_spec) ? pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec) : short_list_format ? "-" : _("n/a");
1565     double duration = (double) i->duration/1000000.0;
1566     if (short_list_format) {
1567         if (format == JSON) {
1568             pa_json_encoder *encoder = pa_json_encoder_new();
1569             pa_json_encoder_begin_element_object(encoder);
1570             pa_json_encoder_add_member_int(encoder, "index", i->index);
1571             pa_json_encoder_add_member_string(encoder, "name", i->name);
1572             pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
1573             pa_json_encoder_add_member_double(encoder, "duration", duration, 3);
1574             pa_json_encoder_end_object(encoder);
1575 
1576             char* json_str = pa_json_encoder_to_string_free(encoder);
1577             pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1578             pa_xfree(json_str);
1579         } else {
1580             printf("%u\t%s\t%s\t%0.3f\n",
1581                i->index,
1582                i->name,
1583                sample_spec,
1584                duration);
1585         }
1586         return;
1587     }
1588 
1589     char *channel_map = pa_sample_spec_valid(&i->sample_spec) ? pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map) : _("n/a");
1590     float balance = pa_cvolume_get_balance(&i->volume, &i->channel_map);
1591     if (format == JSON) {
1592         pa_json_encoder *encoder = pa_json_encoder_new();
1593         pa_json_encoder_begin_element_object(encoder);
1594         pa_json_encoder_add_member_int(encoder, "index", i->index);
1595         pa_json_encoder_add_member_string(encoder, "name", i->name);
1596         pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
1597         pa_json_encoder_add_member_string(encoder, "channel_map", channel_map);
1598         pa_json_encoder_add_member_raw_json(encoder, "volume", pa_cvolume_to_json_object(&i->volume, &i->channel_map, true));
1599         pa_json_encoder_add_member_double(encoder, "balance", balance, 2);
1600         pa_json_encoder_add_member_double(encoder, "duration", duration, 3);
1601         pa_json_encoder_add_member_string(encoder, "size", t);
1602         pa_json_encoder_add_member_bool(encoder, "lazy", i->lazy);
1603         pa_json_encoder_add_member_string(encoder, "filename", i->filename);
1604         pa_json_encoder_add_member_raw_json(encoder, "properties", pl = pa_proplist_to_json_object(i->proplist));
1605         pa_json_encoder_end_object(encoder);
1606 
1607         char* json_str = pa_json_encoder_to_string_free(encoder);
1608         pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1609         pa_xfree(json_str);
1610     } else {
1611         printf(_("Sample #%u\n"
1612              "\tName: %s\n"
1613              "\tSample Specification: %s\n"
1614              "\tChannel Map: %s\n"
1615              "\tVolume: %s\n"
1616              "\t        balance %0.2f\n"
1617              "\tDuration: %0.1fs\n"
1618              "\tSize: %s\n"
1619              "\tLazy: %s\n"
1620              "\tFilename: %s\n"
1621              "\tProperties:\n\t\t%s\n"),
1622            i->index,
1623            i->name,
1624            sample_spec,
1625            channel_map,
1626            pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, true),
1627            balance,
1628            duration,
1629            t,
1630            pa_yes_no_localised(i->lazy),
1631            i->filename ? i->filename : _("n/a"),
1632            pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
1633     }
1634 
1635     pa_xfree(pl);
1636 }
1637 
simple_callback(pa_context * c,int success,void * userdata)1638 static void simple_callback(pa_context *c, int success, void *userdata) {
1639     if (!success) {
1640         pa_log(_("Failure: %s"), pa_strerror(pa_context_errno(c)));
1641         quit(1);
1642         return;
1643     }
1644 
1645     complete_action();
1646 }
1647 
index_callback(pa_context * c,uint32_t idx,void * userdata)1648 static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
1649     if (idx == PA_INVALID_INDEX) {
1650         pa_log(_("Failure: %s"), pa_strerror(pa_context_errno(c)));
1651         quit(1);
1652         return;
1653     }
1654 
1655     if (format == JSON) {
1656         pa_json_encoder *encoder = pa_json_encoder_new();
1657         pa_json_encoder_begin_element_object(encoder);
1658         pa_json_encoder_add_member_int(encoder, "index", idx);
1659         pa_json_encoder_end_object(encoder);
1660 
1661         char* json_str = pa_json_encoder_to_string_free(encoder);
1662         printf("%s", json_str);
1663         pa_xfree(json_str);
1664     } else {
1665         printf("%u\n", idx);
1666     }
1667 
1668     complete_action();
1669 }
1670 
send_message_callback(pa_context * c,int success,char * response,void * userdata)1671 static void send_message_callback(pa_context *c, int success, char *response, void *userdata) {
1672 
1673     if (!success) {
1674         pa_log(_("Send message failed: %s"), pa_strerror(pa_context_errno(c)));
1675         quit(1);
1676         return;
1677     }
1678 
1679     if (format == JSON) {
1680         pa_json_encoder *encoder = pa_json_encoder_new();
1681         pa_json_encoder_begin_element_object(encoder);
1682         pa_json_encoder_add_member_string(encoder, "response", response);
1683         pa_json_encoder_end_object(encoder);
1684 
1685         char* json_str = pa_json_encoder_to_string_free(encoder);
1686         printf("%s", json_str);
1687         pa_xfree(json_str);
1688     } else {
1689         printf("%s\n", response);
1690     }
1691 
1692     complete_action();
1693 }
1694 
list_handlers_callback(pa_context * c,int success,char * response,void * userdata)1695 static void list_handlers_callback(pa_context *c, int success, char *response, void *userdata) {
1696     int err;
1697     pa_json_object *o;
1698     int i;
1699     const pa_json_object *v, *path, *description;
1700 
1701     if (!success) {
1702         pa_log(_("list-handlers message failed: %s"), pa_strerror(pa_context_errno(c)));
1703         quit(1);
1704         return;
1705     }
1706 
1707     // The response is already JSON encoded
1708     if (format == JSON) {
1709         printf("%s\n", response);
1710         fflush(stdout);
1711         complete_action();
1712         return;
1713     }
1714 
1715     o = pa_json_parse(response);
1716 
1717     if (!o) {
1718         pa_log(_("list-handlers message response could not be parsed correctly"));
1719         pa_json_object_free(o);
1720         quit(1);
1721         return;
1722     }
1723 
1724     if (pa_json_object_get_type(o) != PA_JSON_TYPE_ARRAY) {
1725         pa_log(_("list-handlers message response is not a JSON array"));
1726         pa_json_object_free(o);
1727         quit(1);
1728         return;
1729     }
1730 
1731     err = 0;
1732 
1733     for (i = 0; i < pa_json_object_get_array_length(o); ++i) {
1734         v = pa_json_object_get_array_member(o, i);
1735         if (pa_json_object_get_type(v) != PA_JSON_TYPE_OBJECT) {
1736             pa_log(_("list-handlers message response array element %d is not a JSON object"), i);
1737             err = -1;
1738             break;
1739         }
1740 
1741         path = pa_json_object_get_object_member(v, "name");
1742         if (!path || pa_json_object_get_type(path) != PA_JSON_TYPE_STRING) {
1743             err = -1;
1744             break;
1745         }
1746         description = pa_json_object_get_object_member(v, "description");
1747         if (!description || pa_json_object_get_type(description) != PA_JSON_TYPE_STRING) {
1748             err = -1;
1749             break;
1750         }
1751 
1752         if (short_list_format) {
1753             printf("%s\n", pa_json_object_get_string(path));
1754         } else {
1755             if (nl)
1756                 printf("\n");
1757             nl = true;
1758 
1759             printf("Message Handler %s\n"
1760                    "\tDescription: %s\n",
1761                    pa_json_object_get_string(path),
1762                    pa_json_object_get_string(description));
1763         }
1764     }
1765 
1766     if (err < 0) {
1767         pa_log(_("list-handlers message response could not be parsed correctly"));
1768         pa_json_object_free(o);
1769         quit(1);
1770         return;
1771     }
1772 
1773     pa_json_object_free(o);
1774 
1775     complete_action();
1776 }
1777 
volume_relative_adjust(pa_cvolume * cv)1778 static void volume_relative_adjust(pa_cvolume *cv) {
1779     pa_assert(volume_flags & VOL_RELATIVE);
1780 
1781     /* Relative volume change is additive in case of UINT or PERCENT
1782      * and multiplicative for LINEAR or DECIBEL */
1783     if ((volume_flags & 0x0F) == VOL_UINT || (volume_flags & 0x0F) == VOL_PERCENT) {
1784         unsigned i;
1785         for (i = 0; i < cv->channels; i++) {
1786             if (cv->values[i] + volume.values[i] < PA_VOLUME_NORM)
1787                 cv->values[i] = PA_VOLUME_MUTED;
1788             else
1789                 cv->values[i] = cv->values[i] + volume.values[i] - PA_VOLUME_NORM;
1790         }
1791     }
1792     if ((volume_flags & 0x0F) == VOL_LINEAR || (volume_flags & 0x0F) == VOL_DECIBEL)
1793         pa_sw_cvolume_multiply(cv, cv, &volume);
1794 }
1795 
unload_module_by_name_callback(pa_context * c,const pa_module_info * i,int is_last,void * userdata)1796 static void unload_module_by_name_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
1797     static bool unloaded = false;
1798 
1799     if (is_last < 0) {
1800         pa_log(_("Failed to get module information: %s"), pa_strerror(pa_context_errno(c)));
1801         quit(1);
1802         return;
1803     }
1804 
1805     if (is_last) {
1806         if (unloaded == false)
1807             pa_log(_("Failed to unload module: Module %s not loaded"), module_name);
1808         complete_action();
1809         return;
1810     }
1811 
1812     pa_assert(i);
1813 
1814     if (pa_streq(module_name, i->name)) {
1815         unloaded = true;
1816         actions++;
1817         pa_operation_unref(pa_context_unload_module(c, i->index, simple_callback, NULL));
1818     }
1819 }
1820 
fill_volume(pa_cvolume * cv,unsigned supported)1821 static void fill_volume(pa_cvolume *cv, unsigned supported) {
1822     if (volume.channels == 1) {
1823         pa_cvolume_set(&volume, supported, volume.values[0]);
1824     } else if (volume.channels != supported) {
1825         pa_log(ngettext("Failed to set volume: You tried to set volumes for %d channel, whereas channel(s) supported = %d\n",
1826                         "Failed to set volume: You tried to set volumes for %d channels, whereas channel(s) supported = %d\n",
1827                         volume.channels),
1828                volume.channels, supported);
1829         quit(1);
1830         return;
1831     }
1832 
1833     if (volume_flags & VOL_RELATIVE)
1834         volume_relative_adjust(cv);
1835     else
1836         *cv = volume;
1837 }
1838 
get_sink_mute_callback(pa_context * c,const pa_sink_info * i,int is_last,void * userdata)1839 static void get_sink_mute_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
1840     if (is_last < 0) {
1841         pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
1842         quit(1);
1843         return;
1844     }
1845 
1846     if (is_last)
1847         return;
1848 
1849     pa_assert(i);
1850 
1851     printf(("Mute: %s\n"),
1852            pa_yes_no_localised(i->mute));
1853 
1854     complete_action();
1855 }
1856 
get_sink_volume_callback(pa_context * c,const pa_sink_info * i,int is_last,void * userdata)1857 static void get_sink_volume_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
1858     if (is_last < 0) {
1859         pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
1860         quit(1);
1861         return;
1862     }
1863 
1864     if (is_last)
1865         return;
1866 
1867     pa_assert(i);
1868 
1869     char cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
1870     printf(("Volume: %s\n"
1871             "        balance %0.2f\n"),
1872            pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, true),
1873            pa_cvolume_get_balance(&i->volume, &i->channel_map));
1874 
1875     complete_action();
1876 }
1877 
set_sink_volume_callback(pa_context * c,const pa_sink_info * i,int is_last,void * userdata)1878 static void set_sink_volume_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
1879     pa_cvolume cv;
1880 
1881     if (is_last < 0) {
1882         pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
1883         quit(1);
1884         return;
1885     }
1886 
1887     if (is_last)
1888         return;
1889 
1890     pa_assert(i);
1891 
1892     cv = i->volume;
1893     fill_volume(&cv, i->channel_map.channels);
1894 
1895     pa_operation_unref(pa_context_set_sink_volume_by_name(c, sink_name, &cv, simple_callback, NULL));
1896 }
1897 
get_source_mute_callback(pa_context * c,const pa_source_info * i,int is_last,void * userdata)1898 static void get_source_mute_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
1899     if (is_last < 0) {
1900         pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
1901         quit(1);
1902         return;
1903     }
1904 
1905     if (is_last)
1906         return;
1907 
1908     pa_assert(i);
1909 
1910     printf(("Mute: %s\n"),
1911            pa_yes_no_localised(i->mute));
1912 
1913     complete_action();
1914 }
1915 
get_source_volume_callback(pa_context * c,const pa_source_info * i,int is_last,void * userdata)1916 static void get_source_volume_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
1917     if (is_last < 0) {
1918         pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
1919         quit(1);
1920         return;
1921     }
1922 
1923     if (is_last)
1924         return;
1925 
1926     pa_assert(i);
1927 
1928     char cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
1929     printf(("Volume: %s\n"
1930             "        balance %0.2f\n"),
1931            pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, true),
1932            pa_cvolume_get_balance(&i->volume, &i->channel_map));
1933 
1934     complete_action();
1935 }
1936 
set_source_volume_callback(pa_context * c,const pa_source_info * i,int is_last,void * userdata)1937 static void set_source_volume_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
1938     pa_cvolume cv;
1939 
1940     if (is_last < 0) {
1941         pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
1942         quit(1);
1943         return;
1944     }
1945 
1946     if (is_last)
1947         return;
1948 
1949     pa_assert(i);
1950 
1951     cv = i->volume;
1952     fill_volume(&cv, i->channel_map.channels);
1953 
1954     pa_operation_unref(pa_context_set_source_volume_by_name(c, source_name, &cv, simple_callback, NULL));
1955 }
1956 
get_sink_input_volume_callback(pa_context * c,const pa_sink_input_info * i,int is_last,void * userdata)1957 static void get_sink_input_volume_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
1958     pa_cvolume cv;
1959 
1960     if (is_last < 0) {
1961         pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
1962         quit(1);
1963         return;
1964     }
1965 
1966     if (is_last)
1967         return;
1968 
1969     pa_assert(i);
1970 
1971     cv = i->volume;
1972     fill_volume(&cv, i->channel_map.channels);
1973 
1974     pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &cv, simple_callback, NULL));
1975 }
1976 
get_source_output_volume_callback(pa_context * c,const pa_source_output_info * o,int is_last,void * userdata)1977 static void get_source_output_volume_callback(pa_context *c, const pa_source_output_info *o, int is_last, void *userdata) {
1978     pa_cvolume cv;
1979 
1980     if (is_last < 0) {
1981         pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
1982         quit(1);
1983         return;
1984     }
1985 
1986     if (is_last)
1987         return;
1988 
1989     pa_assert(o);
1990 
1991     cv = o->volume;
1992     fill_volume(&cv, o->channel_map.channels);
1993 
1994     pa_operation_unref(pa_context_set_source_output_volume(c, source_output_idx, &cv, simple_callback, NULL));
1995 }
1996 
sink_toggle_mute_callback(pa_context * c,const pa_sink_info * i,int is_last,void * userdata)1997 static void sink_toggle_mute_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
1998     if (is_last < 0) {
1999         pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
2000         quit(1);
2001         return;
2002     }
2003 
2004     if (is_last)
2005         return;
2006 
2007     pa_assert(i);
2008 
2009     pa_operation_unref(pa_context_set_sink_mute_by_name(c, i->name, !i->mute, simple_callback, NULL));
2010 }
2011 
source_toggle_mute_callback(pa_context * c,const pa_source_info * o,int is_last,void * userdata)2012 static void source_toggle_mute_callback(pa_context *c, const pa_source_info *o, int is_last, void *userdata) {
2013     if (is_last < 0) {
2014         pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
2015         quit(1);
2016         return;
2017     }
2018 
2019     if (is_last)
2020         return;
2021 
2022     pa_assert(o);
2023 
2024     pa_operation_unref(pa_context_set_source_mute_by_name(c, o->name, !o->mute, simple_callback, NULL));
2025 }
2026 
sink_input_toggle_mute_callback(pa_context * c,const pa_sink_input_info * i,int is_last,void * userdata)2027 static void sink_input_toggle_mute_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
2028     if (is_last < 0) {
2029         pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
2030         quit(1);
2031         return;
2032     }
2033 
2034     if (is_last)
2035         return;
2036 
2037     pa_assert(i);
2038 
2039     pa_operation_unref(pa_context_set_sink_input_mute(c, i->index, !i->mute, simple_callback, NULL));
2040 }
2041 
source_output_toggle_mute_callback(pa_context * c,const pa_source_output_info * o,int is_last,void * userdata)2042 static void source_output_toggle_mute_callback(pa_context *c, const pa_source_output_info *o, int is_last, void *userdata) {
2043     if (is_last < 0) {
2044         pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
2045         quit(1);
2046         return;
2047     }
2048 
2049     if (is_last)
2050         return;
2051 
2052     pa_assert(o);
2053 
2054     pa_operation_unref(pa_context_set_source_output_mute(c, o->index, !o->mute, simple_callback, NULL));
2055 }
2056 
2057 /* PA_MAX_FORMATS is defined in internal.h so we just define a sane value here */
2058 #define MAX_FORMATS 256
2059 
set_sink_formats(pa_context * c,uint32_t sink,const char * str)2060 static void set_sink_formats(pa_context *c, uint32_t sink, const char *str) {
2061     pa_format_info *f_arr[MAX_FORMATS] = { 0, };
2062     char *format = NULL;
2063     const char *state = NULL;
2064     int i = 0;
2065     pa_operation *o = NULL;
2066 
2067     while ((format = pa_split(str, ";", &state))) {
2068         pa_format_info *f = pa_format_info_from_string(pa_strip(format));
2069 
2070         if (!f) {
2071             pa_log(_("Failed to set format: invalid format string %s"), format);
2072             goto error;
2073         }
2074 
2075         f_arr[i++] = f;
2076         pa_xfree(format);
2077     }
2078 
2079     o = pa_ext_device_restore_save_formats(c, PA_DEVICE_TYPE_SINK, sink, i, f_arr, simple_callback, NULL);
2080     if (o) {
2081         pa_operation_unref(o);
2082         actions++;
2083     }
2084 
2085 done:
2086     if (format)
2087         pa_xfree(format);
2088     while (f_arr[i] && i--)
2089         pa_format_info_free(f_arr[i]);
2090 
2091     return;
2092 
2093 error:
2094     while (f_arr[i] && i--)
2095         pa_format_info_free(f_arr[i]);
2096     quit(1);
2097     goto done;
2098 }
2099 
2100 #ifdef SNDFILE_ENABLE
stream_state_callback(pa_stream * s,void * userdata)2101 static void stream_state_callback(pa_stream *s, void *userdata) {
2102     pa_assert(s);
2103 
2104     switch (pa_stream_get_state(s)) {
2105         case PA_STREAM_CREATING:
2106         case PA_STREAM_READY:
2107             break;
2108 
2109         case PA_STREAM_TERMINATED:
2110             drain();
2111             break;
2112 
2113         case PA_STREAM_FAILED:
2114         default:
2115             pa_log(_("Failed to upload sample: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
2116             quit(1);
2117     }
2118 }
2119 
stream_write_callback(pa_stream * s,size_t length,void * userdata)2120 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
2121     sf_count_t l;
2122     float *d;
2123     pa_assert(s && length && sndfile);
2124 
2125     d = pa_xmalloc(length);
2126 
2127     pa_assert(sample_length >= length);
2128     l = (sf_count_t) (length/pa_frame_size(&sample_spec));
2129 
2130     if ((sf_readf_float(sndfile, d, l)) != l) {
2131         pa_xfree(d);
2132         pa_log(_("Premature end of file"));
2133         quit(1);
2134         return;
2135     }
2136 
2137     pa_stream_write(s, d, length, pa_xfree, 0, PA_SEEK_RELATIVE);
2138 
2139     sample_length -= length;
2140 
2141     if (sample_length <= 0) {
2142         pa_stream_set_write_callback(sample_stream, NULL, NULL);
2143         pa_stream_finish_upload(sample_stream);
2144     }
2145 }
2146 #endif
2147 
subscription_event_type_to_string(pa_subscription_event_type_t t)2148 static const char *subscription_event_type_to_string(pa_subscription_event_type_t t) {
2149 
2150     switch (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
2151 
2152     case PA_SUBSCRIPTION_EVENT_NEW:
2153         return _("new");
2154 
2155     case PA_SUBSCRIPTION_EVENT_CHANGE:
2156         return _("change");
2157 
2158     case PA_SUBSCRIPTION_EVENT_REMOVE:
2159         return _("remove");
2160     }
2161 
2162     return _("unknown");
2163 }
2164 
subscription_event_facility_to_string(pa_subscription_event_type_t t)2165 static const char *subscription_event_facility_to_string(pa_subscription_event_type_t t) {
2166 
2167     switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
2168 
2169     case PA_SUBSCRIPTION_EVENT_SINK:
2170         return _("sink");
2171 
2172     case PA_SUBSCRIPTION_EVENT_SOURCE:
2173         return _("source");
2174 
2175     case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
2176         return _("sink-input");
2177 
2178     case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
2179         return _("source-output");
2180 
2181     case PA_SUBSCRIPTION_EVENT_MODULE:
2182         return _("module");
2183 
2184     case PA_SUBSCRIPTION_EVENT_CLIENT:
2185         return _("client");
2186 
2187     case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE:
2188         return _("sample-cache");
2189 
2190     case PA_SUBSCRIPTION_EVENT_SERVER:
2191         return _("server");
2192 
2193     case PA_SUBSCRIPTION_EVENT_CARD:
2194         return _("card");
2195     }
2196 
2197     return _("unknown");
2198 }
2199 
context_subscribe_callback(pa_context * c,pa_subscription_event_type_t t,uint32_t idx,void * userdata)2200 static void context_subscribe_callback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
2201     pa_assert(c);
2202 
2203     if (format == JSON) {
2204         pa_json_encoder *encoder = pa_json_encoder_new();
2205         pa_json_encoder_begin_element_object(encoder);
2206         pa_json_encoder_add_member_int(encoder, "index", idx);
2207         pa_json_encoder_add_member_string(encoder, "event", subscription_event_type_to_string(t));
2208         pa_json_encoder_add_member_string(encoder, "on", subscription_event_facility_to_string(t));
2209         pa_json_encoder_end_object(encoder);
2210 
2211         char* json_str = pa_json_encoder_to_string_free(encoder);
2212         printf("%s", json_str);
2213         pa_xfree(json_str);
2214     } else {
2215         printf(_("Event '%s' on %s #%u\n"),
2216            subscription_event_type_to_string(t),
2217            subscription_event_facility_to_string(t),
2218            idx);
2219     }
2220     fflush(stdout);
2221 }
2222 
context_state_callback(pa_context * c,void * userdata)2223 static void context_state_callback(pa_context *c, void *userdata) {
2224     pa_operation *o = NULL;
2225 
2226     pa_assert(c);
2227 
2228     switch (pa_context_get_state(c)) {
2229         case PA_CONTEXT_CONNECTING:
2230         case PA_CONTEXT_AUTHORIZING:
2231         case PA_CONTEXT_SETTING_NAME:
2232             break;
2233 
2234         case PA_CONTEXT_READY:
2235             switch (action) {
2236                 case STAT:
2237                     o = pa_context_stat(c, stat_callback, NULL);
2238                     break;
2239 
2240                 case INFO:
2241                     o = pa_context_get_server_info(c, get_server_info_callback, NULL);
2242                     break;
2243 
2244                 case PLAY_SAMPLE:
2245                     o = pa_context_play_sample(c, sample_name, sink_name, PA_VOLUME_NORM, simple_callback, NULL);
2246                     break;
2247 
2248                 case REMOVE_SAMPLE:
2249                     o = pa_context_remove_sample(c, sample_name, simple_callback, NULL);
2250                     break;
2251 #ifdef SNDFILE_ENABLE
2252                 case UPLOAD_SAMPLE:
2253                     sample_stream = pa_stream_new(c, sample_name, &sample_spec, NULL);
2254                     pa_assert(sample_stream);
2255 
2256                     pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL);
2257                     pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL);
2258                     pa_stream_connect_upload(sample_stream, sample_length);
2259                     actions++;
2260                     break;
2261 #endif
2262                 case EXIT:
2263                     o = pa_context_exit_daemon(c, simple_callback, NULL);
2264                     break;
2265 
2266                 case LIST:
2267                     if (list_type) {
2268                         if (pa_streq(list_type, "modules"))
2269                             o = pa_context_get_module_info_list(c, get_module_info_callback, NULL);
2270                         else if (pa_streq(list_type, "sinks"))
2271                             o = pa_context_get_sink_info_list(c, get_sink_info_callback, NULL);
2272                         else if (pa_streq(list_type, "sources"))
2273                             o = pa_context_get_source_info_list(c, get_source_info_callback, NULL);
2274                         else if (pa_streq(list_type, "sink-inputs"))
2275                             o = pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL);
2276                         else if (pa_streq(list_type, "source-outputs"))
2277                             o = pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL);
2278                         else if (pa_streq(list_type, "clients"))
2279                             o = pa_context_get_client_info_list(c, get_client_info_callback, NULL);
2280                         else if (pa_streq(list_type, "samples"))
2281                             o = pa_context_get_sample_info_list(c, get_sample_info_callback, NULL);
2282                         else if (pa_streq(list_type, "cards"))
2283                             o = pa_context_get_card_info_list(c, get_card_info_callback, NULL);
2284                         else if (pa_streq(list_type, "message-handlers"))
2285                             o = pa_context_send_message_to_object(c, "/core", "list-handlers", NULL, list_handlers_callback, NULL);
2286                         else
2287                             pa_assert_not_reached();
2288                     } else {
2289                         if (format == JSON) {
2290                             list_encoder = pa_json_encoder_new();
2291                             pa_json_encoder_begin_element_object(list_encoder);
2292                         }
2293 
2294                         o = pa_context_get_module_info_list(c, get_module_info_callback, NULL);
2295                         if (o) {
2296                             pa_operation_unref(o);
2297                             actions++;
2298                         }
2299 
2300                         o = pa_context_get_sink_info_list(c, get_sink_info_callback, NULL);
2301                         if (o) {
2302                             pa_operation_unref(o);
2303                             actions++;
2304                         }
2305 
2306                         o = pa_context_get_source_info_list(c, get_source_info_callback, NULL);
2307                         if (o) {
2308                             pa_operation_unref(o);
2309                             actions++;
2310                         }
2311                         o = pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL);
2312                         if (o) {
2313                             pa_operation_unref(o);
2314                             actions++;
2315                         }
2316 
2317                         o = pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL);
2318                         if (o) {
2319                             pa_operation_unref(o);
2320                             actions++;
2321                         }
2322 
2323                         o = pa_context_get_client_info_list(c, get_client_info_callback, NULL);
2324                         if (o) {
2325                             pa_operation_unref(o);
2326                             actions++;
2327                         }
2328 
2329                         o = pa_context_get_sample_info_list(c, get_sample_info_callback, NULL);
2330                         if (o) {
2331                             pa_operation_unref(o);
2332                             actions++;
2333                         }
2334 
2335                         o = pa_context_get_card_info_list(c, get_card_info_callback, NULL);
2336                         if (o) {
2337                             pa_operation_unref(o);
2338                             actions++;
2339                         }
2340 
2341                         o = NULL;
2342                     }
2343                     break;
2344 
2345                 case MOVE_SINK_INPUT:
2346                     o = pa_context_move_sink_input_by_name(c, sink_input_idx, sink_name, simple_callback, NULL);
2347                     break;
2348 
2349                 case MOVE_SOURCE_OUTPUT:
2350                     o = pa_context_move_source_output_by_name(c, source_output_idx, source_name, simple_callback, NULL);
2351                     break;
2352 
2353                 case LOAD_MODULE:
2354                     o = pa_context_load_module(c, module_name, module_args, index_callback, NULL);
2355                     break;
2356 
2357                 case UNLOAD_MODULE:
2358                     if (module_name)
2359                         o = pa_context_get_module_info_list(c, unload_module_by_name_callback, NULL);
2360                     else
2361                         o = pa_context_unload_module(c, module_index, simple_callback, NULL);
2362                     break;
2363 
2364                 case SUSPEND_SINK:
2365                     if (sink_name)
2366                         o = pa_context_suspend_sink_by_name(c, sink_name, suspend, simple_callback, NULL);
2367                     else
2368                         o = pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL);
2369                     break;
2370 
2371                 case SUSPEND_SOURCE:
2372                     if (source_name)
2373                         o = pa_context_suspend_source_by_name(c, source_name, suspend, simple_callback, NULL);
2374                     else
2375                         o = pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL);
2376                     break;
2377 
2378                 case SET_CARD_PROFILE:
2379                     o = pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL);
2380                     break;
2381 
2382                 case SET_SINK_PORT:
2383                     o = pa_context_set_sink_port_by_name(c, sink_name, port_name, simple_callback, NULL);
2384                     break;
2385 
2386                 case GET_DEFAULT_SINK:
2387                     o = pa_context_get_server_info(c, get_default_sink, NULL);
2388                     break;
2389 
2390                 case SET_DEFAULT_SINK:
2391                     o = pa_context_set_default_sink(c, sink_name, simple_callback, NULL);
2392                     break;
2393 
2394                 case SET_SOURCE_PORT:
2395                     o = pa_context_set_source_port_by_name(c, source_name, port_name, simple_callback, NULL);
2396                     break;
2397 
2398                 case GET_DEFAULT_SOURCE:
2399                     o = pa_context_get_server_info(c, get_default_source, NULL);
2400                     break;
2401 
2402                 case SET_DEFAULT_SOURCE:
2403                     o = pa_context_set_default_source(c, source_name, simple_callback, NULL);
2404                     break;
2405 
2406                 case GET_SINK_MUTE:
2407                     o = pa_context_get_sink_info_by_name(c, sink_name, get_sink_mute_callback, NULL);
2408                     break;
2409 
2410                 case SET_SINK_MUTE:
2411                     if (mute == TOGGLE_MUTE)
2412                         o = pa_context_get_sink_info_by_name(c, sink_name, sink_toggle_mute_callback, NULL);
2413                     else
2414                         o = pa_context_set_sink_mute_by_name(c, sink_name, mute, simple_callback, NULL);
2415                     break;
2416 
2417                 case GET_SOURCE_MUTE:
2418                     o = pa_context_get_source_info_by_name(c, source_name, get_source_mute_callback, NULL);
2419                     break;
2420 
2421                 case SET_SOURCE_MUTE:
2422                     if (mute == TOGGLE_MUTE)
2423                         o = pa_context_get_source_info_by_name(c, source_name, source_toggle_mute_callback, NULL);
2424                     else
2425                         o = pa_context_set_source_mute_by_name(c, source_name, mute, simple_callback, NULL);
2426                     break;
2427 
2428                 case SET_SINK_INPUT_MUTE:
2429                     if (mute == TOGGLE_MUTE)
2430                         o = pa_context_get_sink_input_info(c, sink_input_idx, sink_input_toggle_mute_callback, NULL);
2431                     else
2432                         o = pa_context_set_sink_input_mute(c, sink_input_idx, mute, simple_callback, NULL);
2433                     break;
2434 
2435                 case SET_SOURCE_OUTPUT_MUTE:
2436                     if (mute == TOGGLE_MUTE)
2437                         o = pa_context_get_source_output_info(c, source_output_idx, source_output_toggle_mute_callback, NULL);
2438                     else
2439                         o = pa_context_set_source_output_mute(c, source_output_idx, mute, simple_callback, NULL);
2440                     break;
2441 
2442                 case GET_SINK_VOLUME:
2443                     o = pa_context_get_sink_info_by_name(c, sink_name, get_sink_volume_callback, NULL);
2444                     break;
2445 
2446                 case SET_SINK_VOLUME:
2447                     o = pa_context_get_sink_info_by_name(c, sink_name, set_sink_volume_callback, NULL);
2448                     break;
2449 
2450                 case GET_SOURCE_VOLUME:
2451                     o = pa_context_get_source_info_by_name(c, source_name, get_source_volume_callback, NULL);
2452                     break;
2453 
2454                 case SET_SOURCE_VOLUME:
2455                     o = pa_context_get_source_info_by_name(c, source_name, set_source_volume_callback, NULL);
2456                     break;
2457 
2458                 case SET_SINK_INPUT_VOLUME:
2459                     o = pa_context_get_sink_input_info(c, sink_input_idx, get_sink_input_volume_callback, NULL);
2460                     break;
2461 
2462                 case SET_SOURCE_OUTPUT_VOLUME:
2463                     o = pa_context_get_source_output_info(c, source_output_idx, get_source_output_volume_callback, NULL);
2464                     break;
2465 
2466                 case SET_SINK_FORMATS:
2467                     set_sink_formats(c, sink_idx, formats);
2468                     break;
2469 
2470                 case SET_PORT_LATENCY_OFFSET:
2471                     o = pa_context_set_port_latency_offset(c, card_name, port_name, latency_offset, simple_callback, NULL);
2472                     break;
2473 
2474                 case SEND_MESSAGE:
2475                     o = pa_context_send_message_to_object(c, object_path, message, message_args, send_message_callback, NULL);
2476                     break;
2477 
2478                 case SUBSCRIBE:
2479                     pa_context_set_subscribe_callback(c, context_subscribe_callback, NULL);
2480 
2481                     o = pa_context_subscribe(c,
2482                                              PA_SUBSCRIPTION_MASK_SINK|
2483                                              PA_SUBSCRIPTION_MASK_SOURCE|
2484                                              PA_SUBSCRIPTION_MASK_SINK_INPUT|
2485                                              PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
2486                                              PA_SUBSCRIPTION_MASK_MODULE|
2487                                              PA_SUBSCRIPTION_MASK_CLIENT|
2488                                              PA_SUBSCRIPTION_MASK_SAMPLE_CACHE|
2489                                              PA_SUBSCRIPTION_MASK_SERVER|
2490                                              PA_SUBSCRIPTION_MASK_CARD,
2491                                              NULL,
2492                                              NULL);
2493                     break;
2494 
2495                 default:
2496                     pa_assert_not_reached();
2497             }
2498 
2499             if (o) {
2500                 pa_operation_unref(o);
2501                 actions++;
2502             }
2503 
2504             if (actions == 0) {
2505                 pa_log("Operation failed: %s", pa_strerror(pa_context_errno(c)));
2506                 quit(1);
2507             }
2508 
2509             break;
2510 
2511         case PA_CONTEXT_TERMINATED:
2512             quit(0);
2513             break;
2514 
2515         case PA_CONTEXT_FAILED:
2516         default:
2517             pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c)));
2518             quit(1);
2519     }
2520 }
2521 
exit_signal_callback(pa_mainloop_api * m,pa_signal_event * e,int sig,void * userdata)2522 static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
2523     pa_log(_("Got SIGINT, exiting."));
2524     quit(0);
2525 }
2526 
parse_volume(const char * vol_spec,pa_volume_t * vol,enum volume_flags * vol_flags)2527 static int parse_volume(const char *vol_spec, pa_volume_t *vol, enum volume_flags *vol_flags) {
2528     double v;
2529     char *vs;
2530     const char *atod_input;
2531 
2532     pa_assert(vol_spec);
2533     pa_assert(vol);
2534     pa_assert(vol_flags);
2535 
2536     vs = pa_xstrdup(vol_spec);
2537 
2538     *vol_flags = (pa_startswith(vs, "+") || pa_startswith(vs, "-")) ? VOL_RELATIVE : VOL_ABSOLUTE;
2539     if (pa_endswith(vs, "%")) {
2540         *vol_flags |= VOL_PERCENT;
2541         vs[strlen(vs)-1] = 0;
2542     }
2543     else if (pa_endswith(vs, "db") || pa_endswith(vs, "dB")) {
2544         *vol_flags |= VOL_DECIBEL;
2545         vs[strlen(vs)-2] = 0;
2546     }
2547     else if (strchr(vs, '.'))
2548         *vol_flags |= VOL_LINEAR;
2549 
2550     atod_input = vs;
2551 
2552     if (atod_input[0] == '+')
2553         atod_input++; /* pa_atod() doesn't accept leading '+', so skip it. */
2554 
2555     if (pa_atod(atod_input, &v) < 0) {
2556         pa_log(_("Invalid volume specification"));
2557         pa_xfree(vs);
2558         return -1;
2559     }
2560 
2561     pa_xfree(vs);
2562 
2563     if (*vol_flags & VOL_RELATIVE) {
2564 	switch (*vol_flags & 0x0F) {
2565 	    case VOL_UINT:
2566 		v += (double) PA_VOLUME_NORM;
2567 		break;
2568 	    case VOL_PERCENT:
2569 		v += 100.0;
2570 		break;
2571 	    case VOL_LINEAR:
2572 		v += 1.0;
2573 		break;
2574 	}
2575     }
2576 
2577     switch (*vol_flags & 0x0F) {
2578 	case VOL_PERCENT:
2579 	    v = v * (double) PA_VOLUME_NORM / 100;
2580 	    break;
2581 	case VOL_LINEAR:
2582 	    v = pa_sw_volume_from_linear(v);
2583 	    break;
2584 	case VOL_DECIBEL:
2585 	    v = pa_sw_volume_from_dB(v);
2586 	    break;
2587     }
2588 
2589     if (!PA_VOLUME_IS_VALID((pa_volume_t) v)) {
2590         pa_log(_("Volume outside permissible range.\n"));
2591         return -1;
2592     }
2593 
2594     *vol = (pa_volume_t) v;
2595 
2596     return 0;
2597 }
2598 
parse_volumes(char * args[],unsigned n)2599 static int parse_volumes(char *args[], unsigned n) {
2600     unsigned i;
2601 
2602     if (n >= PA_CHANNELS_MAX) {
2603         pa_log(_("Invalid number of volume specifications.\n"));
2604         return -1;
2605     }
2606 
2607     volume.channels = n;
2608     for (i = 0; i < volume.channels; i++) {
2609         enum volume_flags flags = 0;
2610 
2611         if (parse_volume(args[i], &volume.values[i], &flags) < 0)
2612             return -1;
2613 
2614         if (i > 0 && flags != volume_flags) {
2615             pa_log(_("Inconsistent volume specification.\n"));
2616             return -1;
2617         } else
2618             volume_flags = flags;
2619     }
2620 
2621     return 0;
2622 }
2623 
parse_mute(const char * mute_text)2624 static enum mute_flags parse_mute(const char *mute_text) {
2625     int b;
2626 
2627     pa_assert(mute_text);
2628 
2629     if (pa_streq("toggle", mute_text))
2630         return TOGGLE_MUTE;
2631 
2632     b = pa_parse_boolean(mute_text);
2633     switch (b) {
2634         case 0:
2635             return UNMUTE;
2636         case 1:
2637             return MUTE;
2638         default:
2639             return INVALID_MUTE;
2640     }
2641 }
2642 
help(const char * argv0)2643 static void help(const char *argv0) {
2644 
2645     printf("%s %s %s\n",    argv0, _("[options]"), "stat");
2646     printf("%s %s %s\n",    argv0, _("[options]"), "info");
2647     printf("%s %s %s %s\n", argv0, _("[options]"), "list [short]", _("[TYPE]"));
2648     printf("%s %s %s\n",    argv0, _("[options]"), "exit");
2649     printf("%s %s %s %s\n", argv0, _("[options]"), "upload-sample", _("FILENAME [NAME]"));
2650     printf("%s %s %s %s\n", argv0, _("[options]"), "play-sample ", _("NAME [SINK]"));
2651     printf("%s %s %s %s\n", argv0, _("[options]"), "remove-sample ", _("NAME"));
2652     printf("%s %s %s %s\n", argv0, _("[options]"), "load-module ", _("NAME [ARGS ...]"));
2653     printf("%s %s %s %s\n", argv0, _("[options]"), "unload-module ", _("NAME|#N"));
2654     printf("%s %s %s %s\n", argv0, _("[options]"), "move-(sink-input|source-output)", _("#N SINK|SOURCE"));
2655     printf("%s %s %s %s\n", argv0, _("[options]"), "suspend-(sink|source)", _("NAME|#N 1|0"));
2656     printf("%s %s %s %s\n", argv0, _("[options]"), "set-card-profile ", _("CARD PROFILE"));
2657     printf("%s %s %s\n", argv0, _("[options]"), "get-default-(sink|source)");
2658     printf("%s %s %s %s\n", argv0, _("[options]"), "set-default-(sink|source)", _("NAME"));
2659     printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-port", _("NAME|#N PORT"));
2660     printf("%s %s %s %s\n", argv0, _("[options]"), "get-(sink|source)-volume", _("NAME|#N"));
2661     printf("%s %s %s %s\n", argv0, _("[options]"), "get-(sink|source)-mute", _("NAME|#N"));
2662     printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-volume", _("NAME|#N VOLUME [VOLUME ...]"));
2663     printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink-input|source-output)-volume", _("#N VOLUME [VOLUME ...]"));
2664     printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-mute", _("NAME|#N 1|0|toggle"));
2665     printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink-input|source-output)-mute", _("#N 1|0|toggle"));
2666     printf("%s %s %s %s\n", argv0, _("[options]"), "set-sink-formats", _("#N FORMATS"));
2667     printf("%s %s %s %s\n", argv0, _("[options]"), "set-port-latency-offset", _("CARD-NAME|CARD-#N PORT OFFSET"));
2668     printf("%s %s %s %s\n", argv0, _("[options]"), "send-message", _("RECIPIENT MESSAGE [MESSAGE_PARAMETERS]"));
2669     printf("%s %s %s\n",    argv0, _("[options]"), "subscribe");
2670     printf(_("\nThe special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
2671              "can be used to specify the default sink, source and monitor.\n"));
2672 
2673     printf(_("\n"
2674              "  -h, --help                            Show this help\n"
2675              "      --version                         Show version\n\n"
2676              "  -f, --format=FORMAT                   The format of the output. Either \"normal\" or \"json\"\n"
2677              "  -s, --server=SERVER                   The name of the server to connect to\n"
2678              "  -n, --client-name=NAME                How to call this client on the server\n"));
2679 }
2680 
2681 enum {
2682     ARG_VERSION = 256
2683 };
2684 
main(int argc,char * argv[])2685 int main(int argc, char *argv[]) {
2686     pa_mainloop *m = NULL;
2687     int ret = 1, c;
2688     char *server = NULL, *opt_format = NULL, *bn;
2689 
2690     static const struct option long_options[] = {
2691         {"server",      1, NULL, 's'},
2692         {"client-name", 1, NULL, 'n'},
2693         {"format",      1, NULL, 'f'},
2694         {"version",     0, NULL, ARG_VERSION},
2695         {"help",        0, NULL, 'h'},
2696         {NULL,          0, NULL, 0}
2697     };
2698 
2699     setlocale(LC_ALL, "");
2700 #ifdef ENABLE_NLS
2701     bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
2702 #endif
2703 
2704     bn = pa_path_get_filename(argv[0]);
2705 
2706     proplist = pa_proplist_new();
2707 
2708     while ((c = getopt_long(argc, argv, "+s:n:f:h", long_options, NULL)) != -1) {
2709         switch (c) {
2710             case 'h' :
2711                 help(bn);
2712                 ret = 0;
2713                 goto quit;
2714 
2715             case ARG_VERSION:
2716                 printf(_("pactl %s\n"
2717                          "Compiled with libpulse %s\n"
2718                          "Linked with libpulse %s\n"),
2719                        PACKAGE_VERSION,
2720                        pa_get_headers_version(),
2721                        pa_get_library_version());
2722                 ret = 0;
2723                 goto quit;
2724 
2725             case 's':
2726                 pa_xfree(server);
2727                 server = pa_xstrdup(optarg);
2728                 break;
2729 
2730             case 'f':
2731                 opt_format = pa_xstrdup(optarg);
2732                 break;
2733 
2734             case 'n': {
2735                 char *t;
2736 
2737                 if (!(t = pa_locale_to_utf8(optarg)) ||
2738                     pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {
2739 
2740                     pa_log(_("Invalid client name '%s'"), t ? t : optarg);
2741                     pa_xfree(t);
2742                     goto quit;
2743                 }
2744 
2745                 pa_xfree(t);
2746                 break;
2747             }
2748 
2749             default:
2750                 goto quit;
2751         }
2752     }
2753 
2754     if (!opt_format || pa_streq(opt_format, "text")) {
2755         format = TEXT;
2756     } else if (pa_streq(opt_format, "json")) {
2757         format = JSON;
2758         setlocale(LC_NUMERIC, "C");
2759     } else {
2760         pa_log(_("Invalid format value '%s'"), opt_format);
2761         goto quit;
2762     }
2763 
2764     if (optind < argc) {
2765         if (pa_streq(argv[optind], "stat")) {
2766             action = STAT;
2767 
2768         } else if (pa_streq(argv[optind], "info"))
2769             action = INFO;
2770 
2771         else if (pa_streq(argv[optind], "exit"))
2772             action = EXIT;
2773 
2774         else if (pa_streq(argv[optind], "list")) {
2775             action = LIST;
2776 
2777             for (int i = optind+1; i < argc; i++) {
2778                 if (pa_streq(argv[i], "modules") || pa_streq(argv[i], "clients") ||
2779                     pa_streq(argv[i], "sinks")   || pa_streq(argv[i], "sink-inputs") ||
2780                     pa_streq(argv[i], "sources") || pa_streq(argv[i], "source-outputs") ||
2781                     pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards") ||
2782                     pa_streq(argv[i], "message-handlers")) {
2783                     list_type = pa_xstrdup(argv[i]);
2784                 } else if (pa_streq(argv[i], "short")) {
2785                     short_list_format = true;
2786                 } else {
2787                     pa_log(_("Specify nothing, or one of: %s"), "modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards, message-handlers");
2788                     goto quit;
2789                 }
2790             }
2791 
2792         } else if (pa_streq(argv[optind], "upload-sample")) {
2793 #ifdef SNDFILE_ENABLE
2794             struct SF_INFO sfi;
2795             action = UPLOAD_SAMPLE;
2796 
2797             if (optind+1 >= argc) {
2798                 pa_log(_("Please specify a sample file to load"));
2799                 goto quit;
2800             }
2801 
2802             if (optind+2 < argc)
2803                 sample_name = pa_xstrdup(argv[optind+2]);
2804             else {
2805                 char *f = pa_path_get_filename(argv[optind+1]);
2806                 sample_name = pa_xstrndup(f, strcspn(f, "."));
2807             }
2808 
2809             pa_zero(sfi);
2810             if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfi))) {
2811                 pa_log(_("Failed to open sound file."));
2812                 goto quit;
2813             }
2814 
2815             if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
2816                 pa_log(_("Failed to determine sample specification from file."));
2817                 goto quit;
2818             }
2819             sample_spec.format = PA_SAMPLE_FLOAT32;
2820 
2821             if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
2822                 if (sample_spec.channels > 2)
2823                     pa_log(_("Warning: Failed to determine sample specification from file."));
2824                 pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
2825             }
2826 
2827             pa_assert(pa_channel_map_compatible(&channel_map, &sample_spec));
2828             sample_length = (size_t) sfi.frames*pa_frame_size(&sample_spec);
2829 #endif
2830         } else if (pa_streq(argv[optind], "play-sample")) {
2831             action = PLAY_SAMPLE;
2832             if (argc != optind+2 && argc != optind+3) {
2833                 pa_log(_("You have to specify a sample name to play"));
2834                 goto quit;
2835             }
2836 
2837             sample_name = pa_xstrdup(argv[optind+1]);
2838 
2839             if (optind+2 < argc)
2840                 sink_name = pa_xstrdup(argv[optind+2]);
2841 
2842         } else if (pa_streq(argv[optind], "remove-sample")) {
2843             action = REMOVE_SAMPLE;
2844             if (argc != optind+2) {
2845                 pa_log(_("You have to specify a sample name to remove"));
2846                 goto quit;
2847             }
2848 
2849             sample_name = pa_xstrdup(argv[optind+1]);
2850 
2851         } else if (pa_streq(argv[optind], "move-sink-input")) {
2852             action = MOVE_SINK_INPUT;
2853             if (argc != optind+3) {
2854                 pa_log(_("You have to specify a sink input index and a sink"));
2855                 goto quit;
2856             }
2857 
2858             sink_input_idx = (uint32_t) atoi(argv[optind+1]);
2859             sink_name = pa_xstrdup(argv[optind+2]);
2860 
2861         } else if (pa_streq(argv[optind], "move-source-output")) {
2862             action = MOVE_SOURCE_OUTPUT;
2863             if (argc != optind+3) {
2864                 pa_log(_("You have to specify a source output index and a source"));
2865                 goto quit;
2866             }
2867 
2868             source_output_idx = (uint32_t) atoi(argv[optind+1]);
2869             source_name = pa_xstrdup(argv[optind+2]);
2870 
2871         } else if (pa_streq(argv[optind], "load-module")) {
2872             int i;
2873             size_t n = 0;
2874             char *p;
2875 
2876             action = LOAD_MODULE;
2877 
2878             if (argc <= optind+1) {
2879                 pa_log(_("You have to specify a module name and arguments."));
2880                 goto quit;
2881             }
2882 
2883             module_name = argv[optind+1];
2884 
2885             for (i = optind+2; i < argc; i++)
2886                 n += strlen(argv[i])+1;
2887 
2888             if (n > 0) {
2889                 p = module_args = pa_xmalloc(n);
2890 
2891                 for (i = optind+2; i < argc; i++)
2892                     p += sprintf(p, "%s%s", p == module_args ? "" : " ", argv[i]);
2893             }
2894 
2895         } else if (pa_streq(argv[optind], "unload-module")) {
2896             action = UNLOAD_MODULE;
2897 
2898             if (argc != optind+2) {
2899                 pa_log(_("You have to specify a module index or name"));
2900                 goto quit;
2901             }
2902 
2903             if (pa_atou(argv[optind + 1], &module_index) < 0)
2904                 module_name = argv[optind + 1];
2905 
2906         } else if (pa_streq(argv[optind], "suspend-sink")) {
2907             int b;
2908 
2909             action = SUSPEND_SINK;
2910 
2911             if (argc > optind+3 || optind+1 >= argc) {
2912                 pa_log(_("You may not specify more than one sink. You have to specify a boolean value."));
2913                 goto quit;
2914             }
2915 
2916             if ((b = pa_parse_boolean(argv[argc-1])) < 0) {
2917                 pa_log(_("Invalid suspend specification."));
2918                 goto quit;
2919             }
2920 
2921             suspend = !!b;
2922 
2923             if (argc > optind+2)
2924                 sink_name = pa_xstrdup(argv[optind+1]);
2925 
2926         } else if (pa_streq(argv[optind], "suspend-source")) {
2927             int b;
2928 
2929             action = SUSPEND_SOURCE;
2930 
2931             if (argc > optind+3 || optind+1 >= argc) {
2932                 pa_log(_("You may not specify more than one source. You have to specify a boolean value."));
2933                 goto quit;
2934             }
2935 
2936             if ((b = pa_parse_boolean(argv[argc-1])) < 0) {
2937                 pa_log(_("Invalid suspend specification."));
2938                 goto quit;
2939             }
2940 
2941             suspend = !!b;
2942 
2943             if (argc > optind+2)
2944                 source_name = pa_xstrdup(argv[optind+1]);
2945         } else if (pa_streq(argv[optind], "set-card-profile")) {
2946             action = SET_CARD_PROFILE;
2947 
2948             if (argc != optind+3) {
2949                 pa_log(_("You have to specify a card name/index and a profile name"));
2950                 goto quit;
2951             }
2952 
2953             card_name = pa_xstrdup(argv[optind+1]);
2954             profile_name = pa_xstrdup(argv[optind+2]);
2955 
2956         } else if (pa_streq(argv[optind], "set-sink-port")) {
2957             action = SET_SINK_PORT;
2958 
2959             if (argc != optind+3) {
2960                 pa_log(_("You have to specify a sink name/index and a port name"));
2961                 goto quit;
2962             }
2963 
2964             sink_name = pa_xstrdup(argv[optind+1]);
2965             port_name = pa_xstrdup(argv[optind+2]);
2966 
2967         } else if (pa_streq(argv[optind], "set-default-sink")) {
2968             action = SET_DEFAULT_SINK;
2969 
2970             if (argc != optind+2) {
2971                 pa_log(_("You have to specify a sink name"));
2972                 goto quit;
2973             }
2974 
2975             sink_name = pa_xstrdup(argv[optind+1]);
2976 
2977         } else if (pa_streq(argv[optind], "get-default-sink")) {
2978             action = GET_DEFAULT_SINK;
2979 
2980         } else if (pa_streq(argv[optind], "set-source-port")) {
2981             action = SET_SOURCE_PORT;
2982 
2983             if (argc != optind+3) {
2984                 pa_log(_("You have to specify a source name/index and a port name"));
2985                 goto quit;
2986             }
2987 
2988             source_name = pa_xstrdup(argv[optind+1]);
2989             port_name = pa_xstrdup(argv[optind+2]);
2990 
2991         } else if (pa_streq(argv[optind], "set-default-source")) {
2992             action = SET_DEFAULT_SOURCE;
2993 
2994             if (argc != optind+2) {
2995                 pa_log(_("You have to specify a source name"));
2996                 goto quit;
2997             }
2998 
2999             source_name = pa_xstrdup(argv[optind+1]);
3000 
3001         } else if (pa_streq(argv[optind], "get-default-source")) {
3002             action = GET_DEFAULT_SOURCE;
3003 
3004         } else if (pa_streq(argv[optind], "get-sink-volume")) {
3005             action = GET_SINK_VOLUME;
3006 
3007             if (argc < optind+2) {
3008                 pa_log(_("You have to specify a sink name/index"));
3009                 goto quit;
3010             }
3011 
3012             sink_name = pa_xstrdup(argv[optind+1]);
3013 
3014         } else if (pa_streq(argv[optind], "set-sink-volume")) {
3015             action = SET_SINK_VOLUME;
3016 
3017             if (argc < optind+3) {
3018                 pa_log(_("You have to specify a sink name/index and a volume"));
3019                 goto quit;
3020             }
3021 
3022             sink_name = pa_xstrdup(argv[optind+1]);
3023 
3024             if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
3025                 goto quit;
3026 
3027         } else if (pa_streq(argv[optind], "get-source-volume")) {
3028             action = GET_SOURCE_VOLUME;
3029 
3030             if (argc < optind+2) {
3031                 pa_log(_("You have to specify a source name/index"));
3032                 goto quit;
3033             }
3034 
3035             source_name = pa_xstrdup(argv[optind+1]);
3036 
3037         } else if (pa_streq(argv[optind], "set-source-volume")) {
3038             action = SET_SOURCE_VOLUME;
3039 
3040             if (argc < optind+3) {
3041                 pa_log(_("You have to specify a source name/index and a volume"));
3042                 goto quit;
3043             }
3044 
3045             source_name = pa_xstrdup(argv[optind+1]);
3046 
3047             if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
3048                 goto quit;
3049 
3050         } else if (pa_streq(argv[optind], "set-sink-input-volume")) {
3051             action = SET_SINK_INPUT_VOLUME;
3052 
3053             if (argc < optind+3) {
3054                 pa_log(_("You have to specify a sink input index and a volume"));
3055                 goto quit;
3056             }
3057 
3058             if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
3059                 pa_log(_("Invalid sink input index"));
3060                 goto quit;
3061             }
3062 
3063             if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
3064                 goto quit;
3065 
3066         } else if (pa_streq(argv[optind], "set-source-output-volume")) {
3067             action = SET_SOURCE_OUTPUT_VOLUME;
3068 
3069             if (argc < optind+3) {
3070                 pa_log(_("You have to specify a source output index and a volume"));
3071                 goto quit;
3072             }
3073 
3074             if (pa_atou(argv[optind+1], &source_output_idx) < 0) {
3075                 pa_log(_("Invalid source output index"));
3076                 goto quit;
3077             }
3078 
3079             if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
3080                 goto quit;
3081 
3082         } else if (pa_streq(argv[optind], "get-sink-mute")) {
3083             action = GET_SINK_MUTE;
3084 
3085             if (argc < optind+2) {
3086                 pa_log(_("You have to specify a sink name/index"));
3087                 goto quit;
3088             }
3089 
3090             sink_name = pa_xstrdup(argv[optind+1]);
3091 
3092         } else if (pa_streq(argv[optind], "set-sink-mute")) {
3093             action = SET_SINK_MUTE;
3094 
3095             if (argc != optind+3) {
3096                 pa_log(_("You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"));
3097                 goto quit;
3098             }
3099 
3100             if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
3101                 pa_log(_("Invalid mute specification"));
3102                 goto quit;
3103             }
3104 
3105             sink_name = pa_xstrdup(argv[optind+1]);
3106 
3107         } else if (pa_streq(argv[optind], "get-source-mute")) {
3108             action = GET_SOURCE_MUTE;
3109 
3110             if (argc < optind+2) {
3111                 pa_log(_("You have to specify a source name/index"));
3112                 goto quit;
3113             }
3114 
3115             source_name = pa_xstrdup(argv[optind+1]);
3116 
3117         } else if (pa_streq(argv[optind], "set-source-mute")) {
3118             action = SET_SOURCE_MUTE;
3119 
3120             if (argc != optind+3) {
3121                 pa_log(_("You have to specify a source name/index and a mute action (0, 1, or 'toggle')"));
3122                 goto quit;
3123             }
3124 
3125             if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
3126                 pa_log(_("Invalid mute specification"));
3127                 goto quit;
3128             }
3129 
3130             source_name = pa_xstrdup(argv[optind+1]);
3131 
3132         } else if (pa_streq(argv[optind], "set-sink-input-mute")) {
3133             action = SET_SINK_INPUT_MUTE;
3134 
3135             if (argc != optind+3) {
3136                 pa_log(_("You have to specify a sink input index and a mute action (0, 1, or 'toggle')"));
3137                 goto quit;
3138             }
3139 
3140             if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
3141                 pa_log(_("Invalid sink input index specification"));
3142                 goto quit;
3143             }
3144 
3145             if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
3146                 pa_log(_("Invalid mute specification"));
3147                 goto quit;
3148             }
3149 
3150         } else if (pa_streq(argv[optind], "set-source-output-mute")) {
3151             action = SET_SOURCE_OUTPUT_MUTE;
3152 
3153             if (argc != optind+3) {
3154                 pa_log(_("You have to specify a source output index and a mute action (0, 1, or 'toggle')"));
3155                 goto quit;
3156             }
3157 
3158             if (pa_atou(argv[optind+1], &source_output_idx) < 0) {
3159                 pa_log(_("Invalid source output index specification"));
3160                 goto quit;
3161             }
3162 
3163             if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
3164                 pa_log(_("Invalid mute specification"));
3165                 goto quit;
3166             }
3167 
3168         } else if (pa_streq(argv[optind], "send-message")) {
3169             action = SEND_MESSAGE;
3170 
3171             if (argc < optind+3) {
3172                 pa_log(_("You have to specify at least an object path and a message name"));
3173                 goto quit;
3174             }
3175 
3176             object_path = pa_xstrdup(argv[optind + 1]);
3177             message = pa_xstrdup(argv[optind + 2]);
3178             if (argc >= optind+4)
3179                 message_args = pa_xstrdup(argv[optind + 3]);
3180 
3181             if (argc > optind+4)
3182                 pa_log(_("Excess arguments given, they will be ignored. Note that all message parameters must be given as a single string."));
3183 
3184         } else if (pa_streq(argv[optind], "subscribe"))
3185 
3186             action = SUBSCRIBE;
3187 
3188         else if (pa_streq(argv[optind], "set-sink-formats")) {
3189             int32_t tmp;
3190 
3191             if (argc != optind+3 || pa_atoi(argv[optind+1], &tmp) < 0) {
3192                 pa_log(_("You have to specify a sink index and a semicolon-separated list of supported formats"));
3193                 goto quit;
3194             }
3195 
3196             sink_idx = tmp;
3197             action = SET_SINK_FORMATS;
3198             formats = pa_xstrdup(argv[optind+2]);
3199 
3200         } else if (pa_streq(argv[optind], "set-port-latency-offset")) {
3201             action = SET_PORT_LATENCY_OFFSET;
3202 
3203             if (argc != optind+4) {
3204                 pa_log(_("You have to specify a card name/index, a port name and a latency offset"));
3205                 goto quit;
3206             }
3207 
3208             card_name = pa_xstrdup(argv[optind+1]);
3209             port_name = pa_xstrdup(argv[optind+2]);
3210             if (pa_atoi(argv[optind + 3], &latency_offset) < 0) {
3211                 pa_log(_("Could not parse latency offset"));
3212                 goto quit;
3213             }
3214 
3215         } else if (pa_streq(argv[optind], "help")) {
3216             help(bn);
3217             ret = 0;
3218             goto quit;
3219         }
3220     }
3221 
3222     if (action == NONE) {
3223         pa_log(_("No valid command specified."));
3224         goto quit;
3225     }
3226 
3227     if (!(m = pa_mainloop_new())) {
3228         pa_log(_("pa_mainloop_new() failed."));
3229         goto quit;
3230     }
3231 
3232     mainloop_api = pa_mainloop_get_api(m);
3233 
3234     pa_assert_se(pa_signal_init(mainloop_api) == 0);
3235     pa_signal_new(SIGINT, exit_signal_callback, NULL);
3236     pa_signal_new(SIGTERM, exit_signal_callback, NULL);
3237     pa_disable_sigpipe();
3238 
3239     if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
3240         pa_log(_("pa_context_new() failed."));
3241         goto quit;
3242     }
3243 
3244     pa_context_set_state_callback(context, context_state_callback, NULL);
3245     if (pa_context_connect(context, server, 0, NULL) < 0) {
3246         pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
3247         goto quit;
3248     }
3249 
3250     if (pa_mainloop_run(m, &ret) < 0) {
3251         pa_log(_("pa_mainloop_run() failed."));
3252         goto quit;
3253     }
3254 
3255     if (format == JSON && list_encoder && !pa_json_encoder_is_empty(list_encoder)) {
3256         pa_json_encoder_end_object(list_encoder);
3257         char* list_json_str = pa_json_encoder_to_string_free(list_encoder);
3258         printf("%s", list_json_str);
3259         pa_xfree(list_json_str);
3260     }
3261 
3262 quit:
3263 #ifdef SNDFILE_ENABLE
3264     if (sample_stream)
3265         pa_stream_unref(sample_stream);
3266 #endif
3267     if (context)
3268         pa_context_unref(context);
3269 
3270     if (m) {
3271         pa_signal_done();
3272         pa_mainloop_free(m);
3273     }
3274 
3275     pa_xfree(server);
3276     pa_xfree(list_type);
3277     pa_xfree(sample_name);
3278     pa_xfree(sink_name);
3279     pa_xfree(source_name);
3280     pa_xfree(module_args);
3281     pa_xfree(card_name);
3282     pa_xfree(profile_name);
3283     pa_xfree(port_name);
3284     pa_xfree(formats);
3285     pa_xfree(object_path);
3286     pa_xfree(message);
3287     pa_xfree(message_args);
3288 #ifdef SNDFILE_ENABLE
3289     if (sndfile)
3290         sf_close(sndfile);
3291 #endif
3292     if (proplist)
3293         pa_proplist_free(proplist);
3294 
3295     return ret;
3296 }
3297