diff --git a/Cargo.toml b/Cargo.toml index 3ac0250..3ad810d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ license = "GPL-3.0-or-later" [dependencies] # reset_daemon = "1.1.0" re_set-lib = { git = "https://github.com/Xetibo/ReSet-Lib" , branch = "audioobject"} -# re_set-lib = "3.1.6" +# re_set-lib = "3.1.7" reset_daemon = { git = "https://github.com/Xetibo/ReSet-Daemon", branch = "dashie" } adw = { version = "0.6.0", package = "libadwaita", features = ["v1_4"] } dbus = "0.9.7" diff --git a/src/components/audio/generic_audio_functions.rs b/src/components/audio/generic_audio_functions.rs index 2ab4192..e13edc5 100644 --- a/src/components/audio/generic_audio_functions.rs +++ b/src/components/audio/generic_audio_functions.rs @@ -1,11 +1,26 @@ -use std::sync::Arc; +use std::{ + sync::Arc, + time::{Duration, SystemTime}, +}; -use adw::prelude::ComboRowExt; -use gtk::prelude::{ButtonExt, CheckButtonExt, RangeExt}; +use adw::{prelude::ComboRowExt, prelude::PreferencesRowExt}; +use glib::{object::Cast, Object, Propagation}; +use gtk::{ + prelude::{ButtonExt, CheckButtonExt, RangeExt}, + StringObject, +}; use re_set_lib::audio::audio_structures::{TAudioObject, TAudioStreamObject}; -use super::generic_entry::{ - TAudioBox, TAudioBoxImpl, TAudioEntry, TAudioEntryImpl, TAudioStream, TAudioStreamImpl, +use crate::components::{ + base::error_impl::ReSetErrorImpl, + utils::{create_dropdown_label_factory, set_combo_row_ellipsis}, +}; + +use super::{ + generic_entry::{ + TAudioBox, TAudioBoxImpl, TAudioEntry, TAudioEntryImpl, TAudioStream, TAudioStreamImpl, + }, + generic_utils::audio_dbus_call, }; pub fn refresh_default_audio_object< @@ -61,3 +76,167 @@ pub fn refresh_default_audio_object< }); }); } + +pub fn new_stream_entry< + AudioObject: TAudioObject + Send + Sync + 'static, + StreamObject: TAudioStreamObject + Send + Sync + 'static, + AudioEntry: TAudioEntry, + AudioEntryImpl: TAudioEntryImpl, + AudioStream: TAudioStream, + AudioStreamImpl: TAudioStreamImpl, + AudioBox: TAudioBox + ReSetErrorImpl + Send + Sync + 'static, + AudioBoxImpl: TAudioBoxImpl, +>( + audio_box: Arc, + stream: StreamObject, +) -> Arc { + let obj: Arc = Arc::new(Object::builder().build()); + // TODO use event callback for progress bar -> this is the "im speaking" indicator + let output_box_mute_ref = audio_box.clone(); + let output_box_volume_ref = audio_box.clone(); + let output_box_sink_ref = audio_box.clone(); + let entry_mute_ref = obj.clone(); + let entry_volume_ref = obj.clone(); + let entry_sink_ref = obj.clone(); + { + let index = stream.audio_object_index(); + let box_imp = audio_box.box_imp(); + let imp = obj.entry_imp(); + let icons = box_imp.icons(); + if stream.muted() { + imp.audio_object_mute().set_icon_name(icons.muted); + } else { + imp.audio_object_mute().set_icon_name(icons.active); + } + let name = stream.application_name().clone() + ": " + stream.name().as_str(); + imp.audio_object_selection().set_title(name.as_str()); + imp.audio_object_selection() + .set_factory(Some(&create_dropdown_label_factory())); + set_combo_row_ellipsis(imp.audio_object_selection().get()); + let volume = stream.volume(); + let volume = volume.first().unwrap_or(&0_u32); + let fraction = (*volume as f64 / 655.36).round(); + let percentage = (fraction).to_string() + "%"; + imp.volume_percentage().set_text(&percentage); + imp.volume_slider().set_value(*volume as f64); + imp.stream_object().replace(stream); + { + let sink = box_imp.default_audio_object(); + let sink = sink.borrow(); + imp.associated_audio_object() + .replace((sink.index(), sink.name().clone())); + } + imp.volume_slider() + .connect_change_value(move |_, _, value| { + let imp = entry_volume_ref.entry_imp(); + let fraction = (value / 655.36).round(); + let percentage = (fraction).to_string() + "%"; + imp.volume_percentage().set_text(&percentage); + let stream = imp.stream_object(); + let mut stream_opt = stream.try_borrow(); + while stream_opt.is_err() { + stream_opt = stream.try_borrow(); + } + let stream = stream_opt.unwrap(); + let index = stream.index(); + let channels = stream.channels(); + { + let mut time = imp.volume_time_stamp().borrow_mut(); + if time.is_some() + && time.unwrap().elapsed().unwrap() < Duration::from_millis(50) + { + return Propagation::Proceed; + } + *time = Some(SystemTime::now()); + } + audio_dbus_call::( + output_box_volume_ref.clone(), + (index, channels, value as u32), + imp.set_volume_fn(), + ); + Propagation::Proceed + }); + { + let list = box_imp.model_list(); + let list = list.read().unwrap(); + imp.audio_object_selection().set_model(Some(&*list)); + let sink_list = box_imp.audio_object_list().read().unwrap(); + let name = sink_list.get(&index); + let index = box_imp.model_index(); + let index = index.read().unwrap(); + if let Some(name) = name { + for entry in 0..*index { + if list.string(entry) == Some(name.2.clone().into()) { + imp.audio_object_selection().set_selected(entry); + break; + } + } + } else { + let name = box_imp.default_audio_object(); + let mut name_opt = name.try_borrow(); + while name_opt.is_err() { + name_opt = name.try_borrow(); + } + let name = name_opt.unwrap(); + for entry in 0..*index { + if list.string(entry) == Some(name.alias().into()) { + imp.audio_object_selection().set_selected(entry); + break; + } + } + } + } + imp.audio_object_selection() + .connect_selected_notify(move |dropdown| { + let imp = entry_sink_ref.entry_imp(); + let box_imp = output_box_sink_ref.box_imp(); + let selected = dropdown.selected_item(); + if selected.is_none() { + return; + } + let selected = selected.unwrap(); + let selected = selected.downcast_ref::().unwrap(); + let selected = selected.string().to_string(); + let sink = box_imp.source_map().read().unwrap(); + let sink = sink.get(&selected); + if sink.is_none() { + return; + } + let stream = imp.stream_object(); + let mut stream_opt = stream.try_borrow(); + while stream_opt.is_err() { + stream_opt = stream.try_borrow(); + } + let stream = stream_opt.unwrap(); + let sink = sink.unwrap().0; + audio_dbus_call::( + output_box_sink_ref.clone(), + (stream.index(), sink), + imp.set_audio_object_fn(), + ); + }); + imp.audio_object_mute().connect_clicked(move |_| { + let imp = entry_mute_ref.entry_imp(); + let stream = imp.stream_object().clone(); + let mut stream_opt = stream.try_borrow_mut(); + while stream_opt.is_err() { + stream_opt = stream.try_borrow_mut(); + } + let mut stream = stream_opt.unwrap(); + stream.toggle_muted(); + let icons = imp.icons(); + let muted = stream.muted(); + if muted { + imp.audio_object_mute().set_icon_name(icons.muted); + } else { + imp.audio_object_mute().set_icon_name(icons.active); + } + audio_dbus_call::( + output_box_mute_ref.clone(), + (stream.index(), muted), + imp.set_mute_fn(), + ); + }); + } + obj +} diff --git a/src/components/audio/generic_const.rs b/src/components/audio/generic_const.rs index 58438b5..ae959b6 100644 --- a/src/components/audio/generic_const.rs +++ b/src/components/audio/generic_const.rs @@ -1,6 +1,6 @@ use super::generic_entry::DBusFunction; -pub const LISTCARDS: DBusFunction = DBusFunction { +pub const GETCARDS: DBusFunction = DBusFunction { function: "ListCards", error: "Failed to get list profiles", }; diff --git a/src/components/audio/generic_entry.rs b/src/components/audio/generic_entry.rs index e28afed..a2f3e53 100644 --- a/src/components/audio/generic_entry.rs +++ b/src/components/audio/generic_entry.rs @@ -59,7 +59,6 @@ pub trait TAudioEntry: IsClass + IsA { fn entry_imp(&self) -> &TAudioEntryImpl; } -#[allow(dead_code)] pub trait TAudioEntryImpl { fn name(&self) -> &TemplateChild; fn selected_audio_object(&self) -> &TemplateChild; @@ -74,7 +73,7 @@ pub trait TAudioEntryImpl { fn icons(&self) -> &AudioIcons; } -pub trait TAudioStream { +pub trait TAudioStream: IsClass + IsA { fn entry_imp(&self) -> &TAudioStreamImpl; } @@ -86,6 +85,10 @@ pub trait TAudioStreamImpl Arc>; fn associated_audio_object(&self) -> Arc>; fn volume_time_stamp(&self) -> &RefCell>; + fn set_volume_fn(&self) -> &'static DBusFunction; + fn set_audio_object_fn(&self) -> &'static DBusFunction; + fn set_mute_fn(&self) -> &'static DBusFunction; + fn icons(&self) -> &AudioIcons; } pub struct AudioIcons { diff --git a/src/components/audio/input/output_stream_entry.rs b/src/components/audio/input/output_stream_entry.rs index e0f58eb..e169587 100644 --- a/src/components/audio/input/output_stream_entry.rs +++ b/src/components/audio/input/output_stream_entry.rs @@ -1,23 +1,13 @@ use std::sync::Arc; -use std::time::{Duration, SystemTime}; +use crate::components::audio::generic_audio_functions::new_stream_entry; use crate::components::audio::generic_entry::TAudioStream; -use crate::components::base::error_impl::show_error; -use crate::components::utils::{ - create_dropdown_label_factory, set_combo_row_ellipsis, AUDIO, BASE, DBUS_PATH, -}; -use adw::glib::Object; -use adw::prelude::{ButtonExt, ComboRowExt, PreferencesRowExt, RangeExt}; -use dbus::blocking::Connection; -use dbus::Error; -use glib::prelude::Cast; use glib::subclass::types::ObjectSubclassIsExt; -use glib::{clone, Propagation}; -use gtk::{gio, StringObject}; -use re_set_lib::audio::audio_structures::OutputStream; +use re_set_lib::audio::audio_structures::{OutputStream, Source}; use super::output_stream_entry_impl; use super::source_box::SourceBox; +use super::source_entry::SourceEntry; glib::wrapper! { pub struct OutputStreamEntry(ObjectSubclass) @@ -35,173 +25,16 @@ impl TAudioStream for Output } 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 - let output_box_volume_ref = source_box.clone(); - let output_box_mute_ref = source_box.clone(); - let output_box_source_ref = source_box.clone(); - { - let index = stream.index; - let box_imp = source_box.imp(); - let imp = obj.imp(); - let name = stream.application_name.clone() + ": " + stream.name.as_str(); - imp.reset_source_selection.set_title(name.as_str()); - imp.reset_source_selection - .set_factory(Some(&create_dropdown_label_factory())); - set_combo_row_ellipsis(imp.reset_source_selection.get()); - let volume = stream.volume.first().unwrap_or(&0_u32); - let fraction = (*volume as f64 / 655.36).round(); - let percentage = (fraction).to_string() + "%"; - imp.reset_volume_percentage.set_text(&percentage); - imp.reset_volume_slider.set_value(*volume as f64); - imp.stream.replace(stream); - imp.reset_volume_slider.connect_change_value( - clone!(@weak imp => @default-return Propagation::Stop, move |_, _, value| { - let fraction = (value / 655.36).round(); - let percentage = (fraction).to_string() + "%"; - imp.reset_volume_percentage.set_text(&percentage); - let mut stream = imp.stream.try_borrow(); - while stream.is_err() { - stream = imp.stream.try_borrow(); - } - let stream = stream.unwrap(); - let index = stream.index; - let channels = stream.channels; - { - let mut time = imp.volume_time_stamp.borrow_mut(); - if time.is_some() - && time.unwrap().elapsed().unwrap() < Duration::from_millis(50) - { - return Propagation::Proceed; - } - *time = Some(SystemTime::now()); - } - set_outputstream_volume(value, index, channels, output_box_volume_ref.clone()); - Propagation::Proceed - }), - ); - { - let list = box_imp.reset_model_list.read().unwrap(); - imp.reset_source_selection.set_model(Some(&*list)); - let source_list = box_imp.reset_source_list.read().unwrap(); - let name = source_list.get(&index); - let index = box_imp.reset_model_index.read().unwrap(); - let model_list = box_imp.reset_model_list.read().unwrap(); - if let Some(name) = name { - for entry in 0..*index { - if model_list.string(entry) == Some(name.2.clone().into()) { - imp.reset_source_selection.set_selected(entry); - break; - } - } - } else { - let mut name = box_imp.reset_default_source.try_borrow(); - while name.is_err() { - name = box_imp.reset_default_source.try_borrow(); - } - let name = name.unwrap(); - for entry in 0..*index { - if model_list.string(entry) == Some(name.alias.clone().into()) { - imp.reset_source_selection.set_selected(entry); - break; - } - } - } - } - imp.reset_source_selection.connect_selected_notify( - clone!(@weak imp, @weak box_imp => move |dropdown| { - let selected = dropdown.selected_item(); - if selected.is_none() { - return; - } - let selected = selected.unwrap(); - let selected = selected.downcast_ref::().unwrap(); - let selected = selected.string().to_string(); - let source = box_imp.reset_source_map.write().unwrap(); - let source = source.get(&selected); - if source.is_none() { - return; - } - let mut stream = imp.stream.try_borrow(); - while stream.is_err() { - stream = imp.stream.try_borrow(); - } - let stream = stream.unwrap(); - let source = source.unwrap().0; - set_source_of_output_stream(stream.index, source, output_box_source_ref.clone()); - }), - ); - imp.reset_source_mute - .connect_clicked(clone!(@weak imp => move |_| { - let stream = imp.stream.clone(); - let mut stream = stream.try_borrow_mut(); - while stream.is_err() { - stream = imp.stream.try_borrow_mut(); - } - let mut stream = stream.unwrap(); - stream.muted = !stream.muted; - let muted = stream.muted; - let index = stream.index; - if muted { - imp.reset_source_mute - .set_icon_name("microphone-disabled-symbolic"); - } else { - imp.reset_source_mute - .set_icon_name("audio-input-microphone-symbolic"); - } - toggle_output_stream_mute(index, muted, output_box_mute_ref.clone()); - })); - } - obj + pub fn new(source_box: Arc, stream: OutputStream) -> Arc { + new_stream_entry::< + Source, + OutputStream, + SourceEntry, + super::source_entry_impl::SourceEntry, + OutputStreamEntry, + super::output_stream_entry_impl::OutputStreamEntry, + SourceBox, + super::source_box_impl::SourceBox, + >(source_box, stream) } } - -fn set_outputstream_volume( - value: f64, - index: u32, - channels: u16, - input_box: Arc, -) -> bool { - gio::spawn_blocking(move || { - let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy(BASE, DBUS_PATH, Duration::from_millis(1000)); - let res: Result<(), Error> = proxy.method_call( - AUDIO, - "SetOutputStreamVolume", - (index, channels, value as u32), - ); - if res.is_err() { - show_error::(input_box.clone(), "Failed to set output stream volume"); - } - }); - true -} - -fn toggle_output_stream_mute(index: u32, muted: bool, input_box: Arc) -> bool { - gio::spawn_blocking(move || { - let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy(BASE, DBUS_PATH, Duration::from_millis(1000)); - let res: Result<(), Error> = - proxy.method_call(AUDIO, "SetOutputStreamMute", (index, muted)); - if res.is_err() { - show_error::(input_box.clone(), "Failed to mute output stream"); - } - }); - true -} - -fn set_source_of_output_stream(stream: u32, source: u32, input_box: Arc) -> bool { - gio::spawn_blocking(move || { - let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy(BASE, DBUS_PATH, Duration::from_millis(1000)); - let res: Result<(bool,), Error> = - proxy.method_call(AUDIO, "SetSourceOfOutputStream", (stream, source)); - if res.is_err() { - show_error::(input_box.clone(), "Failed to set source of output stream"); - } - }); - true -} - -// TODO propagate error from dbus diff --git a/src/components/audio/input/output_stream_entry_impl.rs b/src/components/audio/input/output_stream_entry_impl.rs index a191a51..3279543 100644 --- a/src/components/audio/input/output_stream_entry_impl.rs +++ b/src/components/audio/input/output_stream_entry_impl.rs @@ -5,11 +5,13 @@ use std::cell::RefCell; use std::sync::Arc; use std::time::SystemTime; -use crate::components::audio::generic_entry::TAudioStreamImpl; +use crate::components::audio::generic_entry::{AudioIcons, TAudioStreamImpl}; use crate::components::audio::input::output_stream_entry; use gtk::subclass::prelude::*; use gtk::{Button, CompositeTemplate, Label, Scale}; +use super::source_const::{ICONS, SETSTREAMMUTE, SETSTREAMOBJECT, SETSTREAMVOLUME}; + #[derive(Default, CompositeTemplate)] #[template(resource = "/org/Xetibo/ReSet/resetOutputStreamEntry.ui")] pub struct OutputStreamEntry { @@ -76,4 +78,22 @@ impl TAudioStreamImpl for OutputStreamEntry { fn volume_time_stamp(&self) -> &RefCell> { &self.volume_time_stamp } + + fn set_volume_fn(&self) -> &'static crate::components::audio::generic_entry::DBusFunction { + &SETSTREAMVOLUME + } + + fn set_audio_object_fn( + &self, + ) -> &'static crate::components::audio::generic_entry::DBusFunction { + &SETSTREAMOBJECT + } + + fn set_mute_fn(&self) -> &'static crate::components::audio::generic_entry::DBusFunction { + &SETSTREAMMUTE + } + + fn icons(&self) -> &AudioIcons { + &ICONS + } } diff --git a/src/components/audio/input/source_box.rs b/src/components/audio/input/source_box.rs index f901c90..76cb007 100644 --- a/src/components/audio/input/source_box.rs +++ b/src/components/audio/input/source_box.rs @@ -1,3 +1,4 @@ +use re_set_lib::audio::audio_structures::Source; use re_set_lib::signals::{ OutputStreamAdded, OutputStreamChanged, OutputStreamRemoved, SourceAdded, SourceChanged, SourceRemoved, @@ -15,6 +16,7 @@ use gtk::gio; use gtk::prelude::ActionableExt; use crate::components::audio::generic_entry::TAudioBox; +use crate::components::audio::generic_utils::audio_dbus_call; use crate::components::audio::input::source_box_impl; use crate::components::base::error::{self}; use crate::components::base::error_impl::ReSetErrorImpl; @@ -27,9 +29,9 @@ use super::source_box_handlers::{ source_added_handler, source_changed_handler, source_removed_handler, }; use super::source_box_utils::{ - get_default_source, get_sources, populate_cards, populate_outputstreams, - populate_source_information, + populate_cards, populate_outputstreams, populate_source_information, }; +use super::source_const::{GETDEFAULT, GETOBJECTS}; glib::wrapper! { pub struct SourceBox(ObjectSubclass) @@ -105,15 +107,24 @@ impl Default for SourceBox { pub fn populate_sources(source_box: Arc) { gio::spawn_blocking(move || { - let sources = get_sources(source_box.clone()); + let sources = + audio_dbus_call::,), ()>(source_box.clone(), (), &GETOBJECTS); + if sources.is_none() { + return; + } + let sources = sources.unwrap().0; { let source_box_imp = source_box.imp(); let list = source_box_imp.reset_model_list.write().unwrap(); let mut map = source_box_imp.reset_source_map.write().unwrap(); let mut model_index = source_box_imp.reset_model_index.write().unwrap(); - source_box_imp - .reset_default_source - .replace(get_default_source(source_box.clone())); + + let source = + audio_dbus_call::(source_box.clone(), (), &GETDEFAULT); + if let Some(source) = source { + source_box_imp.reset_default_source.replace(source.0); + } + for source in sources.iter() { list.append(&source.alias); map.insert(source.alias.clone(), (source.index, source.name.clone())); diff --git a/src/components/audio/input/source_box_handlers.rs b/src/components/audio/input/source_box_handlers.rs index 932a696..46244bf 100644 --- a/src/components/audio/input/source_box_handlers.rs +++ b/src/components/audio/input/source_box_handlers.rs @@ -24,8 +24,8 @@ use crate::components::{audio::generic_utils::audio_dbus_call, base::list_entry: use super::{ output_stream_entry::OutputStreamEntry, source_box::SourceBox, - source_box_utils::{get_default_source_name, refresh_default_source}, - source_const::{SETDEFAULT, SETMUTE, SETVOLUME}, + source_box_utils::refresh_default_source, + source_const::{GETDEFAULTNAME, SETDEFAULT, SETMUTE, SETVOLUME}, source_entry::SourceEntry, }; @@ -114,7 +114,12 @@ pub fn source_removed_handler(source_box: Arc, ir: SourceRemoved) -> } pub fn source_changed_handler(source_box: Arc, ir: SourceChanged) -> bool { - let default_source = get_default_source_name(source_box.clone()); + let source = + audio_dbus_call::(source_box.clone(), (), &GETDEFAULTNAME); + if source.is_none() { + return false; + } + let default_source = source.unwrap().0; glib::spawn_future(async move { glib::idle_add_once(move || { let source_box = source_box.clone(); @@ -172,7 +177,7 @@ pub fn output_stream_added_handler(source_box: Arc, ir: OutputStreamA let source_box_imp = source_box.imp(); let mut list = source_box_imp.reset_output_stream_list.write().unwrap(); let index = ir.stream.index; - let output_stream = Arc::new(OutputStreamEntry::new(source_box.clone(), ir.stream)); + let output_stream = OutputStreamEntry::new(source_box.clone(), ir.stream); let entry = Arc::new(ListEntry::new(&*output_stream)); entry.set_activatable(false); list.insert(index, (entry.clone(), output_stream.clone())); diff --git a/src/components/audio/input/source_box_utils.rs b/src/components/audio/input/source_box_utils.rs index f22b8c6..7143b94 100644 --- a/src/components/audio/input/source_box_utils.rs +++ b/src/components/audio/input/source_box_utils.rs @@ -1,7 +1,6 @@ -use std::{sync::Arc, time::Duration}; +use std::sync::Arc; use adw::prelude::{ComboRowExt, PreferencesGroupExt}; -use dbus::{blocking::Connection, Error}; use glib::subclass::types::ObjectSubclassIsExt; use gtk::{ gio, @@ -10,14 +9,15 @@ use gtk::{ use re_set_lib::audio::audio_structures::{Card, OutputStream, Source}; use crate::components::{ - base::{card_entry::CardEntry, error_impl::show_error, list_entry::ListEntry}, - utils::{AUDIO, BASE, DBUS_PATH}, + audio::{generic_const::GETCARDS, generic_utils::audio_dbus_call}, + base::{card_entry::CardEntry, list_entry::ListEntry}, }; use super::{ output_stream_entry::OutputStreamEntry, source_box::SourceBox, source_box_handlers::{dropdown_handler, mute_clicked_handler, volume_slider_handler}, + source_const::GETSTREAMS, source_entry::SourceEntry, }; @@ -136,16 +136,23 @@ pub fn refresh_default_source(new_source: Source, source_box: Arc, en pub fn populate_outputstreams(source_box: Arc) { let source_box_ref = source_box.clone(); - gio::spawn_blocking(move || { - let streams = get_output_streams(source_box.clone()); + let streams = audio_dbus_call::,), ()>( + source_box.clone(), + (), + &GETSTREAMS, + ); + if streams.is_none() { + return; + } + let streams = streams.unwrap().0; glib::spawn_future(async move { glib::idle_add_once(move || { let source_box_imp = source_box_ref.imp(); let mut list = source_box_imp.reset_output_stream_list.write().unwrap(); for stream in streams { let index = stream.index; - let input_stream = Arc::new(OutputStreamEntry::new(source_box.clone(), stream)); + let input_stream = OutputStreamEntry::new(source_box.clone(), stream); let input_stream_clone = input_stream.clone(); let entry = Arc::new(ListEntry::new(&*input_stream)); entry.set_activatable(false); @@ -160,7 +167,12 @@ pub fn populate_outputstreams(source_box: Arc) { pub fn populate_cards(source_box: Arc) { gio::spawn_blocking(move || { let source_box_ref = source_box.clone(); - let cards = get_cards(source_box.clone()); + let cards = + audio_dbus_call::,), ()>(source_box.clone(), (), &GETCARDS); + if cards.is_none() { + return; + } + let cards = cards.unwrap().0; glib::spawn_future(async move { glib::idle_add_once(move || { let imp = source_box_ref.imp(); @@ -171,59 +183,3 @@ pub fn populate_cards(source_box: Arc) { }); }); } - -pub fn get_output_streams(source_box: Arc) -> Vec { - let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy(BASE, DBUS_PATH, Duration::from_millis(1000)); - let res: Result<(Vec,), Error> = - proxy.method_call(AUDIO, "ListOutputStreams", ()); - if res.is_err() { - show_error::(source_box.clone(), "Failed to get output streams"); - return Vec::new(); - } - res.unwrap().0 -} - -pub fn get_sources(source_box: Arc) -> Vec { - let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy(BASE, DBUS_PATH, Duration::from_millis(1000)); - let res: Result<(Vec,), Error> = proxy.method_call(AUDIO, "ListSources", ()); - if res.is_err() { - show_error::(source_box.clone(), "Failed to get sources"); - return Vec::new(); - } - res.unwrap().0 -} - -pub fn get_cards(source_box: Arc) -> Vec { - let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy(BASE, DBUS_PATH, Duration::from_millis(1000)); - let res: Result<(Vec,), Error> = proxy.method_call(AUDIO, "ListCards", ()); - if res.is_err() { - show_error::(source_box.clone(), "Failed to get profiles"); - return Vec::new(); - } - res.unwrap().0 -} - -pub fn get_default_source_name(source_box: Arc) -> String { - let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy(BASE, DBUS_PATH, Duration::from_millis(1000)); - let res: Result<(String,), Error> = proxy.method_call(AUDIO, "GetDefaultSourceName", ()); - if res.is_err() { - show_error::(source_box.clone(), "Failed to get default source name"); - return String::from(""); - } - res.unwrap().0 -} - -pub fn get_default_source(source_box: Arc) -> Source { - let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy(BASE, DBUS_PATH, Duration::from_millis(1000)); - let res: Result<(Source,), Error> = proxy.method_call(AUDIO, "GetDefaultSource", ()); - if res.is_err() { - show_error::(source_box.clone(), "Failed to get default source"); - return Source::default(); - } - res.unwrap().0 -} diff --git a/src/components/audio/input/source_const.rs b/src/components/audio/input/source_const.rs index 2db5a2c..1ab5b92 100644 --- a/src/components/audio/input/source_const.rs +++ b/src/components/audio/input/source_const.rs @@ -1,8 +1,8 @@ use crate::components::audio::generic_entry::{AudioIcons, DBusFunction}; pub const ICONS: AudioIcons = AudioIcons { - muted: "audio-input-microphone-symbolic", - active: "microphone-disabled-symbolic", + muted: "microphone-disabled-symbolic", + active: "audio-input-microphone-symbolic", }; pub const SETVOLUME: DBusFunction = DBusFunction { @@ -39,3 +39,18 @@ pub const GETSTREAMS: DBusFunction = DBusFunction { function: "ListOutputStreams", error: "Failed to list output streams", }; + +pub const SETSTREAMVOLUME: DBusFunction = DBusFunction { + function: "SetOutputStreamVolume", + error: "Failed to set output stream volume", +}; + +pub const SETSTREAMMUTE: DBusFunction = DBusFunction { + function: "SetOutputStreamMute", + error: "Failed to mute output stream", +}; + +pub const SETSTREAMOBJECT: DBusFunction = DBusFunction { + function: "SetSourceOfOutputStream", + error: "Failed to set source of output stream", +}; diff --git a/src/components/audio/input/source_entry_impl.rs b/src/components/audio/input/source_entry_impl.rs index 86d7487..bf60c3e 100644 --- a/src/components/audio/input/source_entry_impl.rs +++ b/src/components/audio/input/source_entry_impl.rs @@ -8,7 +8,7 @@ use std::time::SystemTime; use gtk::subclass::prelude::*; use gtk::{Button, CheckButton, CompositeTemplate, Label, Scale}; -use crate::components::audio::generic_entry::{AudioIcons, TAudioEntryImpl, DBusFunction}; +use crate::components::audio::generic_entry::{AudioIcons, DBusFunction, TAudioEntryImpl}; use super::source_const::{ICONS, SETDEFAULT, SETMUTE, SETVOLUME}; use super::source_entry; diff --git a/src/components/audio/mod.rs b/src/components/audio/mod.rs index bf24a92..2adbd3f 100644 --- a/src/components/audio/mod.rs +++ b/src/components/audio/mod.rs @@ -1,6 +1,6 @@ pub mod generic_audio_functions; +mod generic_const; pub mod generic_entry; +mod generic_utils; pub mod input; pub mod output; -mod generic_const; -mod generic_utils; diff --git a/src/components/audio/output/input_stream_entry.rs b/src/components/audio/output/input_stream_entry.rs index 20c4563..2071e29 100644 --- a/src/components/audio/output/input_stream_entry.rs +++ b/src/components/audio/output/input_stream_entry.rs @@ -1,26 +1,15 @@ use std::sync::Arc; -use std::time::{Duration, SystemTime}; +use crate::components::audio::generic_audio_functions::new_stream_entry; use crate::components::audio::generic_entry::TAudioStream; -use crate::components::base::error_impl::show_error; -use crate::components::utils::{ - create_dropdown_label_factory, set_combo_row_ellipsis, AUDIO, BASE, DBUS_PATH, -}; -use adw::glib::Object; -use adw::prelude::{ButtonExt, ComboRowExt, PreferencesRowExt, RangeExt}; -use dbus::blocking::Connection; -use dbus::Error; -use glib::prelude::Cast; use glib::subclass::types::ObjectSubclassIsExt; -use glib::{clone, Propagation}; -use gtk::{gio, StringObject}; -use re_set_lib::audio::audio_structures::InputStream; +use re_set_lib::audio::audio_structures::{InputStream, Sink}; -use super::input_stream_entry_impl; use super::sink_box::SinkBox; +use super::sink_entry::SinkEntry; glib::wrapper! { - pub struct InputStreamEntry(ObjectSubclass) + pub struct InputStreamEntry(ObjectSubclass) @extends adw::PreferencesGroup, gtk::Widget, @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Orientable; } @@ -35,178 +24,16 @@ impl TAudioStream for InputStr } 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 - let output_box_mute_ref = sink_box.clone(); - let output_box_volume_ref = sink_box.clone(); - let output_box_sink_ref = sink_box.clone(); - { - let index = stream.sink_index; - let box_imp = sink_box.imp(); - let imp = obj.imp(); - if stream.muted { - imp.reset_sink_mute - .set_icon_name("audio-volume-muted-symbolic"); - } else { - imp.reset_sink_mute - .set_icon_name("audio-volume-high-symbolic"); - } - let name = stream.application_name.clone() + ": " + stream.name.as_str(); - imp.reset_sink_selection.set_title(name.as_str()); - imp.reset_sink_selection - .set_factory(Some(&create_dropdown_label_factory())); - set_combo_row_ellipsis(imp.reset_sink_selection.get()); - let volume = stream.volume.first().unwrap_or(&0_u32); - let fraction = (*volume as f64 / 655.36).round(); - let percentage = (fraction).to_string() + "%"; - imp.reset_volume_percentage.set_text(&percentage); - imp.reset_volume_slider.set_value(*volume as f64); - imp.stream.replace(stream); - { - let sink = box_imp.reset_default_sink.borrow(); - imp.associated_sink.replace((sink.index, sink.name.clone())); - } - imp.reset_volume_slider.connect_change_value( - clone!(@weak imp => @default-return Propagation::Stop, move |_, _, value| { - let fraction = (value / 655.36).round(); - let percentage = (fraction).to_string() + "%"; - imp.reset_volume_percentage.set_text(&percentage); - let mut stream = imp.stream.try_borrow(); - while stream.is_err() { - stream = imp.stream.try_borrow(); - } - let stream = stream.unwrap(); - let index = stream.index; - let channels = stream.channels; - { - let mut time = imp.volume_time_stamp.borrow_mut(); - if time.is_some() && time.unwrap().elapsed().unwrap() < Duration::from_millis(50) { - return Propagation::Proceed; - } - *time = Some(SystemTime::now()); - } - set_inputstream_volume(value, index, channels, output_box_volume_ref.clone()); - Propagation::Proceed - }), - ); - { - let list = box_imp.reset_model_list.read().unwrap(); - imp.reset_sink_selection.set_model(Some(&*list)); - let sink_list = box_imp.reset_sink_list.read().unwrap(); - let name = sink_list.get(&index); - let index = box_imp.reset_model_index.read().unwrap(); - let model_list = box_imp.reset_model_list.read().unwrap(); - if let Some(name) = name { - for entry in 0..*index { - if model_list.string(entry) == Some(name.2.clone().into()) { - imp.reset_sink_selection.set_selected(entry); - break; - } - } - } else { - let mut name = box_imp.reset_default_sink.try_borrow(); - while name.is_err() { - name = box_imp.reset_default_sink.try_borrow(); - } - let name = name.unwrap(); - for entry in 0..*index { - if model_list.string(entry) == Some(name.alias.clone().into()) { - imp.reset_sink_selection.set_selected(entry); - break; - } - } - } - } - imp.reset_sink_selection.connect_selected_notify( - clone!(@weak imp, @weak box_imp => move |dropdown| { - let selected = dropdown.selected_item(); - if selected.is_none() { - return; - } - let selected = selected.unwrap(); - let selected = selected.downcast_ref::().unwrap(); - let selected = selected.string().to_string(); - let sink = box_imp.reset_sink_map.read().unwrap(); - // if sink.is_err() { - // return; - // } - // let sink = sink.unwrap(); - let sink = sink.get(&selected); - if sink.is_none() { - return; - } - let mut stream = imp.stream.try_borrow(); - while stream.is_err() { - stream = imp.stream.try_borrow(); - } - let stream = stream.unwrap(); - let sink = sink.unwrap().0; - set_sink_of_input_stream(stream.index, sink, output_box_sink_ref.clone()); - }), - ); - imp.reset_sink_mute - .connect_clicked(clone!(@weak imp => move |_| { - let stream = imp.stream.clone(); - let mut stream = stream.try_borrow_mut(); - while stream.is_err() { - stream = imp.stream.try_borrow_mut(); - } - let mut stream = stream.unwrap(); - stream.muted = !stream.muted; - let muted = stream.muted; - let index = stream.index; - if muted { - imp.reset_sink_mute - .set_icon_name("audio-volume-muted-symbolic"); - } else { - imp.reset_sink_mute - .set_icon_name("audio-volume-high-symbolic"); - } - toggle_input_stream_mute(index, muted, output_box_mute_ref.clone()); - })); - } - obj + pub fn new(source_box: Arc, stream: InputStream) -> Arc { + new_stream_entry::< + Sink, + InputStream, + SinkEntry, + super::sink_entry_impl::SinkEntry, + InputStreamEntry, + super::input_stream_entry_impl::InputStreamEntry, + SinkBox, + super::sink_box_impl::SinkBox, + >(source_box, stream) } } - -fn set_inputstream_volume(value: f64, index: u32, channels: u16, output_box: Arc) -> bool { - gio::spawn_blocking(move || { - let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy(BASE, DBUS_PATH, Duration::from_millis(1000)); - let res: Result<(), Error> = proxy.method_call( - AUDIO, - "SetInputStreamVolume", - (index, channels, value as u32), - ); - if res.is_err() { - show_error::(output_box.clone(), "Failed to set input stream volume"); - } - }); - true -} - -fn toggle_input_stream_mute(index: u32, muted: bool, output_box: Arc) -> bool { - gio::spawn_blocking(move || { - let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy(BASE, DBUS_PATH, Duration::from_millis(1000)); - let res: Result<(), Error> = proxy.method_call(AUDIO, "SetInputStreamMute", (index, muted)); - if res.is_err() { - show_error::(output_box.clone(), "Failed to mute input stream"); - } - }); - true -} - -fn set_sink_of_input_stream(stream: u32, sink: u32, output_box: Arc) -> bool { - gio::spawn_blocking(move || { - let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy(BASE, DBUS_PATH, Duration::from_millis(1000)); - let res: Result<(), Error> = - proxy.method_call(AUDIO, "SetSinkOfInputStream", (stream, sink)); - if res.is_err() { - show_error::(output_box.clone(), "Failed to set sink of input stream"); - } - }); - true -} diff --git a/src/components/audio/output/input_stream_entry_impl.rs b/src/components/audio/output/input_stream_entry_impl.rs index 0eaaddb..660d0a4 100644 --- a/src/components/audio/output/input_stream_entry_impl.rs +++ b/src/components/audio/output/input_stream_entry_impl.rs @@ -8,9 +8,10 @@ use std::time::SystemTime; use gtk::subclass::prelude::*; use gtk::{Button, CompositeTemplate, Label, Scale}; -use crate::components::audio::generic_entry::TAudioStreamImpl; +use crate::components::audio::generic_entry::{AudioIcons, TAudioStreamImpl}; use super::input_stream_entry; +use super::sink_const::{ICONS, SETSTREAMMUTE, SETSTREAMOBJECT, SETSTREAMVOLUME}; #[derive(Default, CompositeTemplate)] #[template(resource = "/org/Xetibo/ReSet/resetInputStreamEntry.ui")] @@ -78,4 +79,22 @@ impl TAudioStreamImpl for InputStreamEntry { fn volume_time_stamp(&self) -> &RefCell> { &self.volume_time_stamp } + + fn set_volume_fn(&self) -> &'static crate::components::audio::generic_entry::DBusFunction { + &SETSTREAMVOLUME + } + + fn set_audio_object_fn( + &self, + ) -> &'static crate::components::audio::generic_entry::DBusFunction { + &SETSTREAMOBJECT + } + + fn set_mute_fn(&self) -> &'static crate::components::audio::generic_entry::DBusFunction { + &SETSTREAMMUTE + } + + fn icons(&self) -> &AudioIcons { + &ICONS + } } diff --git a/src/components/audio/output/sink_box_handlers.rs b/src/components/audio/output/sink_box_handlers.rs index 3fd48f3..2578a31 100644 --- a/src/components/audio/output/sink_box_handlers.rs +++ b/src/components/audio/output/sink_box_handlers.rs @@ -246,7 +246,7 @@ pub fn input_stream_added_handler(sink_box: Arc, ir: InputStreamAdded) let sink_box_imp = sink_box.imp(); let mut list = sink_box_imp.reset_input_stream_list.write().unwrap(); let index = ir.stream.index; - let input_stream = Arc::new(InputStreamEntry::new(sink_box.clone(), ir.stream)); + let input_stream = InputStreamEntry::new(sink_box.clone(), ir.stream); let entry = Arc::new(ListEntry::new(&*input_stream)); entry.set_activatable(false); list.insert(index, (entry.clone(), input_stream.clone())); diff --git a/src/components/audio/output/sink_box_utils.rs b/src/components/audio/output/sink_box_utils.rs index ad148ab..7dd2c51 100644 --- a/src/components/audio/output/sink_box_utils.rs +++ b/src/components/audio/output/sink_box_utils.rs @@ -150,7 +150,7 @@ pub fn populate_inputstreams(sink_box: Arc) { let mut list = sink_box_imp.reset_input_stream_list.write().unwrap(); for stream in streams { let index = stream.index; - let input_stream = Arc::new(InputStreamEntry::new(sink_box.clone(), stream)); + let input_stream = InputStreamEntry::new(sink_box.clone(), stream); let entry = Arc::new(ListEntry::new(&*input_stream)); entry.set_activatable(false); list.insert(index, (entry.clone(), input_stream.clone())); diff --git a/src/components/audio/output/sink_const.rs b/src/components/audio/output/sink_const.rs index 857afe2..49b5fb2 100644 --- a/src/components/audio/output/sink_const.rs +++ b/src/components/audio/output/sink_const.rs @@ -1,8 +1,8 @@ use crate::components::audio::generic_entry::{AudioIcons, DBusFunction}; pub const ICONS: AudioIcons = AudioIcons { - muted: "audio-volume-high-symbolic", - active: "audio-volume-muted-symbolic", + muted: "audio-volume-muted-symbolic", + active: "audio-volume-high-symbolic", }; pub const SETVOLUME: DBusFunction = DBusFunction { @@ -39,3 +39,18 @@ pub const GETSTREAMS: DBusFunction = DBusFunction { function: "ListInputStreams", error: "Failed to list input streams", }; + +pub const SETSTREAMVOLUME: DBusFunction = DBusFunction { + function: "SetInputStreamVolume", + error: "Failed to set input stream volume", +}; + +pub const SETSTREAMMUTE: DBusFunction = DBusFunction { + function: "SetInputStreamMute", + error: "Failed to mute input stream", +}; + +pub const SETSTREAMOBJECT: DBusFunction = DBusFunction { + function: "SetSinkOfInputStream", + error: "Failed to set sink of input stream", +}; diff --git a/src/components/audio/output/sink_entry_impl.rs b/src/components/audio/output/sink_entry_impl.rs index de3e0bd..933f72f 100644 --- a/src/components/audio/output/sink_entry_impl.rs +++ b/src/components/audio/output/sink_entry_impl.rs @@ -5,7 +5,7 @@ use std::cell::RefCell; use std::sync::Arc; use std::time::SystemTime; -use crate::components::audio::generic_entry::{AudioIcons, TAudioEntryImpl, DBusFunction}; +use crate::components::audio::generic_entry::{AudioIcons, DBusFunction, TAudioEntryImpl}; use crate::components::audio::output::sink_entry; use gtk::subclass::prelude::*; use gtk::{Button, CheckButton, CompositeTemplate, Label, Scale};