diff --git "a/src/components/output/\\" "b/src/components/output/\\" deleted file mode 100644 index 1cd0aea..0000000 --- "a/src/components/output/\\" +++ /dev/null @@ -1,461 +0,0 @@ -use adw::prelude::PreferencesGroupExt; -use adw::ComboRow; -use re_set_lib::audio::audio_structures::Card; -use re_set_lib::audio::audio_structures::InputStream; -use re_set_lib::audio::audio_structures::Sink; -use re_set_lib::signals::InputStreamAdded; -use re_set_lib::signals::InputStreamChanged; -use re_set_lib::signals::InputStreamRemoved; -use re_set_lib::signals::SinkAdded; -use re_set_lib::signals::SinkChanged; -use re_set_lib::signals::SinkRemoved; -use std::sync::Arc; -use std::time::{Duration, SystemTime}; - -use adw::glib::Object; -use adw::prelude::{BoxExt, ButtonExt, CheckButtonExt, ComboRowExt, RangeExt}; -use adw::{glib, prelude::ListBoxRowExt}; -use dbus::blocking::Connection; -use dbus::message::SignalArgs; -use dbus::{Error, Path}; -use glib::subclass::prelude::ObjectSubclassIsExt; -use glib::{clone, Cast, Propagation, Variant}; -use gtk::prelude::ActionableExt; -use gtk::{gio, StringObject}; - -use crate::components::base::card_entry::CardEntry; -use crate::components::base::list_entry::ListEntry; -use crate::components::output::sink_entry::set_sink_volume; -use crate::components::utils::AUDIO; -use crate::components::utils::BASE; -use crate::components::utils::DBUS_PATH; -use crate::components::utils::{create_dropdown_label_factory, set_combo_row_ellipsis}; - -use super::input_stream_entry::InputStreamEntry; -use super::sink_box_event_handlers::input_stream_added_handler; -use super::sink_box_event_handlers::input_stream_changed_handler; -use super::sink_box_event_handlers::input_stream_removed_handler; -use super::sink_box_event_handlers::sink_added_handler; -use super::sink_box_event_handlers::sink_changed_handler; -use super::sink_box_event_handlers::sink_removed_handler; -use super::sink_box_impl; -use super::sink_entry::{set_default_sink, toggle_sink_mute, SinkEntry}; - -glib::wrapper! { - pub struct SinkBox(ObjectSubclass) - @extends gtk::Box, gtk::Widget, - @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Orientable; -} - -unsafe impl Send for SinkBox {} -unsafe impl Sync for SinkBox {} - -impl SinkBox { - pub fn new() -> Self { - let obj: Self = Object::builder().build(); - { - let imp = obj.imp(); - let mut model_index = imp.reset_model_index.write().unwrap(); - *model_index = 0; - } - obj - } - - pub fn setup_callbacks(&self) { - let self_imp = self.imp(); - self_imp.reset_sinks_row.set_activatable(true); - self_imp - .reset_sinks_row - .set_action_name(Some("navigation.push")); - self_imp - .reset_sinks_row - .set_action_target_value(Some(&Variant::from("outputDevices"))); - self_imp.reset_cards_row.set_activatable(true); - self_imp - .reset_cards_row - .set_action_name(Some("navigation.push")); - self_imp - .reset_cards_row - .set_action_target_value(Some(&Variant::from("profileConfiguration"))); - - self_imp.reset_input_stream_button.set_activatable(true); - self_imp - .reset_input_stream_button - .set_action_name(Some("navigation.pop")); - - self_imp.reset_input_cards_back_button.set_activatable(true); - self_imp - .reset_input_cards_back_button - .set_action_name(Some("navigation.pop")); - - self_imp - .reset_sink_dropdown - .set_factory(Some(&create_dropdown_label_factory())); - set_combo_row_ellipsis(self_imp.reset_sink_dropdown.get()); - } -} - -impl Default for SinkBox { - fn default() -> Self { - Self::new() - } -} - -pub fn populate_sinks(output_box: Arc) { - gio::spawn_blocking(move || { - let sinks = get_sinks(); - { - let output_box_imp = output_box.imp(); - let list = output_box_imp.reset_model_list.write().unwrap(); - let mut map = output_box_imp.reset_sink_map.write().unwrap(); - let mut model_index = output_box_imp.reset_model_index.write().unwrap(); - output_box_imp - .reset_default_sink - .replace(get_default_sink()); - for sink in sinks.iter() { - list.append(&sink.alias); - map.insert(sink.alias.clone(), (sink.index, sink.name.clone())); - *model_index += 1; - } - } - populate_inputstreams(output_box.clone()); - populate_cards(output_box.clone()); - initialize(output_box, sinks); - }); -} - -fn drop_down_handler(output_box_ref: Arc, dropdown: &ComboRow) { - let output_box_imp = output_box_ref.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 = output_box_imp.reset_sink_map.read().unwrap(); - let sink = sink.get(&selected); - if sink.is_none() { - return; - } - let new_sink_name = Arc::new(sink.unwrap().1.clone()); - gio::spawn_blocking(move || { - let result = set_default_sink(new_sink_name); - if result.is_none() { - return; - } - let new_sink = result.unwrap(); - refresh_default_sink(new_sink, output_box_ref, false); - }); -} - -fn initialize(output_box: Arc, sinks: Vec) { - glib::spawn_future(async move { - glib::idle_add_once(move || { - let output_box_ref_select = output_box.clone(); - 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_imp = output_box_ref.imp(); - let default_sink = output_box_imp.reset_default_sink.clone(); - let sink = default_sink.borrow(); - - if sink.muted { - output_box_imp - .reset_sink_mute - .set_icon_name("audio-volume-muted-symbolic"); - } else { - output_box_imp - .reset_sink_mute - .set_icon_name("audio-volume-high-symbolic"); - } - - let volume = sink.volume.first().unwrap_or(&0); - let fraction = (*volume as f64 / 655.36).round(); - let percentage = (fraction).to_string() + "%"; - output_box_imp.reset_volume_percentage.set_text(&percentage); - output_box_imp.reset_volume_slider.set_value(*volume as f64); - let mut list = output_box_imp.reset_sink_list.write().unwrap(); - for sink in sinks { - let index = sink.index; - let alias = sink.alias.clone(); - let mut is_default = false; - if output_box_imp.reset_default_sink.borrow().name == sink.name { - is_default = true; - } - let sink_entry = Arc::new(SinkEntry::new( - is_default, - output_box_imp.reset_default_check_button.clone(), - sink, - output_box.clone(), - )); - let sink_clone = sink_entry.clone(); - let entry = Arc::new(ListEntry::new(&*sink_entry)); - entry.set_activatable(false); - list.insert(index, (entry.clone(), sink_clone, alias)); - output_box_imp.reset_sinks.append(&*entry); - } - let list = output_box_imp.reset_model_list.read().unwrap(); - output_box_imp.reset_sink_dropdown.set_model(Some(&*list)); - let name = output_box_imp.reset_default_sink.borrow(); - - let index = output_box_imp.reset_model_index.read().unwrap(); - let model_list = output_box_imp.reset_model_list.read().unwrap(); - for entry in 0..*index { - if model_list.string(entry) == Some(name.alias.clone().into()) { - output_box_imp.reset_sink_dropdown.set_selected(entry); - break; - } - } - output_box_imp - .reset_sink_dropdown - .connect_selected_notify(clone!(@weak output_box_imp => move |dropdown| { - })); - } - output_box_ref - .imp() - .reset_volume_slider - .connect_change_value(move |_, _, value| { - let imp = output_box_ref_slider.imp(); - let fraction = (value / 655.36).round(); - let percentage = (fraction).to_string() + "%"; - imp.reset_volume_percentage.set_text(&percentage); - let sink = imp.reset_default_sink.borrow(); - let index = sink.index; - let channels = sink.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_sink_volume(value, index, channels); - Propagation::Proceed - }); - output_box_ref - .imp() - .reset_sink_mute - .connect_clicked(move |_| { - let imp = output_box_ref_mute.imp(); - let mut stream = imp.reset_default_sink.borrow_mut(); - stream.muted = !stream.muted; - 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"); - } - toggle_sink_mute(stream.index, stream.muted); - }); - }); - }); -} - -pub fn refresh_default_sink(new_sink: Sink, output_box: Arc, entry: bool) { - let volume = *new_sink.volume.first().unwrap_or(&0_u32); - let fraction = (volume as f64 / 655.36).round(); - let percentage = (fraction).to_string() + "%"; - glib::spawn_future(async move { - glib::idle_add_once(move || { - let imp = output_box.imp(); - if !entry { - let list = imp.reset_sink_list.read().unwrap(); - let entry = list.get(&new_sink.index); - if entry.is_none() { - return; - } - let entry_imp = entry.unwrap().1.imp(); - entry_imp.reset_selected_sink.set_active(true); - } else { - let index = imp.reset_model_index.read().unwrap(); - let model_list = imp.reset_model_list.read().unwrap(); - for entry in 0..*index { - if model_list.string(entry) == Some(new_sink.alias.clone().into()) { - imp.reset_sink_dropdown.set_selected(entry); - break; - } - } - } - imp.reset_volume_percentage.set_text(&percentage); - imp.reset_volume_slider.set_value(volume as f64); - if new_sink.muted { - imp.reset_sink_mute - .set_icon_name("audio-volume-muted-symbolic"); - } else { - imp.reset_sink_mute - .set_icon_name("audio-volume-high-symbolic"); - } - imp.reset_default_sink.replace(new_sink); - }); - }); -} - -pub fn populate_inputstreams(output_box: Arc) { - let output_box_ref = output_box.clone(); - - gio::spawn_blocking(move || { - let streams = get_input_streams(); - glib::spawn_future(async move { - glib::idle_add_once(move || { - let output_box_imp = output_box_ref.imp(); - let mut list = output_box_imp.reset_input_stream_list.write().unwrap(); - for stream in streams { - let index = stream.index; - let input_stream = Arc::new(InputStreamEntry::new(output_box.clone(), stream)); - let entry = Arc::new(ListEntry::new(&*input_stream)); - entry.set_activatable(false); - list.insert(index, (entry.clone(), input_stream.clone())); - output_box_imp.reset_input_streams.append(&*entry); - } - }); - }); - }); -} - -pub fn populate_cards(output_box: Arc) { - gio::spawn_blocking(move || { - let output_box_ref = output_box.clone(); - let cards = get_cards(); - glib::spawn_future(async move { - glib::idle_add_once(move || { - let imp = output_box_ref.imp(); - for card in cards { - imp.reset_cards.add(&CardEntry::new(card)); - } - }); - }); - }); -} - -fn get_input_streams() -> 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, "ListInputStreams", ()); - if res.is_err() { - return Vec::new(); - } - res.unwrap().0 -} - -fn get_sinks() -> 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, "ListSinks", ()); - if res.is_err() { - return Vec::new(); - } - res.unwrap().0 -} - -fn get_cards() -> 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() { - return Vec::new(); - } - res.unwrap().0 -} - -fn get_default_sink_name() -> 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, "GetDefaultSinkName", ()); - if res.is_err() { - return String::from(""); - } - res.unwrap().0 -} - -fn get_default_sink() -> Sink { - let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy(BASE, DBUS_PATH, Duration::from_millis(1000)); - let res: Result<(Sink,), Error> = proxy.method_call(AUDIO, "GetDefaultSink", ()); - if res.is_err() { - return Sink::default(); - } - res.unwrap().0 -} - -pub fn start_output_box_listener(conn: Connection, sink_box: Arc) -> Connection { - let sink_added = - SinkAdded::match_rule(Some(&BASE.into()), Some(&Path::from(DBUS_PATH))).static_clone(); - let sink_removed = - SinkRemoved::match_rule(Some(&BASE.into()), Some(&Path::from(DBUS_PATH))).static_clone(); - let sink_changed = - SinkChanged::match_rule(Some(&BASE.into()), Some(&Path::from(DBUS_PATH))).static_clone(); - let input_stream_added = - InputStreamAdded::match_rule(Some(&BASE.into()), Some(&Path::from(DBUS_PATH))) - .static_clone(); - let input_stream_removed = - InputStreamRemoved::match_rule(Some(&BASE.into()), Some(&Path::from(DBUS_PATH))) - .static_clone(); - let input_stream_changed = - InputStreamChanged::match_rule(Some(&BASE.into()), Some(&Path::from(DBUS_PATH))) - .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, _, _| { - sink_added_handler(sink_added_box.clone(), ir) - }); - if res.is_err() { - // TODO: handle this with the log/error macro - println!("fail on sink add event"); - return conn; - } - - let res = conn.add_match(sink_removed, move |ir: SinkRemoved, _, _| { - sink_removed_handler(sink_removed_box.clone(), ir) - }); - if res.is_err() { - println!("fail on sink remove event"); - return conn; - } - - let res = conn.add_match(sink_changed, move |ir: SinkChanged, _, _| { - let default_sink = get_default_sink_name(); - sink_changed_handler(sink_changed_box.clone(), ir, default_sink) - }); - if res.is_err() { - println!("fail on sink change event"); - return conn; - } - - let res = conn.add_match(input_stream_added, move |ir: InputStreamAdded, _, _| { - input_stream_added_handler(input_stream_added_box.clone(), ir) - }); - if res.is_err() { - println!("fail on input stream add event"); - return conn; - } - - let res = conn.add_match(input_stream_removed, move |ir: InputStreamRemoved, _, _| { - input_stream_removed_handler(input_stream_removed_box.clone(), ir) - }); - if res.is_err() { - println!("fail on input stream remove event"); - return conn; - } - - let res = conn.add_match(input_stream_changed, move |ir: InputStreamChanged, _, _| { - input_stream_changed_handler(input_stream_changed_box.clone(), ir) - }); - if res.is_err() { - println!("fail on input stream change event"); - return conn; - } - - conn -}