mirror of
https://github.com/Xetibo/ReSet.git
synced 2025-04-19 11:08:32 +02:00
465 lines
17 KiB
Rust
465 lines
17 KiB
Rust
use std::sync::Arc;
|
|
|
|
use adw::{prelude::ComboRowExt, prelude::PreferencesGroupExt};
|
|
use dbus::{
|
|
arg::{Arg, Get, ReadAll},
|
|
blocking::Connection,
|
|
message::SignalArgs,
|
|
Path,
|
|
};
|
|
use glib::{object::IsA, Variant};
|
|
use gtk::{
|
|
gio,
|
|
prelude::{ActionableExt, BoxExt, ButtonExt, CheckButtonExt, ListBoxRowExt, RangeExt},
|
|
};
|
|
use re_set_lib::{
|
|
audio::audio_structures::{Card, TAudioObject, TAudioStreamObject},
|
|
signals::{TAudioEventRemoved, TAudioObjectEvent, TAudioStreamEvent},
|
|
};
|
|
|
|
use crate::components::{
|
|
base::{card_entry::CardEntry, error_impl::ReSetErrorImpl, list_entry::ListEntry},
|
|
utils::{create_dropdown_label_factory, set_combo_row_ellipsis, BASE, DBUS_PATH},
|
|
};
|
|
|
|
use super::{
|
|
audio_box_handlers::{
|
|
audio_stream_added_handler, audio_stream_changed_handler, audio_stream_removed_handler,
|
|
dropdown_handler, mute_clicked_handler, object_added_handler, object_changed_handler,
|
|
object_removed_handler, volume_slider_handler,
|
|
},
|
|
audio_const::GETCARDS,
|
|
audio_entry::{
|
|
new_entry, DBusFunction, TAudioBox, TAudioBoxImpl, TAudioEntry, TAudioEntryImpl,
|
|
TAudioStream, TAudioStreamImpl,
|
|
},
|
|
audio_functions::new_stream_entry,
|
|
audio_utils::audio_dbus_call,
|
|
};
|
|
|
|
pub fn setup_audio_box_callbacks<
|
|
AudioObject: TAudioObject,
|
|
StreamObject: TAudioStreamObject,
|
|
AudioEntry: TAudioEntry<AudioEntryImpl>,
|
|
AudioEntryImpl: TAudioEntryImpl<AudioObject>,
|
|
AudioStream: TAudioStream<AudioStreamImpl>,
|
|
AudioStreamImpl: TAudioStreamImpl<AudioObject, StreamObject>,
|
|
AudioBox: TAudioBox<AudioBoxImpl>,
|
|
AudioBoxImpl: TAudioBoxImpl<AudioObject, AudioEntry, AudioStream>,
|
|
>(
|
|
audio_box: &mut AudioBox,
|
|
) {
|
|
let imp = audio_box.box_imp();
|
|
let object_row = imp.audio_object_row();
|
|
object_row.set_activatable(true);
|
|
object_row.set_action_name(Some("navigation.push"));
|
|
object_row.set_action_target_value(Some(&Variant::from("devices")));
|
|
|
|
let cards_row = imp.cards_row();
|
|
cards_row.set_activatable(true);
|
|
cards_row.set_action_name(Some("navigation.push"));
|
|
cards_row.set_action_target_value(Some(&Variant::from("profileConfiguration")));
|
|
|
|
let stream_button = imp.audio_object_stream_button();
|
|
stream_button.set_activatable(true);
|
|
stream_button.set_action_name(Some("navigation.pop"));
|
|
|
|
let cards_back_button = imp.cards_button();
|
|
cards_back_button.set_activatable(true);
|
|
cards_back_button.set_action_name(Some("navigation.pop"));
|
|
|
|
let audio_object_dropdown = imp.audio_object_dropdown();
|
|
audio_object_dropdown.set_factory(Some(&create_dropdown_label_factory()));
|
|
set_combo_row_ellipsis(audio_object_dropdown.get());
|
|
}
|
|
|
|
pub fn populate_cards<
|
|
AudioObject: TAudioObject,
|
|
StreamObject: TAudioStreamObject,
|
|
AudioEntry: TAudioEntry<AudioEntryImpl>,
|
|
AudioEntryImpl: TAudioEntryImpl<AudioObject>,
|
|
AudioStream: TAudioStream<AudioStreamImpl>,
|
|
AudioStreamImpl: TAudioStreamImpl<AudioObject, StreamObject>,
|
|
AudioBox: TAudioBox<AudioBoxImpl> + ReSetErrorImpl + 'static,
|
|
AudioBoxImpl: TAudioBoxImpl<AudioObject, AudioEntry, AudioStream>,
|
|
>(
|
|
source_box: Arc<AudioBox>,
|
|
) {
|
|
gio::spawn_blocking(move || {
|
|
let source_box_ref = source_box.clone();
|
|
let cards =
|
|
audio_dbus_call::<AudioBox, (Vec<Card>,), ()>(source_box.clone(), (), &GETCARDS);
|
|
if cards.is_none() {
|
|
return;
|
|
}
|
|
let cards = cards.unwrap().0;
|
|
glib::spawn_future(async move {
|
|
glib::idle_add_once(move || {
|
|
let imp = source_box_ref.box_imp();
|
|
for card in cards {
|
|
imp.cards().add(&CardEntry::new(card));
|
|
}
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
pub fn populate_streams<
|
|
AudioObject: TAudioObject + Sync + Send + 'static,
|
|
StreamObject: TAudioStreamObject + Arg + for<'z> Get<'z> + Sync + Send + 'static,
|
|
AudioEntry: TAudioEntry<AudioEntryImpl>,
|
|
AudioEntryImpl: TAudioEntryImpl<AudioObject>,
|
|
AudioStream: TAudioStream<AudioStreamImpl> + IsA<gtk::Widget>,
|
|
AudioStreamImpl: TAudioStreamImpl<AudioObject, StreamObject>,
|
|
AudioBox: TAudioBox<AudioBoxImpl> + ReSetErrorImpl + 'static,
|
|
AudioBoxImpl: TAudioBoxImpl<AudioObject, AudioEntry, AudioStream>,
|
|
>(
|
|
audio_box: Arc<AudioBox>,
|
|
function: &'static DBusFunction,
|
|
) {
|
|
let audio_box_ref = audio_box.clone();
|
|
gio::spawn_blocking(move || {
|
|
let streams =
|
|
audio_dbus_call::<AudioBox, (Vec<StreamObject>,), ()>(audio_box.clone(), (), function);
|
|
if streams.is_none() {
|
|
return;
|
|
}
|
|
let streams = streams.unwrap().0;
|
|
glib::spawn_future(async move {
|
|
glib::idle_add_once(move || {
|
|
let imp = audio_box_ref.box_imp();
|
|
let mut list = imp.audio_object_stream_list().write().unwrap();
|
|
for stream in streams {
|
|
let index = stream.index();
|
|
let stream = new_stream_entry::<
|
|
AudioObject,
|
|
StreamObject,
|
|
AudioEntry,
|
|
AudioEntryImpl,
|
|
AudioStream,
|
|
AudioStreamImpl,
|
|
AudioBox,
|
|
AudioBoxImpl,
|
|
>(audio_box.clone(), stream);
|
|
let stream_clone = stream.clone();
|
|
let entry = Arc::new(ListEntry::new(&*stream));
|
|
entry.set_activatable(false);
|
|
list.insert(index, (entry.clone(), stream_clone));
|
|
imp.audio_object_streams().append(&*entry);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
pub fn refresh_default_audio_object<
|
|
AudioObject: TAudioObject + Sync + Send + 'static,
|
|
StreamObject: TAudioStreamObject + Arg + for<'z> Get<'z> + Sync + Send + 'static,
|
|
AudioEntry: TAudioEntry<AudioEntryImpl>,
|
|
AudioEntryImpl: TAudioEntryImpl<AudioObject>,
|
|
AudioStream: TAudioStream<AudioStreamImpl> + IsA<gtk::Widget>,
|
|
AudioStreamImpl: TAudioStreamImpl<AudioObject, StreamObject>,
|
|
AudioBox: TAudioBox<AudioBoxImpl> + ReSetErrorImpl + 'static,
|
|
AudioBoxImpl: TAudioBoxImpl<AudioObject, AudioEntry, AudioStream>,
|
|
>(
|
|
new_audio_object: AudioObject,
|
|
audio_box: Arc<AudioBox>,
|
|
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 = audio_box.box_imp();
|
|
if !entry {
|
|
let list = imp.audio_object_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 model_list = imp.model_list();
|
|
let model_list = model_list.read().unwrap();
|
|
for entry in 0..*imp.model_index().read().unwrap() {
|
|
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);
|
|
let icons = imp.icons();
|
|
let mute_button = imp.audio_object_mute();
|
|
if new_audio_object.muted() {
|
|
mute_button.set_icon_name(icons.muted);
|
|
} else {
|
|
mute_button.set_icon_name(icons.active);
|
|
}
|
|
imp.default_audio_object().replace(new_audio_object);
|
|
});
|
|
});
|
|
}
|
|
|
|
pub fn populate_audio_object_information<
|
|
AudioObject: TAudioObject + Sync + Send + 'static + Arg + for<'z> Get<'z>,
|
|
StreamObject: TAudioStreamObject + Arg + for<'z> Get<'z> + Sync + Send + 'static,
|
|
AudioEntry: TAudioEntry<AudioEntryImpl> + IsA<gtk::Widget>,
|
|
AudioEntryImpl: TAudioEntryImpl<AudioObject>,
|
|
AudioStream: TAudioStream<AudioStreamImpl> + IsA<gtk::Widget>,
|
|
AudioStreamImpl: TAudioStreamImpl<AudioObject, StreamObject>,
|
|
AudioBox: TAudioBox<AudioBoxImpl> + ReSetErrorImpl + 'static,
|
|
AudioBoxImpl: TAudioBoxImpl<AudioObject, AudioEntry, AudioStream>,
|
|
>(
|
|
audio_box: Arc<AudioBox>,
|
|
audio_objects: Vec<AudioObject>,
|
|
dropdown_function: &'static DBusFunction,
|
|
change_volume_function: &'static DBusFunction,
|
|
mute_function: &'static DBusFunction,
|
|
) {
|
|
glib::spawn_future(async move {
|
|
glib::idle_add_once(move || {
|
|
let source_box_ref_slider = audio_box.clone();
|
|
let source_box_ref_toggle = audio_box.clone();
|
|
let source_box_ref_mute = audio_box.clone();
|
|
let imp = audio_box.box_imp();
|
|
let default_sink = imp.default_audio_object().clone();
|
|
let source = default_sink.borrow();
|
|
|
|
let icons = imp.icons();
|
|
let mute_button = imp.audio_object_mute();
|
|
if source.muted() {
|
|
mute_button.set_icon_name(icons.muted);
|
|
} else {
|
|
mute_button.set_icon_name(icons.active);
|
|
}
|
|
|
|
let volume = source.volume();
|
|
let volume = volume.first().unwrap_or(&0_u32);
|
|
let fraction = (*volume as f64 / 655.36).round();
|
|
let percentage = (fraction).to_string() + "%";
|
|
imp.volume_percentage().set_text(&percentage);
|
|
imp.volume_slider().set_value(*volume as f64);
|
|
let list = imp.audio_object_list();
|
|
let mut list = list.write().unwrap();
|
|
for source in audio_objects {
|
|
let index = source.index();
|
|
let alias = source.alias().clone();
|
|
let mut is_default = false;
|
|
if imp.default_audio_object().borrow().name() == source.name() {
|
|
is_default = true;
|
|
}
|
|
let source_entry = new_entry::<
|
|
AudioObject,
|
|
StreamObject,
|
|
AudioEntry,
|
|
AudioEntryImpl,
|
|
AudioStream,
|
|
AudioStreamImpl,
|
|
AudioBox,
|
|
AudioBoxImpl,
|
|
>(
|
|
is_default,
|
|
imp.default_check_button().clone(),
|
|
source,
|
|
audio_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));
|
|
imp.audio_objects().append(&*entry);
|
|
}
|
|
let list = imp.model_list();
|
|
let list = list.read().unwrap();
|
|
imp.audio_object_dropdown().set_model(Some(&*list));
|
|
let name = imp.default_audio_object();
|
|
let name = name.borrow();
|
|
|
|
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(name.alias().clone().into()) {
|
|
imp.audio_object_dropdown().set_selected(entry);
|
|
break;
|
|
}
|
|
}
|
|
imp.audio_object_dropdown()
|
|
.connect_selected_notify(move |dropdown| {
|
|
dropdown_handler(source_box_ref_toggle.clone(), dropdown, dropdown_function);
|
|
});
|
|
imp.volume_slider()
|
|
.connect_change_value(move |_, _, value| {
|
|
volume_slider_handler(
|
|
source_box_ref_slider.clone(),
|
|
value,
|
|
change_volume_function,
|
|
)
|
|
});
|
|
|
|
imp.audio_object_mute().connect_clicked(move |_| {
|
|
mute_clicked_handler(source_box_ref_mute.clone(), mute_function);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
pub fn start_audio_box_listener<
|
|
AudioObject: TAudioObject,
|
|
StreamObject: TAudioStreamObject,
|
|
AudioEntry: TAudioEntry<AudioEntryImpl> + IsA<gtk::Widget>,
|
|
AudioEntryImpl: TAudioEntryImpl<AudioObject>,
|
|
AudioStream: TAudioStream<AudioStreamImpl> + IsA<gtk::Widget>,
|
|
AudioStreamImpl: TAudioStreamImpl<AudioObject, StreamObject>,
|
|
AudioBox: TAudioBox<AudioBoxImpl> + ReSetErrorImpl + 'static,
|
|
AudioBoxImpl: TAudioBoxImpl<AudioObject, AudioEntry, AudioStream>,
|
|
ObjectAdded: TAudioObjectEvent<AudioObject> + ReadAll + SignalArgs,
|
|
ObjectChanged: TAudioObjectEvent<AudioObject> + ReadAll + SignalArgs,
|
|
ObjectRemoved: TAudioEventRemoved + ReadAll + SignalArgs,
|
|
StreamAdded: TAudioStreamEvent<StreamObject> + ReadAll + SignalArgs,
|
|
StreamChanged: TAudioStreamEvent<StreamObject> + ReadAll + SignalArgs,
|
|
StreamRemoved: TAudioEventRemoved + ReadAll + SignalArgs,
|
|
>(
|
|
conn: Connection,
|
|
source_box: Arc<AudioBox>,
|
|
get_default_name_function: &'static DBusFunction,
|
|
dummy_name: &'static str,
|
|
) -> Connection {
|
|
// TODO: make the failed logs generically sound -> deynamic output for both
|
|
let object_added =
|
|
ObjectAdded::match_rule(Some(&BASE.into()), Some(&Path::from(DBUS_PATH))).static_clone();
|
|
let object_changed =
|
|
ObjectChanged::match_rule(Some(&BASE.into()), Some(&Path::from(DBUS_PATH))).static_clone();
|
|
let object_removed =
|
|
ObjectRemoved::match_rule(Some(&BASE.into()), Some(&Path::from(DBUS_PATH))).static_clone();
|
|
let stream_added =
|
|
StreamAdded::match_rule(Some(&BASE.into()), Some(&Path::from(DBUS_PATH))).static_clone();
|
|
let stream_changed =
|
|
StreamChanged::match_rule(Some(&BASE.into()), Some(&Path::from(DBUS_PATH))).static_clone();
|
|
let stream_removed =
|
|
StreamRemoved::match_rule(Some(&BASE.into()), Some(&Path::from(DBUS_PATH))).static_clone();
|
|
|
|
let object_added_box = source_box.clone();
|
|
let object_removed_box = source_box.clone();
|
|
let object_changed_box = source_box.clone();
|
|
let stream_added_box = source_box.clone();
|
|
let stream_removed_box = source_box.clone();
|
|
let stream_changed_box = source_box.clone();
|
|
|
|
let res = conn.add_match(object_added, move |ir: ObjectAdded, _, _| {
|
|
object_added_handler::<
|
|
AudioObject,
|
|
StreamObject,
|
|
AudioEntry,
|
|
AudioEntryImpl,
|
|
AudioStream,
|
|
AudioStreamImpl,
|
|
AudioBox,
|
|
AudioBoxImpl,
|
|
ObjectAdded,
|
|
>(object_added_box.clone(), ir, dummy_name)
|
|
});
|
|
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(object_changed, move |ir: ObjectChanged, _, _| {
|
|
object_changed_handler::<
|
|
AudioObject,
|
|
StreamObject,
|
|
AudioEntry,
|
|
AudioEntryImpl,
|
|
AudioStream,
|
|
AudioStreamImpl,
|
|
AudioBox,
|
|
AudioBoxImpl,
|
|
ObjectChanged,
|
|
>(object_changed_box.clone(), ir, get_default_name_function)
|
|
});
|
|
if res.is_err() {
|
|
println!("fail on source change event");
|
|
return conn;
|
|
}
|
|
|
|
let res = conn.add_match(object_removed, move |ir: ObjectRemoved, _, _| {
|
|
object_removed_handler::<
|
|
AudioObject,
|
|
StreamObject,
|
|
AudioEntry,
|
|
AudioEntryImpl,
|
|
AudioStream,
|
|
AudioStreamImpl,
|
|
AudioBox,
|
|
AudioBoxImpl,
|
|
ObjectRemoved,
|
|
>(object_removed_box.clone(), ir, dummy_name)
|
|
});
|
|
if res.is_err() {
|
|
println!("fail on source remove event");
|
|
return conn;
|
|
}
|
|
|
|
let res = conn.add_match(stream_added, move |ir: StreamAdded, _, _| {
|
|
audio_stream_added_handler::<
|
|
AudioObject,
|
|
StreamObject,
|
|
AudioEntry,
|
|
AudioEntryImpl,
|
|
AudioStream,
|
|
AudioStreamImpl,
|
|
AudioBox,
|
|
AudioBoxImpl,
|
|
StreamAdded,
|
|
>(stream_added_box.clone(), ir)
|
|
});
|
|
if res.is_err() {
|
|
println!("fail on output stream add event");
|
|
return conn;
|
|
}
|
|
|
|
let res = conn.add_match(stream_changed, move |ir: StreamChanged, _, _| {
|
|
audio_stream_changed_handler::<
|
|
AudioObject,
|
|
StreamObject,
|
|
AudioEntry,
|
|
AudioEntryImpl,
|
|
AudioStream,
|
|
AudioStreamImpl,
|
|
AudioBox,
|
|
AudioBoxImpl,
|
|
StreamChanged,
|
|
>(stream_changed_box.clone(), ir)
|
|
});
|
|
if res.is_err() {
|
|
println!("fail on output stream change event");
|
|
return conn;
|
|
}
|
|
|
|
let res = conn.add_match(stream_removed, move |ir: StreamRemoved, _, _| {
|
|
audio_stream_removed_handler::<
|
|
AudioObject,
|
|
StreamObject,
|
|
AudioEntry,
|
|
AudioEntryImpl,
|
|
AudioStream,
|
|
AudioStreamImpl,
|
|
AudioBox,
|
|
AudioBoxImpl,
|
|
StreamRemoved,
|
|
>(stream_removed_box.clone(), ir)
|
|
});
|
|
if res.is_err() {
|
|
println!("fail on output stream remove event");
|
|
return conn;
|
|
}
|
|
|
|
conn
|
|
}
|