From b34264027a258a6de6f5de2668a688819e8cb607 Mon Sep 17 00:00:00 2001 From: dashie Date: Tue, 19 Dec 2023 11:51:38 +0000 Subject: [PATCH 001/111] chore: Bump version of Daemon --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4259065..8e18bc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ repository = "https://github.com/Xetibo/ReSet" license = "GPL-3.0-only" [dependencies] -reset_daemon = "0.7.0" +reset_daemon = "0.7.1" re_set-lib = "0.8.7" adw = { version = "0.5.3", package = "libadwaita", features = ["v1_4"] } dbus = "0.9.7" From be3ff8580065169f5ae906b3d57884280a2234d3 Mon Sep 17 00:00:00 2001 From: dashie Date: Tue, 19 Dec 2023 12:27:14 +0000 Subject: [PATCH 002/111] chore: Bump version --- Cargo.toml | 2 +- src/components/window/reset_window.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8e18bc4..fd15d6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "reset" -version = "0.1.5" +version = "0.1.6" edition = "2021" description = "A wip universal Linux settings application." repository = "https://github.com/Xetibo/ReSet" diff --git a/src/components/window/reset_window.rs b/src/components/window/reset_window.rs index 8db153b..e59ecb4 100644 --- a/src/components/window/reset_window.rs +++ b/src/components/window/reset_window.rs @@ -103,7 +103,7 @@ impl ReSetWindow { .license_type(gtk::License::Gpl30) .website("https://github.com/Xetibo/ReSet") .issue_url("https://github.com/Xetibo/ReSet/issues") - .version("0.0.1") + .version("0.1.6") .transient_for(window) .modal(true) .copyright("© 2022-2023 Xetibo") From e1be42a55d1acd0db8835c465f1bc7cbc67171c8 Mon Sep 17 00:00:00 2001 From: dashie Date: Tue, 19 Dec 2023 12:28:25 +0000 Subject: [PATCH 003/111] chore: Bump version of PKGBUILD --- PKGBUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PKGBUILD b/PKGBUILD index a09017c..427a0f3 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Fabio Lenherr pkgname=reset -pkgver=0.1.5 +pkgver=0.1.6 pkgrel=0 arch=('x86_64') pkgdir="/usr/bin/${pkgname}" From 8796abb08fce05a566b11813b3c5c0d27299c096 Mon Sep 17 00:00:00 2001 From: Fabio Lenherr / DashieTM Date: Tue, 19 Dec 2023 21:38:35 +0100 Subject: [PATCH 004/111] fix: Properly handle wifi and bluetooth turned on and off --- Cargo.toml | 4 +- PKGBUILD | 2 +- src/components/bluetooth/bluetooth_box.rs | 43 ++++++++++++------- .../bluetooth/bluetooth_box_impl.rs | 2 + src/components/wifi/wifi_box.rs | 17 +++++--- src/components/wifi/wifi_box_impl.rs | 2 + src/components/window/reset_window.rs | 2 +- src/resources/resetUI.cmb | 1 - src/resources/resetWiFi.ui | 1 - 9 files changed, 47 insertions(+), 27 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fd15d6a..e8372c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "reset" -version = "0.1.6" +version = "0.1.8" edition = "2021" description = "A wip universal Linux settings application." repository = "https://github.com/Xetibo/ReSet" license = "GPL-3.0-only" [dependencies] -reset_daemon = "0.7.1" +reset_daemon = "0.7.2" re_set-lib = "0.8.7" adw = { version = "0.5.3", package = "libadwaita", features = ["v1_4"] } dbus = "0.9.7" diff --git a/PKGBUILD b/PKGBUILD index 427a0f3..6e8f132 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Fabio Lenherr pkgname=reset -pkgver=0.1.6 +pkgver=0.1.8 pkgrel=0 arch=('x86_64') pkgdir="/usr/bin/${pkgname}" diff --git a/src/components/bluetooth/bluetooth_box.rs b/src/components/bluetooth/bluetooth_box.rs index 5581026..5d5197c 100644 --- a/src/components/bluetooth/bluetooth_box.rs +++ b/src/components/bluetooth/bluetooth_box.rs @@ -9,7 +9,7 @@ use adw::subclass::prelude::ObjectSubclassIsExt; use dbus::blocking::Connection; use dbus::message::SignalArgs; use dbus::{Error, Path}; -use glib::{clone, Cast}; +use glib::{clone, Cast, PropertySet}; use gtk::glib::Variant; use gtk::prelude::{ActionableExt, ButtonExt, ListBoxRowExt, WidgetExt}; use gtk::{gio, StringObject}; @@ -48,6 +48,7 @@ fn setup_callbacks( let bluetooth_box_ref = bluetooth_box.clone(); let listeners_ref = listeners.clone(); let imp = bluetooth_box.imp(); + imp.reset_switch_initial.set(true); imp.reset_visibility.set_activatable(true); imp.reset_visibility .set_action_name(Some("navigation.push")); @@ -77,11 +78,15 @@ fn setup_callbacks( set_bluetooth_adapter_pairability(imp.reset_current_bluetooth_adapter.borrow().path.clone(), state.is_active()); })); - imp.reset_bluetooth_switch - .connect_state_set(move |_, state| { + imp.reset_bluetooth_switch.connect_state_set( + clone!(@weak imp => @default-return glib::Propagation::Proceed, move |_, state| { + if imp.reset_switch_initial.load(Ordering::SeqCst) { + return glib::Propagation::Proceed; + } if !state { let imp = bluetooth_box_ref.imp(); let mut available_devices = imp.available_devices.borrow_mut(); + let mut current_adapter = imp.reset_current_bluetooth_adapter.borrow_mut(); for entry in available_devices.iter() { imp.reset_bluetooth_available_devices.remove(&**entry.1); } @@ -102,10 +107,13 @@ fn setup_callbacks( listeners_ref .bluetooth_listener .store(false, Ordering::SeqCst); - set_adapter_enabled( - imp.reset_current_bluetooth_adapter.borrow().path.clone(), + let res = set_adapter_enabled( + current_adapter.path.clone(), false, ); + if res { + current_adapter.powered = false; + } } else { let restart_ref = bluetooth_box_ref.clone(); let restart_listener_ref = listeners_ref.clone(); @@ -115,21 +123,21 @@ fn setup_callbacks( imp.reset_bluetooth_pairable_switch.set_sensitive(true); } gio::spawn_blocking(move || { + let mut current_adapter = restart_ref.imp().reset_current_bluetooth_adapter.borrow_mut(); if set_adapter_enabled( - restart_ref - .imp() - .reset_current_bluetooth_adapter - .borrow() + current_adapter .path .clone(), true, ) { + current_adapter.powered = true; start_bluetooth_listener(restart_listener_ref.clone(), restart_ref.clone()); } }); } glib::Propagation::Proceed - }); + }), + ); bluetooth_box } @@ -169,12 +177,14 @@ pub fn populate_conntected_bluetooth_devices(bluetooth_box: Arc) { { let current_adapter = imp.reset_current_bluetooth_adapter.borrow(); - imp.reset_bluetooth_switch - .set_state(current_adapter.powered); + let powered = current_adapter.powered; + imp.reset_bluetooth_switch.set_state(powered); + imp.reset_bluetooth_switch.set_active(powered); imp.reset_bluetooth_discoverable_switch .set_active(current_adapter.discoverable); imp.reset_bluetooth_pairable_switch .set_active(current_adapter.pairable); + imp.reset_switch_initial.set(false); } imp.reset_bluetooth_adapter.connect_selected_notify( @@ -219,6 +229,11 @@ pub fn start_bluetooth_listener(listeners: Arc, bluetooth_box: Arc, bluetooth_box: Arc = proxy.method_call(BLUETOOTH, "StartBluetoothListener", ()); - loop_box - .imp() - .reset_bluetooth_available_devices + imp.reset_bluetooth_available_devices .set_description(Some("Scanning...")); let device_added = BluetoothDeviceAdded::match_rule(Some(&BASE.into()), Some(&Path::from(DBUS_PATH))) diff --git a/src/components/bluetooth/bluetooth_box_impl.rs b/src/components/bluetooth/bluetooth_box_impl.rs index 40a3080..6c1973d 100644 --- a/src/components/bluetooth/bluetooth_box_impl.rs +++ b/src/components/bluetooth/bluetooth_box_impl.rs @@ -6,6 +6,7 @@ use gtk::{prelude::*, StringList}; use re_set_lib::bluetooth::bluetooth_structures::BluetoothAdapter; use std::cell::RefCell; use std::collections::HashMap; +use std::sync::atomic::AtomicBool; use std::sync::{Arc, RwLock}; use crate::components::base::list_entry::ListEntry; @@ -41,6 +42,7 @@ pub struct BluetoothBox { pub reset_current_bluetooth_adapter: Arc>, pub reset_model_list: Arc>, pub reset_model_index: Arc>, + pub reset_switch_initial: AtomicBool, } #[glib::object_subclass] diff --git a/src/components/wifi/wifi_box.rs b/src/components/wifi/wifi_box.rs index 0384495..fa5e697 100644 --- a/src/components/wifi/wifi_box.rs +++ b/src/components/wifi/wifi_box.rs @@ -50,6 +50,7 @@ impl WifiBox { fn setup_callbacks(listeners: Arc, wifi_box: Arc) -> Arc { let imp = wifi_box.imp(); let wifibox_ref = wifi_box.clone(); + imp.reset_switch_initial.set(true); imp.reset_saved_networks.set_activatable(true); imp.reset_saved_networks .set_action_name(Some("navigation.push")); @@ -62,6 +63,9 @@ fn setup_callbacks(listeners: Arc, wifi_box: Arc) -> Arc @default-return glib::Propagation::Proceed, move |_, value| { + if imp.reset_switch_initial.load(Ordering::SeqCst) { + return glib::Propagation::Proceed; + } set_wifi_enabled(value); if !value { imp.reset_wifi_devices.write().unwrap().clear(); @@ -93,17 +97,17 @@ pub fn scan_for_wifi(wifi_box: Arc) { let wifi_entries_path = wifi_box.imp().wifi_entries_path.clone(); gio::spawn_blocking(move || { - let devices = get_wifi_devices(); - let access_points = get_access_points(); let wifi_status = get_wifi_status(); + let devices = get_wifi_devices(); + if devices.is_empty() { + return; + } + let access_points = get_access_points(); { let imp = wifibox_ref.imp(); let list = imp.reset_model_list.write().unwrap(); let mut model_index = imp.reset_model_index.write().unwrap(); let mut map = imp.reset_wifi_devices.write().unwrap(); - if devices.is_empty() { - return; - } imp.reset_current_wifi_device .replace(devices.last().unwrap().clone()); for (index, device) in devices.into_iter().enumerate() { @@ -121,8 +125,9 @@ pub fn scan_for_wifi(wifi_box: Arc) { let mut wifi_entries_path = wifi_entries_path.write().unwrap(); let imp = wifibox_ref.imp(); - imp.reset_wifi_switch.set_active(wifi_status); imp.reset_wifi_switch.set_state(wifi_status); + imp.reset_wifi_switch.set_active(wifi_status); + imp.reset_switch_initial.set(false); let list = imp.reset_model_list.read().unwrap(); imp.reset_wifi_device.set_model(Some(&*list)); diff --git a/src/components/wifi/wifi_box_impl.rs b/src/components/wifi/wifi_box_impl.rs index 8db8512..7e22a41 100644 --- a/src/components/wifi/wifi_box_impl.rs +++ b/src/components/wifi/wifi_box_impl.rs @@ -7,6 +7,7 @@ use gtk::{prelude::*, StringList}; use re_set_lib::network::network_structures::WifiDevice; use std::cell::RefCell; use std::collections::HashMap; +use std::sync::atomic::AtomicBool; use std::sync::{Arc, RwLock}; use crate::components::base::list_entry::ListEntry; @@ -37,6 +38,7 @@ pub struct WifiBox { pub reset_current_wifi_device: Arc>, pub reset_model_list: Arc>, pub reset_model_index: Arc>, + pub reset_switch_initial: AtomicBool, } unsafe impl Send for WifiBox {} diff --git a/src/components/window/reset_window.rs b/src/components/window/reset_window.rs index e59ecb4..3318e89 100644 --- a/src/components/window/reset_window.rs +++ b/src/components/window/reset_window.rs @@ -103,7 +103,7 @@ impl ReSetWindow { .license_type(gtk::License::Gpl30) .website("https://github.com/Xetibo/ReSet") .issue_url("https://github.com/Xetibo/ReSet/issues") - .version("0.1.6") + .version("0.1.8") .transient_for(window) .modal(true) .copyright("© 2022-2023 Xetibo") diff --git a/src/resources/resetUI.cmb b/src/resources/resetUI.cmb index 1fd2a41..c63c77c 100644 --- a/src/resources/resetUI.cmb +++ b/src/resources/resetUI.cmb @@ -373,7 +373,6 @@ (4,183,"GtkLabel","label","WiFi",None,None,None,None,None,None,None,None,None), (4,183,"GtkWidget","css-classes","resetSettingLabel",None,None,None,None,None,None,None,None,None), (4,183,"GtkWidget","margin-start","5",None,None,None,None,None,None,None,None,None), - (4,184,"GtkSwitch","active","True",None,None,None,None,None,None,None,None,None), (4,184,"GtkWidget","halign","end",None,None,None,None,None,None,None,None,None), (4,184,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None), (4,184,"GtkWidget","margin-end","5",None,None,None,None,None,None,None,None,None), diff --git a/src/resources/resetWiFi.ui b/src/resources/resetWiFi.ui index 9f6a3c7..526f664 100644 --- a/src/resources/resetWiFi.ui +++ b/src/resources/resetWiFi.ui @@ -21,7 +21,6 @@ - True end True 5 From e5263b0dc728db510afa8f57b0469170b05d72a9 Mon Sep 17 00:00:00 2001 From: Fabio Lenherr / DashieTM Date: Thu, 21 Dec 2023 13:10:11 +0100 Subject: [PATCH 005/111] chore: Bump version --- Cargo.toml | 6 +++--- PKGBUILD | 2 +- flatpak/cargo-sources.json | 20 ++++++++++---------- src/components/window/reset_window.rs | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e8372c8..14b4f35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "reset" -version = "0.1.8" +version = "1.0.0" edition = "2021" description = "A wip universal Linux settings application." repository = "https://github.com/Xetibo/ReSet" license = "GPL-3.0-only" [dependencies] -reset_daemon = "0.7.2" -re_set-lib = "0.8.7" +reset_daemon = "1.0.0" +re_set-lib = "1.0.0" adw = { version = "0.5.3", package = "libadwaita", features = ["v1_4"] } dbus = "0.9.7" gtk = { version = "0.7.3", package = "gtk4", features = ["v4_12"] } diff --git a/PKGBUILD b/PKGBUILD index 6e8f132..644c87a 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Fabio Lenherr pkgname=reset -pkgver=0.1.8 +pkgver=1.0.0 pkgrel=0 arch=('x86_64') pkgdir="/usr/bin/${pkgname}" diff --git a/flatpak/cargo-sources.json b/flatpak/cargo-sources.json index 642bf6d..d7831be 100644 --- a/flatpak/cargo-sources.json +++ b/flatpak/cargo-sources.json @@ -1107,14 +1107,14 @@ { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/re_set-lib/re_set-lib-0.8.7.crate", - "sha256": "31baa92abef5520f3566949e443ad10fee6a7f6029fda106f9a7850488019574", - "dest": "cargo/vendor/re_set-lib-0.8.7" + "url": "https://static.crates.io/crates/re_set-lib/re_set-lib-1.0.0.crate", + "sha256": "4ff687ecbf24dcd5b2076e7f608a0db5d079e012b11264b6eca72b18d7e1c39c", + "dest": "cargo/vendor/re_set-lib-1.0.0" }, { "type": "inline", - "contents": "{\"package\": \"31baa92abef5520f3566949e443ad10fee6a7f6029fda106f9a7850488019574\", \"files\": {}}", - "dest": "cargo/vendor/re_set-lib-0.8.7", + "contents": "{\"package\": \"4ff687ecbf24dcd5b2076e7f608a0db5d079e012b11264b6eca72b18d7e1c39c\", \"files\": {}}", + "dest": "cargo/vendor/re_set-lib-1.0.0", "dest-filename": ".cargo-checksum.json" }, { @@ -1146,14 +1146,14 @@ { "type": "archive", "archive-type": "tar-gzip", - "url": "https://static.crates.io/crates/reset_daemon/reset_daemon-0.7.0.crate", - "sha256": "e6126bce7f51d2626d96a3dc04c3c6e88b13fde10c347a7c5e503ca62295040a", - "dest": "cargo/vendor/reset_daemon-0.7.0" + "url": "https://static.crates.io/crates/reset_daemon/reset_daemon-1.0.0.crate", + "sha256": "4ac44390e711dd73b2a2c365890d28f0ceec5c3e9f760deafbab2ad7410e90ed", + "dest": "cargo/vendor/reset_daemon-1.0.0" }, { "type": "inline", - "contents": "{\"package\": \"e6126bce7f51d2626d96a3dc04c3c6e88b13fde10c347a7c5e503ca62295040a\", \"files\": {}}", - "dest": "cargo/vendor/reset_daemon-0.7.0", + "contents": "{\"package\": \"4ac44390e711dd73b2a2c365890d28f0ceec5c3e9f760deafbab2ad7410e90ed\", \"files\": {}}", + "dest": "cargo/vendor/reset_daemon-1.0.0", "dest-filename": ".cargo-checksum.json" }, { diff --git a/src/components/window/reset_window.rs b/src/components/window/reset_window.rs index 3318e89..2848274 100644 --- a/src/components/window/reset_window.rs +++ b/src/components/window/reset_window.rs @@ -103,7 +103,7 @@ impl ReSetWindow { .license_type(gtk::License::Gpl30) .website("https://github.com/Xetibo/ReSet") .issue_url("https://github.com/Xetibo/ReSet/issues") - .version("0.1.8") + .version("1.0.0") .transient_for(window) .modal(true) .copyright("© 2022-2023 Xetibo") From a510147d19e81509303d30ee2a37d3ce3e20fbb9 Mon Sep 17 00:00:00 2001 From: Fabio Lenherr / DashieTM Date: Thu, 21 Dec 2023 13:14:24 +0100 Subject: [PATCH 006/111] chore: Bump version of daemon --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 14b4f35..65e2c1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ repository = "https://github.com/Xetibo/ReSet" license = "GPL-3.0-only" [dependencies] -reset_daemon = "1.0.0" +reset_daemon = "1.0.1" re_set-lib = "1.0.0" adw = { version = "0.5.3", package = "libadwaita", features = ["v1_4"] } dbus = "0.9.7" From ed6983830c2e3665f52c7a02cde48fe6eb511744 Mon Sep 17 00:00:00 2001 From: DashieTM Date: Sun, 10 Mar 2024 22:08:50 +0100 Subject: [PATCH 007/111] refactor: sinkbox --- "src/components/output/\\" | 461 ++++++++++++++++ src/components/output/mod.rs | 1 + src/components/output/sink_box.rs | 500 ++++++------------ .../output/sink_box_event_handlers.rs | 239 +++++++++ 4 files changed, 858 insertions(+), 343 deletions(-) create mode 100644 "src/components/output/\\" create mode 100644 src/components/output/sink_box_event_handlers.rs diff --git "a/src/components/output/\\" "b/src/components/output/\\" new file mode 100644 index 0000000..1cd0aea --- /dev/null +++ "b/src/components/output/\\" @@ -0,0 +1,461 @@ +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 +} diff --git a/src/components/output/mod.rs b/src/components/output/mod.rs index f26058a..1d67696 100644 --- a/src/components/output/mod.rs +++ b/src/components/output/mod.rs @@ -1,6 +1,7 @@ pub mod input_stream_entry; pub mod input_stream_entry_impl; pub mod sink_box; +mod sink_box_event_handlers; pub mod sink_box_impl; pub mod sink_entry; pub mod sink_entry_impl; diff --git a/src/components/output/sink_box.rs b/src/components/output/sink_box.rs index 88bb76b..9246c06 100644 --- a/src/components/output/sink_box.rs +++ b/src/components/output/sink_box.rs @@ -1,5 +1,5 @@ use adw::prelude::PreferencesGroupExt; -use adw::prelude::PreferencesRowExt; +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; @@ -19,7 +19,7 @@ use dbus::blocking::Connection; use dbus::message::SignalArgs; use dbus::{Error, Path}; use glib::subclass::prelude::ObjectSubclassIsExt; -use glib::{clone, Cast, Propagation, Variant}; +use glib::{Cast, Propagation, Variant}; use gtk::prelude::ActionableExt; use gtk::{gio, StringObject}; @@ -32,6 +32,12 @@ 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}; @@ -114,132 +120,147 @@ pub fn populate_sinks(output_box: Arc) { } populate_inputstreams(output_box.clone()); populate_cards(output_box.clone()); - 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(); + initialize(output_box, sinks); + }); +} - 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"); - } +fn drop_down_handler(output_box: Arc, dropdown: &ComboRow) { + let output_box_ref = output_box.clone(); + let output_box_imp = output_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 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 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); + }); +} - 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| { - let output_box_ref = output_box_ref_select.clone(); - 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(); +fn volume_slider_handler(output_box_ref_slider: Arc, value: f64) -> glib::Propagation { + 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 +} - 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 mute_handler(output_box_ref_mute: Arc) { + 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); +} + +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"); } - 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 + + 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(move |dropdown| { + drop_down_handler(output_box_ref_select.clone(), dropdown); }); - 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); - }); - }); + } + output_box_ref + .imp() + .reset_volume_slider + .connect_change_value(move |_, _, value| { + volume_slider_handler(output_box_ref_slider.clone(), value) + }); + output_box_ref + .imp() + .reset_sink_mute + .connect_clicked(move |_| { + mute_handler(output_box_ref_mute.clone()); + }); }); }); } @@ -395,99 +416,16 @@ pub fn start_output_box_listener(conn: Connection, sink_box: Arc) -> Co 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 sink_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.reset_default_sink.borrow().name == ir.sink.name { - is_default = true; - } - let sink_entry = Arc::new(SinkEntry::new( - is_default, - output_box_imp.reset_default_check_button.clone(), - ir.sink, - output_box.clone(), - )); - let sink_clone = sink_entry.clone(); - let entry = Arc::new(ListEntry::new(&*sink_entry)); - entry.set_activatable(false); - let mut list = output_box_imp.reset_sink_list.write().unwrap(); - list.insert(sink_index, (entry.clone(), sink_clone, alias.clone())); - output_box_imp.reset_sinks.append(&*entry); - let mut map = output_box_imp.reset_sink_map.write().unwrap(); - let mut index = output_box_imp.reset_model_index.write().unwrap(); - let model_list = output_box_imp.reset_model_list.write().unwrap(); - if model_list.string(*index - 1) == Some("Dummy Output".into()) { - model_list.append(&alias); - model_list.remove(*index - 1); - map.insert(alias, (sink_index, name)); - output_box_imp.reset_sink_dropdown.set_selected(0); - } else { - model_list.append(&alias); - map.insert(alias.clone(), (sink_index, name)); - if alias == "Dummy Output" { - output_box_imp.reset_sink_dropdown.set_selected(0); - } - *index += 1; - } - }); - }); - true + 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, _, _| { - 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 entry: Option<(Arc, Arc, String)>; - { - let mut list = output_box_imp.reset_sink_list.write().unwrap(); - entry = list.remove(&ir.index); - if entry.is_none() { - return; - } - } - output_box_imp - .reset_sinks - .remove(&*entry.clone().unwrap().0); - let alias = entry.unwrap().2; - let mut index = output_box_imp.reset_model_index.write().unwrap(); - let model_list = output_box_imp.reset_model_list.write().unwrap(); - - // add dummy entry when no other devices are available - if *index == 1 { - model_list.append("Dummy Output"); - } - - let mut map = output_box_imp.reset_sink_map.write().unwrap(); - map.remove(&alias); - - for entry in 0..*index { - if model_list.string(entry) == Some(alias.clone().into()) { - model_list.splice(entry, 1, &[]); - break; - } - } - - // dummy enforces a minimum of 1 - if *index > 1 { - *index -= 1; - } - }); - }); - true + sink_removed_handler(sink_removed_box.clone(), ir) }); if res.is_err() { println!("fail on sink remove event"); @@ -495,54 +433,8 @@ pub fn start_output_box_listener(conn: Connection, sink_box: Arc) -> Co } let res = conn.add_match(sink_changed, move |ir: SinkChanged, _, _| { - let sink_box = sink_changed_box.clone(); let default_sink = get_default_sink_name(); - glib::spawn_future(async move { - glib::idle_add_once(move || { - let output_box = sink_box.clone(); - let output_box_imp = output_box.imp(); - let is_default = ir.sink.name == default_sink; - let volume = ir.sink.volume.first().unwrap_or(&0_u32); - let fraction = (*volume as f64 / 655.36).round(); - let percentage = (fraction).to_string() + "%"; - - let list = output_box_imp.reset_sink_list.read().unwrap(); - let entry = list.get(&ir.sink.index); - if entry.is_none() { - return; - } - let imp = entry.unwrap().1.imp(); - if is_default { - output_box_imp.reset_volume_percentage.set_text(&percentage); - output_box_imp.reset_volume_slider.set_value(*volume as f64); - output_box_imp.reset_default_sink.replace(ir.sink.clone()); - if ir.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"); - } - imp.reset_selected_sink.set_active(true); - } else { - imp.reset_selected_sink.set_active(false); - } - imp.reset_sink_name - .set_title(ir.sink.alias.clone().as_str()); - imp.reset_volume_percentage.set_text(&percentage); - imp.reset_volume_slider.set_value(*volume as f64); - if ir.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"); - } - }); - }); - true + sink_changed_handler(sink_changed_box.clone(), ir, default_sink) }); if res.is_err() { println!("fail on sink change event"); @@ -550,106 +442,28 @@ pub fn start_output_box_listener(conn: Connection, sink_box: Arc) -> Co } 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.reset_input_stream_list.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.reset_input_streams.append(&*entry); - }); - }); - true + 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_changed, move |ir: InputStreamChanged, _, _| { - let imp = input_stream_changed_box.imp(); - let alias: String; - { - let sink_list = imp.reset_sink_list.read().unwrap(); - if let Some(alias_opt) = sink_list.get(&ir.stream.sink_index) { - alias = alias_opt.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.reset_input_stream_list.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.reset_sink_mute - .set_icon_name("audio-volume-muted-symbolic"); - } else { - imp.reset_sink_mute - .set_icon_name("audio-volume-high-symbolic"); - } - let name = ir.stream.application_name.clone() + ": " + ir.stream.name.as_str(); - imp.reset_sink_selection.set_title(name.as_str()); - let volume = ir.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); - 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(alias.clone().into()) { - imp.reset_sink_selection.set_selected(entry); - break; - } - } - }); - }); - true - }); - if res.is_err() { - println!("fail on input stream change event"); - 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.reset_input_stream_list.write().unwrap(); - let entry = list.remove(&ir.index); - if entry.is_none() { - return; - } - output_box_imp - .reset_input_streams - .remove(&*entry.unwrap().0); - }); - }); - true + 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 } diff --git a/src/components/output/sink_box_event_handlers.rs b/src/components/output/sink_box_event_handlers.rs new file mode 100644 index 0000000..ff89f3a --- /dev/null +++ b/src/components/output/sink_box_event_handlers.rs @@ -0,0 +1,239 @@ +use std::sync::Arc; + +use adw::prelude::{ComboRowExt, PreferencesRowExt}; +use glib::subclass::types::ObjectSubclassIsExt; +use gtk::prelude::{BoxExt, ButtonExt, CheckButtonExt, ListBoxRowExt, RangeExt}; +use re_set_lib::signals::{ + InputStreamAdded, InputStreamChanged, InputStreamRemoved, SinkAdded, SinkChanged, SinkRemoved, +}; + +use crate::components::base::list_entry::ListEntry; + +use super::{input_stream_entry::InputStreamEntry, sink_box::SinkBox, sink_entry::SinkEntry}; + +pub fn sink_added_handler(output_box: Arc, ir: SinkAdded) -> bool { + glib::spawn_future(async move { + glib::idle_add_once(move || { + let output_box_imp = output_box.imp(); + let sink_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.reset_default_sink.borrow().name == ir.sink.name { + is_default = true; + } + let sink_entry = Arc::new(SinkEntry::new( + is_default, + output_box_imp.reset_default_check_button.clone(), + ir.sink, + output_box.clone(), + )); + let sink_clone = sink_entry.clone(); + let entry = Arc::new(ListEntry::new(&*sink_entry)); + entry.set_activatable(false); + let mut list = output_box_imp.reset_sink_list.write().unwrap(); + list.insert(sink_index, (entry.clone(), sink_clone, alias.clone())); + output_box_imp.reset_sinks.append(&*entry); + let mut map = output_box_imp.reset_sink_map.write().unwrap(); + let mut index = output_box_imp.reset_model_index.write().unwrap(); + let model_list = output_box_imp.reset_model_list.write().unwrap(); + if model_list.string(*index - 1) == Some("Dummy Output".into()) { + model_list.append(&alias); + model_list.remove(*index - 1); + map.insert(alias, (sink_index, name)); + output_box_imp.reset_sink_dropdown.set_selected(0); + } else { + model_list.append(&alias); + map.insert(alias.clone(), (sink_index, name)); + if alias == "Dummy Output" { + output_box_imp.reset_sink_dropdown.set_selected(0); + } + *index += 1; + } + }); + }); + true +} + +pub fn sink_removed_handler(output_box: Arc, ir: SinkRemoved) -> bool { + glib::spawn_future(async move { + glib::idle_add_once(move || { + let output_box_imp = output_box.imp(); + + let entry: Option<(Arc, Arc, String)>; + { + let mut list = output_box_imp.reset_sink_list.write().unwrap(); + entry = list.remove(&ir.index); + if entry.is_none() { + return; + } + } + output_box_imp + .reset_sinks + .remove(&*entry.clone().unwrap().0); + let alias = entry.unwrap().2; + let mut index = output_box_imp.reset_model_index.write().unwrap(); + let model_list = output_box_imp.reset_model_list.write().unwrap(); + + // add dummy entry when no other devices are available + if *index == 1 { + model_list.append("Dummy Output"); + } + + let mut map = output_box_imp.reset_sink_map.write().unwrap(); + map.remove(&alias); + + for entry in 0..*index { + if model_list.string(entry) == Some(alias.clone().into()) { + model_list.splice(entry, 1, &[]); + break; + } + } + + // dummy enforces a minimum of 1 + if *index > 1 { + *index -= 1; + } + }); + }); + true +} + +pub fn sink_changed_handler( + output_box: Arc, + ir: SinkChanged, + default_sink: String, +) -> bool { + glib::spawn_future(async move { + glib::idle_add_once(move || { + let output_box_imp = output_box.imp(); + let is_default = ir.sink.name == default_sink; + let volume = ir.sink.volume.first().unwrap_or(&0_u32); + let fraction = (*volume as f64 / 655.36).round(); + let percentage = (fraction).to_string() + "%"; + + let list = output_box_imp.reset_sink_list.read().unwrap(); + let entry = list.get(&ir.sink.index); + if entry.is_none() { + return; + } + let imp = entry.unwrap().1.imp(); + if is_default { + output_box_imp.reset_volume_percentage.set_text(&percentage); + output_box_imp.reset_volume_slider.set_value(*volume as f64); + output_box_imp.reset_default_sink.replace(ir.sink.clone()); + if ir.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"); + } + imp.reset_selected_sink.set_active(true); + } else { + imp.reset_selected_sink.set_active(false); + } + imp.reset_sink_name + .set_title(ir.sink.alias.clone().as_str()); + imp.reset_volume_percentage.set_text(&percentage); + imp.reset_volume_slider.set_value(*volume as f64); + if ir.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"); + } + }); + }); + true +} + +pub fn input_stream_added_handler(output_box: Arc, ir: InputStreamAdded) -> bool { + glib::spawn_future(async move { + glib::idle_add_once(move || { + let output_box_imp = output_box.imp(); + let mut list = output_box_imp.reset_input_stream_list.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.reset_input_streams.append(&*entry); + }); + }); + true +} + +pub fn input_stream_removed_handler(output_box: Arc, ir: InputStreamRemoved) -> bool { + glib::spawn_future(async move { + glib::idle_add_once(move || { + let output_box_imp = output_box.imp(); + let mut list = output_box_imp.reset_input_stream_list.write().unwrap(); + let entry = list.remove(&ir.index); + if entry.is_none() { + return; + } + output_box_imp + .reset_input_streams + .remove(&*entry.unwrap().0); + }); + }); + true +} + +pub fn input_stream_changed_handler(output_box: Arc, ir: InputStreamChanged) -> bool { + let imp = output_box.imp(); + let alias: String; + { + let sink_list = imp.reset_sink_list.read().unwrap(); + if let Some(alias_opt) = sink_list.get(&ir.stream.sink_index) { + alias = alias_opt.2.clone(); + } else { + alias = String::from(""); + } + } + let sink_box = output_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.reset_input_stream_list.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.reset_sink_mute + .set_icon_name("audio-volume-muted-symbolic"); + } else { + imp.reset_sink_mute + .set_icon_name("audio-volume-high-symbolic"); + } + let name = ir.stream.application_name.clone() + ": " + ir.stream.name.as_str(); + imp.reset_sink_selection.set_title(name.as_str()); + let volume = ir.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); + 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(alias.clone().into()) { + imp.reset_sink_selection.set_selected(entry); + break; + } + } + }); + }); + true +} From 260077006ee47dfd15c6c51a3aa7bc88a123d0fc Mon Sep 17 00:00:00 2001 From: DashieTM Date: Sun, 10 Mar 2024 22:09:27 +0100 Subject: [PATCH 008/111] chore: Remove accidental file --- "src/components/output/\\" | 461 ------------------------------------- 1 file changed, 461 deletions(-) delete mode 100644 "src/components/output/\\" 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 -} From cc73c425f7d9fdac4e79b3e17aa0920fe0b6ecf1 Mon Sep 17 00:00:00 2001 From: Fabio Lenherr / DashieTM Date: Mon, 11 Mar 2024 13:39:30 +0100 Subject: [PATCH 009/111] refactor: audio sources --- src/components/input/source_box.rs | 489 ++++++------------ .../input/source_box_event_handlers.rs | 240 +++++++++ src/components/output/sink_box.rs | 4 +- 3 files changed, 398 insertions(+), 335 deletions(-) create mode 100644 src/components/input/source_box_event_handlers.rs diff --git a/src/components/input/source_box.rs b/src/components/input/source_box.rs index a1409df..b620b98 100644 --- a/src/components/input/source_box.rs +++ b/src/components/input/source_box.rs @@ -16,7 +16,7 @@ use dbus::blocking::Connection; use dbus::message::SignalArgs; use dbus::{Error, Path}; use glib::subclass::prelude::ObjectSubclassIsExt; -use glib::{clone, Cast, Propagation, Variant}; +use glib::{clone, Cast, ControlFlow, Propagation, Variant}; use gtk::prelude::ActionableExt; use gtk::{gio, StringObject}; @@ -29,6 +29,10 @@ use crate::components::utils::{ }; use super::output_stream_entry::OutputStreamEntry; +use super::source_box_event_handlers::{ + output_stream_added_handler, output_stream_changed_handler, output_stream_removed_handler, + source_added_handler, source_changed_handler, source_removed_handler, +}; use super::source_entry::{set_default_source, toggle_source_mute, SourceEntry}; glib::wrapper! { @@ -111,136 +115,159 @@ pub fn populate_sources(input_box: Arc) { populate_outputstreams(input_box.clone()); populate_cards(input_box.clone()); - glib::spawn_future(async move { - glib::idle_add_once(move || { - let input_box_ref_slider = input_box.clone(); - let input_box_ref_toggle = input_box.clone(); - let input_box_ref_mute = input_box.clone(); - let input_box_ref = input_box.clone(); - { - let input_box_imp = input_box_ref.imp(); - let default_sink = input_box_imp.reset_default_source.clone(); - let source = default_sink.borrow(); + populate_source_information(input_box, sources); + }); +} - if source.muted { - input_box_imp - .reset_source_mute - .set_icon_name("microphone-disabled-symbolic"); - } else { - input_box_imp - .reset_source_mute - .set_icon_name("audio-input-microphone-symbolic"); - } +fn populate_source_information(input_box: Arc, sources: Vec) { + glib::spawn_future(async move { + glib::idle_add_once(move || { + let input_box_ref_slider = input_box.clone(); + let input_box_ref_toggle = input_box.clone(); + let input_box_ref_mute = input_box.clone(); + let input_box_ref = input_box.clone(); + { + let input_box_imp = input_box_ref.imp(); + let default_sink = input_box_imp.reset_default_source.clone(); + let source = default_sink.borrow(); - let volume = source.volume.first().unwrap_or(&0_u32); - let fraction = (*volume as f64 / 655.36).round(); - let percentage = (fraction).to_string() + "%"; - input_box_imp.reset_volume_percentage.set_text(&percentage); - input_box_imp.reset_volume_slider.set_value(*volume as f64); - let mut list = input_box_imp.reset_source_list.write().unwrap(); - for source in sources { - let index = source.index; - let alias = source.alias.clone(); - let mut is_default = false; - if input_box_imp.reset_default_source.borrow().name == source.name { - is_default = true; - } - let source_entry = Arc::new(SourceEntry::new( - is_default, - input_box_imp.reset_default_check_button.clone(), - source, - input_box.clone(), - )); - 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)); - input_box_imp.reset_sources.append(&*entry); - } - let list = input_box_imp.reset_model_list.read().unwrap(); - input_box_imp.reset_source_dropdown.set_model(Some(&*list)); - let name = input_box_imp.reset_default_source.borrow(); - - let index = input_box_imp.reset_model_index.read().unwrap(); - let model_list = input_box_imp.reset_model_list.read().unwrap(); - for entry in 0..*index { - if model_list.string(entry) == Some(name.alias.clone().into()) { - input_box_imp.reset_source_dropdown.set_selected(entry); - break; - } - } - input_box_imp.reset_source_dropdown.connect_selected_notify( - clone!(@weak input_box_imp => move |dropdown| { - let input_box = input_box_ref_toggle.clone(); - 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 = input_box_imp.reset_source_map.read().unwrap(); - let source = source.get(&selected); - if source.is_none() { - return; - } - let source = Arc::new(source.unwrap().1.clone()); - gio::spawn_blocking(move || { - let result = set_default_source(source); - if result.is_none(){ - return; - } - refresh_default_source(result.unwrap(), input_box.clone(), false); - }); - }), - ); + if source.muted { + input_box_imp + .reset_source_mute + .set_icon_name("microphone-disabled-symbolic"); + } else { + input_box_imp + .reset_source_mute + .set_icon_name("audio-input-microphone-symbolic"); } - input_box_ref - .imp() - .reset_volume_slider - .connect_change_value(move |_, _, value| { - let imp = input_box_ref_slider.imp(); - let fraction = (value / 655.36).round(); - let percentage = (fraction).to_string() + "%"; - imp.reset_volume_percentage.set_text(&percentage); - let source = imp.reset_default_source.borrow(); - let index = source.index; - let channels = source.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_source_volume(value, index, channels); - Propagation::Proceed - }); - input_box_ref - .imp() - .reset_source_mute - .connect_clicked(move |_| { - let imp = input_box_ref_mute.imp(); - let mut source = imp.reset_default_source.borrow_mut(); - source.muted = !source.muted; - if source.muted { - imp.reset_source_mute - .set_icon_name("microphone-disabled-symbolic"); - } else { - imp.reset_source_mute - .set_icon_name("audio-input-microphone-symbolic"); + let volume = source.volume.first().unwrap_or(&0_u32); + let fraction = (*volume as f64 / 655.36).round(); + let percentage = (fraction).to_string() + "%"; + input_box_imp.reset_volume_percentage.set_text(&percentage); + input_box_imp.reset_volume_slider.set_value(*volume as f64); + let mut list = input_box_imp.reset_source_list.write().unwrap(); + for source in sources { + let index = source.index; + let alias = source.alias.clone(); + let mut is_default = false; + if input_box_imp.reset_default_source.borrow().name == source.name { + is_default = true; + } + let source_entry = Arc::new(SourceEntry::new( + is_default, + input_box_imp.reset_default_check_button.clone(), + source, + input_box.clone(), + )); + 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)); + input_box_imp.reset_sources.append(&*entry); + } + let list = input_box_imp.reset_model_list.read().unwrap(); + input_box_imp.reset_source_dropdown.set_model(Some(&*list)); + let name = input_box_imp.reset_default_source.borrow(); + + let index = input_box_imp.reset_model_index.read().unwrap(); + let model_list = input_box_imp.reset_model_list.read().unwrap(); + for entry in 0..*index { + if model_list.string(entry) == Some(name.alias.clone().into()) { + input_box_imp.reset_source_dropdown.set_selected(entry); + break; + } + } + input_box_imp + .reset_source_dropdown + .connect_selected_notify(move |dropdown| { + if let ControlFlow::Break() = + dropdown_handler(input_box_ref_toggle, dropdown, input_box_imp) + { + return; } - toggle_source_mute(source.index, source.muted); }); - }); + } + input_box_ref + .imp() + .reset_volume_slider + .connect_change_value(move |_, _, value| { + volume_slider_handler(input_box_ref_slider, value) + }); + + input_box_ref + .imp() + .reset_source_mute + .connect_clicked(move |_| { + mute_clicked_handler(input_box_ref_mute); + }); }); }); } +fn dropdown_handler( + input_box_ref_toggle: Arc, + dropdown: &adw::ComboRow, + input_box_imp: &source_box_impl::SourceBox, +) -> ControlFlow<()> { + let input_box = input_box_ref_toggle.clone(); + let selected = dropdown.selected_item(); + if selected.is_none() { + return ControlFlow::Break(()); + } + let selected = selected.unwrap(); + let selected = selected.downcast_ref::().unwrap(); + let selected = selected.string().to_string(); + let source = input_box_imp.reset_source_map.read().unwrap(); + let source = source.get(&selected); + if source.is_none() { + return ControlFlow::Break(()); + } + let source = Arc::new(source.unwrap().1.clone()); + gio::spawn_blocking(move || { + let result = set_default_source(source); + if result.is_none() { + return ControlFlow::Break(()); + } + refresh_default_source(result.unwrap(), input_box.clone(), false); + }); + + ControlFlow::Continue(()) +} + +fn volume_slider_handler(input_box_ref_slider: Arc, value: f64) -> Propagation { + let imp = input_box_ref_slider.imp(); + let fraction = (value / 655.36).round(); + let percentage = (fraction).to_string() + "%"; + imp.reset_volume_percentage.set_text(&percentage); + let source = imp.reset_default_source.borrow(); + let index = source.index; + let channels = source.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_source_volume(value, index, channels); + Propagation::Proceed +} + +fn mute_clicked_handler(input_box_ref_mute: Arc) { + let imp = input_box_ref_mute.imp(); + let mut source = imp.reset_default_source.borrow_mut(); + source.muted = !source.muted; + if source.muted { + imp.reset_source_mute + .set_icon_name("microphone-disabled-symbolic"); + } else { + imp.reset_source_mute + .set_icon_name("audio-input-microphone-symbolic"); + } + toggle_source_mute(source.index, source.muted); +} + pub fn refresh_default_source(new_source: Source, input_box: Arc, entry: bool) { let volume = *new_source.volume.first().unwrap_or(&0_u32); let fraction = (volume as f64 / 655.36).round(); @@ -348,7 +375,7 @@ fn get_cards() -> Vec { res.unwrap().0 } -fn get_default_source_name() -> String { +pub fn get_default_source_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, "GetDefaultSourceName", ()); @@ -393,93 +420,16 @@ pub fn start_input_box_listener(conn: Connection, source_box: Arc) -> 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 input_box = source_box.clone(); - let input_box_imp = input_box.imp(); - let source_index = ir.source.index; - let alias = ir.source.alias.clone(); - let name = ir.source.name.clone(); - let mut is_default = false; - if input_box_imp.reset_default_source.borrow().name == ir.source.name { - is_default = true; - } - let source_entry = Arc::new(SourceEntry::new( - is_default, - input_box_imp.reset_default_check_button.clone(), - ir.source, - input_box.clone(), - )); - let source_clone = source_entry.clone(); - let entry = Arc::new(ListEntry::new(&*source_entry)); - entry.set_activatable(false); - let mut list = input_box_imp.reset_source_list.write().unwrap(); - list.insert(source_index, (entry.clone(), source_clone, alias.clone())); - input_box_imp.reset_sources.append(&*entry); - let mut map = input_box_imp.reset_source_map.write().unwrap(); - let mut index = input_box_imp.reset_model_index.write().unwrap(); - let model_list = input_box_imp.reset_model_list.write().unwrap(); - if model_list.string(*index - 1) == Some("Monitor of Dummy Output".into()) { - model_list.append(&alias); - model_list.remove(*index - 1); - map.insert(alias, (source_index, name)); - input_box_imp.reset_source_dropdown.set_selected(0); - } else { - model_list.append(&alias); - map.insert(alias.clone(), (source_index, name)); - if alias == "Monitor of Dummy Output" { - input_box_imp.reset_source_dropdown.set_selected(0); - } - *index += 1; - } - }); - }); - true + source_added_handler(source_box, ir) }); if res.is_err() { + // TODO: handle this with the log/error macro println!("fail on source add event"); 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 input_box = source_box.clone(); - let input_box_imp = input_box.imp(); - let entry: Option<(Arc, Arc, String)>; - { - let mut list = input_box_imp.reset_source_list.write().unwrap(); - entry = list.remove(&ir.index); - if entry.is_none() { - return; - } - } - input_box_imp - .reset_sources - .remove(&*entry.clone().unwrap().0); - let mut map = input_box_imp.reset_source_map.write().unwrap(); - let alias = entry.unwrap().2; - map.remove(&alias); - let mut index = input_box_imp.reset_model_index.write().unwrap(); - let model_list = input_box_imp.reset_model_list.write().unwrap(); - - if *index == 1 { - model_list.append("Monitor of Dummy Output"); - } - for entry in 0..*index { - if model_list.string(entry) == Some(alias.clone().into()) { - model_list.splice(entry, 1, &[]); - break; - } - } - if *index > 1 { - *index -= 1; - } - }); - }); - true + source_removed_handler(source_box, ir) }); if res.is_err() { println!("fail on source remove event"); @@ -487,56 +437,7 @@ pub fn start_input_box_listener(conn: Connection, source_box: Arc) -> } let res = conn.add_match(source_changed, move |ir: SourceChanged, _, _| { - let source_box = source_changed_box.clone(); - let default_source = get_default_source_name(); - glib::spawn_future(async move { - glib::idle_add_once(move || { - let input_box = source_box.clone(); - let input_box_imp = input_box.imp(); - let is_default = ir.source.name == default_source; - let volume = ir.source.volume.first().unwrap_or(&0_u32); - let fraction = (*volume as f64 / 655.36).round(); - let percentage = (fraction).to_string() + "%"; - - let list = input_box_imp.reset_source_list.read().unwrap(); - let entry = list.get(&ir.source.index); - if entry.is_none() { - return; - } - let imp = entry.unwrap().1.imp(); - if is_default { - input_box_imp.reset_volume_percentage.set_text(&percentage); - input_box_imp.reset_volume_slider.set_value(*volume as f64); - input_box_imp - .reset_default_source - .replace(ir.source.clone()); - if ir.source.muted { - input_box_imp - .reset_source_mute - .set_icon_name("microphone-disabled-symbolic"); - } else { - input_box_imp - .reset_source_mute - .set_icon_name("audio-input-microphone-symbolic"); - } - imp.reset_selected_source.set_active(true); - } else { - imp.reset_selected_source.set_active(false); - } - imp.reset_source_name - .set_title(ir.source.alias.clone().as_str()); - imp.reset_volume_percentage.set_text(&percentage); - imp.reset_volume_slider.set_value(*volume as f64); - if ir.source.muted { - imp.reset_source_mute - .set_icon_name("microphone-disabled-symbolic"); - } else { - imp.reset_source_mute - .set_icon_name("audio-input-microphone-symbolic"); - } - }); - }); - true + source_changed_handler(source_box, ir) }); if res.is_err() { println!("fail on source change event"); @@ -544,21 +445,7 @@ pub fn start_input_box_listener(conn: Connection, source_box: Arc) -> } 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 input_box = source_box.clone(); - let input_box_imp = input_box.imp(); - let mut list = input_box_imp.reset_output_stream_list.write().unwrap(); - let index = ir.stream.index; - let output_stream = Arc::new(OutputStreamEntry::new(input_box.clone(), ir.stream)); - let entry = Arc::new(ListEntry::new(&*output_stream)); - entry.set_activatable(false); - list.insert(index, (entry.clone(), output_stream.clone())); - input_box_imp.reset_output_streams.append(&*entry); - }); - }); - true + output_stream_added_handler(output_stream_added_box, ir) }); if res.is_err() { println!("fail on output stream add event"); @@ -568,56 +455,7 @@ pub fn start_input_box_listener(conn: Connection, source_box: Arc) -> 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.reset_source_list.read().unwrap(); - if let Some(alias_opt) = source_list.get(&ir.stream.source_index) { - alias = alias_opt.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 input_box = source_box.clone(); - let input_box_imp = input_box.imp(); - let entry: Arc; - { - let list = input_box_imp.reset_output_stream_list.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.reset_source_mute - .set_icon_name("microphone-disabled-symbolic"); - } else { - imp.reset_source_mute - .set_icon_name("audio-input-microphone-symbolic"); - } - let name = ir.stream.application_name.clone() + ": " + ir.stream.name.as_str(); - imp.reset_source_selection.set_title(name.as_str()); - let volume = ir.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); - let index = input_box_imp.reset_model_index.read().unwrap(); - let model_list = input_box_imp.reset_model_list.read().unwrap(); - for entry in 0..*index { - if model_list.string(entry) == Some(alias.clone().into()) { - imp.reset_source_selection.set_selected(entry); - break; - } - } - }); - }); - true + output_stream_changed_handler(output_stream_changed_box, ir) }, ); if res.is_err() { @@ -628,22 +466,7 @@ pub fn start_input_box_listener(conn: Connection, source_box: Arc) -> 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 input_box = source_box.clone(); - let input_box_imp = input_box.imp(); - let mut list = input_box_imp.reset_output_stream_list.write().unwrap(); - let entry = list.remove(&ir.index); - if entry.is_none() { - return; - } - input_box_imp - .reset_output_streams - .remove(&*entry.unwrap().0); - }); - }); - true + output_stream_removed_handler(output_stream_removed_box, ir) }, ); if res.is_err() { diff --git a/src/components/input/source_box_event_handlers.rs b/src/components/input/source_box_event_handlers.rs new file mode 100644 index 0000000..73350a0 --- /dev/null +++ b/src/components/input/source_box_event_handlers.rs @@ -0,0 +1,240 @@ +use std::sync::Arc; + +use adw::prelude::{ComboRowExt, PreferencesRowExt}; +use glib::subclass::types::ObjectSubclassIsExt; +use gtk::prelude::{BoxExt, ButtonExt, CheckButtonExt, ListBoxRowExt, RangeExt}; +use re_set_lib::signals::{ + OutputStreamAdded, OutputStreamChanged, OutputStreamRemoved, SourceAdded, SourceChanged, + SourceRemoved, +}; + +use crate::components::base::list_entry::ListEntry; + +use super::{ + output_stream_entry::OutputStreamEntry, + source_box::{get_default_source_name, SourceBox}, + source_entry::SourceEntry, +}; + +pub fn source_added_handler(source_box: Arc, ir: SourceAdded) -> bool { + glib::spawn_future(async move { + glib::idle_add_once(move || { + let input_box = source_box.clone(); + let input_box_imp = input_box.imp(); + let source_index = ir.source.index; + let alias = ir.source.alias.clone(); + let name = ir.source.name.clone(); + let mut is_default = false; + if input_box_imp.reset_default_source.borrow().name == ir.source.name { + is_default = true; + } + let source_entry = Arc::new(SourceEntry::new( + is_default, + input_box_imp.reset_default_check_button.clone(), + ir.source, + input_box.clone(), + )); + let source_clone = source_entry.clone(); + let entry = Arc::new(ListEntry::new(&*source_entry)); + entry.set_activatable(false); + let mut list = input_box_imp.reset_source_list.write().unwrap(); + list.insert(source_index, (entry.clone(), source_clone, alias.clone())); + input_box_imp.reset_sources.append(&*entry); + let mut map = input_box_imp.reset_source_map.write().unwrap(); + let mut index = input_box_imp.reset_model_index.write().unwrap(); + let model_list = input_box_imp.reset_model_list.write().unwrap(); + if model_list.string(*index - 1) == Some("Monitor of Dummy Output".into()) { + model_list.append(&alias); + model_list.remove(*index - 1); + map.insert(alias, (source_index, name)); + input_box_imp.reset_source_dropdown.set_selected(0); + } else { + model_list.append(&alias); + map.insert(alias.clone(), (source_index, name)); + if alias == "Monitor of Dummy Output" { + input_box_imp.reset_source_dropdown.set_selected(0); + } + *index += 1; + } + }); + }); + true +} + +pub fn source_removed_handler(source_box: Arc, ir: SourceRemoved) -> bool { + glib::spawn_future(async move { + glib::idle_add_once(move || { + let input_box = source_box.clone(); + let input_box_imp = input_box.imp(); + let entry: Option<(Arc, Arc, String)>; + { + let mut list = input_box_imp.reset_source_list.write().unwrap(); + entry = list.remove(&ir.index); + if entry.is_none() { + return; + } + } + input_box_imp + .reset_sources + .remove(&*entry.clone().unwrap().0); + let mut map = input_box_imp.reset_source_map.write().unwrap(); + let alias = entry.unwrap().2; + map.remove(&alias); + let mut index = input_box_imp.reset_model_index.write().unwrap(); + let model_list = input_box_imp.reset_model_list.write().unwrap(); + + if *index == 1 { + model_list.append("Monitor of Dummy Output"); + } + for entry in 0..*index { + if model_list.string(entry) == Some(alias.clone().into()) { + model_list.splice(entry, 1, &[]); + break; + } + } + if *index > 1 { + *index -= 1; + } + }); + }); + true +} + +pub fn source_changed_handler(source_box: Arc, ir: SourceChanged) -> bool { + let default_source = get_default_source_name(); + glib::spawn_future(async move { + glib::idle_add_once(move || { + let input_box = source_box.clone(); + let input_box_imp = input_box.imp(); + let is_default = ir.source.name == default_source; + let volume = ir.source.volume.first().unwrap_or(&0_u32); + let fraction = (*volume as f64 / 655.36).round(); + let percentage = (fraction).to_string() + "%"; + + let list = input_box_imp.reset_source_list.read().unwrap(); + let entry = list.get(&ir.source.index); + if entry.is_none() { + return; + } + let imp = entry.unwrap().1.imp(); + if is_default { + input_box_imp.reset_volume_percentage.set_text(&percentage); + input_box_imp.reset_volume_slider.set_value(*volume as f64); + input_box_imp + .reset_default_source + .replace(ir.source.clone()); + if ir.source.muted { + input_box_imp + .reset_source_mute + .set_icon_name("microphone-disabled-symbolic"); + } else { + input_box_imp + .reset_source_mute + .set_icon_name("audio-input-microphone-symbolic"); + } + imp.reset_selected_source.set_active(true); + } else { + imp.reset_selected_source.set_active(false); + } + imp.reset_source_name + .set_title(ir.source.alias.clone().as_str()); + imp.reset_volume_percentage.set_text(&percentage); + imp.reset_volume_slider.set_value(*volume as f64); + if ir.source.muted { + imp.reset_source_mute + .set_icon_name("microphone-disabled-symbolic"); + } else { + imp.reset_source_mute + .set_icon_name("audio-input-microphone-symbolic"); + } + }); + }); + true +} + +pub fn output_stream_added_handler(source_box: Arc, ir: OutputStreamAdded) -> bool { + glib::spawn_future(async move { + glib::idle_add_once(move || { + let input_box = source_box.clone(); + let input_box_imp = input_box.imp(); + let mut list = input_box_imp.reset_output_stream_list.write().unwrap(); + let index = ir.stream.index; + let output_stream = Arc::new(OutputStreamEntry::new(input_box.clone(), ir.stream)); + let entry = Arc::new(ListEntry::new(&*output_stream)); + entry.set_activatable(false); + list.insert(index, (entry.clone(), output_stream.clone())); + input_box_imp.reset_output_streams.append(&*entry); + }); + }); + true +} + +pub fn output_stream_changed_handler(source_box: Arc, ir: OutputStreamChanged) -> bool { + let imp = source_box.imp(); + let alias: String; + { + let source_list = imp.reset_source_list.read().unwrap(); + if let Some(alias_opt) = source_list.get(&ir.stream.source_index) { + alias = alias_opt.2.clone(); + } else { + alias = String::from(""); + } + } + glib::spawn_future(async move { + glib::idle_add_once(move || { + let input_box = source_box.clone(); + let input_box_imp = input_box.imp(); + let entry: Arc; + { + let list = input_box_imp.reset_output_stream_list.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.reset_source_mute + .set_icon_name("microphone-disabled-symbolic"); + } else { + imp.reset_source_mute + .set_icon_name("audio-input-microphone-symbolic"); + } + let name = ir.stream.application_name.clone() + ": " + ir.stream.name.as_str(); + imp.reset_source_selection.set_title(name.as_str()); + let volume = ir.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); + let index = input_box_imp.reset_model_index.read().unwrap(); + let model_list = input_box_imp.reset_model_list.read().unwrap(); + for entry in 0..*index { + if model_list.string(entry) == Some(alias.clone().into()) { + imp.reset_source_selection.set_selected(entry); + break; + } + } + }); + }); + true +} + +pub fn output_stream_removed_handler(source_box: Arc, ir: OutputStreamRemoved) -> bool { + glib::spawn_future(async move { + glib::idle_add_once(move || { + let input_box = source_box.clone(); + let input_box_imp = input_box.imp(); + let mut list = input_box_imp.reset_output_stream_list.write().unwrap(); + let entry = list.remove(&ir.index); + if entry.is_none() { + return; + } + input_box_imp + .reset_output_streams + .remove(&*entry.unwrap().0); + }); + }); + true +} diff --git a/src/components/output/sink_box.rs b/src/components/output/sink_box.rs index 9246c06..87301e1 100644 --- a/src/components/output/sink_box.rs +++ b/src/components/output/sink_box.rs @@ -120,7 +120,7 @@ pub fn populate_sinks(output_box: Arc) { } populate_inputstreams(output_box.clone()); populate_cards(output_box.clone()); - initialize(output_box, sinks); + populate_sink_information(output_box, sinks); }); } @@ -184,7 +184,7 @@ fn mute_handler(output_box_ref_mute: Arc) { toggle_sink_mute(stream.index, stream.muted); } -fn initialize(output_box: Arc, sinks: Vec) { +fn populate_sink_information(output_box: Arc, sinks: Vec) { glib::spawn_future(async move { glib::idle_add_once(move || { let output_box_ref_select = output_box.clone(); From 16a30a7bddfecd59165002057a198cad0178652b Mon Sep 17 00:00:00 2001 From: Fabio Lenherr / DashieTM Date: Mon, 11 Mar 2024 17:19:38 +0100 Subject: [PATCH 010/111] feat: Add Error popup --- src/components/base/card_entry.rs | 8 +- src/components/base/error.rs | 41 ++++ src/components/base/error_impl.rs | 77 +++++++ src/components/base/mod.rs | 2 + src/components/bluetooth/bluetooth_entry.rs | 34 +-- src/components/input/mod.rs | 1 + src/components/input/source_box.rs | 204 +++++++++--------- .../input/source_box_event_handlers.rs | 2 +- src/components/input/source_box_impl.rs | 3 + src/components/input/source_entry.rs | 72 +++---- src/components/output/sink_box.rs | 50 +++-- .../output/sink_box_event_handlers.rs | 13 +- src/components/output/sink_box_impl.rs | 3 + src/components/output/sink_entry.rs | 65 +++--- src/components/window/reset_window.rs | 15 +- src/components/window/reset_window_impl.rs | 2 + src/resources/resetAudioInput.ui | 5 +- src/resources/resetAudioOutput.ui | 5 +- src/resources/resetBluetooth.ui | 2 +- src/resources/resetBluetoothEntry.ui | 2 +- src/resources/resetCardEntry.ui | 2 +- src/resources/resetError.ui | 25 +++ src/resources/resetInputStreamEntry.ui | 2 +- src/resources/resetListBoxRow.ui | 2 +- src/resources/resetMainWindow.ui | 2 +- src/resources/resetOutputStreamEntry.ui | 2 +- src/resources/resetPopup.ui | 2 +- src/resources/resetSavedWifiEntry.ui | 2 +- src/resources/resetSettingBox.ui | 2 +- src/resources/resetShortcuts.ui | 2 +- src/resources/resetSidebarEntry.ui | 2 +- src/resources/resetSinkEntry.ui | 2 +- src/resources/resetSourceEntry.ui | 2 +- src/resources/resetUI.cmb | 22 +- src/resources/resetWiFi.ui | 2 +- src/resources/resetWifiAddressEntry.ui | 2 +- src/resources/resetWifiEntry.ui | 2 +- src/resources/resetWifiOptions.ui | 2 +- src/resources/resetWifiRouteEntry.ui | 2 +- src/resources/resources.gresource.xml | 1 + 40 files changed, 422 insertions(+), 266 deletions(-) create mode 100644 src/components/base/error.rs create mode 100644 src/components/base/error_impl.rs create mode 100644 src/resources/resetError.ui diff --git a/src/components/base/card_entry.rs b/src/components/base/card_entry.rs index 17c53ca..e1aab70 100644 --- a/src/components/base/card_entry.rs +++ b/src/components/base/card_entry.rs @@ -13,7 +13,7 @@ use components::utils::create_dropdown_label_factory; use re_set_lib::audio::audio_structures::Card; use crate::components; -use crate::components::utils::{BASE, DBUS_PATH, AUDIO}; +use crate::components::utils::{AUDIO, BASE, DBUS_PATH}; use super::card_entry_impl; @@ -66,11 +66,7 @@ impl CardEntry { fn set_card_profile_of_device(device_index: u32, profile_name: String) -> bool { gio::spawn_blocking(move || { let conn = Connection::new_session().unwrap(); - let proxy = conn.with_proxy( - BASE, - DBUS_PATH, - Duration::from_millis(1000), - ); + let proxy = conn.with_proxy(BASE, DBUS_PATH, Duration::from_millis(1000)); let _: Result<(), Error> = proxy.method_call( AUDIO, "SetCardProfileOfDevice", diff --git a/src/components/base/error.rs b/src/components/base/error.rs new file mode 100644 index 0000000..a0f9c25 --- /dev/null +++ b/src/components/base/error.rs @@ -0,0 +1,41 @@ +use std::{rc::Rc, sync::Arc}; + +use adw::glib; +use adw::glib::Object; +use glib::{clone, subclass::types::ObjectSubclassIsExt}; +use gtk::{ + gdk, + prelude::{ButtonExt, PopoverExt}, + Editable, Popover, +}; + +use super::error_impl; + +glib::wrapper! { + pub struct ReSetError(ObjectSubclass) + @extends Popover, gtk::Widget, + @implements Editable,gdk::Popup, gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Orientable; +} + +unsafe impl Send for ReSetError {} +unsafe impl Sync for ReSetError {} + +impl ReSetError { + pub fn new() -> Self { + let error: ReSetError = Object::builder().build(); + error + .imp() + .reset_error_button + .connect_clicked(clone!(@strong error => move |_| { + println!("pingpangpung"); + error.popdown(); + })); + error + } +} + +impl Default for ReSetError { + fn default() -> Self { + Self::new() + } +} diff --git a/src/components/base/error_impl.rs b/src/components/base/error_impl.rs new file mode 100644 index 0000000..5c84083 --- /dev/null +++ b/src/components/base/error_impl.rs @@ -0,0 +1,77 @@ +use std::cell::RefCell; +use std::sync::Arc; + +use glib::clone; +use gtk::prelude::{ButtonExt, PopoverExt}; +use gtk::subclass::prelude::*; +use gtk::{glib, Button, CompositeTemplate, Label, PasswordEntry, PasswordEntryBuffer, Popover}; + +use crate::components::input::source_box::SourceBox; + +use super::error; + +#[derive(Default, CompositeTemplate)] +#[template(resource = "/org/Xetibo/ReSet/resetError.ui")] +pub struct ReSetError { + #[template_child] + pub reset_error_label: TemplateChild