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