From 472584649badca48c7b6671128940e8b57907473 Mon Sep 17 00:00:00 2001 From: Fabio Lenherr / DashieTM Date: Mon, 1 Apr 2024 18:02:26 +0200 Subject: [PATCH] wip: generic audio implementation --- Cargo.toml | 5 +- .../audio/generic_audio_functions.rs | 129 ++++++++++++ src/components/audio/generic_entry.rs | 197 ++++++++++++++++++ src/components/audio/input/source_box.rs | 7 + .../audio/input/source_box_handlers.rs | 4 +- src/components/audio/input/source_box_impl.rs | 88 +++++++- .../audio/input/source_box_utils.rs | 4 +- src/components/audio/input/source_entry.rs | 165 ++++++++------- .../audio/input/source_entry_impl.rs | 44 ++++ src/components/audio/mod.rs | 2 + src/components/window/sidebar_entry.rs | 4 +- 11 files changed, 567 insertions(+), 82 deletions(-) create mode 100644 src/components/audio/generic_audio_functions.rs create mode 100644 src/components/audio/generic_entry.rs diff --git a/Cargo.toml b/Cargo.toml index 85d6655..3ac0250 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,8 +7,9 @@ repository = "https://github.com/Xetibo/ReSet" license = "GPL-3.0-or-later" [dependencies] -#reset_daemon = "1.1.0" -re_set-lib = { git = "https://github.com/Xetibo/ReSet-Lib" } +# reset_daemon = "1.1.0" +re_set-lib = { git = "https://github.com/Xetibo/ReSet-Lib" , branch = "audioobject"} +# re_set-lib = "3.1.6" reset_daemon = { git = "https://github.com/Xetibo/ReSet-Daemon", branch = "dashie" } adw = { version = "0.6.0", package = "libadwaita", features = ["v1_4"] } dbus = "0.9.7" diff --git a/src/components/audio/generic_audio_functions.rs b/src/components/audio/generic_audio_functions.rs new file mode 100644 index 0000000..b59d926 --- /dev/null +++ b/src/components/audio/generic_audio_functions.rs @@ -0,0 +1,129 @@ +use std::{sync::Arc, time::Duration}; + +use adw::traits::ComboRowExt; +use dbus::{ + arg::{Arg, Get, ReadAll}, + blocking::Connection, + Error, +}; +use gtk::{ + gio, + prelude::{BoxExt, ButtonExt, CheckButtonExt, ListBoxRowExt, RangeExt}, +}; +use re_set_lib::audio::audio_structures::AudioObject; + +use crate::components::{ + base::error_impl::{show_error, ReSetErrorImpl}, + utils::{AUDIO, BASE, DBUS_PATH}, +}; + +use super::generic_entry::{Audio, AudioBox, AudioBoxImpl, AudioImpl}; + +pub fn set_volume( + value: f64, + index: u32, + channels: u16, + reset_box: Arc, + function: (&'static str, &'static str), +) -> bool { + gio::spawn_blocking(move || { + let conn = Connection::new_session().unwrap(); + let proxy = conn.with_proxy(BASE, DBUS_PATH, Duration::from_millis(1000)); + let res: Result<(), Error> = + proxy.method_call(AUDIO, function.0, (index, channels, value as u32)); + if res.is_err() { + // TODO: also log this with LOG/ERROR + show_error::(reset_box.clone(), function.1); + } + }); + true +} + +pub fn toggle_audio_object_mute( + index: u32, + muted: bool, + input_box: Arc, + function: (&'static str, &'static str), +) -> bool { + gio::spawn_blocking(move || { + let conn = Connection::new_session().unwrap(); + let proxy = conn.with_proxy(BASE, DBUS_PATH, Duration::from_millis(1000)); + let res: Result<(), Error> = proxy.method_call(AUDIO, function.0, (index, muted)); + if res.is_err() { + // TODO: also log this with LOG/ERROR + show_error::(input_box.clone(), function.1); + } + }); + true +} + +pub fn set_default_audio_object( + name: Arc, + input_box: Arc, + function: (&'static str, &'static str), +) -> Option +where + T: ReSetErrorImpl + 'static, + R: Arg + for<'z> Get<'z>, +{ + let conn = Connection::new_session().unwrap(); + let proxy = conn.with_proxy(BASE, DBUS_PATH, Duration::from_millis(1000)); + let res: Result<(R,), Error> = proxy.method_call(AUDIO, function.0, (name.as_str(),)); + if res.is_err() { + show_error::(input_box.clone(), function.1); + return None; + } + Some(res.unwrap().0) +} + +pub fn refresh_default_audio_object< + A: AudioBox + Send + Sync + 'static, + OBJ: AudioObject + Send + Sync + 'static, + Entry: Audio, + EntryImpl: AudioImpl, + BoxImpl: AudioBoxImpl, +>( + new_audio_object: OBJ, + reset_box: Arc, + entry: bool, +) { + let volume = *new_audio_object.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 = reset_box.box_imp(); + if !entry { + let list = imp.audio_object_list(); + let list = list.read().unwrap(); + let entry = list.get(&new_audio_object.index()); + if entry.is_none() { + return; + } + let entry_imp = entry.unwrap().1.entry_imp(); + entry_imp.selected_audio_object().set_active(true); + } else { + let index = imp.model_index(); + let index = index.read().unwrap(); + let model_list = imp.model_list(); + let model_list = model_list.read().unwrap(); + for entry in 0..*index { + if model_list.string(entry) == Some(new_audio_object.alias().clone().into()) { + imp.audio_object_dropdown().set_selected(entry); + break; + } + } + } + imp.volume_percentage().set_text(&percentage); + imp.volume_slider().set_value(volume as f64); + if new_audio_object.muted() { + imp.audio_object_mute() + .set_icon_name("audio-volume-muted-symbolic"); + } else { + imp.audio_object_mute() + .set_icon_name("audio-volume-high-symbolic"); + } + imp.default_audio_object().replace(new_audio_object); + }); + }); +} diff --git a/src/components/audio/generic_entry.rs b/src/components/audio/generic_entry.rs new file mode 100644 index 0000000..5668c67 --- /dev/null +++ b/src/components/audio/generic_entry.rs @@ -0,0 +1,197 @@ +use std::collections::HashMap; +use std::rc::Rc; +use std::sync::RwLock; +use std::time::Duration; +use std::{cell::RefCell, sync::Arc, time::SystemTime}; + +use adw::prelude::{ButtonExt, CheckButtonExt, PreferencesRowExt, RangeExt}; +use adw::{ActionRow, ComboRow, PreferencesGroup}; +use dbus::arg::{Arg, Get, ReadAll}; +use glib::clone::{Downgrade, Upgrade}; +use glib::{clone, Propagation}; +use glib::{ + object::{IsA, IsClass, ObjectSubclassIs, ParentClassIs}, + subclass::types::{InstanceStruct, InstanceStructExt, ObjectSubclass, ObjectSubclassType}, + Object, +}; +use gtk::{gio, Button, CheckButton, Label, Scale, StringList, TemplateChild}; +use re_set_lib::audio::audio_structures::{AudioObject, Source, Volume}; + +use crate::components::base::error::ReSetError; +use crate::components::base::error_impl::ReSetErrorImpl; +use crate::components::base::list_entry::ListEntry; +use crate::components::utils::set_action_row_ellipsis; + +use super::generic_audio_functions::{ + refresh_default_audio_object, set_default_audio_object, set_volume, toggle_audio_object_mute, +}; + +pub trait Audio>: IsClass + IsA { + fn entry_imp(&self) -> &IMP; +} + +pub trait AudioBox { + fn box_imp(&self) -> &AudioBoxImpl; +} + +pub type AudioEntryMap = Arc, Arc, String)>>>; +pub type AudioStreamEntryMap = Arc, Arc)>>>; +pub type AudioMap = Arc>>; + +pub trait AudioBoxImpl { + fn audio_object_row(&self) -> &TemplateChild; + fn cards_row(&self) -> &TemplateChild; + fn audio_object_dropdown(&self) -> &TemplateChild; + fn audio_object_mute(&self) -> &TemplateChild