• 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     pa_operation *o;
1881 
1882     if (is_last < 0) {
1883         pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
1884         quit(1);
1885         return;
1886     }
1887 
1888     if (is_last)
1889         return;
1890 
1891     pa_assert(i);
1892 
1893     cv = i->volume;
1894     fill_volume(&cv, i->channel_map.channels);
1895 
1896     o = pa_context_set_sink_volume_by_name(c, sink_name, &cv, simple_callback, NULL);
1897     if (o)
1898         pa_operation_unref(o);
1899     else {
1900         pa_log(_("Failed to set sink volume: %s"), pa_strerror(pa_context_errno(c)));
1901         complete_action();
1902     }
1903 }
1904 
get_source_mute_callback(pa_context * c,const pa_source_info * i,int is_last,void * userdata)1905 static void get_source_mute_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
1906     if (is_last < 0) {
1907         pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
1908         quit(1);
1909         return;
1910     }
1911 
1912     if (is_last)
1913         return;
1914 
1915     pa_assert(i);
1916 
1917     printf(("Mute: %s\n"),
1918            pa_yes_no_localised(i->mute));
1919 
1920     complete_action();
1921 }
1922 
get_source_volume_callback(pa_context * c,const pa_source_info * i,int is_last,void * userdata)1923 static void get_source_volume_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
1924     if (is_last < 0) {
1925         pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
1926         quit(1);
1927         return;
1928     }
1929 
1930     if (is_last)
1931         return;
1932 
1933     pa_assert(i);
1934 
1935     char cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
1936     printf(("Volume: %s\n"
1937             "        balance %0.2f\n"),
1938            pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, true),
1939            pa_cvolume_get_balance(&i->volume, &i->channel_map));
1940 
1941     complete_action();
1942 }
1943 
set_source_volume_callback(pa_context * c,const pa_source_info * i,int is_last,void * userdata)1944 static void set_source_volume_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
1945     pa_cvolume cv;
1946     pa_operation *o;
1947 
1948     if (is_last < 0) {
1949         pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
1950         quit(1);
1951         return;
1952     }
1953 
1954     if (is_last)
1955         return;
1956 
1957     pa_assert(i);
1958 
1959     cv = i->volume;
1960     fill_volume(&cv, i->channel_map.channels);
1961 
1962     o = pa_context_set_source_volume_by_name(c, source_name, &cv, simple_callback, NULL);
1963     if (o)
1964         pa_operation_unref(o);
1965     else {
1966         pa_log(_("Failed to set source volume: %s"), pa_strerror(pa_context_errno(c)));
1967         complete_action();
1968     }
1969 }
1970 
get_sink_input_volume_callback(pa_context * c,const pa_sink_input_info * i,int is_last,void * userdata)1971 static void get_sink_input_volume_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
1972     pa_cvolume cv;
1973 
1974     if (is_last < 0) {
1975         pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
1976         quit(1);
1977         return;
1978     }
1979 
1980     if (is_last)
1981         return;
1982 
1983     pa_assert(i);
1984 
1985     cv = i->volume;
1986     fill_volume(&cv, i->channel_map.channels);
1987 
1988     pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &cv, simple_callback, NULL));
1989 }
1990 
get_source_output_volume_callback(pa_context * c,const pa_source_output_info * o,int is_last,void * userdata)1991 static void get_source_output_volume_callback(pa_context *c, const pa_source_output_info *o, int is_last, void *userdata) {
1992     pa_cvolume cv;
1993 
1994     if (is_last < 0) {
1995         pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
1996         quit(1);
1997         return;
1998     }
1999 
2000     if (is_last)
2001         return;
2002 
2003     pa_assert(o);
2004 
2005     cv = o->volume;
2006     fill_volume(&cv, o->channel_map.channels);
2007 
2008     pa_operation_unref(pa_context_set_source_output_volume(c, source_output_idx, &cv, simple_callback, NULL));
2009 }
2010 
sink_toggle_mute_callback(pa_context * c,const pa_sink_info * i,int is_last,void * userdata)2011 static void sink_toggle_mute_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
2012     if (is_last < 0) {
2013         pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
2014         quit(1);
2015         return;
2016     }
2017 
2018     if (is_last)
2019         return;
2020 
2021     pa_assert(i);
2022 
2023     pa_operation_unref(pa_context_set_sink_mute_by_name(c, i->name, !i->mute, simple_callback, NULL));
2024 }
2025 
source_toggle_mute_callback(pa_context * c,const pa_source_info * o,int is_last,void * userdata)2026 static void source_toggle_mute_callback(pa_context *c, const pa_source_info *o, int is_last, void *userdata) {
2027     if (is_last < 0) {
2028         pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
2029         quit(1);
2030         return;
2031     }
2032 
2033     if (is_last)
2034         return;
2035 
2036     pa_assert(o);
2037 
2038     pa_operation_unref(pa_context_set_source_mute_by_name(c, o->name, !o->mute, simple_callback, NULL));
2039 }
2040 
sink_input_toggle_mute_callback(pa_context * c,const pa_sink_input_info * i,int is_last,void * userdata)2041 static void sink_input_toggle_mute_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
2042     if (is_last < 0) {
2043         pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
2044         quit(1);
2045         return;
2046     }
2047 
2048     if (is_last)
2049         return;
2050 
2051     pa_assert(i);
2052 
2053     pa_operation_unref(pa_context_set_sink_input_mute(c, i->index, !i->mute, simple_callback, NULL));
2054 }
2055 
source_output_toggle_mute_callback(pa_context * c,const pa_source_output_info * o,int is_last,void * userdata)2056 static void source_output_toggle_mute_callback(pa_context *c, const pa_source_output_info *o, int is_last, void *userdata) {
2057     if (is_last < 0) {
2058         pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
2059         quit(1);
2060         return;
2061     }
2062 
2063     if (is_last)
2064         return;
2065 
2066     pa_assert(o);
2067 
2068     pa_operation_unref(pa_context_set_source_output_mute(c, o->index, !o->mute, simple_callback, NULL));
2069 }
2070 
2071 /* PA_MAX_FORMATS is defined in internal.h so we just define a sane value here */
2072 #define MAX_FORMATS 256
2073 
set_sink_formats(pa_context * c,uint32_t sink,const char * str)2074 static void set_sink_formats(pa_context *c, uint32_t sink, const char *str) {
2075     pa_format_info *f_arr[MAX_FORMATS] = { 0, };
2076     char *format = NULL;
2077     const char *state = NULL;
2078     int i = 0;
2079     pa_operation *o = NULL;
2080 
2081     while ((format = pa_split(str, ";", &state))) {
2082         pa_format_info *f = pa_format_info_from_string(pa_strip(format));
2083 
2084         if (!f) {
2085             pa_log(_("Failed to set format: invalid format string %s"), format);
2086             goto error;
2087         }
2088 
2089         f_arr[i++] = f;
2090         pa_xfree(format);
2091     }
2092 
2093     o = pa_ext_device_restore_save_formats(c, PA_DEVICE_TYPE_SINK, sink, i, f_arr, simple_callback, NULL);
2094     if (o) {
2095         pa_operation_unref(o);
2096         actions++;
2097     }
2098 
2099 done:
2100     if (format)
2101         pa_xfree(format);
2102     while (f_arr[i] && i--)
2103         pa_format_info_free(f_arr[i]);
2104 
2105     return;
2106 
2107 error:
2108     while (f_arr[i] && i--)
2109         pa_format_info_free(f_arr[i]);
2110     quit(1);
2111     goto done;
2112 }
2113 
2114 #ifdef SNDFILE_ENABLE
stream_state_callback(pa_stream * s,void * userdata)2115 static void stream_state_callback(pa_stream *s, void *userdata) {
2116     pa_assert(s);
2117 
2118     switch (pa_stream_get_state(s)) {
2119         case PA_STREAM_CREATING:
2120         case PA_STREAM_READY:
2121             break;
2122 
2123         case PA_STREAM_TERMINATED:
2124             drain();
2125             break;
2126 
2127         case PA_STREAM_FAILED:
2128         default:
2129             pa_log(_("Failed to upload sample: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
2130             quit(1);
2131     }
2132 }
2133 
stream_write_callback(pa_stream * s,size_t length,void * userdata)2134 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
2135     sf_count_t l;
2136     float *d;
2137     pa_assert(s && length && sndfile);
2138 
2139     d = pa_xmalloc(length);
2140 
2141     pa_assert(sample_length >= length);
2142     l = (sf_count_t) (length/pa_frame_size(&sample_spec));
2143 
2144     if ((sf_readf_float(sndfile, d, l)) != l) {
2145         pa_xfree(d);
2146         pa_log(_("Premature end of file"));
2147         quit(1);
2148         return;
2149     }
2150 
2151     pa_stream_write(s, d, length, pa_xfree, 0, PA_SEEK_RELATIVE);
2152 
2153     sample_length -= length;
2154 
2155     if (sample_length <= 0) {
2156         pa_stream_set_write_callback(sample_stream, NULL, NULL);
2157         pa_stream_finish_upload(sample_stream);
2158     }
2159 }
2160 #endif
2161 
subscription_event_type_to_string(pa_subscription_event_type_t t)2162 static const char *subscription_event_type_to_string(pa_subscription_event_type_t t) {
2163 
2164     switch (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
2165 
2166     case PA_SUBSCRIPTION_EVENT_NEW:
2167         return _("new");
2168 
2169     case PA_SUBSCRIPTION_EVENT_CHANGE:
2170         return _("change");
2171 
2172     case PA_SUBSCRIPTION_EVENT_REMOVE:
2173         return _("remove");
2174     }
2175 
2176     return _("unknown");
2177 }
2178 
subscription_event_facility_to_string(pa_subscription_event_type_t t)2179 static const char *subscription_event_facility_to_string(pa_subscription_event_type_t t) {
2180 
2181     switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
2182 
2183     case PA_SUBSCRIPTION_EVENT_SINK:
2184         return _("sink");
2185 
2186     case PA_SUBSCRIPTION_EVENT_SOURCE:
2187         return _("source");
2188 
2189     case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
2190         return _("sink-input");
2191 
2192     case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
2193         return _("source-output");
2194 
2195     case PA_SUBSCRIPTION_EVENT_MODULE:
2196         return _("module");
2197 
2198     case PA_SUBSCRIPTION_EVENT_CLIENT:
2199         return _("client");
2200 
2201     case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE:
2202         return _("sample-cache");
2203 
2204     case PA_SUBSCRIPTION_EVENT_SERVER:
2205         return _("server");
2206 
2207     case PA_SUBSCRIPTION_EVENT_CARD:
2208         return _("card");
2209     }
2210 
2211     return _("unknown");
2212 }
2213 
context_subscribe_callback(pa_context * c,pa_subscription_event_type_t t,uint32_t idx,void * userdata)2214 static void context_subscribe_callback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
2215     pa_assert(c);
2216 
2217     if (format == JSON) {
2218         pa_json_encoder *encoder = pa_json_encoder_new();
2219         pa_json_encoder_begin_element_object(encoder);
2220         pa_json_encoder_add_member_int(encoder, "index", idx);
2221         pa_json_encoder_add_member_string(encoder, "event", subscription_event_type_to_string(t));
2222         pa_json_encoder_add_member_string(encoder, "on", subscription_event_facility_to_string(t));
2223         pa_json_encoder_end_object(encoder);
2224 
2225         char* json_str = pa_json_encoder_to_string_free(encoder);
2226         printf("%s\n", json_str);
2227         pa_xfree(json_str);
2228     } else {
2229         printf(_("Event '%s' on %s #%u\n"),
2230            subscription_event_type_to_string(t),
2231            subscription_event_facility_to_string(t),
2232            idx);
2233     }
2234     fflush(stdout);
2235 }
2236 
context_state_callback(pa_context * c,void * userdata)2237 static void context_state_callback(pa_context *c, void *userdata) {
2238     pa_operation *o = NULL;
2239 
2240     pa_assert(c);
2241 
2242     switch (pa_context_get_state(c)) {
2243         case PA_CONTEXT_CONNECTING:
2244         case PA_CONTEXT_AUTHORIZING:
2245         case PA_CONTEXT_SETTING_NAME:
2246             break;
2247 
2248         case PA_CONTEXT_READY:
2249             switch (action) {
2250                 case STAT:
2251                     o = pa_context_stat(c, stat_callback, NULL);
2252                     break;
2253 
2254                 case INFO:
2255                     o = pa_context_get_server_info(c, get_server_info_callback, NULL);
2256                     break;
2257 
2258                 case PLAY_SAMPLE:
2259                     o = pa_context_play_sample(c, sample_name, sink_name, PA_VOLUME_NORM, simple_callback, NULL);
2260                     break;
2261 
2262                 case REMOVE_SAMPLE:
2263                     o = pa_context_remove_sample(c, sample_name, simple_callback, NULL);
2264                     break;
2265 #ifdef SNDFILE_ENABLE
2266                 case UPLOAD_SAMPLE:
2267                     sample_stream = pa_stream_new(c, sample_name, &sample_spec, NULL);
2268                     pa_assert(sample_stream);
2269 
2270                     pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL);
2271                     pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL);
2272                     pa_stream_connect_upload(sample_stream, sample_length);
2273                     actions++;
2274                     break;
2275 #endif
2276                 case EXIT:
2277                     o = pa_context_exit_daemon(c, simple_callback, NULL);
2278                     break;
2279 
2280                 case LIST:
2281                     if (list_type) {
2282                         if (pa_streq(list_type, "modules"))
2283                             o = pa_context_get_module_info_list(c, get_module_info_callback, NULL);
2284                         else if (pa_streq(list_type, "sinks"))
2285                             o = pa_context_get_sink_info_list(c, get_sink_info_callback, NULL);
2286                         else if (pa_streq(list_type, "sources"))
2287                             o = pa_context_get_source_info_list(c, get_source_info_callback, NULL);
2288                         else if (pa_streq(list_type, "sink-inputs"))
2289                             o = pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL);
2290                         else if (pa_streq(list_type, "source-outputs"))
2291                             o = pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL);
2292                         else if (pa_streq(list_type, "clients"))
2293                             o = pa_context_get_client_info_list(c, get_client_info_callback, NULL);
2294                         else if (pa_streq(list_type, "samples"))
2295                             o = pa_context_get_sample_info_list(c, get_sample_info_callback, NULL);
2296                         else if (pa_streq(list_type, "cards"))
2297                             o = pa_context_get_card_info_list(c, get_card_info_callback, NULL);
2298                         else if (pa_streq(list_type, "message-handlers"))
2299                             o = pa_context_send_message_to_object(c, "/core", "list-handlers", NULL, list_handlers_callback, NULL);
2300                         else
2301                             pa_assert_not_reached();
2302                     } else {
2303                         if (format == JSON) {
2304                             list_encoder = pa_json_encoder_new();
2305                             pa_json_encoder_begin_element_object(list_encoder);
2306                         }
2307 
2308                         o = pa_context_get_module_info_list(c, get_module_info_callback, NULL);
2309                         if (o) {
2310                             pa_operation_unref(o);
2311                             actions++;
2312                         }
2313 
2314                         o = pa_context_get_sink_info_list(c, get_sink_info_callback, NULL);
2315                         if (o) {
2316                             pa_operation_unref(o);
2317                             actions++;
2318                         }
2319 
2320                         o = pa_context_get_source_info_list(c, get_source_info_callback, NULL);
2321                         if (o) {
2322                             pa_operation_unref(o);
2323                             actions++;
2324                         }
2325                         o = pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL);
2326                         if (o) {
2327                             pa_operation_unref(o);
2328                             actions++;
2329                         }
2330 
2331                         o = pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL);
2332                         if (o) {
2333                             pa_operation_unref(o);
2334                             actions++;
2335                         }
2336 
2337                         o = pa_context_get_client_info_list(c, get_client_info_callback, NULL);
2338                         if (o) {
2339                             pa_operation_unref(o);
2340                             actions++;
2341                         }
2342 
2343                         o = pa_context_get_sample_info_list(c, get_sample_info_callback, NULL);
2344                         if (o) {
2345                             pa_operation_unref(o);
2346                             actions++;
2347                         }
2348 
2349                         o = pa_context_get_card_info_list(c, get_card_info_callback, NULL);
2350                         if (o) {
2351                             pa_operation_unref(o);
2352                             actions++;
2353                         }
2354 
2355                         o = NULL;
2356                     }
2357                     break;
2358 
2359                 case MOVE_SINK_INPUT:
2360                     o = pa_context_move_sink_input_by_name(c, sink_input_idx, sink_name, simple_callback, NULL);
2361                     break;
2362 
2363                 case MOVE_SOURCE_OUTPUT:
2364                     o = pa_context_move_source_output_by_name(c, source_output_idx, source_name, simple_callback, NULL);
2365                     break;
2366 
2367                 case LOAD_MODULE:
2368                     o = pa_context_load_module(c, module_name, module_args, index_callback, NULL);
2369                     break;
2370 
2371                 case UNLOAD_MODULE:
2372                     if (module_name)
2373                         o = pa_context_get_module_info_list(c, unload_module_by_name_callback, NULL);
2374                     else
2375                         o = pa_context_unload_module(c, module_index, simple_callback, NULL);
2376                     break;
2377 
2378                 case SUSPEND_SINK:
2379                     if (sink_name)
2380                         o = pa_context_suspend_sink_by_name(c, sink_name, suspend, simple_callback, NULL);
2381                     else
2382                         o = pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL);
2383                     break;
2384 
2385                 case SUSPEND_SOURCE:
2386                     if (source_name)
2387                         o = pa_context_suspend_source_by_name(c, source_name, suspend, simple_callback, NULL);
2388                     else
2389                         o = pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL);
2390                     break;
2391 
2392                 case SET_CARD_PROFILE:
2393                     o = pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL);
2394                     break;
2395 
2396                 case SET_SINK_PORT:
2397                     o = pa_context_set_sink_port_by_name(c, sink_name, port_name, simple_callback, NULL);
2398                     break;
2399 
2400                 case GET_DEFAULT_SINK:
2401                     o = pa_context_get_server_info(c, get_default_sink, NULL);
2402                     break;
2403 
2404                 case SET_DEFAULT_SINK:
2405                     o = pa_context_set_default_sink(c, sink_name, simple_callback, NULL);
2406                     break;
2407 
2408                 case SET_SOURCE_PORT:
2409                     o = pa_context_set_source_port_by_name(c, source_name, port_name, simple_callback, NULL);
2410                     break;
2411 
2412                 case GET_DEFAULT_SOURCE:
2413                     o = pa_context_get_server_info(c, get_default_source, NULL);
2414                     break;
2415 
2416                 case SET_DEFAULT_SOURCE:
2417                     o = pa_context_set_default_source(c, source_name, simple_callback, NULL);
2418                     break;
2419 
2420                 case GET_SINK_MUTE:
2421                     o = pa_context_get_sink_info_by_name(c, sink_name, get_sink_mute_callback, NULL);
2422                     break;
2423 
2424                 case SET_SINK_MUTE:
2425                     if (mute == TOGGLE_MUTE)
2426                         o = pa_context_get_sink_info_by_name(c, sink_name, sink_toggle_mute_callback, NULL);
2427                     else
2428                         o = pa_context_set_sink_mute_by_name(c, sink_name, mute, simple_callback, NULL);
2429                     break;
2430 
2431                 case GET_SOURCE_MUTE:
2432                     o = pa_context_get_source_info_by_name(c, source_name, get_source_mute_callback, NULL);
2433                     break;
2434 
2435                 case SET_SOURCE_MUTE:
2436                     if (mute == TOGGLE_MUTE)
2437                         o = pa_context_get_source_info_by_name(c, source_name, source_toggle_mute_callback, NULL);
2438                     else
2439                         o = pa_context_set_source_mute_by_name(c, source_name, mute, simple_callback, NULL);
2440                     break;
2441 
2442                 case SET_SINK_INPUT_MUTE:
2443                     if (mute == TOGGLE_MUTE)
2444                         o = pa_context_get_sink_input_info(c, sink_input_idx, sink_input_toggle_mute_callback, NULL);
2445                     else
2446                         o = pa_context_set_sink_input_mute(c, sink_input_idx, mute, simple_callback, NULL);
2447                     break;
2448 
2449                 case SET_SOURCE_OUTPUT_MUTE:
2450                     if (mute == TOGGLE_MUTE)
2451                         o = pa_context_get_source_output_info(c, source_output_idx, source_output_toggle_mute_callback, NULL);
2452                     else
2453                         o = pa_context_set_source_output_mute(c, source_output_idx, mute, simple_callback, NULL);
2454                     break;
2455 
2456                 case GET_SINK_VOLUME:
2457                     o = pa_context_get_sink_info_by_name(c, sink_name, get_sink_volume_callback, NULL);
2458                     break;
2459 
2460                 case SET_SINK_VOLUME:
2461                     o = pa_context_get_sink_info_by_name(c, sink_name, set_sink_volume_callback, NULL);
2462                     break;
2463 
2464                 case GET_SOURCE_VOLUME:
2465                     o = pa_context_get_source_info_by_name(c, source_name, get_source_volume_callback, NULL);
2466                     break;
2467 
2468                 case SET_SOURCE_VOLUME:
2469                     o = pa_context_get_source_info_by_name(c, source_name, set_source_volume_callback, NULL);
2470                     break;
2471 
2472                 case SET_SINK_INPUT_VOLUME:
2473                     o = pa_context_get_sink_input_info(c, sink_input_idx, get_sink_input_volume_callback, NULL);
2474                     break;
2475 
2476                 case SET_SOURCE_OUTPUT_VOLUME:
2477                     o = pa_context_get_source_output_info(c, source_output_idx, get_source_output_volume_callback, NULL);
2478                     break;
2479 
2480                 case SET_SINK_FORMATS:
2481                     set_sink_formats(c, sink_idx, formats);
2482                     break;
2483 
2484                 case SET_PORT_LATENCY_OFFSET:
2485                     o = pa_context_set_port_latency_offset(c, card_name, port_name, latency_offset, simple_callback, NULL);
2486                     break;
2487 
2488                 case SEND_MESSAGE:
2489                     o = pa_context_send_message_to_object(c, object_path, message, message_args, send_message_callback, NULL);
2490                     break;
2491 
2492                 case SUBSCRIBE:
2493                     pa_context_set_subscribe_callback(c, context_subscribe_callback, NULL);
2494 
2495                     o = pa_context_subscribe(c,
2496                                              PA_SUBSCRIPTION_MASK_SINK|
2497                                              PA_SUBSCRIPTION_MASK_SOURCE|
2498                                              PA_SUBSCRIPTION_MASK_SINK_INPUT|
2499                                              PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
2500                                              PA_SUBSCRIPTION_MASK_MODULE|
2501                                              PA_SUBSCRIPTION_MASK_CLIENT|
2502                                              PA_SUBSCRIPTION_MASK_SAMPLE_CACHE|
2503                                              PA_SUBSCRIPTION_MASK_SERVER|
2504                                              PA_SUBSCRIPTION_MASK_CARD,
2505                                              NULL,
2506                                              NULL);
2507                     break;
2508 
2509                 default:
2510                     pa_assert_not_reached();
2511             }
2512 
2513             if (o) {
2514                 pa_operation_unref(o);
2515                 actions++;
2516             }
2517 
2518             if (actions == 0) {
2519                 pa_log("Operation failed: %s", pa_strerror(pa_context_errno(c)));
2520                 quit(1);
2521             }
2522 
2523             break;
2524 
2525         case PA_CONTEXT_TERMINATED:
2526             quit(0);
2527             break;
2528 
2529         case PA_CONTEXT_FAILED:
2530         default:
2531             pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c)));
2532             quit(1);
2533     }
2534 }
2535 
exit_signal_callback(pa_mainloop_api * m,pa_signal_event * e,int sig,void * userdata)2536 static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
2537     pa_log(_("Got SIGINT, exiting."));
2538     quit(0);
2539 }
2540 
parse_volume(const char * vol_spec,pa_volume_t * vol,enum volume_flags * vol_flags)2541 static int parse_volume(const char *vol_spec, pa_volume_t *vol, enum volume_flags *vol_flags) {
2542     double v;
2543     char *vs;
2544     const char *atod_input;
2545 
2546     pa_assert(vol_spec);
2547     pa_assert(vol);
2548     pa_assert(vol_flags);
2549 
2550     vs = pa_xstrdup(vol_spec);
2551 
2552     *vol_flags = (pa_startswith(vs, "+") || pa_startswith(vs, "-")) ? VOL_RELATIVE : VOL_ABSOLUTE;
2553     if (pa_endswith(vs, "%")) {
2554         *vol_flags |= VOL_PERCENT;
2555         vs[strlen(vs)-1] = 0;
2556     }
2557     else if (pa_endswith(vs, "db") || pa_endswith(vs, "dB")) {
2558         *vol_flags |= VOL_DECIBEL;
2559         vs[strlen(vs)-2] = 0;
2560     }
2561     else if (strchr(vs, '.'))
2562         *vol_flags |= VOL_LINEAR;
2563 
2564     atod_input = vs;
2565 
2566     if (atod_input[0] == '+')
2567         atod_input++; /* pa_atod() doesn't accept leading '+', so skip it. */
2568 
2569     if (pa_atod(atod_input, &v) < 0) {
2570         pa_log(_("Invalid volume specification"));
2571         pa_xfree(vs);
2572         return -1;
2573     }
2574 
2575     pa_xfree(vs);
2576 
2577     if (*vol_flags & VOL_RELATIVE) {
2578 	switch (*vol_flags & 0x0F) {
2579 	    case VOL_UINT:
2580 		v += (double) PA_VOLUME_NORM;
2581 		break;
2582 	    case VOL_PERCENT:
2583 		v += 100.0;
2584 		break;
2585 	    case VOL_LINEAR:
2586 		v += 1.0;
2587 		break;
2588 	}
2589     }
2590 
2591     switch (*vol_flags & 0x0F) {
2592 	case VOL_PERCENT:
2593 	    v = v * (double) PA_VOLUME_NORM / 100;
2594 	    break;
2595 	case VOL_LINEAR:
2596 	    v = pa_sw_volume_from_linear(v);
2597 	    break;
2598 	case VOL_DECIBEL:
2599 	    v = pa_sw_volume_from_dB(v);
2600 	    break;
2601     }
2602 
2603     if (!PA_VOLUME_IS_VALID((pa_volume_t) v)) {
2604         pa_log(_("Volume outside permissible range.\n"));
2605         return -1;
2606     }
2607 
2608     *vol = (pa_volume_t) v;
2609 
2610     return 0;
2611 }
2612 
parse_volumes(char * args[],unsigned n)2613 static int parse_volumes(char *args[], unsigned n) {
2614     unsigned i;
2615 
2616     if (n >= PA_CHANNELS_MAX) {
2617         pa_log(_("Invalid number of volume specifications.\n"));
2618         return -1;
2619     }
2620 
2621     volume.channels = n;
2622     for (i = 0; i < volume.channels; i++) {
2623         enum volume_flags flags = 0;
2624 
2625         if (parse_volume(args[i], &volume.values[i], &flags) < 0)
2626             return -1;
2627 
2628         if (i > 0 && flags != volume_flags) {
2629             pa_log(_("Inconsistent volume specification.\n"));
2630             return -1;
2631         } else
2632             volume_flags = flags;
2633     }
2634 
2635     return 0;
2636 }
2637 
parse_mute(const char * mute_text)2638 static enum mute_flags parse_mute(const char *mute_text) {
2639     int b;
2640 
2641     pa_assert(mute_text);
2642 
2643     if (pa_streq("toggle", mute_text))
2644         return TOGGLE_MUTE;
2645 
2646     b = pa_parse_boolean(mute_text);
2647     switch (b) {
2648         case 0:
2649             return UNMUTE;
2650         case 1:
2651             return MUTE;
2652         default:
2653             return INVALID_MUTE;
2654     }
2655 }
2656 
help(const char * argv0)2657 static void help(const char *argv0) {
2658 
2659     printf("%s %s %s\n",    argv0, _("[options]"), "stat");
2660     printf("%s %s %s\n",    argv0, _("[options]"), "info");
2661     printf("%s %s %s %s\n", argv0, _("[options]"), "list [short]", _("[TYPE]"));
2662     printf("%s %s %s\n",    argv0, _("[options]"), "exit");
2663     printf("%s %s %s %s\n", argv0, _("[options]"), "upload-sample", _("FILENAME [NAME]"));
2664     printf("%s %s %s %s\n", argv0, _("[options]"), "play-sample ", _("NAME [SINK]"));
2665     printf("%s %s %s %s\n", argv0, _("[options]"), "remove-sample ", _("NAME"));
2666     printf("%s %s %s %s\n", argv0, _("[options]"), "load-module ", _("NAME [ARGS ...]"));
2667     printf("%s %s %s %s\n", argv0, _("[options]"), "unload-module ", _("NAME|#N"));
2668     printf("%s %s %s %s\n", argv0, _("[options]"), "move-(sink-input|source-output)", _("#N SINK|SOURCE"));
2669     printf("%s %s %s %s\n", argv0, _("[options]"), "suspend-(sink|source)", _("NAME|#N 1|0"));
2670     printf("%s %s %s %s\n", argv0, _("[options]"), "set-card-profile ", _("CARD PROFILE"));
2671     printf("%s %s %s\n", argv0, _("[options]"), "get-default-(sink|source)");
2672     printf("%s %s %s %s\n", argv0, _("[options]"), "set-default-(sink|source)", _("NAME"));
2673     printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-port", _("NAME|#N PORT"));
2674     printf("%s %s %s %s\n", argv0, _("[options]"), "get-(sink|source)-volume", _("NAME|#N"));
2675     printf("%s %s %s %s\n", argv0, _("[options]"), "get-(sink|source)-mute", _("NAME|#N"));
2676     printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-volume", _("NAME|#N VOLUME [VOLUME ...]"));
2677     printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink-input|source-output)-volume", _("#N VOLUME [VOLUME ...]"));
2678     printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-mute", _("NAME|#N 1|0|toggle"));
2679     printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink-input|source-output)-mute", _("#N 1|0|toggle"));
2680     printf("%s %s %s %s\n", argv0, _("[options]"), "set-sink-formats", _("#N FORMATS"));
2681     printf("%s %s %s %s\n", argv0, _("[options]"), "set-port-latency-offset", _("CARD-NAME|CARD-#N PORT OFFSET"));
2682     printf("%s %s %s %s\n", argv0, _("[options]"), "send-message", _("RECIPIENT MESSAGE [MESSAGE_PARAMETERS]"));
2683     printf("%s %s %s\n",    argv0, _("[options]"), "subscribe");
2684     printf(_("\nThe special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
2685              "can be used to specify the default sink, source and monitor.\n"));
2686 
2687     printf(_("\n"
2688              "  -h, --help                            Show this help\n"
2689              "      --version                         Show version\n\n"
2690              "  -f, --format=FORMAT                   The format of the output. Either \"normal\" or \"json\"\n"
2691              "  -s, --server=SERVER                   The name of the server to connect to\n"
2692              "  -n, --client-name=NAME                How to call this client on the server\n"));
2693 }
2694 
2695 enum {
2696     ARG_VERSION = 256
2697 };
2698 
main(int argc,char * argv[])2699 int main(int argc, char *argv[]) {
2700     pa_mainloop *m = NULL;
2701     int ret = 1, c;
2702     char *server = NULL, *opt_format = NULL, *bn;
2703 
2704     static const struct option long_options[] = {
2705         {"server",      1, NULL, 's'},
2706         {"client-name", 1, NULL, 'n'},
2707         {"format",      1, NULL, 'f'},
2708         {"version",     0, NULL, ARG_VERSION},
2709         {"help",        0, NULL, 'h'},
2710         {NULL,          0, NULL, 0}
2711     };
2712 
2713     setlocale(LC_ALL, "");
2714 #ifdef ENABLE_NLS
2715     bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
2716 #endif
2717 
2718     bn = pa_path_get_filename(argv[0]);
2719 
2720     proplist = pa_proplist_new();
2721 
2722     while ((c = getopt_long(argc, argv, "+s:n:f:h", long_options, NULL)) != -1) {
2723         switch (c) {
2724             case 'h' :
2725                 help(bn);
2726                 ret = 0;
2727                 goto quit;
2728 
2729             case ARG_VERSION:
2730                 printf(_("pactl %s\n"
2731                          "Compiled with libpulse %s\n"
2732                          "Linked with libpulse %s\n"),
2733                        PACKAGE_VERSION,
2734                        pa_get_headers_version(),
2735                        pa_get_library_version());
2736                 ret = 0;
2737                 goto quit;
2738 
2739             case 's':
2740                 pa_xfree(server);
2741                 server = pa_xstrdup(optarg);
2742                 break;
2743 
2744             case 'f':
2745                 opt_format = pa_xstrdup(optarg);
2746                 break;
2747 
2748             case 'n': {
2749                 char *t;
2750 
2751                 if (!(t = pa_locale_to_utf8(optarg)) ||
2752                     pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {
2753 
2754                     pa_log(_("Invalid client name '%s'"), t ? t : optarg);
2755                     pa_xfree(t);
2756                     goto quit;
2757                 }
2758 
2759                 pa_xfree(t);
2760                 break;
2761             }
2762 
2763             default:
2764                 goto quit;
2765         }
2766     }
2767 
2768     if (!opt_format || pa_streq(opt_format, "text")) {
2769         format = TEXT;
2770     } else if (pa_streq(opt_format, "json")) {
2771         format = JSON;
2772         setlocale(LC_NUMERIC, "C");
2773     } else {
2774         pa_log(_("Invalid format value '%s'"), opt_format);
2775         goto quit;
2776     }
2777 
2778     if (optind < argc) {
2779         if (pa_streq(argv[optind], "stat")) {
2780             action = STAT;
2781 
2782         } else if (pa_streq(argv[optind], "info"))
2783             action = INFO;
2784 
2785         else if (pa_streq(argv[optind], "exit"))
2786             action = EXIT;
2787 
2788         else if (pa_streq(argv[optind], "list")) {
2789             action = LIST;
2790 
2791             for (int i = optind+1; i < argc; i++) {
2792                 if (pa_streq(argv[i], "modules") || pa_streq(argv[i], "clients") ||
2793                     pa_streq(argv[i], "sinks")   || pa_streq(argv[i], "sink-inputs") ||
2794                     pa_streq(argv[i], "sources") || pa_streq(argv[i], "source-outputs") ||
2795                     pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards") ||
2796                     pa_streq(argv[i], "message-handlers")) {
2797                     list_type = pa_xstrdup(argv[i]);
2798                 } else if (pa_streq(argv[i], "short")) {
2799                     short_list_format = true;
2800                 } else {
2801                     pa_log(_("Specify nothing, or one of: %s"), "modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards, message-handlers");
2802                     goto quit;
2803                 }
2804             }
2805 
2806         } else if (pa_streq(argv[optind], "upload-sample")) {
2807 #ifdef SNDFILE_ENABLE
2808             struct SF_INFO sfi;
2809             action = UPLOAD_SAMPLE;
2810 
2811             if (optind+1 >= argc) {
2812                 pa_log(_("Please specify a sample file to load"));
2813                 goto quit;
2814             }
2815 
2816             if (optind+2 < argc)
2817                 sample_name = pa_xstrdup(argv[optind+2]);
2818             else {
2819                 char *f = pa_path_get_filename(argv[optind+1]);
2820                 sample_name = pa_xstrndup(f, strcspn(f, "."));
2821             }
2822 
2823             pa_zero(sfi);
2824             if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfi))) {
2825                 pa_log(_("Failed to open sound file."));
2826                 goto quit;
2827             }
2828 
2829             if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
2830                 pa_log(_("Failed to determine sample specification from file."));
2831                 goto quit;
2832             }
2833             sample_spec.format = PA_SAMPLE_FLOAT32;
2834 
2835             if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
2836                 if (sample_spec.channels > 2)
2837                     pa_log(_("Warning: Failed to determine sample specification from file."));
2838                 pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
2839             }
2840 
2841             pa_assert(pa_channel_map_compatible(&channel_map, &sample_spec));
2842             sample_length = (size_t) sfi.frames*pa_frame_size(&sample_spec);
2843 #endif
2844         } else if (pa_streq(argv[optind], "play-sample")) {
2845             action = PLAY_SAMPLE;
2846             if (argc != optind+2 && argc != optind+3) {
2847                 pa_log(_("You have to specify a sample name to play"));
2848                 goto quit;
2849             }
2850 
2851             sample_name = pa_xstrdup(argv[optind+1]);
2852 
2853             if (optind+2 < argc)
2854                 sink_name = pa_xstrdup(argv[optind+2]);
2855 
2856         } else if (pa_streq(argv[optind], "remove-sample")) {
2857             action = REMOVE_SAMPLE;
2858             if (argc != optind+2) {
2859                 pa_log(_("You have to specify a sample name to remove"));
2860                 goto quit;
2861             }
2862 
2863             sample_name = pa_xstrdup(argv[optind+1]);
2864 
2865         } else if (pa_streq(argv[optind], "move-sink-input")) {
2866             action = MOVE_SINK_INPUT;
2867             if (argc != optind+3) {
2868                 pa_log(_("You have to specify a sink input index and a sink"));
2869                 goto quit;
2870             }
2871 
2872             sink_input_idx = (uint32_t) atoi(argv[optind+1]);
2873             sink_name = pa_xstrdup(argv[optind+2]);
2874 
2875         } else if (pa_streq(argv[optind], "move-source-output")) {
2876             action = MOVE_SOURCE_OUTPUT;
2877             if (argc != optind+3) {
2878                 pa_log(_("You have to specify a source output index and a source"));
2879                 goto quit;
2880             }
2881 
2882             source_output_idx = (uint32_t) atoi(argv[optind+1]);
2883             source_name = pa_xstrdup(argv[optind+2]);
2884 
2885         } else if (pa_streq(argv[optind], "load-module")) {
2886             int i;
2887             size_t n = 0;
2888             char *p;
2889 
2890             action = LOAD_MODULE;
2891 
2892             if (argc <= optind+1) {
2893                 pa_log(_("You have to specify a module name and arguments."));
2894                 goto quit;
2895             }
2896 
2897             module_name = argv[optind+1];
2898 
2899             for (i = optind+2; i < argc; i++)
2900                 n += strlen(argv[i])+1;
2901 
2902             if (n > 0) {
2903                 p = module_args = pa_xmalloc(n);
2904 
2905                 for (i = optind+2; i < argc; i++)
2906                     p += sprintf(p, "%s%s", p == module_args ? "" : " ", argv[i]);
2907             }
2908 
2909         } else if (pa_streq(argv[optind], "unload-module")) {
2910             action = UNLOAD_MODULE;
2911 
2912             if (argc != optind+2) {
2913                 pa_log(_("You have to specify a module index or name"));
2914                 goto quit;
2915             }
2916 
2917             if (pa_atou(argv[optind + 1], &module_index) < 0)
2918                 module_name = argv[optind + 1];
2919 
2920         } else if (pa_streq(argv[optind], "suspend-sink")) {
2921             int b;
2922 
2923             action = SUSPEND_SINK;
2924 
2925             if (argc > optind+3 || optind+1 >= argc) {
2926                 pa_log(_("You may not specify more than one sink. You have to specify a boolean value."));
2927                 goto quit;
2928             }
2929 
2930             if ((b = pa_parse_boolean(argv[argc-1])) < 0) {
2931                 pa_log(_("Invalid suspend specification."));
2932                 goto quit;
2933             }
2934 
2935             suspend = !!b;
2936 
2937             if (argc > optind+2)
2938                 sink_name = pa_xstrdup(argv[optind+1]);
2939 
2940         } else if (pa_streq(argv[optind], "suspend-source")) {
2941             int b;
2942 
2943             action = SUSPEND_SOURCE;
2944 
2945             if (argc > optind+3 || optind+1 >= argc) {
2946                 pa_log(_("You may not specify more than one source. You have to specify a boolean value."));
2947                 goto quit;
2948             }
2949 
2950             if ((b = pa_parse_boolean(argv[argc-1])) < 0) {
2951                 pa_log(_("Invalid suspend specification."));
2952                 goto quit;
2953             }
2954 
2955             suspend = !!b;
2956 
2957             if (argc > optind+2)
2958                 source_name = pa_xstrdup(argv[optind+1]);
2959         } else if (pa_streq(argv[optind], "set-card-profile")) {
2960             action = SET_CARD_PROFILE;
2961 
2962             if (argc != optind+3) {
2963                 pa_log(_("You have to specify a card name/index and a profile name"));
2964                 goto quit;
2965             }
2966 
2967             card_name = pa_xstrdup(argv[optind+1]);
2968             profile_name = pa_xstrdup(argv[optind+2]);
2969 
2970         } else if (pa_streq(argv[optind], "set-sink-port")) {
2971             action = SET_SINK_PORT;
2972 
2973             if (argc != optind+3) {
2974                 pa_log(_("You have to specify a sink name/index and a port name"));
2975                 goto quit;
2976             }
2977 
2978             sink_name = pa_xstrdup(argv[optind+1]);
2979             port_name = pa_xstrdup(argv[optind+2]);
2980 
2981         } else if (pa_streq(argv[optind], "set-default-sink")) {
2982             action = SET_DEFAULT_SINK;
2983 
2984             if (argc != optind+2) {
2985                 pa_log(_("You have to specify a sink name"));
2986                 goto quit;
2987             }
2988 
2989             sink_name = pa_xstrdup(argv[optind+1]);
2990 
2991         } else if (pa_streq(argv[optind], "get-default-sink")) {
2992             action = GET_DEFAULT_SINK;
2993 
2994         } else if (pa_streq(argv[optind], "set-source-port")) {
2995             action = SET_SOURCE_PORT;
2996 
2997             if (argc != optind+3) {
2998                 pa_log(_("You have to specify a source name/index and a port name"));
2999                 goto quit;
3000             }
3001 
3002             source_name = pa_xstrdup(argv[optind+1]);
3003             port_name = pa_xstrdup(argv[optind+2]);
3004 
3005         } else if (pa_streq(argv[optind], "set-default-source")) {
3006             action = SET_DEFAULT_SOURCE;
3007 
3008             if (argc != optind+2) {
3009                 pa_log(_("You have to specify a source name"));
3010                 goto quit;
3011             }
3012 
3013             source_name = pa_xstrdup(argv[optind+1]);
3014 
3015         } else if (pa_streq(argv[optind], "get-default-source")) {
3016             action = GET_DEFAULT_SOURCE;
3017 
3018         } else if (pa_streq(argv[optind], "get-sink-volume")) {
3019             action = GET_SINK_VOLUME;
3020 
3021             if (argc < optind+2) {
3022                 pa_log(_("You have to specify a sink name/index"));
3023                 goto quit;
3024             }
3025 
3026             sink_name = pa_xstrdup(argv[optind+1]);
3027 
3028         } else if (pa_streq(argv[optind], "set-sink-volume")) {
3029             action = SET_SINK_VOLUME;
3030 
3031             if (argc < optind+3) {
3032                 pa_log(_("You have to specify a sink name/index and a volume"));
3033                 goto quit;
3034             }
3035 
3036             sink_name = pa_xstrdup(argv[optind+1]);
3037 
3038             if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
3039                 goto quit;
3040 
3041         } else if (pa_streq(argv[optind], "get-source-volume")) {
3042             action = GET_SOURCE_VOLUME;
3043 
3044             if (argc < optind+2) {
3045                 pa_log(_("You have to specify a source name/index"));
3046                 goto quit;
3047             }
3048 
3049             source_name = pa_xstrdup(argv[optind+1]);
3050 
3051         } else if (pa_streq(argv[optind], "set-source-volume")) {
3052             action = SET_SOURCE_VOLUME;
3053 
3054             if (argc < optind+3) {
3055                 pa_log(_("You have to specify a source name/index and a volume"));
3056                 goto quit;
3057             }
3058 
3059             source_name = pa_xstrdup(argv[optind+1]);
3060 
3061             if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
3062                 goto quit;
3063 
3064         } else if (pa_streq(argv[optind], "set-sink-input-volume")) {
3065             action = SET_SINK_INPUT_VOLUME;
3066 
3067             if (argc < optind+3) {
3068                 pa_log(_("You have to specify a sink input index and a volume"));
3069                 goto quit;
3070             }
3071 
3072             if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
3073                 pa_log(_("Invalid sink input index"));
3074                 goto quit;
3075             }
3076 
3077             if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
3078                 goto quit;
3079 
3080         } else if (pa_streq(argv[optind], "set-source-output-volume")) {
3081             action = SET_SOURCE_OUTPUT_VOLUME;
3082 
3083             if (argc < optind+3) {
3084                 pa_log(_("You have to specify a source output index and a volume"));
3085                 goto quit;
3086             }
3087 
3088             if (pa_atou(argv[optind+1], &source_output_idx) < 0) {
3089                 pa_log(_("Invalid source output index"));
3090                 goto quit;
3091             }
3092 
3093             if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
3094                 goto quit;
3095 
3096         } else if (pa_streq(argv[optind], "get-sink-mute")) {
3097             action = GET_SINK_MUTE;
3098 
3099             if (argc < optind+2) {
3100                 pa_log(_("You have to specify a sink name/index"));
3101                 goto quit;
3102             }
3103 
3104             sink_name = pa_xstrdup(argv[optind+1]);
3105 
3106         } else if (pa_streq(argv[optind], "set-sink-mute")) {
3107             action = SET_SINK_MUTE;
3108 
3109             if (argc != optind+3) {
3110                 pa_log(_("You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"));
3111                 goto quit;
3112             }
3113 
3114             if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
3115                 pa_log(_("Invalid mute specification"));
3116                 goto quit;
3117             }
3118 
3119             sink_name = pa_xstrdup(argv[optind+1]);
3120 
3121         } else if (pa_streq(argv[optind], "get-source-mute")) {
3122             action = GET_SOURCE_MUTE;
3123 
3124             if (argc < optind+2) {
3125                 pa_log(_("You have to specify a source name/index"));
3126                 goto quit;
3127             }
3128 
3129             source_name = pa_xstrdup(argv[optind+1]);
3130 
3131         } else if (pa_streq(argv[optind], "set-source-mute")) {
3132             action = SET_SOURCE_MUTE;
3133 
3134             if (argc != optind+3) {
3135                 pa_log(_("You have to specify a source name/index and a mute action (0, 1, or 'toggle')"));
3136                 goto quit;
3137             }
3138 
3139             if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
3140                 pa_log(_("Invalid mute specification"));
3141                 goto quit;
3142             }
3143 
3144             source_name = pa_xstrdup(argv[optind+1]);
3145 
3146         } else if (pa_streq(argv[optind], "set-sink-input-mute")) {
3147             action = SET_SINK_INPUT_MUTE;
3148 
3149             if (argc != optind+3) {
3150                 pa_log(_("You have to specify a sink input index and a mute action (0, 1, or 'toggle')"));
3151                 goto quit;
3152             }
3153 
3154             if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
3155                 pa_log(_("Invalid sink input index specification"));
3156                 goto quit;
3157             }
3158 
3159             if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
3160                 pa_log(_("Invalid mute specification"));
3161                 goto quit;
3162             }
3163 
3164         } else if (pa_streq(argv[optind], "set-source-output-mute")) {
3165             action = SET_SOURCE_OUTPUT_MUTE;
3166 
3167             if (argc != optind+3) {
3168                 pa_log(_("You have to specify a source output index and a mute action (0, 1, or 'toggle')"));
3169                 goto quit;
3170             }
3171 
3172             if (pa_atou(argv[optind+1], &source_output_idx) < 0) {
3173                 pa_log(_("Invalid source output index specification"));
3174                 goto quit;
3175             }
3176 
3177             if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
3178                 pa_log(_("Invalid mute specification"));
3179                 goto quit;
3180             }
3181 
3182         } else if (pa_streq(argv[optind], "send-message")) {
3183             action = SEND_MESSAGE;
3184 
3185             if (argc < optind+3) {
3186                 pa_log(_("You have to specify at least an object path and a message name"));
3187                 goto quit;
3188             }
3189 
3190             object_path = pa_xstrdup(argv[optind + 1]);
3191             message = pa_xstrdup(argv[optind + 2]);
3192             if (argc >= optind+4)
3193                 message_args = pa_xstrdup(argv[optind + 3]);
3194 
3195             if (argc > optind+4)
3196                 pa_log(_("Excess arguments given, they will be ignored. Note that all message parameters must be given as a single string."));
3197 
3198         } else if (pa_streq(argv[optind], "subscribe"))
3199 
3200             action = SUBSCRIBE;
3201 
3202         else if (pa_streq(argv[optind], "set-sink-formats")) {
3203             int32_t tmp;
3204 
3205             if (argc != optind+3 || pa_atoi(argv[optind+1], &tmp) < 0) {
3206                 pa_log(_("You have to specify a sink index and a semicolon-separated list of supported formats"));
3207                 goto quit;
3208             }
3209 
3210             sink_idx = tmp;
3211             action = SET_SINK_FORMATS;
3212             formats = pa_xstrdup(argv[optind+2]);
3213 
3214         } else if (pa_streq(argv[optind], "set-port-latency-offset")) {
3215             action = SET_PORT_LATENCY_OFFSET;
3216 
3217             if (argc != optind+4) {
3218                 pa_log(_("You have to specify a card name/index, a port name and a latency offset"));
3219                 goto quit;
3220             }
3221 
3222             card_name = pa_xstrdup(argv[optind+1]);
3223             port_name = pa_xstrdup(argv[optind+2]);
3224             if (pa_atoi(argv[optind + 3], &latency_offset) < 0) {
3225                 pa_log(_("Could not parse latency offset"));
3226                 goto quit;
3227             }
3228 
3229         } else if (pa_streq(argv[optind], "help")) {
3230             help(bn);
3231             ret = 0;
3232             goto quit;
3233         }
3234     }
3235 
3236     if (action == NONE) {
3237         pa_log(_("No valid command specified."));
3238         goto quit;
3239     }
3240 
3241     if (!(m = pa_mainloop_new())) {
3242         pa_log(_("pa_mainloop_new() failed."));
3243         goto quit;
3244     }
3245 
3246     mainloop_api = pa_mainloop_get_api(m);
3247 
3248     pa_assert_se(pa_signal_init(mainloop_api) == 0);
3249     pa_signal_new(SIGINT, exit_signal_callback, NULL);
3250     pa_signal_new(SIGTERM, exit_signal_callback, NULL);
3251     pa_disable_sigpipe();
3252 
3253     if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
3254         pa_log(_("pa_context_new() failed."));
3255         goto quit;
3256     }
3257 
3258     pa_context_set_state_callback(context, context_state_callback, NULL);
3259     if (pa_context_connect(context, server, 0, NULL) < 0) {
3260         pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
3261         goto quit;
3262     }
3263 
3264     if (pa_mainloop_run(m, &ret) < 0) {
3265         pa_log(_("pa_mainloop_run() failed."));
3266         goto quit;
3267     }
3268 
3269     if (format == JSON && list_encoder && !pa_json_encoder_is_empty(list_encoder)) {
3270         pa_json_encoder_end_object(list_encoder);
3271         char* list_json_str = pa_json_encoder_to_string_free(list_encoder);
3272         printf("%s", list_json_str);
3273         pa_xfree(list_json_str);
3274     }
3275 
3276 quit:
3277 #ifdef SNDFILE_ENABLE
3278     if (sample_stream)
3279         pa_stream_unref(sample_stream);
3280 #endif
3281     if (context)
3282         pa_context_unref(context);
3283 
3284     if (m) {
3285         pa_signal_done();
3286         pa_mainloop_free(m);
3287     }
3288 
3289     pa_xfree(server);
3290     pa_xfree(list_type);
3291     pa_xfree(sample_name);
3292     pa_xfree(sink_name);
3293     pa_xfree(source_name);
3294     pa_xfree(module_args);
3295     pa_xfree(card_name);
3296     pa_xfree(profile_name);
3297     pa_xfree(port_name);
3298     pa_xfree(formats);
3299     pa_xfree(object_path);
3300     pa_xfree(message);
3301     pa_xfree(message_args);
3302 #ifdef SNDFILE_ENABLE
3303     if (sndfile)
3304         sf_close(sndfile);
3305 #endif
3306     if (proplist)
3307         pa_proplist_free(proplist);
3308 
3309     return ret;
3310 }
3311