From cc2ff29def5534aec28345136a7b49ddabc83071 Mon Sep 17 00:00:00 2001 From: GHOSCHT <31184695+GHOSCHT@users.noreply.github.com> Date: Tue, 30 Apr 2024 20:21:40 +0200 Subject: [PATCH] Add basic implementation of pulse mainloop --- src/pulse_client.rs | 98 +++++++++++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 38 deletions(-) diff --git a/src/pulse_client.rs b/src/pulse_client.rs index ac97b8b..c7b1436 100644 --- a/src/pulse_client.rs +++ b/src/pulse_client.rs @@ -1,13 +1,15 @@ extern crate libpulse_binding as pulse; + use pulse::context::{Context, FlagSet as ContextFlagSet}; -use pulse::def::Retval; -use pulse::mainloop::standard::{IterateResult, Mainloop}; +use pulse::mainloop::threaded::Mainloop; use pulse::proplist::Proplist; +use regex::Regex; use std::cell::RefCell; use std::ops::Deref; use std::rc::Rc; +use std::time; -pub fn set_default_sink<'a>(name: &'a str) { +pub fn run() { let mut proplist = Proplist::new().unwrap(); proplist .set_str( @@ -29,60 +31,80 @@ pub fn set_default_sink<'a>(name: &'a str) { .expect("Failed to create new context"), )); + // Context state change callback + { + let ml_ref = Rc::clone(&mainloop); + let context_ref = Rc::clone(&context); + context + .borrow_mut() + .set_state_callback(Some(Box::new(move || { + let state = unsafe { (*context_ref.as_ptr()).get_state() }; + match state { + pulse::context::State::Ready + | pulse::context::State::Failed + | pulse::context::State::Terminated => unsafe { + (*ml_ref.as_ptr()).signal(false); + }, + _ => {} + } + }))); + } + context .borrow_mut() .connect(None, ContextFlagSet::NOFLAGS, None) .expect("Failed to connect context"); + mainloop.borrow_mut().lock(); + mainloop + .borrow_mut() + .start() + .expect("Failed to start mainloop"); + + // Wait for context to be ready loop { - match mainloop.borrow_mut().iterate(false) { - IterateResult::Quit(_) | IterateResult::Err(_) => { - eprintln!("Iterate state was not success, quitting..."); - return; - } - IterateResult::Success(_) => {} - } match context.borrow().get_state() { pulse::context::State::Ready => { break; } pulse::context::State::Failed | pulse::context::State::Terminated => { eprintln!("Context state failed/terminated, quitting..."); + mainloop.borrow_mut().unlock(); + mainloop.borrow_mut().stop(); return; } - _ => {} + _ => { + mainloop.borrow_mut().wait(); + } } } + context.borrow_mut().set_state_callback(None); + mainloop.borrow_mut().unlock(); - let op = context + //--------------------- + mainloop.borrow_mut().lock(); + + let sink_filter = Regex::new("USB_Audio_CODEC").expect("Creating RegEx failed"); + let callback_context = context.clone(); + context .borrow_mut() - .set_default_sink(name, |was_successful| match was_successful { - true => { - println!("Successfully set default sink"); - } - false => { - eprintln!("Failed to set default sink"); - } - }); - loop { - match mainloop.borrow_mut().iterate(false) { - IterateResult::Quit(_) | IterateResult::Err(_) => { - eprintln!("Iterate state was not success, quitting..."); - return; - } - IterateResult::Success(_) => {} - } - match op.get_state() { - pulse::operation::State::Done => { - break; - } - pulse::operation::State::Cancelled => { - eprintln!("Setting default sink was cancelled..."); - return; + .introspect() + .get_sink_info_list(move |items_result| match items_result { + pulse::callbacks::ListResult::Item(item) => { + let matches_regex = sink_filter.is_match(item.name.as_ref().unwrap()); + if matches_regex { + let name = item.name.as_ref().unwrap(); + println!("Setting default sink to: {}", name); + callback_context.borrow_mut().set_default_sink(name, |_| {}); + } } _ => {} - } - } + }); - mainloop.borrow_mut().quit(Retval(0)); + mainloop.borrow_mut().unlock(); + //--------------------- + + loop { + std::thread::sleep(time::Duration::from_secs(u64::MAX)); + } }