diff --git a/rofi_sound.pl b/rofi_sound.pl new file mode 100755 index 0000000..d9b8a54 --- /dev/null +++ b/rofi_sound.pl @@ -0,0 +1,114 @@ +#!/usr/bin/env perl + +use strict; +use warnings; +use Data::Dumper; + +my $selection = $ARGV[0]; + +my $app = {}; + +if (defined $selection) { + select_sink($app, $selection); +} else { + show_sinks($app); +} + +# ----------- +# Subroutines +# ----------- +sub show_sinks { + my ($app) = @_; + + my @sinks = get_sinks($app); + print join("\n", @sinks); +} + +# ----------- +sub select_sink { + my ($app, $sink) = @_; + + my $wp_h; + my ($id,$name) = $sink =~ /(\d+):(.*)/; + my $seld_sink = "NONE"; + my $seld_id = "NONE"; + + open($wp_h, "pw-cli info $id |") or die "Could not run pw-cli command"; + + while (my $ln = <$wp_h>) { + if (my ($node_name) = $ln =~ /node.name\s*=\s*"(.*)"/) { + warn "Selected [$id] ($name) - $node_name\n"; + $seld_sink = $node_name; + $seld_id = $id; + last; + } + } + + enable_sink($app, $seld_sink, $seld_id); +} + +# ----------- +sub enable_sink { + my ($app, $sink, $id) = @_; + + `wpctl set-default $id`; + unmute_master($app); +} + +# ----------- +sub unmute_master { + my ($app) = @_; + + `amixer -c 0 set Master unmute`; +} + +# ----------- +sub parse_wpctl { + my $wp_h; + + open($wp_h, "wpctl status |") or die "Could not run wpctl command"; + + my @lines = <$wp_h>; + + my $section = "None"; + my $sub_section = "None"; + my $wp_data = {}; + + foreach my $ln (@lines) { + # Section + if (my ($mat) = $ln =~ /(^(PipeWire)|^(\w+)$)/) { + $section = $mat; + $wp_data->{$section} = {} unless (exists $wp_data->{$section}); + } + + # Sub-section + if (my ($mat) = $ln =~ /\s+(\w+):$/) { + $sub_section = $mat; + $wp_data->{$section}->{$sub_section} = [] unless (exists $wp_data->{$section}->{$sub_section}); + } + + # Entries + if (($section ne "None") && ($sub_section ne "None")) { + if (my ($sel, $id, $name) = $ln =~ /\s+(\*)?\s+(\d+)\.\s+(.*?)(\[|$)/) { + my $seld = (defined $sel) ? 1 : 0; + $name =~ s/^\s*//; $name =~ s/\s*$//; + push(@{$wp_data->{$section}->{$sub_section}}, { + 'sel' => $seld, + 'id' => $id, + 'name' => $name + }); + } + } + } + $app->{'wp_data'} = $wp_data; +} + +# ----------- +sub get_sinks { + my ($app) = @_; + + parse_wpctl($app); + # warn Data::Dumper->Dump([$app->{'wp_data'}]); + + return map { "$_->{'id'}:$_->{'name'}" } @{$app->{'wp_data'}->{'Audio'}->{'Sinks'}} +}