From 8ef97ac0d9d1a09bb5c781915ec0a8f9eda6f96c Mon Sep 17 00:00:00 2001 From: Fabio Lenherr / DashieTM Date: Wed, 15 Nov 2023 19:32:16 +0100 Subject: [PATCH] feat: Add basic event handling for audio --- src/components/base/utils.rs | 172 ++++++++++++- src/components/output/inputStreamEntry.rs | 40 +-- src/components/output/sinkBox.rs | 267 +++++++++++++++++++- src/components/output/sinkBoxImpl.rs | 10 +- src/components/window/handleSidebarClick.rs | 9 +- 5 files changed, 458 insertions(+), 40 deletions(-) diff --git a/src/components/base/utils.rs b/src/components/base/utils.rs index 771ebdb..d8795a1 100644 --- a/src/components/base/utils.rs +++ b/src/components/base/utils.rs @@ -4,7 +4,15 @@ use std::{ time::Duration, }; -use dbus::{blocking::Connection, Error}; +use dbus::{ + arg::{self, Append}, + blocking::Connection, + Error, +}; +use ReSet_Lib::{ + audio::audio::{InputStream, Sink}, + signals::GetVal, +}; #[derive(Default)] pub struct Listeners { @@ -31,3 +39,165 @@ impl Listeners { }); } } + +#[derive(Debug)] +pub struct SinkAdded { + pub sink: Sink, +} + +impl arg::AppendAll for SinkAdded { + fn append(&self, i: &mut arg::IterAppend) { + self.sink.append_by_ref(i); + } +} + +impl arg::ReadAll for SinkAdded { + fn read(i: &mut arg::Iter) -> Result { + Ok(SinkAdded { sink: i.read()? }) + } +} + +impl dbus::message::SignalArgs for SinkAdded { + const NAME: &'static str = "SinkAdded"; + const INTERFACE: &'static str = "org.xetibo.ReSet"; +} + +impl GetVal<(Sink,)> for SinkAdded { + fn get_value(&self) -> (Sink,) { + (self.sink.clone(),) + } +} + +#[derive(Debug)] +pub struct SinkChanged { + pub sink: Sink, +} + +impl arg::AppendAll for SinkChanged { + fn append(&self, i: &mut arg::IterAppend) { + self.sink.append_by_ref(i); + } +} + +impl arg::ReadAll for SinkChanged { + fn read(i: &mut arg::Iter) -> Result { + Ok(SinkChanged { sink: i.read()? }) + } +} + +impl dbus::message::SignalArgs for SinkChanged { + const NAME: &'static str = "SinkChanged"; + const INTERFACE: &'static str = "org.xetibo.ReSet"; +} + +impl GetVal<(Sink,)> for SinkChanged { + fn get_value(&self) -> (Sink,) { + (self.sink.clone(),) + } +} + +#[derive(Debug)] +pub struct SinkRemoved { + pub index: u32, +} + +impl arg::AppendAll for SinkRemoved { + fn append(&self, i: &mut arg::IterAppend) { + self.index.append_by_ref(i); + } +} + +impl arg::ReadAll for SinkRemoved { + fn read(i: &mut arg::Iter) -> Result { + Ok(SinkRemoved { index: i.read()? }) + } +} + +impl dbus::message::SignalArgs for SinkRemoved { + const NAME: &'static str = "SinkRemoved"; + const INTERFACE: &'static str = "org.xetibo.ReSet"; +} + +impl GetVal<(u32,)> for SinkRemoved { + fn get_value(&self) -> (u32,) { + (self.index.clone(),) + } +} + +#[derive(Debug)] +pub struct InputStreamAdded { + pub stream: InputStream, +} + +impl arg::AppendAll for InputStreamAdded { + fn append(&self, i: &mut arg::IterAppend) { + self.stream.append_by_ref(i); + } +} + +impl arg::ReadAll for InputStreamAdded { + fn read(i: &mut arg::Iter) -> Result { + Ok(InputStreamAdded { stream: i.read()? }) + } +} + +impl dbus::message::SignalArgs for InputStreamAdded { + const NAME: &'static str = "InputStreamAdded"; + const INTERFACE: &'static str = "org.xetibo.ReSet"; +} + +impl GetVal<(InputStream,)> for InputStreamAdded { + fn get_value(&self) -> (InputStream,) { + (self.stream.clone(),) + } +} + +#[derive(Debug)] +pub struct InputStreamChanged { + pub stream: InputStream, +} + +impl arg::AppendAll for InputStreamChanged { + fn append(&self, i: &mut arg::IterAppend) { + self.stream.append_by_ref(i); + } +} + +impl arg::ReadAll for InputStreamChanged { + fn read(i: &mut arg::Iter) -> Result { + Ok(InputStreamChanged { stream: i.read()? }) + } +} + +impl dbus::message::SignalArgs for InputStreamChanged { + const NAME: &'static str = "InputStreamChanged"; + const INTERFACE: &'static str = "org.xetibo.ReSet"; +} + +#[derive(Debug)] +pub struct InputStreamRemoved { + pub index: u32, +} + +impl arg::AppendAll for InputStreamRemoved { + fn append(&self, i: &mut arg::IterAppend) { + self.index.append_by_ref(i); + } +} + +impl arg::ReadAll for InputStreamRemoved { + fn read(i: &mut arg::Iter) -> Result { + Ok(InputStreamRemoved { index: i.read()? }) + } +} + +impl dbus::message::SignalArgs for InputStreamRemoved { + const NAME: &'static str = "InputStreamRemoved"; + const INTERFACE: &'static str = "org.xetibo.ReSet"; +} + +impl GetVal<(u32,)> for InputStreamRemoved { + fn get_value(&self) -> (u32,) { + (self.index.clone(),) + } +} diff --git a/src/components/output/inputStreamEntry.rs b/src/components/output/inputStreamEntry.rs index 6f591d1..78f50ea 100644 --- a/src/components/output/inputStreamEntry.rs +++ b/src/components/output/inputStreamEntry.rs @@ -26,6 +26,7 @@ impl InputStreamEntry { // TODO use event callback for progress bar -> this is the "im speaking" indicator // TODO handle events { + let index = stream.sink_index; let box_imp = sink_box.imp(); let imp = obj.imp(); if stream.muted { @@ -70,20 +71,25 @@ impl InputStreamEntry { } let list = list.unwrap(); imp.resetSelectedSink.set_model(Some(&*list)); - let mut map = box_imp.resetSinkMap.try_borrow(); - while map.is_err() { - map = box_imp.resetSinkMap.try_borrow(); - } - let map = map.unwrap(); - let mut name = box_imp.resetDefaultSink.try_borrow(); - while name.is_err() { - name = box_imp.resetDefaultSink.try_borrow(); - } - let name = name.unwrap(); - let name = &name.alias; - let index = map.get(name); - if index.is_some() { - imp.resetSelectedSink.set_selected(index.unwrap().1); + let map = box_imp.resetSinkMap.lock().unwrap(); + let sink_list = box_imp.resetSinkList.lock().unwrap(); + let name = sink_list.get(&index); + if name.is_some() { + let name = &name.unwrap().2; + let index = map.get(name); + if index.is_some() { + imp.resetSelectedSink.set_selected(index.unwrap().1); + } + } else { + let mut name = box_imp.resetDefaultSink.try_borrow(); + while name.is_err() { + name = box_imp.resetDefaultSink.try_borrow(); + } + let name = &name.unwrap().alias; + let index = map.get(name); + if index.is_some() { + imp.resetSelectedSink.set_selected(index.unwrap().1); + } } } imp.resetSelectedSink.connect_selected_notify( @@ -95,9 +101,9 @@ impl InputStreamEntry { let selected = selected.unwrap(); let selected = selected.downcast_ref::().unwrap(); let selected = selected.string().to_string(); - let mut sink = box_imp.resetSinkMap.try_borrow(); - while sink.is_err() { - sink = box_imp.resetSinkMap.try_borrow(); + let sink = box_imp.resetSinkMap.try_lock(); + if sink.is_err() { + return; } let sink = sink.unwrap(); let sink = sink.get(&selected); diff --git a/src/components/output/sinkBox.rs b/src/components/output/sinkBox.rs index 56e43e2..f895604 100644 --- a/src/components/output/sinkBox.rs +++ b/src/components/output/sinkBox.rs @@ -1,19 +1,26 @@ +use std::sync::atomic::Ordering; use std::sync::Arc; +use std::thread; use std::time::Duration; use crate::components::base::listEntry::ListEntry; -use crate::components::base::utils::Listeners; +use crate::components::base::utils::{ + InputStreamAdded, InputStreamChanged, InputStreamRemoved, Listeners, SinkAdded, SinkChanged, + SinkRemoved, +}; use crate::components::output::sinkEntry::set_sink_volume; use adw::glib::Object; use adw::prelude::{BoxExt, ButtonExt, RangeExt}; use adw::{glib, prelude::ListBoxRowExt}; use dbus::blocking::Connection; -use dbus::Error; +use dbus::message::SignalArgs; +use dbus::{Error, Path}; use glib::subclass::prelude::ObjectSubclassIsExt; use glib::{clone, Cast, Propagation, Variant}; use gtk::prelude::ActionableExt; use gtk::{gio, StringObject}; use ReSet_Lib::audio::audio::{InputStream, Sink}; +use ReSet_Lib::signals::GetVal; use super::inputStreamEntry::InputStreamEntry; use super::sinkBoxImpl; @@ -54,9 +61,9 @@ pub fn populate_sinks(output_box: Arc) { let sinks = get_sinks(); { let output_box_imp = output_box.imp(); - output_box_imp.resetDefaultSink.replace(get_default_sink()); + let mut map = output_box_imp.resetSinkMap.lock().unwrap(); let list = output_box_imp.resetModelList.borrow_mut(); - let mut map = output_box_imp.resetSinkMap.borrow_mut(); + output_box_imp.resetDefaultSink.replace(get_default_sink()); let mut i: u32 = 0; for sink in sinks.iter() { dbg!(sink.clone()); @@ -65,6 +72,7 @@ pub fn populate_sinks(output_box: Arc) { i += 1; } } + populate_inputstreams(output_box.clone()); glib::spawn_future(async move { glib::idle_add_once(move || { let output_box_ref_slider = output_box.clone(); @@ -79,22 +87,29 @@ pub fn populate_sinks(output_box: Arc) { let percentage = (fraction).to_string() + "%"; output_box_imp.resetVolumePercentage.set_text(&percentage); output_box_imp.resetVolumeSlider.set_value(*volume as f64); - for stream in sinks { + let mut list = output_box_imp.resetSinkList.lock().unwrap(); + println!("locked"); + for sink in sinks { + let index = sink.index; + let alias = sink.alias.clone(); let mut is_default = false; - if output_box_imp.resetDefaultSink.borrow().name == stream.name { + if output_box_imp.resetDefaultSink.borrow().name == sink.name { is_default = true; } - let entry = ListEntry::new(&SinkEntry::new( + let sink_entry = Arc::new(SinkEntry::new( is_default, output_box_imp.resetDefaultCheckButton.clone(), - stream, + sink, )); + let sink_clone = sink_entry.clone(); + let entry = Arc::new(ListEntry::new(&*sink_entry)); entry.set_activatable(false); - output_box_imp.resetSinks.append(&entry); + list.insert(index, (entry.clone(), sink_clone, alias)); + output_box_imp.resetSinks.append(&*entry); } let list = output_box_imp.resetModelList.borrow(); output_box_imp.resetSinkDropdown.set_model(Some(&*list)); - let map = output_box_imp.resetSinkMap.borrow(); + let map = output_box_imp.resetSinkMap.lock().unwrap(); let name = output_box_imp.resetDefaultSink.borrow(); let name = &name.alias; let index = map.get(name); @@ -113,7 +128,7 @@ pub fn populate_sinks(output_box: Arc) { let selected = selected.downcast_ref::().unwrap(); let selected = selected.string().to_string(); - let sink = output_box_imp.resetSinkMap.borrow(); + let sink = output_box_imp.resetSinkMap.lock().unwrap(); let sink = sink.get(&selected); if sink.is_none() { return; @@ -162,7 +177,7 @@ pub fn populate_sinks(output_box: Arc) { }); } -pub fn populate_inputstreams(_listeners: Arc, output_box: Arc) { +pub fn populate_inputstreams(output_box: Arc) { // TODO add listener let output_box_ref = output_box.clone(); @@ -171,10 +186,14 @@ pub fn populate_inputstreams(_listeners: Arc, output_box: Arc Sink { } res.unwrap().0 } + +pub fn start_output_box_listener(listeners: Arc, sink_box: Arc) { + gio::spawn_blocking(move || { + if listeners.network_listener.load(Ordering::SeqCst) { + return; + } + listeners.network_listener.store(true, Ordering::SeqCst); + + let conn = Connection::new_session().unwrap(); + let sink_added = SinkAdded::match_rule( + Some(&"org.xetibo.ReSet".into()), + Some(&Path::from("/org/xetibo/ReSet")), + ) + .static_clone(); + let sink_removed = SinkRemoved::match_rule( + Some(&"org.xetibo.ReSet".into()), + Some(&Path::from("/org/xetibo/ReSet")), + ) + .static_clone(); + let sink_changed = SinkChanged::match_rule( + Some(&"org.xetibo.ReSet".into()), + Some(&Path::from("/org/xetibo/ReSet")), + ) + .static_clone(); + let input_stream_added = InputStreamAdded::match_rule( + Some(&"org.xetibo.ReSet".into()), + Some(&Path::from("/org/xetibo/ReSet")), + ) + .static_clone(); + let input_stream_removed = InputStreamRemoved::match_rule( + Some(&"org.xetibo.ReSet".into()), + Some(&Path::from("/org/xetibo/ReSet")), + ) + .static_clone(); + let input_stream_changed = InputStreamChanged::match_rule( + Some(&"org.xetibo.ReSet".into()), + Some(&Path::from("/org/xetibo/ReSet")), + ) + .static_clone(); + let sink_added_box = sink_box.clone(); + let sink_removed_box = sink_box.clone(); + let sink_changed_box = sink_box.clone(); + let input_stream_added_box = sink_box.clone(); + let input_stream_removed_box = sink_box.clone(); + let input_stream_changed_box = sink_box.clone(); + let res = conn.add_match(sink_added, move |ir: SinkAdded, _, _| { + let sink_box = sink_added_box.clone(); + glib::spawn_future(async move { + glib::idle_add_once(move || { + let output_box = sink_box.clone(); + let output_box_imp = output_box.imp(); + let mut list = output_box_imp.resetSinkList.lock().unwrap(); + let index = ir.sink.index; + let alias = ir.sink.alias.clone(); + let mut is_default = false; + if output_box_imp.resetDefaultSink.borrow().name == ir.sink.name { + is_default = true; + } + let sink_entry = Arc::new(SinkEntry::new( + is_default, + output_box_imp.resetDefaultCheckButton.clone(), + ir.sink, + )); + let sink_clone = sink_entry.clone(); + let entry = Arc::new(ListEntry::new(&*sink_entry)); + entry.set_activatable(false); + list.insert(index, (entry.clone(), sink_clone, alias)); + output_box_imp.resetSinks.append(&*entry); + // TODO add to other map -> alias to index in dropdown + }); + }); + true + }); + if res.is_err() { + println!("fail on sink add"); + return; + } + let res = conn.add_match(sink_removed, move |ir: SinkRemoved, _, _| { + let sink_box = sink_removed_box.clone(); + glib::spawn_future(async move { + glib::idle_add_once(move || { + let output_box = sink_box.clone(); + let output_box_imp = output_box.imp(); + let mut list = output_box_imp.resetSinkList.lock().unwrap(); + let entry = list.get(&ir.index); + if entry.is_none() { + return; + } + output_box_imp.resetSinks.remove(&*entry.unwrap().0); + list.remove(&ir.index); + // TODO delete from other map -> alias to index in dropdown + }); + }); + true + }); + if res.is_err() { + println!("fail on sink remove"); + return; + } + let res = conn.add_match(input_stream_added, move |ir: InputStreamAdded, _, _| { + let sink_box = input_stream_added_box.clone(); + glib::spawn_future(async move { + glib::idle_add_once(move || { + let output_box = sink_box.clone(); + let output_box_imp = output_box.imp(); + let mut list = output_box_imp.resetInputStreamList.lock().unwrap(); + let index = ir.stream.index; + let input_stream = + Arc::new(InputStreamEntry::new(output_box.clone(), ir.stream)); + let entry = Arc::new(ListEntry::new(&*input_stream)); + entry.set_activatable(false); + list.insert(index, (entry.clone(), input_stream.clone())); + output_box_imp.resetInputStreams.append(&*entry); + }); + }); + true + }); + if res.is_err() { + println!("fail on stream add"); + return; + } + let res = conn.add_match(input_stream_changed, move |ir: InputStreamChanged, _, _| { + let imp = input_stream_changed_box.imp(); + dbg!(ir.stream.clone()); + let alias: String; + { + let sink_list = imp.resetSinkList.lock().unwrap(); + let alias_opt = sink_list.get(&ir.stream.sink_index); + if alias_opt.is_some() { + alias = alias_opt.unwrap().2.clone(); + } else { + alias = String::from(""); + } + dbg!(alias.clone()); + } + let sink_box = input_stream_changed_box.clone(); + glib::spawn_future(async move { + glib::idle_add_once(move || { + let output_box = sink_box.clone(); + let output_box_imp = output_box.imp(); + let entry: Arc; + { + println!("getting lock on streamlist"); + let list = output_box_imp.resetInputStreamList.lock().unwrap(); + let entry_opt = list.get(&ir.stream.index); + if entry_opt.is_none() { + return; + } + entry = entry_opt.unwrap().1.clone(); + } + println!("dropped lock on streamlist"); + let imp = entry.imp(); + if ir.stream.muted { + imp.resetSinkMute + .set_icon_name("audio-volume-muted-symbolic"); + } else { + imp.resetSinkMute + .set_icon_name("audio-volume-high-symbolic"); + } + let name = ir.stream.application_name.clone() + ": " + ir.stream.name.as_str(); + imp.resetSinkName.set_text(name.as_str()); + let volume = ir.stream.volume.first().unwrap_or_else(|| &(0 as u32)); + let fraction = (*volume as f64 / 655.36).round(); + let percentage = (fraction).to_string() + "%"; + imp.resetVolumePercentage.set_text(&percentage); + imp.resetVolumeSlider.set_value(*volume as f64); + // imp.stream.replace(ir.stream); + // { + // let sink = output_box_imp.resetDefaultSink.borrow(); + // imp.associatedSink.replace((sink.index, sink.name.clone())); + // } + println!("getting lock on map"); + let map = output_box_imp.resetSinkMap.lock().unwrap(); + let index = map.get(&alias); + if index.is_some() { + imp.resetSelectedSink.set_selected(index.unwrap().1); + } + println!("dropped lock on streamlist"); + }); + }); + true + }); + if res.is_err() { + println!("fail on stream change"); + return; + } + let res = conn.add_match(input_stream_removed, move |ir: InputStreamRemoved, _, _| { + let sink_box = input_stream_removed_box.clone(); + glib::spawn_future(async move { + glib::idle_add_once(move || { + let output_box = sink_box.clone(); + let output_box_imp = output_box.imp(); + let mut list = output_box_imp.resetInputStreamList.lock().unwrap(); + let entry = list.get(&ir.index); + if entry.is_none() { + return; + } + output_box_imp.resetInputStreams.remove(&*entry.unwrap().0); + list.remove(&ir.index); + }); + }); + true + }); + if res.is_err() { + println!("fail on stream remove"); + return; + } + listeners.network_listener.store(true, Ordering::SeqCst); + println!("starting thread listener"); + loop { + let _ = conn.process(Duration::from_millis(1000)); + if !listeners.network_listener.load(Ordering::SeqCst) { + println!("stopping thread listener"); + break; + } + // thread::sleep(Duration::from_millis(1000)); + // TODO is this really how we should do this? + } + }); +} diff --git a/src/components/output/sinkBoxImpl.rs b/src/components/output/sinkBoxImpl.rs index c25548e..3ba8712 100644 --- a/src/components/output/sinkBoxImpl.rs +++ b/src/components/output/sinkBoxImpl.rs @@ -38,11 +38,13 @@ pub struct SinkBox { pub resetInputStreams: TemplateChild, pub resetDefaultCheckButton: Arc, pub resetDefaultSink: Arc>, - pub resetSinkList: Arc>>, - pub resetInputStreamList: Arc>>, + pub resetSinkList: Arc, Arc, String)>>>, + pub resetInputStreamList: Arc, Arc)>>>, pub resetModelList: Arc>, - // first u32 is the index of the sink, the second the index in the model list - pub resetSinkMap: Arc>>, + // first u32 is the index of the sink, the second the index in the model list and the third is + // the full name + pub resetSinkMap: Arc>>, + // pub : Arc>>, } #[glib::object_subclass] diff --git a/src/components/window/handleSidebarClick.rs b/src/components/window/handleSidebarClick.rs index 3a5f062..7757561 100644 --- a/src/components/window/handleSidebarClick.rs +++ b/src/components/window/handleSidebarClick.rs @@ -5,8 +5,10 @@ use std::sync::Arc; use crate::components::base::settingBox::SettingBox; use crate::components::base::utils::Listeners; use crate::components::bluetooth::bluetoothBox::BluetoothBox; -use crate::components::input::sourceBox::{SourceBox, populate_outputstreams, populate_sources}; -use crate::components::output::sinkBox::{SinkBox, populate_inputstreams, populate_sinks}; +use crate::components::input::sourceBox::{populate_outputstreams, populate_sources, SourceBox}; +use crate::components::output::sinkBox::{ + populate_inputstreams, populate_sinks, start_output_box_listener, SinkBox, +}; use crate::components::wifi::wifiBox::{scanForWifi, show_stored_connections, WifiBox}; use gtk::prelude::WidgetExt; use gtk::{FlowBox, Frame, Label}; @@ -64,7 +66,6 @@ pub const HANDLE_AUDIO_CLICK: fn(Arc, FlowBox) = listeners.bluetooth_listener.store(false, Ordering::SeqCst); listeners.pulse_listener.store(true, Ordering::SeqCst); let audioOutput = Arc::new(SinkBox::new()); - populate_inputstreams(listeners.clone(), audioOutput.clone()); populate_sinks(audioOutput.clone()); let audioFrame = wrapInFrame(SettingBox::new(&*audioOutput)); let audioInput = Arc::new(SourceBox::new()); @@ -83,8 +84,8 @@ pub const HANDLE_VOLUME_CLICK: fn(Arc, FlowBox) = listeners.bluetooth_listener.store(false, Ordering::SeqCst); listeners.pulse_listener.store(false, Ordering::SeqCst); let audioOutput = Arc::new(SinkBox::new()); - populate_inputstreams(listeners.clone(), audioOutput.clone()); populate_sinks(audioOutput.clone()); + start_output_box_listener(listeners.clone(), audioOutput.clone()); let audioFrame = wrapInFrame(SettingBox::new(&*audioOutput)); resetMain.remove_all(); resetMain.insert(&audioFrame, -1);