diff options
Diffstat (limited to 'perl-install')
-rw-r--r-- | perl-install/harddrake/sound.pm | 446 |
1 files changed, 425 insertions, 21 deletions
diff --git a/perl-install/harddrake/sound.pm b/perl-install/harddrake/sound.pm index edf7208f2..4c2e41ab0 100644 --- a/perl-install/harddrake/sound.pm +++ b/perl-install/harddrake/sound.pm @@ -1,5 +1,15 @@ package harddrake::sound; +# 2023-03-18 ghibo: cleanup against perl_checker +# 2023-03-18 anaselli: add reboot requester +# 2023-03-17 ghibo: stop also wireplumber in disable_all_pipewire +# 2023-03-17 ghibo: stop pulseaudio with the running user +# 2023-03-17 anaselli: suspend pipewire before configuring pulseaudio +# 2023-03-13 ghibo: added {start|stop}_{pipewire,wireplumber,pipewire-media-session}(), _pidof_pid(); _user_pid() +# 2023-03-13 ghibo: use rooted_get_stdout instead of get_stdout +# 2023-03-11 Angelo Naselli: added pipewire support +# +# # TODO: # o ensure sound is not user (either dsp/midi/sequencer/mixer) # o fix sound/alsa services @@ -61,8 +71,9 @@ sub set_pulseaudio_glitchfree { } $pa_startup_scriptfile; } - -sub rooted { run_program::rooted($::prefix, @_) } +sub rooted { + run_program::rooted($::prefix, @_); +} sub unload { modules::unload(@_) if $::isStandalone } @@ -71,6 +82,352 @@ sub load { modules::load_and_configure($modules_conf, $name) if $::isStandalone; } +sub _pidof { + my ($name) = @_; + rooted('/usr/bin/pidof', $name); +} + +# return the pid of a running command +sub _pidof_pid { + my ($name) = @_; + my ($pid) = chomp_(run_program::rooted_get_stdout($::prefix, '/usr/bin/pidof', $name)); + + return $pid; +} + +# return the username of a running command +sub _user_pid { + my ($name) = @_; + my ($pid) = _pidof_pid($name); + my $user; + + if ($pid) { + ($user) = chomp_(run_program::rooted_get_stdout($::prefix, '/usr/bin/ps', '-o', 'uname=', '-p', $pid)); + } + + return $user; +} + +# stop pulseaudio for the running user +sub stop_pulseaudio { + my ($pulseaudio_user) = _user_pid('/usr/bin/pulseaudio'); + + if ($pulseaudio_user) { + if (-x '/usr/bin/pulseaudio') { + rooted('/usr/bin/su', '-l', $pulseaudio_user, '-c', '/usr/bin/pulseaudio --kill'); + } + } + + # try stopping again (in case the /usr/bin/pulseaudio process is still running, but the executable no longer available) + my ($pulseaudio_user) = _user_pid('/usr/bin/pulseaudio'); + if ($pulseaudio_user) { + my ($pulseaudio_pid) = _pidof_pid('/usr/bin/pulseaudio'); + if ($pulseaudio_pid) { + rooted('/usr/bin/su', '-l', $pulseaudio_user, '-c', '/usr/bin/kill -TERM ' . $pulseaudio_pid); + } + } + return $pulseaudio_user; +} + +# stop pipewire services for the running user +sub stop_pipewire { + my ($pipewire_user) = _user_pid('/usr/bin/pipewire'); + + if ($pipewire_user) { + rooted('/usr/bin/systemctl', '--machine=' . $pipewire_user . '@.host', '--user', 'stop', 'pipewire-pulse.service', 'pipewire-pulse.socket', 'pipewire.service', 'pipewire.socket'); + } + + return $pipewire_user; +} + +# start pulseaudio for the specified user +sub start_pulseaudio { + my ($pulseaudio_user) = @_; + + if ($pulseaudio_user) { + rooted('/usr/bin/su', '-l', $pulseaudio_user, '-c', '/usr/bin/pulseaudio --start'); + } +} + +# start pipewire services for the specified user +sub start_pipewire { + my ($pipewire_user) = @_; + + if ($pipewire_user) { + rooted('/usr/bin/systemctl', '--machine=' . $pipewire_user . '@.host', '--user', 'start', 'pipewire.socket', 'pipewire.service', 'pipewire-pulse.socket', 'pipewire-pulse.service'); + } +} + +# stop wireplumber services for the running user +sub stop_wireplumber { + my ($pipewire_user) = _user_pid('/usr/bin/wireplumber'); + + if ($pipewire_user) { + rooted('/usr/bin/systemctl', '--machine=' . $pipewire_user . '@.host', '--user', 'stop', 'wireplumber.service'); + } + + return $pipewire_user; +} + +# start wireplumber +sub start_wireplumber { + my ($pipewire_user) = @_; + + if ($pipewire_user) { + rooted('/usr/bin/systemctl', '--machine=' . $pipewire_user . '@.host', '--user', 'start', 'wireplumber.service'); + } +} + +# stop pipewire-media-session services for the running user +sub stop_pipewire_media_session { + my ($pipewire_user) = _user_pid('/usr/bin/pipewire-media-session'); + + if ($pipewire_user) { + rooted('/usr/bin/systemctl', '--machine=' . $pipewire_user . '@.host', '--user', 'stop', 'pipewire-media-session.service'); + } + + return $pipewire_user; +} + +# start pipewire-media-session services for the specified user +sub start_pipewire_media_session { + my ($pipewire_user) = @_; + + if ($pipewire_user) { + rooted('/usr/bin/systemctl', '--machine=' . $pipewire_user . '@.host', '--user', 'start', 'pipewire-media-session.service'); + } +} + +sub configure_pipewire_wireplumber { + my ($in) = @_; + my ($pipewire_wp_user); + + # preserve pulseaudio user + my ($pulseaudio_user) = _user_pid('/usr/bin/pulseaudio'); + + # stop pipewire and pipewire-media-session services (if any) before removing any packages + # and preserve pipewire-media-session user + $pipewire_wp_user = stop_pipewire_media_session(); + stop_pipewire(); + + my $required_installed = $in->do_pkgs->ensure_are_installed( + ['task-pipewire', + 'wireplumber', + 'pavucontrol'] + ); + + if (!$required_installed) + { + $in->ask_warn(N("Couldn't install the required packages"), + N("Please check the repositories are correctly configured") + ); + return; + } + + set_pulseaudio(0); + + # first of all disabling what has to be disabled to avoid conflicts + if ($in->do_pkgs->is_installed('pipewire-media-session')) { + rooted('/usr/bin/systemctl', '--global', 'disable', 'pipewire-media-session.service'); + } + + if ($in->do_pkgs->is_installed('pipewire')) { + foreach ('pipewire.socket', 'pipewire.service') { + rooted('/usr/bin/systemctl', '--global', 'enable', $_); + } + } + + if ($in->do_pkgs->is_installed('pipewire-pulseaudio')) { + foreach ('pipewire-pulse.socket', 'pipewire-pulse.service') { + rooted('/usr/bin/systemctl', '--global', 'enable', $_); + } + } + + if ($in->do_pkgs->is_installed('wireplumber')) { + rooted('/usr/bin/systemctl', '--global', 'enable', 'wireplumber.service'); + } + + + # stop pulseaudio process if still floating after the switch + stop_pulseaudio(); + + if ($pulseaudio_user) { + # restart pipewire and wireplumber with the same user as initial pulseaudio + start_pipewire($pulseaudio_user); + start_wireplumber($pulseaudio_user); + } + else { + if ($pipewire_wp_user) { + # restart pipewire and wireplumber with the same user as pipewire-media-session + start_pipewire($pipewire_wp_user); + start_wireplumber($pipewire_wp_user); + } + } + + #Plasma tricks + if ($in->do_pkgs->is_installed('plasma-desktop')) { + $in->do_pkgs->ensure_are_installed( + [ 'pavucontrol-qt', + 'kpipewire' ] + ); + } +} + +sub configure_pipewire_media_session { + my ($in) = @_; + my ($pipewire_ms_user); + + # preserve pulseaudio user + my ($pulseaudio_user) = _user_pid('/usr/bin/pulseaudio'); + + # stop pipewire and wireplumber services (if any) before removing any package + # and preserve wireplumber user + $pipewire_ms_user = stop_wireplumber(); + stop_pipewire(); + + + my $required_installed = $in->do_pkgs->ensure_are_installed( + ['task-pipewire', + 'pipewire-media-session', + 'pavucontrol'] + ); + + if (!$required_installed) + { + $in->ask_warn(N("Couldn't install the required packages"), + N("Please check the repositories are correctly configured") + ); + return; + } + + set_pulseaudio(0); + + # first of all disabling what has to be disabled to avoid conflicts + if ($in->do_pkgs->is_installed('wireplumber')) { + rooted('/usr/bin/systemctl', '--global', 'disable', 'wireplumber.service'); + } + + if ($in->do_pkgs->is_installed('pipewire')) { + foreach ('pipewire.socket', 'pipewire.service') { + rooted('/usr/bin/systemctl', '--global', 'enable', $_); + } + } + + if ($in->do_pkgs->is_installed('pipewire-pulseaudio')) { + foreach ('pipewire-pulse.socket', 'pipewire-pulse.service') { + rooted('/usr/bin/systemctl', '--global', 'enable', $_); + } + } + + if ($in->do_pkgs->is_installed('pipewire-media-session')) { + rooted('/usr/bin/systemctl', '--global', 'enable', 'pipewire-media-session.service'); + } + + # stop pulseaudio process if still floating after the switch + stop_pulseaudio(); + + if ($pulseaudio_user) { + # restart pipewire and wireplumber with the same user as initial pulseaudio + start_pipewire($pulseaudio_user); + start_pipewire_media_session($pulseaudio_user); + } + else { + if ($pipewire_ms_user) { + # restart pipewire and wireplumber with the same user as pipewire-media-session + start_pipewire($pipewire_ms_user); + start_pipewire_media_session($pipewire_ms_user); + } + } + + #Plasma tricks + if ($in->do_pkgs->is_installed('plasma-desktop')) { + $in->do_pkgs->ensure_are_installed( + [ 'pavucontrol-qt', + 'kpipewire' ] + ); + } +} + +sub disable_all_pipewire { + my ($in) = @_; + + if ($in->do_pkgs->is_installed('wireplumber')) { + rooted('/usr/bin/systemctl', '--global', 'disable', 'wireplumber.service'); + } + + if ($in->do_pkgs->is_installed('pipewire')) { + foreach ('pipewire.socket', 'pipewire.service') { + rooted('/usr/bin/systemctl', '--global', 'disable', $_); + } + } + + if ($in->do_pkgs->is_installed('pipewire-pulseaudio')) { + foreach ('pipewire-pulse.socket', 'pipewire-pulse.service') { + rooted('/usr/bin/systemctl', '--global', 'disable', $_); + } + } + + if ($in->do_pkgs->is_installed('pipewire-media-session')) { + rooted('/usr/bin/systemctl', '--global', 'disable', 'pipewire-media-session.service'); + } +} + +sub configure_pulseaudio { + my ($in) = @_; + + # preserve pipewire running user + my ($pipewire_user) = _user_pid('/usr/bin/pipewire'); + + # stop pipewire, wireplumber and pipewire-media-session services (if any) before removing any packages + my ($wireplumber_user) = stop_wireplumber(); + my ($pipewire_media_session_user) = stop_pipewire_media_session(); + my ($pipewire_user_preserve) = stop_pipewire(); + + # now packages + $in->do_pkgs->remove('pipewire-alsa'); + + my $required_installed = $in->do_pkgs->ensure_are_installed( + ['task-pulseaudio', + 'pavucontrol'] + ); + + if (!$required_installed) + { + $in->ask_warn(N("Couldn't install the required packages"), + N("Please check the repositories are correctly configured") + ); + + # restore previous pipewire (if any) processes if something goes wrong with pulseaudio installation + if ($pipewire_user_preserve) { + start_pipewire($pipewire_user_preserve); + } + + if ($wireplumber_user) { + start_wireplumber($wireplumber_user); + } + + if ($pipewire_media_session_user) { + start_pipewire_media_session($pipewire_media_session_user); + } + + return; + } + + + # disable all the pipewire stuff before managing packages + disable_all_pipewire($in); + + #Plasma tricks + if ($in->do_pkgs->is_installed('plasma-desktop')) { + $in->do_pkgs->ensure_is_installed('pavucontrol-qt'); + } + + # start pulseaudio with same pipewire preserved user + if ($pipewire_user) { + start_pulseaudio($pipewire_user); + } +} + sub config { my ($in, $modules_conf, $device) = @_; my $driver = $device->{current_driver} || $device->{driver}; @@ -85,36 +442,83 @@ sub config { my %des = modules::category2modules_and_description('multimedia/sound'); my $is_pulseaudio_installed = -f $pa_startup_scriptfile && -d '/etc/sound/profiles/pulse'; - my $is_pulseaudio_enabled = is_pulseaudio_enabled(); - my $is_pulseaudio_glitchfree_enabled = is_pulseaudio_glitchfree_enabled(); - - my $old_value = $is_pulseaudio_enabled; + my $is_pulseaudio_enabled_val = is_pulseaudio_enabled(); + my $is_pulseaudio_glitchfree_enabled_val = is_pulseaudio_glitchfree_enabled(); + + my $audiosystem = 'None'; + if ($is_pulseaudio_enabled_val && $is_pulseaudio_glitchfree_enabled_val) { + $audiosystem = 'PulseAudioGF'; + } elsif ($is_pulseaudio_enabled_val) { + $audiosystem = 'PulseAudio'; + } elsif (_pidof('/usr/bin/wireplumber')) { + $audiosystem = 'PipeWireWP'; + } elsif (_pidof('/usr/bin/pipewire-media-session')) { + $audiosystem = 'PipeWireMS'; + } my $write_config = sub { - return if !$is_pulseaudio_installed; - set_pulseaudio($is_pulseaudio_enabled); - set_pulseaudio_glitchfree($is_pulseaudio_glitchfree_enabled); - if ($is_pulseaudio_enabled) { + if ($audiosystem eq 'None') { + #### ALSA alone + disable_all_pipewire($in); + set_pulseaudio(0); + set_pulseaudio_glitchfree(0); + # TODO check if adding autospawn = no to /etc/pulse/client.conf is needed + } elsif ($audiosystem eq 'PulseAudio') { + #### PulseAudio + configure_pulseaudio($in); + set_pulseaudio(1); + set_pulseaudio_glitchfree(0); my $lib = get_libdir(); $in->do_pkgs->ensure_is_installed($lib . 'alsa-plugins-pulseaudio', - '/usr/' . $lib . '/alsa-lib/libasound_module_pcm_pulse.so'); - } - if ($old_value ne $is_pulseaudio_enabled) { - require any; - any::ask_for_X_restart($in); + '/usr/' . $lib . '/alsa-lib/libasound_module_pcm_pulse.so'); + } elsif ($audiosystem eq 'PulseAudioGF') { + #### PulseAudio with Glitch-Free mode + configure_pulseaudio($in); + set_pulseaudio(1); + set_pulseaudio_glitchfree(1); + my $lib = get_libdir(); + $in->do_pkgs->ensure_is_installed($lib . 'alsa-plugins-pulseaudio', + '/usr/' . $lib . '/alsa-lib/libasound_module_pcm_pulse.so'); + } elsif ($audiosystem eq 'PipeWireWP') { + #### PipeWire with WirePlumber + configure_pipewire_wireplumber($in); + } elsif ($audiosystem eq 'PipeWireMS') { + #### PipeWire with PipeWire Media Session + configure_pipewire_media_session($in); + } else { + #### Unmanaged value + #TODO Error here } + + $in->ask_warn('', N("You need to reboot for changes to take effect")); + }; my @common = ( { - text => N("Enable PulseAudio"), - type => 'bool', val => \$is_pulseaudio_enabled, - disabled => sub { !$is_pulseaudio_installed }, + label => N("Select the sound server"), + title => 1, }, { - text => N("Use Glitch-Free mode"), - type => 'bool', val => \$is_pulseaudio_glitchfree_enabled, - disabled => sub { !$is_pulseaudio_installed || !$is_pulseaudio_enabled }, + val => \$audiosystem, + list => [ + 'None', + 'PulseAudio', + 'PulseAudioGF', + 'PipeWireWP', + 'PipeWireMS', + ], + format => sub { + my ($choice) = @_; + +{ + 'None' => N("None"), + 'PulseAudio' => N("PulseAudio"), + 'PulseAudioGF' => N("PulseAudio with Glitch-Free mode"), + 'PipeWireWP' => N("PipeWire with WirePlumber"), + 'PipeWireMS' => N("PipeWire with PipeWire Media Session"), + }->{$choice}; + }, + type => 'list', }, { advanced => 1, |