1
#compdef pulseaudio pactl pacmd pacat paplay parec parecord padsp pasuspender
4
for (( i = 0; i < ${#words[@]}; i++ )) do
5
if [[ ${words[$i]} == -s || ${words[$i]} == --server ]]; then
6
remote="-s ${words[$i+1]}"
8
elif [[ ${words[$i]} == --server=* ]]; then
16
local cmd _device _device_description
18
if [[ $service == pactl || $service == pacmd ]]; then
19
case $words[$((CURRENT - 1))] in
20
set-sink-input-*) cmd=('sink-inputs');;
21
set-sink-*) cmd=('sinks');;
22
set-default-sink) cmd=('sinks');;
23
set-default-source) cmd=('sources');;
24
set-source-output-*) cmd=('source-outputs');;
25
set-source-*) cmd=('sources');;
26
suspend-sink) cmd=('sinks');;
27
suspend-source) cmd=('sources');;
28
move-sink-input) cmd=('sink-inputs');;
29
move-source-output) cmd=('source-outputs');;
30
kill-sink-input) cmd=('sink-inputs');;
31
kill-source-output) cmd=('source-outputs');;
34
case $words[$((CURRENT - 2))] in
35
move-sink-input) cmd=('sinks');;
36
move-source-output) cmd=('sources');;
39
elif [[ $service == (pacat|paplay|parec|parecord) ]]; then
40
case $words[$((CURRENT))] in
42
if [[ $words == *(--playback|-p)[[:space:]]* ||
43
$service == paplay ]]; then
45
elif [[ $words == *(--record|-r)[[:space:]]* ||
46
$service == (parec|parecord) ]]; then
49
cmd=('sinks' 'sources')
52
--monitor-stream=*) cmd=('sink-inputs');;
55
case $words[$((CURRENT - 1))] in
57
if [[ $words == *(--playback|-p)[[:space:]]* ||
58
$service == paplay ]]; then
60
elif [[ $words == *(--record|-r)[[:space:]]* ||
61
$service == (parec|parecord) ]]; then
64
cmd=('sinks' 'sources')
71
for target in $cmd; do
72
for device_info in ${(ps:\n\n:)"$(_call_program device_tag "pactl $remote list $target 2> /dev/null")"}; do
73
for line in ${(f)device_info}; do
74
if [[ $target == (sink-inputs|source-outputs) ]]; then
75
if [[ $line == (Sink*Input|Source*Output)* ]]; then
77
elif [[ $line == *application.name* ]]; then
78
_device_description=${line#*= }
82
if [[ $words[$((CURRENT - 1))] == *set-sink-formats* ]]; then
83
if [[ $line == Sink* ]]; then
85
elif [[ $line == *Description:* ]]; then
86
_device_description=${line#*: }
90
if [[ $line == *Name:* ]]; then
92
elif [[ $line == *Description:* ]]; then
93
_device_description=${line#*: }
98
_device_list+=($_device:$_device_description)
102
_describe 'device list' _device_list
106
local -a _profile_list
107
local _current_card _raw_profiles _profile_name _profile_description
109
_current_card=$words[$((CURRENT - 1))]
111
for card in ${(ps:\n\n:)"$(_call_program profiles_tag "pactl $remote list cards 2> /dev/null")"}; do
112
if [[ $card == *$_current_card* ]]; then
113
_raw_profiles=${card##*Profiles:}
114
_raw_profiles=${_raw_profiles%%Active Profile:*}
115
for profile in ${(f)_raw_profiles}; do
116
if [[ $profile != [[:blank:]] ]]; then
117
_profile_name=${profile%%: *}
118
_profile_name=${_profile_name//[[:blank:]]/}
119
_profile_name=${_profile_name//:/\\:}
120
_profile_description=${profile#*: }
121
_profile_list+=($_profile_name:$_profile_description)
127
_describe 'profile list' _profile_list
132
local _raw_ports _port_name _port_description _current_device
134
case $words[$((CURRENT - 2))] in
135
set-sink-port) cmd="sinks";;
136
set-source-port) cmd="sources";;
137
set-port-latency-offset) cmd="cards";;
140
_current_device=$words[$((CURRENT - 1))]
142
for device in ${(ps:\n\n:)"$(_call_program port_tag "pactl $remote list $cmd 2> /dev/null")"}; do
143
if [[ $device == *Ports:* && $device == *$_current_device* ]]; then
144
_raw_ports=${device##*Ports:}
145
_raw_ports=${_raw_ports%%Active Port:*}
146
for line in ${(f)_raw_ports}; do
147
if [[ $line != [[:blank:]] &&
148
$line != (*Part?of*|*Properties:*|*device.icon_name*) ]]; then
149
_port_name=${line%%: *}
150
_port_name=${_port_name//[[:blank:]]/}
151
_port_description=${line#*: }
152
_port_list+=($_port_name:$_port_description)
158
_describe 'port list' _port_list
163
local _card _cad_name
165
for card_info in ${(ps:\n\n:)"$(_call_program card_tag "pactl $remote list cards 2> /dev/null")"}; do
166
for line in ${(f)card_info}; do
167
if [[ $line == *Name:* ]]; then
169
elif [[ $line == *alsa.long_card_name* ]]; then
170
_card_name=${line#*= \"}
171
_card_name=${_card_name%at*}
174
_card_list+=($_card:$_card_name)
177
_describe 'card list' _card_list
181
local -a _all_modules_list
182
for module in ${(f)"$(_call_program modules_tag "pulseaudio --dump-modules 2> /dev/null")"}; do
183
_all_modules_list+=${module%% *}
185
_describe 'module list' _all_modules_list
189
local -a _loaded_modules_list
191
for module in ${(f)"$(_call_program modules_tag "pactl $remote list modules short 2> /dev/null")"}; do
192
_loaded_modules_list+=(${${(ps:\t:)module}[1]}:${${(ps:\t:)module}[2]})
194
_describe 'module list' _loaded_modules_list
197
_resample_methods() {
198
local -a _resample_method_list
199
for method in ${(f)"$(_call_program modules_tag "pulseaudio --dump-resample-methods 2> /dev/null")"}; do
200
_resample_method_list+=$method
202
_describe 'resample method list' _resample_method_list
206
local -a _client_list
207
local _client _client_description
209
for client_info in ${(ps:\n\n:)"$(_call_program clients_tag "pactl $remote list clients 2> /dev/null")"}; do
210
for line in ${(f)client_info}; do
211
if [[ $line == Client[[:space:]]#* ]]; then
213
elif [[ $line == *application.name* ]]; then
214
_client_description=${line#*=}
217
_client_list+=($_client:$_client_description)
219
_describe 'client list' _client_list
222
_pacat_file_formats() {
223
local -a _file_format_list
224
for format in ${(f)"$(_call_program fformats_tag "pacat --list-file-formats")"}; do
225
_file_format_list+=(${${(ps:\t:)format}[1]}:${${(ps:\t:)format}[2]})
227
_describe 'file format list' _file_format_list
230
_pactl_completion() {
234
local -a _pactl_commands
237
'help: show help and exit'
238
'stat: dump statistics about the PulseAudio daemon'
239
'info: dump info about the PulseAudio daemon'
240
'list: list modules/sources/streams/cards etc...'
241
'exit: ask the PulseAudio daemon to exit'
242
'upload-sample: upload a sound from a file into the sample cache'
243
'play-sample: play the specified sample from the sample cache'
244
'remove-sample: remove the specified sample from the sample cache'
245
'load-module: load a module'
246
'unload-module: unload a module'
247
'move-sink-input: move a stream to a sink'
248
'move-source-output: move a recording stream to a source'
249
'suspend-sink: suspend or resume a sink'
250
'suspend-source: suspend or resume a source'
251
'set-card-profile: set a card profile'
252
'set-default-sink: set the default sink'
253
'set-default-source: set the default source'
254
'set-sink-port: set the sink port of a sink'
255
'set-source-port: set the source port of a source'
256
'set-port-latency-offset: set a latency offset on a port'
257
'set-sink-volume: set the volume of a sink'
258
'set-source-volume: set the volume of a source'
259
'set-sink-input-volume: set the volume of a stream'
260
'set-source-output-volume: set the volume of a recording stream'
261
'set-sink-mute: mute a sink'
262
'set-source-mute: mute a source'
263
'set-sink-input-mute: mute a stream'
264
'set-source-output-mute: mute a recording stream'
265
'set-sink-formats: set supported formats of a sink'
266
'subscribe: subscribe to events'
269
_describe 'pactl commands' _pactl_commands
272
_pactl_command_parameter() {
279
'modules: list loaded modules'
280
'sinks: list available sinks'
281
'sources: list available sources'
282
'sink-inputs: list connected sink inputs'
283
'source-outputs: list connected source outputs'
284
'clients: list connected clients'
285
'samples: list samples'
286
'cards: list available cards'
289
if ((CURRENT == 2)); then
290
# We're completing the first parameter after "list".
291
# "pactl list cards short" and "pactl list short cards" are
292
# treated as equivalent by pactl, but here we only support the
293
# first form, so "short" isn't a valid completion.
294
_describe 'objects' _objects
295
elif ((CURRENT == 3)); then
296
# We're completing the second parameter after "list". As
297
# explained in the previous comment, we only support the
298
# "pactl list cards short" form, so "short" is the only valid
304
_play_sample_parameter() {
305
if ((CURRENT == 2)); then
306
# We're completing the first parameter after "play-sample".
307
# TODO: Implement sample name completion.
308
elif ((CURRENT == 3)); then
309
# We're completing the second parameter after "play-sample".
310
# TODO: Implement sink name completion.
314
_load_module_parameter() {
315
if ((CURRENT == 2)); then
316
# We're completing the first parameter after "load-module".
319
# We're completing the second or later parameter after
320
# "load-module", i.e. the module arguments.
321
# TODO: Implement module argument completion.
325
_move_sink_input_parameter() {
326
if ((CURRENT == 2)); then
327
# We're completing the first parameter after "move-sink-input".
328
# Even though the function name is "_devices", it actually
329
# completes the sink input index. _devices is magical like
332
elif ((CURRENT == 3)); then
333
# We're completing the second parameter after
339
_move_source_output_parameter() {
340
if ((CURRENT == 2)); then
341
# We're completing the first parameter after
342
# "move-source-output". Even though the function name is
343
# "_devices", it actually completes the source output index.
344
# _devices is magical like that.
346
elif ((CURRENT == 3)); then
347
# We're completing the second parameter after
348
# "move-source-output".
353
_suspend_sink_parameter() {
354
if ((CURRENT == 2)); then
355
# We're completing the first parameter after "suspend-sink".
357
elif ((CURRENT == 3)); then
358
# We're completing the second parameter after "suspend-sink".
363
_suspend_source_parameter() {
364
if ((CURRENT == 2)); then
365
# We're completing the first parameter after "suspend-source".
367
elif ((CURRENT == 3)); then
368
# We're completing the second parameter after "suspend-source".
373
_set_card_profile_parameter() {
374
if ((CURRENT == 2)); then
375
# We're completing the first parameter after
376
# "set-card-profile".
378
elif ((CURRENT == 3)); then
379
# We're completing the second parameter after
380
# "set-card-profile".
385
_set_sink_port_parameter() {
386
if ((CURRENT == 2)); then
387
# We're completing the first parameter after "set-sink-port".
389
elif ((CURRENT == 3)); then
390
# We're completing the second parameter after "set-sink-port".
395
_set_source_port_parameter() {
396
if ((CURRENT == 2)); then
397
# We're completing the first parameter after "set-source-port".
399
elif ((CURRENT == 3)); then
400
# We're completing the second parameter after
406
_set_sink_mute_parameter() {
407
if ((CURRENT == 2)); then
408
# We're completing the first parameter after "set-sink-mute".
410
elif ((CURRENT == 3)); then
411
# We're completing the second parameter after "set-sink-mute".
412
compadd true false toggle
416
_set_source_mute_parameter() {
417
if ((CURRENT == 2)); then
418
# We're completing the first parameter after "set-source-mute".
420
elif ((CURRENT == 3)); then
421
# We're completing the second parameter after
423
compadd true false toggle
427
_set_sink_input_mute_parameter() {
428
if ((CURRENT == 2)); then
429
# We're completing the first parameter after
430
# "set-sink-input-mute". Even though the function name is
431
# "_devices", it actually completes the sink input index.
432
# _devices is magical like that.
434
elif ((CURRENT == 3)); then
435
# We're completing the second parameter after
436
# "set-sink-input-mute".
437
compadd true false toggle
441
_set_source_output_mute_parameter() {
442
if ((CURRENT == 2)); then
443
# We're completing the first parameter after
444
# "set-source-output-mute". Even though the function name is
445
# "_devices", it actually completes the source output index.
446
# _devices is magical like that.
448
elif ((CURRENT == 3)); then
449
# We're completing the second parameter after
450
# "set-source-output-mute".
451
compadd true false toggle
455
_set_port_latency_offset_parameter() {
456
if ((CURRENT == 2)); then
457
# We're completing the first parameter after
458
# "set-port-latency-offset".
460
elif ((CURRENT == 3)); then
461
# We're completing the second parameter after
462
# "set-port-latency-offset".
470
list) _list_parameter;;
471
upload-sample) if ((CURRENT == 2)); then _files; fi;;
472
play-sample) _play_sample_parameter;;
473
remove-sample) ;; # TODO: Implement sample name completion.
474
load-module) _load_module_parameter;;
475
unload-module) if ((CURRENT == 2)); then _loaded_modules; fi;;
476
move-sink-input) _move_sink_input_parameter;;
477
move-source-output) _move_source_output_parameter;;
478
suspend-sink) _suspend_sink_parameter;;
479
suspend-source) _suspend_source_parameter;;
480
set-card-profile) _set_card_profile_parameter;;
481
set-default-sink) if ((CURRENT == 2)); then _devices; fi;;
482
set-default-source) if ((CURRENT == 2)); then _devices; fi;;
483
set-sink-port) _set_sink_port_parameter;;
484
set-source-port) _set_source_port_parameter;;
485
set-sink-volume) if ((CURRENT == 2)); then _devices; fi;;
486
set-source-volume) if ((CURRENT == 2)); then _devices; fi;;
487
set-sink-input-volume) if ((CURRENT == 2)); then _devices; fi;;
488
set-source-output-volume) if ((CURRENT == 2)); then _devices; fi;;
489
set-sink-mute) _set_sink_mute_parameter;;
490
set-source-mute) _set_source_mute_parameter;;
491
set-sink-input-mute) _set_sink_input_mute_parameter;;
492
set-source-output-mute) _set_source_output_mute_parameter;;
493
set-sink-formats) if ((CURRENT == 2)); then _devices; fi;;
494
set-port-latency-offset) _set_port_latency_offset_parameter;;
498
_arguments -C -S -A '-*' \
499
{-h,--help}'[display help and exit]' \
500
'--version[show version and exit]' \
501
{-s,--server=}'[name of server to connect to]:host:_hosts' \
502
{-n,--client-name=}'[client name to use]:name' \
503
'::pactl command:_pactl_command' \
504
'*::pactl command parameter:_pactl_command_parameter'
507
_pacmd_completion() {
510
'help: show help and exit'
511
'list-modules: list modules'
512
'list-cards: list cards'
513
'list-sinks: list sinks'
514
'list-sources: list sources'
515
'list-clients: list clients'
516
'list-sink-inputs: list sink-inputs'
517
'list-source-outputs: list source-outputs'
518
'stat: dump statistics about the PulseAudio daemon'
519
'info: dump info about the PulseAudio daemon'
520
'load-module: load a module'
521
'unload-module: unload a module'
522
'describe-module: print info for a module'
523
'set-sink-volume: set the volume of a sink'
524
'set-source-volume: set the volume of a source'
525
'set-sink-mute: mute a sink'
526
'set-source-mute: mute a source'
527
'set-sink-input-volume: set the volume of a stream'
528
'set-source-output-volume: set the volume of a recording stream'
529
'set-sink-input-mute: mute a stream'
530
'set-source-output-mute: mute a recording stream'
531
'set-default-sink: set the default sink'
532
'set-default-source: set the default source'
533
'set-card-profile: set a card profile'
534
'set-sink-port: set the sink port of a sink'
535
'set-source-port: set the source port of a source'
536
'set-port-latency-offset: set a latency offset on a port'
537
'suspend-sink: suspend or resume a sink'
538
'suspend-source: suspend or resume a source'
539
'suspend: suspend all sinks and sources'
540
'move-sink-input: move a stream to a sink'
541
'move-source-output: move a recording stream to a source'
542
'update-sink-proplist: update the properties of a sink'
543
'update-source-proplist: update the properties of a source'
544
'update-sink-input-proplist: update the properties of a sink-input'
545
'update-source-output-proplist: update the properties of a source-output'
546
'list-samples: list samples'
547
'play-sample: play the specified sample from the sample cache' # TODO
548
'remove-sample: remove the specified sample from the sample cache' # TODO
549
'load-sample: upload a sound from a file into the sample cache'
550
'load-sample-lazy: lazily upload a sound file into the sample cache'
551
'load-sample-dir-lazy: lazily upload all sound files in a directory into the sample cache'
552
'kill-client: kill a client'
553
'kill-sink-input: kill a sink input'
554
'kill-source-output: kill a source output'
555
'set-log-target: change the log target'
556
'set-log-level: change the log level'
557
'set-log-meta: show source code location in log messages'
558
'set-log-time: show timestamps in log messages'
559
'set-log-backtrace: show backtrace in log messages'
560
'play-file: play a sound file'
561
'dump: show daemon configuration'
562
'dump-volumes: show the state of all volumes'
563
'shared: show shared properties'
564
'exit: ask the PulseAudio daemon to exit'
566
_describe 'pacmd commands' _pacmd_commands
569
_arguments -C -S -A "-*" \
570
{-h,--help}'[display help and exit]' \
571
'--version[show version and exit]' \
572
'::pacmd commands:_pacmd_command' \
574
case $words[$((CURRENT - 1))] in
575
set-card-profile) _cards;;
576
set-sink-*) _devices;;
577
set-source-*) _devices;;
578
load-module) _all_modules;;
579
describe-module) _all_modules;;
580
unload-module) _loaded_modules;;
581
suspend-*) _devices;;
583
set-port-latency-offset) _cards;;
584
load-sample*) _files;;
585
kill-client) _clients;;
586
kill-(sink|source)-*) _devices;;
587
set-log-target) compadd null auto syslog stderr file:;;
588
set-log-*) compadd true false;;
592
case $words[$((CURRENT - 2))] in
593
set-card-profile) _profiles;;
594
set-(sink|source)-port) _ports;;
595
set-port-latency-offset) _ports;;
596
set-*-mute) compadd true false;;
597
suspend-*) compadd true false;;
602
_pasuspender_completion() {
603
_arguments -S -A "-*" -C \
604
{-h,--help}'[display help and exit]' \
605
'--version[show version and exit]' \
606
{-s,--server=}'[name of server to connect to]:host:_hosts' \
609
_padsp_completion() {
610
_arguments -C -S -A "-*" \
611
'-h[display help and exit]' \
612
'-s[name of server to connect to]:host:_hosts' \
613
'-n[client name to use]:name:' \
614
'-m[stream name to use]:name:' \
615
'-M[disable /dev/mixer emulation]' \
616
'-S[disable /dev/sndstat emulation]' \
617
'-D[disable /dev/dsp emulation]' \
618
'-d[enable debug output]' \
619
'--[disable further command line parsing]' \
622
# TODO channel map completion
623
_pacat_completion() {
626
_pacat_sample_formats=('s16le' 's16be' 'u8' 'float32le' 'float32be'
627
'ulaw' 'alaw' 's32le' 's32be' 's24le' 's24-32le' 's24-32be')
629
_arguments -C -S -A "-*" \
630
{-h,--help}'[display this help and exit]' \
631
'--version[show version and exit]' \
632
{-r,--record}'[create a connection for recording]' \
633
{-p,--playback}'[create a connection for playback]' \
634
{-s,--server=}'[name of server to connect to]:host:_hosts' \
635
{-d,--device=}'[name of sink/source to connect to]:device:_devices' \
636
'--monitor-stream=[index of the sink input to record from]:device:_devices' \
637
{-n,--client-name=}'[client name to use]:name' \
638
'--stream-name=[how to call this stream]:name' \
639
'--volume=[initial volume to use]:volume' \
640
'--rate=[sample rate to use]:rate:(44100 48000 96000)' \
641
'--format=[sample type to use]:format:((${(q)_pacat_sample_formats}))' \
642
'--channels=[number of channels to use]:number:(1 2)' \
643
'--channel-map=[channel map to use]:map' \
644
'--fix-format[use the sample format of the sink]' \
645
'--fix-rate[use the rate of the sink]' \
646
'--fix-channels[channel map of the sink]' \
647
'--no-remix[do not upmix or downmix channels]' \
648
'--no-remap[map channels by index instead of name]' \
649
'--latency=[request the specified latency]:bytes' \
650
'--process-time=[request the specified process time]:bytes' \
651
'--latency-msec=[request the specified latency in msec]:msec' \
652
'--process-time-msec=[request the specified process time in msec]:msec' \
653
'--property=[set the specified property]:property' \
654
'--raw[record/play raw PCM data]' \
655
'--passthrough[passtrough data]' \
656
'--file-format=[record/play formatted PCM data]:format:_pacat_file_formats' \
657
'--list-file-formats[list available formats]' \
661
# TODO log-target file completion
662
_pulseaudio_completion() {
664
{-h,--help}'[display this help and exit]' \
665
'--version[show version and exit]' \
666
'--dump-conf[show default configuration]' \
667
'--dump-modules[show available modules]' \
668
'--dump-resample-methods[show available resample methods]' \
669
'--cleanup-shm[cleanup shared memory]' \
670
'--start[start the daemon]' \
671
{-k,--kill}'[kill a running daemon]' \
672
'--check[check for a running daemon]' \
673
'--system=[run as systemd-wide daemon]:bool:(true false)' \
674
{-D,--daemonize=}'[daemonize after startup]:bool:(true false)' \
675
'--fail=[quit when startup fails]:bool:(true false)' \
676
'--high-priority=[try to set high nice level]:bool:(true false)' \
677
'--realtime=[try to enable rt scheduling]:bool:(true false)' \
678
'--disallow-module-loading=[disallow module loading]:bool:(true false)' \
679
'--disallow-exit=[disallow user requested exit]' \
680
'--exit-idle-time=[terminate the daemon on passed idle time]:time' \
681
'--scache-idle-time=[unload autoloaded samples on passed idle time]:time' \
682
'--log-level=[set the verbosity level]:level' \
683
'-v[increase the verbosity level]' \
684
'--log-target=[set the log target]:target:(auto syslog stderr file\: new_file\:):file' \
685
'--log-meta=[include code location in log messages]:bool:(true false)' \
686
'--log-time=[include timestamps in log messages]:bool:(true false)' \
687
'--log-backtrace=[include backtrace in log messages]:frames' \
688
{-p,--dl-search-path=}'[set the search path for plugins]:dir:_files' \
689
'--resample-method=[set the resample method]:method:_resample_methods' \
690
'--use-pid-file=[create a PID file]:bool:(true false)' \
691
'--no-cpu-limit=[do not install CPU load limiter]:bool:(true false)' \
692
'--disable-shm=[disable shared memory support]:bool:(true false)' \
693
{-L,--load=}'[load the specified module]:modules:_all_modules' \
694
{-F,--file=}'[run the specified script]:file:_files' \
695
'-C[open a command line on the running tty]' \
696
'-n[do not load the default script file]' \
700
local state line curcontext="$curcontext"
702
# Some commands, like pactl and pacat, have an option for specifying the
703
# server address, like "--server=somehost". If that option is set, then the
704
# helper commands that are run as part of the autocompletion need to use
705
# that same option. The option is saved in this variable in _set_remote(),
706
# which is called in the beginning of _pactl_completion() and others. The
707
# autocompletion commands can then find the option in that variable if the
712
pulseaudio) _pulseaudio_completion;;
713
pactl) _pactl_completion;;
714
pacmd) _pacmd_completion;;
715
pacat) _pacat_completion;;
716
paplay)_pacat_completion;;
717
parec) _pacat_completion;;
718
parecord)_pacat_completion;;
719
padsp) _padsp_completion;;
720
pasuspender) _pasuspender_completion;;
727
#vim: set ft=zsh sw=4 ts=4 noet