diff --git a/Cargo.toml b/Cargo.toml index a71328b..2cdc7a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" description = "A wip universal Linux settings application." [dependencies] -reset_daemon = "0.1.3" +reset_daemon = "0.1.7" ReSet-Lib = "0.2.8" adw = { version = "0.5.3", package = "libadwaita", features = ["v1_4"] } dbus = "0.9.7" diff --git a/flatpak/README.md b/flatpak/README.md index 75d76f6..58925fa 100644 --- a/flatpak/README.md +++ b/flatpak/README.md @@ -10,3 +10,8 @@ ### instructions for installation: `flatpak install --user reset.flatpak` + +### permissions +currently ReSet uses permission on all devices, for some reason otherwise it can't access sound settings like volume changes etc. + +This can likely be fixed by implementing portal integration later. diff --git a/flatpak/cargo-sources.json b/flatpak/cargo-sources.json index ef16d58..9ff0d58 100644 --- a/flatpak/cargo-sources.json +++ b/flatpak/cargo-sources.json @@ -1042,14 +1042,14 @@ { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/reset_daemon/reset_daemon-0.1.3.crate", - "sha256": "1d7b1c575b773eadd0fc8991de8abb883cfb7bce9c5e8c4f9e10b85cb142efee", - "dest": "cargo/vendor/reset_daemon-0.1.3" + "url": "https://static.crates.io/crates/reset_daemon/reset_daemon-0.1.7.crate", + "sha256": "f7b48ba0698bdb4ead119140a557346907b40ae17cac324687a032b4d6ea9851", + "dest": "cargo/vendor/reset_daemon-0.1.7" }, { "type": "inline", - "contents": "{\"package\": \"1d7b1c575b773eadd0fc8991de8abb883cfb7bce9c5e8c4f9e10b85cb142efee\", \"files\": {}}", - "dest": "cargo/vendor/reset_daemon-0.1.3", + "contents": "{\"package\": \"f7b48ba0698bdb4ead119140a557346907b40ae17cac324687a032b4d6ea9851\", \"files\": {}}", + "dest": "cargo/vendor/reset_daemon-0.1.7", "dest-filename": ".cargo-checksum.json" }, { diff --git a/flatpak/org.xetibo.ReSet.json b/flatpak/org.xetibo.ReSet.json index 34a7a20..67aa329 100644 --- a/flatpak/org.xetibo.ReSet.json +++ b/flatpak/org.xetibo.ReSet.json @@ -3,15 +3,20 @@ "runtime": "org.gnome.Platform", "runtime-version": "45", "sdk": "org.gnome.Sdk", - "sdk-extensions": ["org.freedesktop.Sdk.Extension.rust-stable"], + "sdk-extensions": [ + "org.freedesktop.Sdk.Extension.rust-stable" + ], "command": "reset", "finish-args": [ "--socket=session-bus", + "--socket=system-bus", "--socket=pulseaudio", "--share=ipc", "--socket=fallback-x11", "--socket=wayland", - "--device=dri" + "--device=dri", + "--device=all", + "--allow=bluetooth" ], "build-options": { "append-path": "/usr/lib/sdk/rust-stable/bin" @@ -29,7 +34,7 @@ "cargo --offline fetch --manifest-path Cargo.toml --verbose", "cargo --offline build --release --verbose", "install -Dm755 ./target/release/reset -t /app/bin/", - "install -Dm644 ./src/resources/icons/ReSet.svg /app/share/icons/hicolor/scalable/apps/org.xetibo.ReSet.svg", + "install -Dm644 ./src/resources/icons/ReSet.svg /app/share/icons/hicolor/scalable/apps/org.xetibo.ReSet.svg", "install -Dm644 ./flatpak/org.xetibo.ReSet.desktop /app/share/applications/org.xetibo.ReSet.desktop" ], "sources": [ diff --git a/src/components/base/utils.rs b/src/components/base/utils.rs index 771ebdb..bbd1a1b 100644 --- a/src/components/base/utils.rs +++ b/src/components/base/utils.rs @@ -1,10 +1,21 @@ use std::{ - sync::atomic::{AtomicBool, Ordering}, + sync::{atomic::{AtomicBool, Ordering}, Arc}, thread, time::Duration, }; -use dbus::{blocking::Connection, Error}; +use dbus::{ + arg::{self, Append}, + blocking::Connection, + Error, +}; +use gtk::gio; +use ReSet_Lib::{ + audio::audio::{InputStream, OutputStream, Sink, Source}, + signals::GetVal, +}; + +use crate::components::{input::sourceBox::{SourceBox, start_input_box_listener}, output::sinkBox::{SinkBox, start_output_box_listener}}; #[derive(Default)] pub struct Listeners { @@ -31,3 +42,350 @@ 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(),) + } +} + +#[derive(Debug)] +pub struct SourceAdded { + pub source: Source, +} + +impl arg::AppendAll for SourceAdded { + fn append(&self, i: &mut arg::IterAppend) { + self.source.append_by_ref(i); + } +} + +impl arg::ReadAll for SourceAdded { + fn read(i: &mut arg::Iter) -> Result { + Ok(SourceAdded { source: i.read()? }) + } +} + +impl dbus::message::SignalArgs for SourceAdded { + const NAME: &'static str = "SourceAdded"; + const INTERFACE: &'static str = "org.xetibo.ReSet"; +} + +impl GetVal<(Source,)> for SourceAdded { + fn get_value(&self) -> (Source,) { + (self.source.clone(),) + } +} + +#[derive(Debug)] +pub struct SourceChanged { + pub source: Source, +} + +impl arg::AppendAll for SourceChanged { + fn append(&self, i: &mut arg::IterAppend) { + self.source.append_by_ref(i); + } +} + +impl arg::ReadAll for SourceChanged { + fn read(i: &mut arg::Iter) -> Result { + Ok(SourceChanged { source: i.read()? }) + } +} + +impl dbus::message::SignalArgs for SourceChanged { + const NAME: &'static str = "SourceChanged"; + const INTERFACE: &'static str = "org.xetibo.ReSet"; +} + +impl GetVal<(Source,)> for SourceChanged { + fn get_value(&self) -> (Source,) { + (self.source.clone(),) + } +} + +#[derive(Debug)] +pub struct SourceRemoved { + pub index: u32, +} + +impl arg::AppendAll for SourceRemoved { + fn append(&self, i: &mut arg::IterAppend) { + self.index.append_by_ref(i); + } +} + +impl arg::ReadAll for SourceRemoved { + fn read(i: &mut arg::Iter) -> Result { + Ok(SourceRemoved { index: i.read()? }) + } +} + +impl dbus::message::SignalArgs for SourceRemoved { + const NAME: &'static str = "SourceRemoved"; + const INTERFACE: &'static str = "org.xetibo.ReSet"; +} + +impl GetVal<(u32,)> for SourceRemoved { + fn get_value(&self) -> (u32,) { + (self.index.clone(),) + } +} + +#[derive(Debug)] +pub struct OutputStreamAdded { + pub stream: OutputStream, +} + +impl arg::AppendAll for OutputStreamAdded { + fn append(&self, i: &mut arg::IterAppend) { + self.stream.append_by_ref(i); + } +} + +impl arg::ReadAll for OutputStreamAdded { + fn read(i: &mut arg::Iter) -> Result { + Ok(OutputStreamAdded { stream: i.read()? }) + } +} + +impl dbus::message::SignalArgs for OutputStreamAdded { + const NAME: &'static str = "OutputStreamAdded"; + const INTERFACE: &'static str = "org.xetibo.ReSet"; +} + +impl GetVal<(OutputStream,)> for OutputStreamAdded { + fn get_value(&self) -> (OutputStream,) { + (self.stream.clone(),) + } +} + +#[derive(Debug)] +pub struct OutputStreamChanged { + pub stream: OutputStream, +} + +impl arg::AppendAll for OutputStreamChanged { + fn append(&self, i: &mut arg::IterAppend) { + self.stream.append_by_ref(i); + } +} + +impl arg::ReadAll for OutputStreamChanged { + fn read(i: &mut arg::Iter) -> Result { + Ok(OutputStreamChanged { stream: i.read()? }) + } +} + +impl dbus::message::SignalArgs for OutputStreamChanged { + const NAME: &'static str = "OutputStreamChanged"; + const INTERFACE: &'static str = "org.xetibo.ReSet"; +} + +#[derive(Debug)] +pub struct OutputStreamRemoved { + pub index: u32, +} + +impl arg::AppendAll for OutputStreamRemoved { + fn append(&self, i: &mut arg::IterAppend) { + self.index.append_by_ref(i); + } +} + +impl arg::ReadAll for OutputStreamRemoved { + fn read(i: &mut arg::Iter) -> Result { + Ok(OutputStreamRemoved { index: i.read()? }) + } +} + +impl dbus::message::SignalArgs for OutputStreamRemoved { + const NAME: &'static str = "OutputStreamRemoved"; + const INTERFACE: &'static str = "org.xetibo.ReSet"; +} + +impl GetVal<(u32,)> for OutputStreamRemoved { + fn get_value(&self) -> (u32,) { + (self.index.clone(),) + } +} + +pub fn start_event_listener(listeners: Arc, sink_box: Option>,source_box: Option>) { + gio::spawn_blocking(move || { + let mut conn = Connection::new_session().unwrap(); + + if sink_box.is_some() { + conn = start_output_box_listener(conn, listeners.clone(), sink_box.unwrap()); + } + if source_box.is_some() { + conn = start_input_box_listener(conn, listeners.clone(), source_box.unwrap()); + } + + loop { + let _ = conn.process(Duration::from_millis(1000)); + if !listeners.network_listener.load(Ordering::SeqCst) { + println!("stopping audio listener"); + break; + } + // thread::sleep(Duration::from_millis(1000)); + // TODO is this really how we should do this? + } + }); +} diff --git a/src/components/input/outputStreamEntry.rs b/src/components/input/outputStreamEntry.rs index 00a0d2a..afff09f 100644 --- a/src/components/input/outputStreamEntry.rs +++ b/src/components/input/outputStreamEntry.rs @@ -8,7 +8,7 @@ use dbus::blocking::Connection; use dbus::Error; use glib::subclass::types::ObjectSubclassIsExt; use glib::{clone, Cast, Propagation}; -use gtk::StringObject; +use gtk::{gio, StringObject}; use ReSet_Lib::audio::audio::OutputStream; use super::outputStreamEntryImpl; @@ -24,8 +24,6 @@ impl OutputStreamEntry { pub fn new(source_box: Arc, stream: OutputStream) -> Self { let obj: Self = Object::builder().build(); // TODO use event callback for progress bar -> this is the "im speaking" indicator - // TODO map mute to callback - // TODO map dropdown { let box_imp = source_box.imp(); let imp = obj.imp(); @@ -54,17 +52,17 @@ impl OutputStreamEntry { }), ); { - let mut list = box_imp.resetModelList.try_borrow(); - while list.is_err() { - list = box_imp.resetModelList.try_borrow(); - } - let list = list.unwrap(); + let list = box_imp.resetModelList.read().unwrap(); + // while list.is_err() { + // list = box_imp.resetModelList.try_borrow(); + // } + // let list = list.unwrap(); imp.resetSelectedSource.set_model(Some(&*list)); - let mut map = box_imp.resetSourceMap.try_borrow(); - while map.is_err() { - map = box_imp.resetSourceMap.try_borrow(); - } - let map = map.unwrap(); + let map = box_imp.resetSourceMap.write().unwrap(); + // while map.is_err() { + // map = box_imp.resetSourceMap.try_borrow(); + // } + // let map = map.unwrap(); let mut name = box_imp.resetDefaultSource.try_borrow(); while name.is_err() { name = box_imp.resetDefaultSource.try_borrow(); @@ -85,11 +83,7 @@ impl OutputStreamEntry { let selected = selected.unwrap(); let selected = selected.downcast_ref::().unwrap(); let selected = selected.string().to_string(); - let mut source = box_imp.resetSourceMap.try_borrow(); - while source.is_err() { - source = box_imp.resetSourceMap.try_borrow(); - } - let source = source.unwrap(); + let source = box_imp.resetSourceMap.write().unwrap(); let source = source.get(&selected); if source.is_none() { return; @@ -116,10 +110,10 @@ impl OutputStreamEntry { let index = stream.index; if muted { imp.resetSourceMute - .set_icon_name("audio-volume-muted-symbolic"); + .set_icon_name("microphone-disabled-symbolic"); } else { imp.resetSourceMute - .set_icon_name("audio-volume-high-symbolic"); + .set_icon_name("audio-input-microphone-symbolic"); } toggle_output_stream_mute(index, muted); })); @@ -129,52 +123,63 @@ impl OutputStreamEntry { } fn set_outputstream_volume(value: f64, index: u32, channels: u16) -> bool { + gio::spawn_blocking(move || { let conn = Connection::new_session().unwrap(); let proxy = conn.with_proxy( "org.xetibo.ReSet", "/org/xetibo/ReSet", Duration::from_millis(1000), ); - let res: Result<(bool,), Error> = proxy.method_call( + let _: Result<(), Error> = proxy.method_call( "org.xetibo.ReSet", "SetOutputStreamVolume", (index, channels, value as u32), ); - if res.is_err() { - return false; - } - res.unwrap().0 + // if res.is_err() { + // return false; + // } + // res.unwrap().0 + }); + true } fn toggle_output_stream_mute(index: u32, muted: bool) -> bool { + gio::spawn_blocking(move || { let conn = Connection::new_session().unwrap(); let proxy = conn.with_proxy( "org.xetibo.ReSet", "/org/xetibo/ReSet", Duration::from_millis(1000), ); - let res: Result<(bool,), Error> = + let _: Result<(), Error> = proxy.method_call("org.xetibo.ReSet", "SetOutputStreamMute", (index, muted)); - if res.is_err() { - return false; - } - res.unwrap().0 + // if res.is_err() { + // return false; + // } + // res.unwrap().0 + }); + true } fn set_source_of_output_stream(stream: u32, source: u32) -> bool { - let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy( - "org.xetibo.ReSet", - "/org/xetibo/ReSet", - Duration::from_millis(1000), - ); - let res: Result<(bool,), Error> = proxy.method_call( - "org.xetibo.ReSet", - "SetSourceOfOutputStream", - (stream, source), - ); - if res.is_err() { - return false; - } - res.unwrap().0 + gio::spawn_blocking(move || { + let conn = Connection::new_session().unwrap(); + let proxy = conn.with_proxy( + "org.xetibo.ReSet", + "/org/xetibo/ReSet", + Duration::from_millis(1000), + ); + let _: Result<(bool,), Error> = proxy.method_call( + "org.xetibo.ReSet", + "SetSourceOfOutputStream", + (stream, source), + ); + // if res.is_err() { + // return false; + // } + // res.unwrap().0 + }); + true } + +// TODO propagate error from dbus diff --git a/src/components/input/sourceBox.rs b/src/components/input/sourceBox.rs index 87773b3..87fc214 100644 --- a/src/components/input/sourceBox.rs +++ b/src/components/input/sourceBox.rs @@ -1,15 +1,20 @@ +use std::sync::atomic::Ordering; use std::sync::Arc; use std::time::Duration; use crate::components::base::listEntry::ListEntry; -use crate::components::base::utils::Listeners; +use crate::components::base::utils::{ + Listeners, OutputStreamAdded, OutputStreamChanged, OutputStreamRemoved, SourceAdded, + SourceChanged, SourceRemoved, +}; use crate::components::input::sourceBoxImpl; use crate::components::input::sourceEntry::set_source_volume; use adw::glib; use adw::glib::Object; -use adw::prelude::{BoxExt, ButtonExt, ListBoxRowExt, RangeExt}; +use adw::prelude::{BoxExt, ButtonExt, CheckButtonExt, ListBoxRowExt, RangeExt}; 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; @@ -48,55 +53,65 @@ impl SourceBox { } } -pub fn populate_sources(output_box: Arc) { +pub fn populate_sources(input_box: Arc) { gio::spawn_blocking(move || { - let output_box_imp = output_box.imp(); + let output_box_imp = input_box.imp(); let sources = get_sources(); { - let list = output_box_imp.resetModelList.borrow_mut(); - let mut map = output_box_imp.resetSourceMap.borrow_mut(); + let list = output_box_imp.resetModelList.write().unwrap(); + let mut map = output_box_imp.resetSourceMap.write().unwrap(); + let mut model_index = output_box_imp.resetModelIndex.write().unwrap(); let mut i: u32 = 0; for source in sources.iter() { list.append(&source.alias); map.insert(source.alias.clone(), (source.index, i, source.name.clone())); i += 1; + *model_index += 1; } } output_box_imp .resetDefaultSource .replace(get_default_source()); + + populate_outputstreams(input_box.clone()); glib::spawn_future(async move { glib::idle_add_once(move || { // TODO handle events - let output_box_ref_slider = output_box.clone(); - let output_box_ref_mute = output_box.clone(); - let output_box_ref = output_box.clone(); + let output_box_ref_slider = input_box.clone(); + let output_box_ref_mute = input_box.clone(); + let output_box_ref = input_box.clone(); { let output_box_imp = output_box_ref.imp(); - let default_sink = output_box_imp.resetDefaultSource.clone(); // Clone outside closure - let source = default_sink.borrow(); // + let default_sink = output_box_imp.resetDefaultSource.clone(); + let source = default_sink.borrow(); let volume = source.volume.first().unwrap_or_else(|| &(0 as u32)); let fraction = (*volume as f64 / 655.36).round(); let percentage = (fraction).to_string() + "%"; output_box_imp.resetVolumePercentage.set_text(&percentage); output_box_imp.resetVolumeSlider.set_value(*volume as f64); + let mut list = output_box_imp.resetSourceList.write().unwrap(); for stream in sources { + let index = source.index; + let alias = source.alias.clone(); let mut is_default = false; if output_box_imp.resetDefaultSource.borrow().name == stream.name { is_default = true; } - let entry = ListEntry::new(&SourceEntry::new( + let source_entry = Arc::new(SourceEntry::new( is_default, output_box_imp.resetDefaultCheckButton.clone(), stream, )); + let source_clone = source_entry.clone(); + let entry = Arc::new(ListEntry::new(&*source_entry)); entry.set_activatable(false); - output_box_imp.resetSources.append(&entry); + list.insert(index, (entry.clone(), source_clone, alias)); + output_box_imp.resetSources.append(&*entry); } - let list = output_box_imp.resetModelList.borrow(); + let list = output_box_imp.resetModelList.read().unwrap(); output_box_imp.resetSourceDropdown.set_model(Some(&*list)); - let map = output_box_imp.resetSourceMap.borrow(); + let map = output_box_imp.resetSourceMap.read().unwrap(); let name = output_box_imp.resetDefaultSource.borrow(); let name = &name.alias; let index = map.get(name); @@ -115,7 +130,7 @@ pub fn populate_sources(output_box: Arc) { let selected = selected.downcast_ref::().unwrap(); let selected = selected.string().to_string(); - let source = output_box_imp.resetSourceMap.borrow(); + let source = output_box_imp.resetSourceMap.read().unwrap(); let source = source.get(&selected); if source.is_none() { return; @@ -131,7 +146,6 @@ pub fn populate_sources(output_box: Arc) { .connect_change_value(move |_, _, value| { let imp = output_box_ref_slider.imp(); let fraction = (value / 655.36).round(); - println!("{fraction}"); let percentage = (fraction).to_string() + "%"; imp.resetVolumePercentage.set_text(&percentage); let source = imp.resetDefaultSource.borrow(); @@ -153,10 +167,10 @@ pub fn populate_sources(output_box: Arc) { let index = stream.index; if muted { imp.resetSourceMute - .set_icon_name("audio-volume-muted-symbolic"); + .set_icon_name("microphone-disabled-symbolic"); } else { imp.resetSourceMute - .set_icon_name("audio-volume-high-symbolic"); + .set_icon_name("audio-input-microphone-symbolic"); } toggle_source_mute(index, muted); }); @@ -165,19 +179,23 @@ pub fn populate_sources(output_box: Arc) { }); } -pub fn populate_outputstreams(_listeners: Arc, output_box: Arc) { +pub fn populate_outputstreams(input_box: Arc) { // TODO add listener - let output_box_ref = output_box.clone(); + let input_box_ref = input_box.clone(); gio::spawn_blocking(move || { let streams = get_output_streams(); glib::spawn_future(async move { glib::idle_add_once(move || { - let output_box_imp = output_box_ref.imp(); + let input_box_imp = input_box_ref.imp(); + let mut list = input_box_imp.resetOutputStreamList.write().unwrap(); for stream in streams { - let entry = ListEntry::new(&OutputStreamEntry::new(output_box.clone(), stream)); + let index = stream.index; + let input_stream = Arc::new(OutputStreamEntry::new(input_box.clone(), stream)); + let entry = Arc::new(ListEntry::new(&*input_stream)); entry.set_activatable(false); - output_box_imp.resetOutputStreams.append(&entry); + list.insert(index, (entry.clone(), input_stream.clone())); + input_box_imp.resetOutputStreams.append(&*entry); } }); }); @@ -228,3 +246,262 @@ fn get_default_source() -> Source { } res.unwrap().0 } + +pub fn start_input_box_listener( + conn: Connection, + listeners: Arc, + source_box: Arc, +) -> Connection { + if listeners.network_listener.load(Ordering::SeqCst) { + return conn; + } + listeners.network_listener.store(true, Ordering::SeqCst); + + let source_added = SourceAdded::match_rule( + Some(&"org.xetibo.ReSet".into()), + Some(&Path::from("/org/xetibo/ReSet")), + ) + .static_clone(); + let source_removed = SourceRemoved::match_rule( + Some(&"org.xetibo.ReSet".into()), + Some(&Path::from("/org/xetibo/ReSet")), + ) + .static_clone(); + let source_changed = SourceChanged::match_rule( + Some(&"org.xetibo.ReSet".into()), + Some(&Path::from("/org/xetibo/ReSet")), + ) + .static_clone(); + let output_stream_added = OutputStreamAdded::match_rule( + Some(&"org.xetibo.ReSet".into()), + Some(&Path::from("/org/xetibo/ReSet")), + ) + .static_clone(); + let output_stream_removed = OutputStreamRemoved::match_rule( + Some(&"org.xetibo.ReSet".into()), + Some(&Path::from("/org/xetibo/ReSet")), + ) + .static_clone(); + let output_stream_changed = OutputStreamChanged::match_rule( + Some(&"org.xetibo.ReSet".into()), + Some(&Path::from("/org/xetibo/ReSet")), + ) + .static_clone(); + + let source_added_box = source_box.clone(); + let source_removed_box = source_box.clone(); + let source_changed_box = source_box.clone(); + let output_stream_added_box = source_box.clone(); + let output_stream_removed_box = source_box.clone(); + let output_stream_changed_box = source_box.clone(); + + let res = conn.add_match(source_added, move |ir: SourceAdded, _, _| { + let source_box = source_added_box.clone(); + glib::spawn_future(async move { + glib::idle_add_once(move || { + let output_box = source_box.clone(); + let output_box_imp = output_box.imp(); + let mut list = output_box_imp.resetSourceList.write().unwrap(); + let index = ir.source.index; + let alias = ir.source.alias.clone(); + let name = ir.source.name.clone(); + let mut is_default = false; + if output_box_imp.resetDefaultSource.borrow().name == ir.source.name { + is_default = true; + } + let source_entry = Arc::new(SourceEntry::new( + is_default, + output_box_imp.resetDefaultCheckButton.clone(), + ir.source, + )); + let source_clone = source_entry.clone(); + let entry = Arc::new(ListEntry::new(&*source_entry)); + entry.set_activatable(false); + list.insert(index, (entry.clone(), source_clone, alias.clone())); + output_box_imp.resetSources.append(&*entry); + let mut map = output_box_imp.resetSourceMap.write().unwrap(); + let mut index = output_box_imp.resetModelIndex.write().unwrap(); + map.insert(alias, (*index, *index, name)); + *index += 1; + }); + }); + true + }); + if res.is_err() { + println!("fail on source add"); + return conn; + } + + let res = conn.add_match(source_removed, move |ir: SourceRemoved, _, _| { + let source_box = source_removed_box.clone(); + glib::spawn_future(async move { + glib::idle_add_once(move || { + let output_box = source_box.clone(); + let output_box_imp = output_box.imp(); + let mut list = output_box_imp.resetSourceList.write().unwrap(); + let entry = list.get(&ir.index); + if entry.is_none() { + return; + } + output_box_imp.resetSources.remove(&*entry.unwrap().0); + list.remove(&ir.index); + let alias = list.remove(&ir.index); + if alias.is_none() { + return; + } + let mut map = output_box_imp.resetSourceMap.write().unwrap(); + map.remove(&alias.unwrap().2); + let mut index = output_box_imp.resetModelIndex.write().unwrap(); + *index -= 1; + }); + }); + true + }); + if res.is_err() { + println!("fail on source remove"); + return conn; + } + + let res = conn.add_match(source_changed, move |ir: SourceChanged, _, _| { + let source_box = source_changed_box.clone(); + let default_source = get_default_source(); + glib::spawn_future(async move { + glib::idle_add_once(move || { + let output_box = source_box.clone(); + let output_box_imp = output_box.imp(); + let list = output_box_imp.resetSourceList.read().unwrap(); + let entry = list.get(&ir.source.index); + if entry.is_none() { + return; + } + let imp = entry.unwrap().1.imp(); + let is_default = ir.source.name == default_source.name; + imp.resetSourceName + .set_text(ir.source.alias.clone().as_str()); + let volume = ir.source.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); + if is_default { + imp.resetSelectedSource.set_active(true); + } else { + imp.resetSelectedSource.set_active(false); + } + }); + }); + true + }); + if res.is_err() { + println!("fail on source remove"); + return conn; + } + + let res = conn.add_match(output_stream_added, move |ir: OutputStreamAdded, _, _| { + let source_box = output_stream_added_box.clone(); + glib::spawn_future(async move { + glib::idle_add_once(move || { + let output_box = source_box.clone(); + let output_box_imp = output_box.imp(); + let mut list = output_box_imp.resetOutputStreamList.write().unwrap(); + let index = ir.stream.index; + let output_stream = Arc::new(OutputStreamEntry::new(output_box.clone(), ir.stream)); + let entry = Arc::new(ListEntry::new(&*output_stream)); + entry.set_activatable(false); + list.insert(index, (entry.clone(), output_stream.clone())); + output_box_imp.resetOutputStreams.append(&*entry); + }); + }); + true + }); + if res.is_err() { + println!("fail on stream add"); + return conn; + } + + let res = conn.add_match( + output_stream_changed, + move |ir: OutputStreamChanged, _, _| { + let imp = output_stream_changed_box.imp(); + let alias: String; + { + let source_list = imp.resetSourceList.read().unwrap(); + let alias_opt = source_list.get(&ir.stream.source_index); + if alias_opt.is_some() { + alias = alias_opt.unwrap().2.clone(); + } else { + alias = String::from(""); + } + } + let source_box = output_stream_changed_box.clone(); + glib::spawn_future(async move { + glib::idle_add_once(move || { + let output_box = source_box.clone(); + let output_box_imp = output_box.imp(); + let entry: Arc; + { + let list = output_box_imp.resetOutputStreamList.read().unwrap(); + let entry_opt = list.get(&ir.stream.index); + if entry_opt.is_none() { + return; + } + entry = entry_opt.unwrap().1.clone(); + } + let imp = entry.imp(); + if ir.stream.muted { + imp.resetSourceMute + .set_icon_name("microphone-disabled-symbolic"); + } else { + imp.resetSourceMute + .set_icon_name("audio-input-microphone-symbolic"); + } + let name = ir.stream.application_name.clone() + ": " + ir.stream.name.as_str(); + imp.resetSourceName.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); + let map = output_box_imp.resetSourceMap.read().unwrap(); + let index = map.get(&alias); + if index.is_some() { + imp.resetSelectedSource.set_selected(index.unwrap().1); + } + }); + }); + true + }, + ); + if res.is_err() { + println!("fail on stream change"); + return conn; + } + + let res = conn.add_match( + output_stream_removed, + move |ir: OutputStreamRemoved, _, _| { + let source_box = output_stream_removed_box.clone(); + glib::spawn_future(async move { + glib::idle_add_once(move || { + let output_box = source_box.clone(); + let output_box_imp = output_box.imp(); + let mut list = output_box_imp.resetOutputStreamList.write().unwrap(); + let entry = list.get(&ir.index); + if entry.is_none() { + return; + } + output_box_imp.resetOutputStreams.remove(&*entry.unwrap().0); + list.remove(&ir.index); + }); + }); + true + }, + ); + if res.is_err() { + println!("fail on stream remove"); + return conn; + } + + listeners.network_listener.store(true, Ordering::SeqCst); + conn +} diff --git a/src/components/input/sourceBoxImpl.rs b/src/components/input/sourceBoxImpl.rs index 1e491f2..b141dd2 100644 --- a/src/components/input/sourceBoxImpl.rs +++ b/src/components/input/sourceBoxImpl.rs @@ -1,13 +1,13 @@ use std::cell::RefCell; use std::collections::HashMap; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, RwLock}; use crate::components::base::listEntry::ListEntry; use crate::components::input::sourceBox; use gtk::subclass::prelude::*; use gtk::{glib, CheckButton, CompositeTemplate, DropDown, StringList, TemplateChild}; use gtk::{prelude::*, Button, Label, ProgressBar, Scale}; -use ReSet_Lib::audio::audio::{OutputStream, Source}; +use ReSet_Lib::audio::audio::Source; use super::outputStreamEntry::OutputStreamEntry; use super::sourceEntry::SourceEntry; @@ -36,11 +36,13 @@ pub struct SourceBox { pub resetOutputStreams: TemplateChild, pub resetDefaultCheckButton: Arc, pub resetDefaultSource: Arc>, - pub resetSourceList: Arc>>, - pub resetOutputStreamList: Arc>>, - pub resetModelList: Arc>, - // first u32 is the index of the source, the second the index in the model list - pub resetSourceMap: Arc>>, + pub resetSourceList: Arc, Arc, String)>>>, + pub resetOutputStreamList: Arc, Arc)>>>, + pub resetModelList: Arc>, + pub resetModelIndex: Arc>, + // first u32 is the index of the source, the second the index in the model list and the third is + // the full name + pub resetSourceMap: Arc>>, } #[glib::object_subclass] diff --git a/src/components/input/sourceEntry.rs b/src/components/input/sourceEntry.rs index 2b286dc..067c63b 100644 --- a/src/components/input/sourceEntry.rs +++ b/src/components/input/sourceEntry.rs @@ -1,16 +1,15 @@ use std::sync::Arc; -use std::thread; use std::time::Duration; use adw::glib; use adw::glib::Object; -use adw::prelude::{ButtonExt, RangeExt, CheckButtonExt}; +use adw::prelude::{ButtonExt, CheckButtonExt, RangeExt}; use dbus::blocking::Connection; use dbus::Error; use glib::subclass::types::ObjectSubclassIsExt; use glib::{clone, Propagation}; +use gtk::{gio, CheckButton}; use ReSet_Lib::audio::audio::Source; -use gtk::CheckButton; use super::sourceEntryImpl; @@ -24,7 +23,6 @@ impl SourceEntry { pub fn new(is_default: bool, check_group: Arc, stream: Source) -> Self { let obj: Self = Object::builder().build(); // TODO use event callback for progress bar -> this is the "im speaking" indicator - // TODO handle events { let imp = obj.imp(); imp.resetSourceName.set_text(stream.alias.clone().as_str()); @@ -38,7 +36,6 @@ impl SourceEntry { imp.resetVolumeSlider.connect_change_value( clone!(@weak imp => @default-return Propagation::Stop, move |_, _, value| { let fraction = (value / 655.36).round(); - println!("{fraction}"); let percentage = (fraction).to_string() + "%"; imp.resetVolumePercentage.set_text(&percentage); let source = imp.stream.borrow(); @@ -69,10 +66,10 @@ impl SourceEntry { let index = stream.index; if muted { imp.resetSourceMute - .set_icon_name("audio-volume-muted-symbolic"); + .set_icon_name("microphone-disabled-symbolic"); } else { imp.resetSourceMute - .set_icon_name("audio-volume-high-symbolic"); + .set_icon_name("audio-input-microphone-symbolic"); } toggle_source_mute(index, muted); })); @@ -82,51 +79,60 @@ impl SourceEntry { } pub fn set_source_volume(value: f64, index: u32, channels: u16) -> bool { + gio::spawn_blocking(move || { let conn = Connection::new_session().unwrap(); let proxy = conn.with_proxy( "org.xetibo.ReSet", "/org/xetibo/ReSet", Duration::from_millis(1000), ); - let res: Result<(bool,), Error> = proxy.method_call( + let _: Result<(), Error> = proxy.method_call( "org.xetibo.ReSet", "SetSourceVolume", (index, channels, value as u32), ); - if res.is_err() { - return false; - } - res.unwrap().0 + // if res.is_err() { + // return false; + // } + // res.unwrap().0 + }); + true } pub fn toggle_source_mute(index: u32, muted: bool) -> bool { - let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy( - "org.xetibo.ReSet", - "/org/xetibo/ReSet", - Duration::from_millis(1000), - ); - let res: Result<(bool,), Error> = - proxy.method_call("org.xetibo.ReSet", "SetSourceMute", (index, muted)); - if res.is_err() { - return false; - } - res.unwrap().0 -} - -pub fn set_default_source(name: Arc) { - thread::spawn(move || { + gio::spawn_blocking(move || { let conn = Connection::new_session().unwrap(); let proxy = conn.with_proxy( "org.xetibo.ReSet", "/org/xetibo/ReSet", Duration::from_millis(1000), ); - let res: Result<(bool,), Error> = + let _: Result<(), Error> = + proxy.method_call("org.xetibo.ReSet", "SetSourceMute", (index, muted)); + // if res.is_err() { + // return false; + // } + // res.unwrap().0 + }); + true +} + +pub fn set_default_source(name: Arc) -> bool { + gio::spawn_blocking(move || { + let conn = Connection::new_session().unwrap(); + let proxy = conn.with_proxy( + "org.xetibo.ReSet", + "/org/xetibo/ReSet", + Duration::from_millis(1000), + ); + let _: Result<(), Error> = proxy.method_call("org.xetibo.ReSet", "SetDefaultSink", (name.as_str(),)); - if res.is_err() { - return; - } + // if res.is_err() { + // return; + // } // handle change }); + true } + +// TODO propagate error from dbus diff --git a/src/components/output/inputStreamEntry.rs b/src/components/output/inputStreamEntry.rs index 6f591d1..bfd69ae 100644 --- a/src/components/output/inputStreamEntry.rs +++ b/src/components/output/inputStreamEntry.rs @@ -8,7 +8,7 @@ use dbus::blocking::Connection; use dbus::Error; use glib::subclass::types::ObjectSubclassIsExt; use glib::{clone, Cast, Propagation}; -use gtk::StringObject; +use gtk::{gio, StringObject}; use ReSet_Lib::audio::audio::InputStream; use super::inputStreamEntryImpl; @@ -24,8 +24,8 @@ impl InputStreamEntry { pub fn new(sink_box: Arc, stream: InputStream) -> Self { let obj: Self = Object::builder().build(); // 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 { @@ -64,26 +64,31 @@ impl InputStreamEntry { }), ); { - let mut list = box_imp.resetModelList.try_borrow(); - while list.is_err() { - list = box_imp.resetModelList.try_borrow(); - } - let list = list.unwrap(); + let list = box_imp.resetModelList.read().unwrap(); + // while list.is_err() { + // list = box_imp.resetModelList.try_borrow(); + // } + // 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.read().unwrap(); + let sink_list = box_imp.resetSinkList.read().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,11 +100,11 @@ 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 = sink.unwrap(); + let sink = box_imp.resetSinkMap.read().unwrap(); + // if sink.is_err() { + // return; + // } + // let sink = sink.unwrap(); let sink = sink.get(&selected); if sink.is_none() { return; @@ -139,49 +144,60 @@ impl InputStreamEntry { } fn set_inputstream_volume(value: f64, index: u32, channels: u16) -> bool { - let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy( - "org.xetibo.ReSet", - "/org/xetibo/ReSet", - Duration::from_millis(1000), - ); - let res: Result<(bool,), Error> = proxy.method_call( - "org.xetibo.ReSet", - "SetInputStreamVolume", - (index, channels, value as u32), - ); - if res.is_err() { - return false; - } - res.unwrap().0 + gio::spawn_blocking(move || { + let conn = Connection::new_session().unwrap(); + let proxy = conn.with_proxy( + "org.xetibo.ReSet", + "/org/xetibo/ReSet", + Duration::from_millis(1000), + ); + let _: Result<(), Error> = proxy.method_call( + "org.xetibo.ReSet", + "SetInputStreamVolume", + (index, channels, value as u32), + ); + // if res.is_err() { + // return false; + // } + // res.unwrap().0 + }); + true } fn toggle_input_stream_mute(index: u32, muted: bool) -> bool { - let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy( - "org.xetibo.ReSet", - "/org/xetibo/ReSet", - Duration::from_millis(1000), - ); - let res: Result<(bool,), Error> = - proxy.method_call("org.xetibo.ReSet", "SetInputStreamMute", (index, muted)); - if res.is_err() { - return false; - } - res.unwrap().0 + gio::spawn_blocking(move || { + let conn = Connection::new_session().unwrap(); + let proxy = conn.with_proxy( + "org.xetibo.ReSet", + "/org/xetibo/ReSet", + Duration::from_millis(1000), + ); + let _: Result<(), Error> = + proxy.method_call("org.xetibo.ReSet", "SetInputStreamMute", (index, muted)); + // if res.is_err() { + // return false; + // } + // res.unwrap().0 + }); + true } fn set_sink_of_input_stream(stream: u32, sink: u32) -> bool { - let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy( - "org.xetibo.ReSet", - "/org/xetibo/ReSet", - Duration::from_millis(1000), - ); - let res: Result<(bool,), Error> = - proxy.method_call("org.xetibo.ReSet", "SetSinkOfInputStream", (stream, sink)); - if res.is_err() { - return false; - } - res.unwrap().0 + gio::spawn_blocking(move || { + let conn = Connection::new_session().unwrap(); + let proxy = conn.with_proxy( + "org.xetibo.ReSet", + "/org/xetibo/ReSet", + Duration::from_millis(1000), + ); + let _: Result<(), Error> = + proxy.method_call("org.xetibo.ReSet", "SetSinkOfInputStream", (stream, sink)); + // if res.is_err() { + // return false; + // } + // res.unwrap().0 + }); + true } + +// TODO propagate error from dbus diff --git a/src/components/output/sinkBox.rs b/src/components/output/sinkBox.rs index 56e43e2..9430ee5 100644 --- a/src/components/output/sinkBox.rs +++ b/src/components/output/sinkBox.rs @@ -1,14 +1,19 @@ +use std::sync::atomic::Ordering; use std::sync::Arc; 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::prelude::{BoxExt, ButtonExt, CheckButtonExt, 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; @@ -54,17 +59,19 @@ pub fn populate_sinks(output_box: Arc) { let sinks = get_sinks(); { let output_box_imp = output_box.imp(); + let mut map = output_box_imp.resetSinkMap.write().unwrap(); + let list = output_box_imp.resetModelList.write().unwrap(); + let mut model_index = output_box_imp.resetModelIndex.write().unwrap(); output_box_imp.resetDefaultSink.replace(get_default_sink()); - let list = output_box_imp.resetModelList.borrow_mut(); - let mut map = output_box_imp.resetSinkMap.borrow_mut(); let mut i: u32 = 0; for sink in sinks.iter() { - dbg!(sink.clone()); list.append(&sink.alias); map.insert(sink.alias.clone(), (sink.index, i, sink.name.clone())); i += 1; + *model_index += 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 +86,28 @@ 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.write().unwrap(); + 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(); + let list = output_box_imp.resetModelList.read().unwrap(); output_box_imp.resetSinkDropdown.set_model(Some(&*list)); - let map = output_box_imp.resetSinkMap.borrow(); + let map = output_box_imp.resetSinkMap.read().unwrap(); let name = output_box_imp.resetDefaultSink.borrow(); let name = &name.alias; let index = map.get(name); @@ -113,7 +126,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.read().unwrap(); let sink = sink.get(&selected); if sink.is_none() { return; @@ -129,7 +142,6 @@ pub fn populate_sinks(output_box: Arc) { .connect_change_value(move |_, _, value| { let imp = output_box_ref_slider.imp(); let fraction = (value / 655.36).round(); - println!("{fraction}"); let percentage = (fraction).to_string() + "%"; imp.resetVolumePercentage.set_text(&percentage); let sink = imp.resetDefaultSink.borrow(); @@ -162,8 +174,7 @@ pub fn populate_sinks(output_box: Arc) { }); } -pub fn populate_inputstreams(_listeners: Arc, output_box: Arc) { - // TODO add listener +pub fn populate_inputstreams(output_box: Arc) { let output_box_ref = output_box.clone(); gio::spawn_blocking(move || { @@ -171,10 +182,14 @@ pub fn populate_inputstreams(_listeners: Arc, output_box: Arc Sink { } res.unwrap().0 } + +pub fn start_output_box_listener(conn: Connection, listeners: Arc, sink_box: Arc) -> Connection { + if listeners.network_listener.load(Ordering::SeqCst) { + return conn; + } + listeners.network_listener.store(true, Ordering::SeqCst); + + 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.write().unwrap(); + let index = ir.sink.index; + let alias = ir.sink.alias.clone(); + let name = ir.sink.name.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.clone())); + output_box_imp.resetSinks.append(&*entry); + let mut map = output_box_imp.resetSinkMap.write().unwrap(); + let mut index = output_box_imp.resetModelIndex.write().unwrap(); + map.insert(alias, (*index, *index, name)); + *index += 1; + }); + }); + true + }); + if res.is_err() { + println!("fail on sink add"); + return conn; + } + + 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.write().unwrap(); + let entry = list.get(&ir.index); + if entry.is_none() { + return; + } + output_box_imp.resetSinks.remove(&*entry.unwrap().0); + let alias = list.remove(&ir.index); + if alias.is_none() { + return; + } + let mut map = output_box_imp.resetSinkMap.write().unwrap(); + map.remove(&alias.unwrap().2); + let mut index = output_box_imp.resetModelIndex.write().unwrap(); + *index -= 1; + }); + }); + true + }); + if res.is_err() { + println!("fail on sink remove"); + return conn; + } + + let res = conn.add_match(sink_changed, move |ir: SinkChanged, _, _| { + let sink_box = sink_changed_box.clone(); + let default_sink = get_default_sink(); + glib::spawn_future(async move { + glib::idle_add_once(move || { + let output_box = sink_box.clone(); + let output_box_imp = output_box.imp(); + let list = output_box_imp.resetSinkList.read().unwrap(); + let entry = list.get(&ir.sink.index); + if entry.is_none() { + return; + } + let imp = entry.unwrap().1.imp(); + let is_default = ir.sink.name == default_sink.name; + imp.resetSinkName.set_text(ir.sink.alias.clone().as_str()); + let volume = ir.sink.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); + if is_default { + imp.resetSelectedSink.set_active(true); + } else { + imp.resetSelectedSink.set_active(false); + } + }); + }); + true + }); + if res.is_err() { + println!("fail on sink remove"); + return conn; + } + + 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.write().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 conn; + } + + let res = conn.add_match(input_stream_changed, move |ir: InputStreamChanged, _, _| { + let imp = input_stream_changed_box.imp(); + let alias: String; + { + let sink_list = imp.resetSinkList.read().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(""); + } + } + 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; + { + let list = output_box_imp.resetInputStreamList.read().unwrap(); + let entry_opt = list.get(&ir.stream.index); + if entry_opt.is_none() { + return; + } + entry = entry_opt.unwrap().1.clone(); + } + 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); + let map = output_box_imp.resetSinkMap.read().unwrap(); + let index = map.get(&alias); + if index.is_some() { + imp.resetSelectedSink.set_selected(index.unwrap().1); + } + }); + }); + true + }); + if res.is_err() { + println!("fail on stream change"); + return conn; + } + + 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.write().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 conn; + } + + listeners.network_listener.store(true, Ordering::SeqCst); + conn +} diff --git a/src/components/output/sinkBoxImpl.rs b/src/components/output/sinkBoxImpl.rs index c25548e..d472158 100644 --- a/src/components/output/sinkBoxImpl.rs +++ b/src/components/output/sinkBoxImpl.rs @@ -1,6 +1,6 @@ use std::cell::RefCell; use std::collections::HashMap; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, RwLock}; use crate::components::base::listEntry::ListEntry; use crate::components::output::inputStreamEntry::InputStreamEntry; @@ -9,7 +9,7 @@ use gtk::{ glib, Box, Button, CheckButton, CompositeTemplate, DropDown, Label, StringList, TemplateChild, }; use gtk::{prelude::*, ProgressBar, Scale}; -use ReSet_Lib::audio::audio::{InputStream, Sink}; +use ReSet_Lib::audio::audio::Sink; use super::sinkBox; use super::sinkEntry::SinkEntry; @@ -38,11 +38,14 @@ pub struct SinkBox { pub resetInputStreams: TemplateChild, pub resetDefaultCheckButton: Arc, pub resetDefaultSink: Arc>, - pub resetSinkList: Arc>>, - pub resetInputStreamList: Arc>>, - pub resetModelList: Arc>, - // first u32 is the index of the sink, the second the index in the model list - pub resetSinkMap: Arc>>, + pub resetSinkList: Arc, Arc, String)>>>, + pub resetInputStreamList: Arc, Arc)>>>, + pub resetModelList: Arc>, + pub resetModelIndex: 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/output/sinkEntry.rs b/src/components/output/sinkEntry.rs index 07c6886..d49e5ac 100644 --- a/src/components/output/sinkEntry.rs +++ b/src/components/output/sinkEntry.rs @@ -1,5 +1,4 @@ use std::sync::Arc; -use std::thread; use std::time::Duration; use adw::glib; @@ -9,7 +8,7 @@ use dbus::blocking::Connection; use dbus::Error; use glib::subclass::types::ObjectSubclassIsExt; use glib::{clone, Propagation}; -use gtk::CheckButton; +use gtk::{gio, CheckButton}; use ReSet_Lib::audio::audio::Sink; use super::sinkEntryImpl; @@ -38,7 +37,6 @@ impl SinkEntry { imp.resetVolumeSlider.connect_change_value( clone!(@weak imp => @default-return Propagation::Stop, move |_, _, value| { let fraction = (value / 655.36).round(); - println!("{fraction}"); let percentage = (fraction).to_string() + "%"; imp.resetVolumePercentage.set_text(&percentage); let sink = imp.stream.borrow(); @@ -81,52 +79,59 @@ impl SinkEntry { } pub fn set_sink_volume(value: f64, index: u32, channels: u16) -> bool { - let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy( - "org.xetibo.ReSet", - "/org/xetibo/ReSet", - Duration::from_millis(1000), - ); - let res: Result<(bool,), Error> = proxy.method_call( - "org.xetibo.ReSet", - "SetSinkVolume", - (index, channels, value as u32), - ); - if res.is_err() { - return false; - } - res.unwrap().0 -} - -pub fn toggle_sink_mute(index: u32, muted: bool) -> bool { - let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy( - "org.xetibo.ReSet", - "/org/xetibo/ReSet", - Duration::from_millis(1000), - ); - let res: Result<(bool,), Error> = - proxy.method_call("org.xetibo.ReSet", "SetSinkMute", (index, muted)); - if res.is_err() { - return false; - } - res.unwrap().0 -} - -pub fn set_default_sink(name: Arc) { - thread::spawn(move || { - dbg!(name.clone()); + gio::spawn_blocking(move || { let conn = Connection::new_session().unwrap(); let proxy = conn.with_proxy( "org.xetibo.ReSet", "/org/xetibo/ReSet", Duration::from_millis(1000), ); - let res: Result<(bool,), Error> = + let _: Result<(), Error> = proxy.method_call( + "org.xetibo.ReSet", + "SetSinkVolume", + (index, channels, value as u32), + ); + // if res.is_err() { + // return false; + // } + // res.unwrap().0 + }); + true +} + +pub fn toggle_sink_mute(index: u32, muted: bool) -> bool { + gio::spawn_blocking(move || { + let conn = Connection::new_session().unwrap(); + let proxy = conn.with_proxy( + "org.xetibo.ReSet", + "/org/xetibo/ReSet", + Duration::from_millis(1000), + ); + let _: Result<(), Error> = + proxy.method_call("org.xetibo.ReSet", "SetSinkMute", (index, muted)); + // if res.is_err() { + // return false; + // } + // res.unwrap().0 + }); + true +} + +pub fn set_default_sink(name: Arc) { + gio::spawn_blocking(move || { + let conn = Connection::new_session().unwrap(); + let proxy = conn.with_proxy( + "org.xetibo.ReSet", + "/org/xetibo/ReSet", + Duration::from_millis(1000), + ); + let _: Result<(), Error> = proxy.method_call("org.xetibo.ReSet", "SetDefaultSink", (name.as_str(),)); - if res.is_err() { - return; - } + // if res.is_err() { + // return; + // } // handle change }); } + +// TODO propagate error from dbus diff --git a/src/components/wifi/savedWifiEntry.rs b/src/components/wifi/savedWifiEntry.rs index c1bb03c..ff7f6a0 100644 --- a/src/components/wifi/savedWifiEntry.rs +++ b/src/components/wifi/savedWifiEntry.rs @@ -32,10 +32,8 @@ impl SavedWifiEntry { let res: Result<(bool,), Error> = proxy.method_call("org.xetibo.ReSet", "DeleteConnection", (entry.imp().resetConnectionPath.take(),)); if res.is_err() || res.unwrap() == (false,) { // TODO handle error -> inform user - println!("no worky"); return; } - println!("worked, should be ded"); let parent = entry.parent().unwrap(); parent.set_visible(false); parent.unparent(); diff --git a/src/components/wifi/wifiBox.rs b/src/components/wifi/wifiBox.rs index f26f89a..d39ad86 100644 --- a/src/components/wifi/wifiBox.rs +++ b/src/components/wifi/wifiBox.rs @@ -108,7 +108,6 @@ pub fn scanForWifi(listeners: Arc, wifiBox: Arc) { { break; } - println!("receiving!"); let res = receiver.recv(); if res.is_ok() { let access_point = res.unwrap(); @@ -211,7 +210,6 @@ pub fn get_stored_connections() -> Vec<(Path<'static>, Vec)> { return Vec::new(); } let (connections,) = res.unwrap(); - dbg!(connections.clone()); connections } @@ -227,13 +225,11 @@ pub fn getConnectionSettings(path: Path<'static>) -> Option { Error, > = proxy.method_call("org.xetibo.ReSet", "GetConnectionSettings", (path,)); if res.is_err() { - println!("lol not work"); return None; } let (res,) = res.unwrap(); let res = ResetConnection::convert_from_propmap(res); if res.is_err() { - println!("lol none"); return None; } Some(res.unwrap()) diff --git a/src/components/wifi/wifiEntry.rs b/src/components/wifi/wifiEntry.rs index 0b4ba7c..25ad980 100644 --- a/src/components/wifi/wifiEntry.rs +++ b/src/components/wifi/wifiEntry.rs @@ -79,8 +79,7 @@ impl WifiEntry { let selfImp = self.imp(); selfImp.resetWifiEditButton.connect_clicked(clone!(@ weak selfImp => move |_| { // TODO open navigationpage - let option = getConnectionSettings(selfImp.accessPoint.borrow().associated_connection.clone()); - dbg!(option); + let _option = getConnectionSettings(selfImp.accessPoint.borrow().associated_connection.clone()); })); } } @@ -120,7 +119,6 @@ pub fn click_stored_network(entry: Arc) { } return; } - dbg!(access_point.clone()); let res: Result<(bool,), Error> = proxy.method_call( "org.xetibo.ReSet", "ConnectToKnownAccessPoint", @@ -214,8 +212,4 @@ pub fn click_new_network(entry: Arc) { }), ); entryImp.resetWifiPopup.popup(); - println!( - "result is {}", - result.load(std::sync::atomic::Ordering::SeqCst) - ); } diff --git a/src/components/window/handleSidebarClick.rs b/src/components/window/handleSidebarClick.rs index 3a5f062..374f361 100644 --- a/src/components/window/handleSidebarClick.rs +++ b/src/components/window/handleSidebarClick.rs @@ -3,10 +3,10 @@ use std::sync::atomic::Ordering; use std::sync::Arc; use crate::components::base::settingBox::SettingBox; -use crate::components::base::utils::Listeners; +use crate::components::base::utils::{start_event_listener, 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_sources, SourceBox}; +use crate::components::output::sinkBox::{populate_sinks, SinkBox}; use crate::components::wifi::wifiBox::{scanForWifi, show_stored_connections, WifiBox}; use gtk::prelude::WidgetExt; use gtk::{FlowBox, Frame, Label}; @@ -64,12 +64,15 @@ 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()); - populate_outputstreams(listeners.clone(), audioInput.clone()); populate_sources(audioInput.clone()); + start_event_listener( + listeners.clone(), + Some(audioOutput.clone()), + Some(audioInput.clone()), + ); let sourceFrame = wrapInFrame(SettingBox::new(&*audioInput)); resetMain.remove_all(); resetMain.insert(&audioFrame, -1); @@ -83,8 +86,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_event_listener(listeners.clone(), Some(audioOutput.clone()), None); let audioFrame = wrapInFrame(SettingBox::new(&*audioOutput)); resetMain.remove_all(); resetMain.insert(&audioFrame, -1); @@ -97,8 +100,8 @@ pub const HANDLE_MICROPHONE_CLICK: fn(Arc, FlowBox) = listeners.bluetooth_listener.store(false, Ordering::SeqCst); listeners.pulse_listener.store(false, Ordering::SeqCst); let audioInput = Arc::new(SourceBox::new()); - populate_outputstreams(listeners.clone(), audioInput.clone()); populate_sources(audioInput.clone()); + start_event_listener(listeners.clone(), None, Some(audioInput.clone())); let sourceFrame = wrapInFrame(SettingBox::new(&*audioInput)); resetMain.remove_all(); resetMain.insert(&sourceFrame, -1);