1- use std:: cell:: RefCell ;
1+ use std:: cell:: { Cell , RefCell } ;
22use std:: collections:: { HashMap , HashSet } ;
3+ use std:: io:: Cursor ;
34use std:: mem:: MaybeUninit ;
45use std:: rc:: Rc ;
56use std:: str:: FromStr ;
@@ -19,17 +20,20 @@ use ::pipewire::{
1920 properties:: properties,
2021 proxy:: { Listener , ProxyListener , ProxyT } ,
2122 spa:: {
22- param:: ParamType ,
23+ param:: { ParamType , audio :: AudioInfoRaw } ,
2324 pod:: {
24- Pod , Value , ValueArray , builder:: Builder as PodBuilder , deserialize:: PodDeserializer ,
25+ Object , Pod , Value , ValueArray , builder:: Builder as PodBuilder ,
26+ deserialize:: PodDeserializer , serialize:: PodSerializer ,
2527 } ,
2628 sys:: {
27- SPA_DIRECTION_INPUT , SPA_DIRECTION_OUTPUT , SPA_PARAM_ROUTE_device ,
28- SPA_PARAM_ROUTE_direction , SPA_PARAM_ROUTE_index , SPA_PARAM_ROUTE_name ,
29- SPA_PARAM_ROUTE_props , SPA_PARAM_ROUTE_save , SPA_PROP_channelVolumes , SPA_PROP_mute ,
29+ SPA_DIRECTION_INPUT , SPA_DIRECTION_OUTPUT , SPA_PARAM_EnumFormat ,
30+ SPA_PARAM_ROUTE_device , SPA_PARAM_ROUTE_direction , SPA_PARAM_ROUTE_index ,
31+ SPA_PARAM_ROUTE_name , SPA_PARAM_ROUTE_props , SPA_PARAM_ROUTE_save ,
32+ SPA_PROP_channelVolumes , SPA_PROP_mute , SPA_TYPE_OBJECT_Format ,
3033 } ,
3134 utils:: { SpaTypes , dict:: DictRef } ,
3235 } ,
36+ stream:: { StreamBox , StreamFlags } ,
3337 types:: ObjectType ,
3438} ;
3539use bitflags:: bitflags;
@@ -639,10 +643,17 @@ impl Client {
639643 update_copy. replace_with ( |v| * v | EventKind :: NODE_ADDED ) ;
640644 }
641645 ObjectType :: Link => {
646+ let mut client_data = client. data . lock ( ) . unwrap ( ) ;
642647 let Some ( link) = Link :: new ( global_props) else {
643648 return ;
644649 } ;
645- client. data . lock ( ) . unwrap ( ) . links . insert ( global_id, link) ;
650+ if let Some ( node) = client_data. nodes . get ( & link. link_input_node )
651+ && node. name == env ! ( "CARGO_PKG_NAME" )
652+ {
653+ return ;
654+ }
655+
656+ client_data. links . insert ( global_id, link) ;
646657 update_copy. replace_with ( |v| * v | EventKind :: LINK_ADDED ) ;
647658 }
648659 ObjectType :: Port => {
@@ -825,6 +836,124 @@ impl Client {
825836 } )
826837 . register ( ) ;
827838
839+ /* START WORKAROUND FOR PIPEWIRE LOOPBACK BUG
840+
841+ This workaround fixes an issue caused by the loopback device.
842+ Without this workaround, if nothing has interacted with the sink/source then setting
843+ the volume/mute will fail.
844+
845+ This workaround creates input and output streams which forces pipewire
846+ to use the real sink/source instead of the loopback device.
847+
848+ For context see:
849+
850+ * https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/4949
851+ * https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/4610
852+ * https://gitlab.freedesktop.org/pipewire/wireplumber/-/issues/822
853+ */
854+
855+ let output_done = Rc :: new ( Cell :: new ( false ) ) ;
856+ let output_done_clone = output_done. clone ( ) ;
857+ let input_done = Rc :: new ( Cell :: new ( false ) ) ;
858+ let input_done_clone = input_done. clone ( ) ;
859+
860+ let values: Vec < u8 > = PodSerializer :: serialize (
861+ Cursor :: new ( Vec :: new ( ) ) ,
862+ & Value :: Object ( Object {
863+ type_ : SPA_TYPE_OBJECT_Format ,
864+ id : SPA_PARAM_EnumFormat ,
865+ properties : AudioInfoRaw :: new ( ) . into ( ) ,
866+ } ) ,
867+ )
868+ . expect ( "Failed to serialize pod" )
869+ . 0
870+ . into_inner ( ) ;
871+
872+ let mut params = [ Pod :: from_bytes ( & values) . expect ( "Failed to create pod" ) ] ;
873+
874+ let output_stream = StreamBox :: new (
875+ & core,
876+ "i3status_pipewire_workaround_output_stream" ,
877+ properties ! {
878+ * keys:: MEDIA_TYPE => "Audio" ,
879+ * keys:: MEDIA_ROLE => "Music" ,
880+ * keys:: MEDIA_CATEGORY => "Playback" ,
881+ * keys:: AUDIO_CHANNELS => "2" ,
882+ } ,
883+ )
884+ . expect ( "Could not create output_stream" ) ;
885+
886+ let output_stream_listener = output_stream
887+ . add_local_listener ( )
888+ . process ( move |_stream, _acc : & mut f64 | {
889+ output_done_clone. set ( true ) ;
890+ } )
891+ . register ( )
892+ . expect ( "Could not add output_stream listener" ) ;
893+
894+ output_stream
895+ . connect (
896+ :: pipewire:: spa:: utils:: Direction :: Output ,
897+ None ,
898+ StreamFlags :: AUTOCONNECT | StreamFlags :: MAP_BUFFERS | StreamFlags :: RT_PROCESS ,
899+ & mut params,
900+ )
901+ . expect ( "Could not connect output_stream" ) ;
902+
903+ let input_stream = StreamBox :: new (
904+ & core,
905+ "i3status_pipewire_workaround_input_stream" ,
906+ properties ! {
907+ * keys:: MEDIA_TYPE => "Audio" ,
908+ * keys:: MEDIA_ROLE => "Music" ,
909+ * keys:: MEDIA_CATEGORY => "Playback" ,
910+ * keys:: AUDIO_CHANNELS => "2" ,
911+ } ,
912+ )
913+ . expect ( "Could not create input_stream" ) ;
914+ let input_stream_listener = input_stream
915+ . add_local_listener ( )
916+ . process ( move |_stream, _acc : & mut f64 | {
917+ input_done_clone. set ( true ) ;
918+ } )
919+ . register ( )
920+ . expect ( "Could not add input_stream listener" ) ;
921+
922+ input_stream
923+ . connect (
924+ :: pipewire:: spa:: utils:: Direction :: Input ,
925+ None ,
926+ StreamFlags :: AUTOCONNECT | StreamFlags :: MAP_BUFFERS | StreamFlags :: RT_PROCESS ,
927+ & mut params,
928+ )
929+ . expect ( "Could not connect input_stream" ) ;
930+
931+ while !output_done. get ( ) {
932+ main_loop. loop_ ( ) . iterate ( Duration :: from_secs ( 1 ) ) ;
933+ let event = update. take ( ) ;
934+ if !event. is_empty ( ) {
935+ client. send_update_event ( event) ;
936+ }
937+ }
938+ output_stream
939+ . disconnect ( )
940+ . expect ( "Unable to disconnect output_stream" ) ;
941+ output_stream_listener. unregister ( ) ;
942+
943+ while !input_done. get ( ) {
944+ main_loop. loop_ ( ) . iterate ( Duration :: from_secs ( 1 ) ) ;
945+ let event = update. take ( ) ;
946+ if !event. is_empty ( ) {
947+ client. send_update_event ( event) ;
948+ }
949+ }
950+ input_stream
951+ . disconnect ( )
952+ . expect ( "Unable to disconnect input_stream" ) ;
953+ input_stream_listener. unregister ( ) ;
954+
955+ // END WORKAROUND FOR PIPEWIRE LOOPBACK BUG
956+
828957 loop {
829958 main_loop. loop_ ( ) . iterate ( Duration :: from_hours ( 24 ) ) ;
830959 let event = update. take ( ) ;
0 commit comments