feat: Add initial Sound Mapping

This commit is contained in:
Fabio Lenherr / DashieTM 2023-11-13 18:12:36 +01:00
parent 679b4f0331
commit 5999bb1b45
8 changed files with 206 additions and 52 deletions

View file

@ -1,9 +1,21 @@
use adw::glib;
use std::sync::Arc;
use std::time::Duration;
use crate::components::base::listEntry::ListEntry;
use crate::components::base::utils::Listeners;
use crate::components::output::audioBoxImpl;
use adw::glib::Object;
use adw::prelude::BoxExt;
use adw::{glib, prelude::ListBoxRowExt};
use dbus::blocking::Connection;
use dbus::Error;
use glib::subclass::prelude::ObjectSubclassIsExt;
use glib::Variant;
use gtk::gio;
use gtk::prelude::ActionableExt;
use crate::components::output::audioBoxImpl;
use ReSet_Lib::audio::audio::{InputStream, Sink};
use super::audioSource::{self, AudioSourceEntry};
glib::wrapper! {
pub struct AudioBox(ObjectSubclass<audioBoxImpl::AudioBox>)
@ -11,6 +23,9 @@ glib::wrapper! {
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Orientable;
}
unsafe impl Send for AudioBox {}
unsafe impl Sync for AudioBox {}
impl AudioBox {
pub fn new() -> Self {
Object::builder().build()
@ -18,9 +33,113 @@ impl AudioBox {
pub fn setupCallbacks(&self) {
let selfImp = self.imp();
selfImp.resetSinksRow.set_action_name(Some("navigation.push"));
selfImp.resetSinksRow.set_action_target_value(Some(&Variant::from("outputDevices")));
selfImp
.resetSinksRow
.set_action_name(Some("navigation.push"));
selfImp
.resetSinksRow
.set_action_target_value(Some(&Variant::from("outputDevices")));
selfImp.resetOutputStreamButton.set_action_name(Some("navigation.pop"));
selfImp
.resetOutputStreamButton
.set_action_name(Some("navigation.pop"));
}
}
pub fn populate_sinks(output_box: Arc<AudioBox>) {
let output_box_ref = output_box.clone();
gio::spawn_blocking(move || {
let output_box_imp = output_box.imp();
let sinks = get_sinks();
output_box_imp.resetDefaultSink.replace(get_default_sink());
glib::spawn_future(async move {
glib::idle_add_once(move || {
let output_box_imp = output_box_ref.imp();
// TODO handle default mapping
// output_box_imp.resetVolumePercentage.set_text();
// output_box_imp.resetVolumeSlider.let
for stream in sinks {
let entry = ListEntry::new(&AudioSourceEntry::new(
stream.name,
stream.volume,
stream.muted,
stream.index,
));
entry.set_activatable(false);
output_box_imp.resetSinks.append(&entry);
}
});
});
});
}
pub fn populate_streams(listeners: Arc<Listeners>, output_box: Arc<AudioBox>) {
// TODO add listener
let output_box_ref = output_box.clone();
// let output_box_ref_listener = output_box.clone();
let output_box_imp = output_box.imp();
// let sources = output_box_imp.resetSinks.clone();
// let output_streams = output_box_imp.resetInputStreams.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();
for stream in streams {
let entry = ListEntry::new(&AudioSourceEntry::new(
stream.name,
stream.volume,
stream.muted,
stream.index,
));
entry.set_activatable(false);
output_box_imp.resetOutputStreams.append(&entry);
}
});
});
});
}
fn get_input_streams() -> Vec<InputStream> {
let conn = Connection::new_session().unwrap();
let proxy = conn.with_proxy(
"org.xetibo.ReSet",
"/org/xetibo/ReSet",
Duration::from_millis(1000),
);
let res: Result<(Vec<InputStream>,), Error> =
proxy.method_call("org.xetibo.ReSet", "ListInputStreams", ());
if res.is_err() {
return Vec::new();
}
res.unwrap().0
}
fn get_sinks() -> Vec<Sink> {
let conn = Connection::new_session().unwrap();
let proxy = conn.with_proxy(
"org.xetibo.ReSet",
"/org/xetibo/ReSet",
Duration::from_millis(1000),
);
let res: Result<(Vec<Sink>,), Error> = proxy.method_call("org.xetibo.ReSet", "ListSinks", ());
if res.is_err() {
return Vec::new();
}
res.unwrap().0
}
fn get_default_sink() -> Option<Sink> {
let conn = Connection::new_session().unwrap();
let proxy = conn.with_proxy(
"org.xetibo.ReSet",
"/org/xetibo/ReSet",
Duration::from_millis(1000),
);
let res: Result<(Sink,), Error> = proxy.method_call("org.xetibo.ReSet", "GetDefaultSink", ());
if res.is_err() {
return None;
}
Some(res.unwrap().0)
}

View file

@ -1,23 +1,41 @@
use gtk::{CompositeTemplate, DropDown, TemplateChild, glib};
use gtk::prelude::*;
use gtk::subclass::prelude::*;
use std::cell::RefCell;
use std::sync::{Arc, Mutex};
use crate::components::base::listEntry::ListEntry;
use crate::components::output::audioBox;
use crate::components::output::audioSource::AudioSourceEntry;
use crate::components::base::listEntry::ListEntry;
use gtk::subclass::prelude::*;
use gtk::{glib, Box, Button, CompositeTemplate, DropDown, Label, TemplateChild};
use gtk::{prelude::*, ProgressBar, Scale};
use ReSet_Lib::audio::audio::{InputStream, Sink};
#[allow(non_snake_case)]
#[derive(Default, CompositeTemplate)]
#[template(resource = "/org/Xetibo/ReSet/resetAudio.ui")]
pub struct AudioBox {
#[template_child]
pub resetOutputDevice: TemplateChild<DropDown>,
#[template_child]
pub resetSinksRow: TemplateChild<ListEntry>,
#[template_child]
pub resetOutputDevice: TemplateChild<DropDown>,
#[template_child]
pub resetSinkMute: TemplateChild<Button>,
#[template_child]
pub resetVolumeSlider: TemplateChild<Scale>,
#[template_child]
pub resetVolumePercentage: TemplateChild<Label>,
#[template_child]
pub resetVolumeMeter: TemplateChild<ProgressBar>,
#[template_child]
pub resetSinks: TemplateChild<Box>,
#[template_child]
pub resetOutputStreamButton: TemplateChild<ListEntry>,
#[template_child]
pub resetOutputStreams: TemplateChild<Box>,
pub resetDefaultSink: RefCell<Option<Sink>>,
pub resetSinkList: Arc<Mutex<Vec<Sink>>>,
pub resetInputStreamList: Arc<Mutex<Vec<InputStream>>>,
}
#[glib::object_subclass]
impl ObjectSubclass for AudioBox {
const NAME: &'static str = "resetAudio";

View file

@ -1,6 +1,7 @@
use crate::components::output::audioSourceImpl;
use adw::glib;
use adw::glib::Object;
use crate::components::output::audioSourceImpl;
use glib::subclass::types::ObjectSubclassIsExt;
glib::wrapper! {
pub struct AudioSourceEntry(ObjectSubclass<audioSourceImpl::AudioSourceEntry>)
@ -9,7 +10,25 @@ glib::wrapper! {
}
impl AudioSourceEntry {
pub fn new() -> Self {
Object::builder().build()
pub fn new(name: String, volume: Vec<u32>, muted: bool, index: u32) -> Self {
let obj: Self = Object::builder().build();
// TODO use event callback for progress bar -> this is the "im speaking" indicator
// TODO map the slider to volume
// TODO properly use volume fraction
// TODO map mute to callback
// TODO map dropdown
{
let imp = obj.imp();
imp.associatedIndex.set(index);
imp.isMuted.set(muted);
let mut volume_borrow = imp.volume.borrow_mut();
*volume_borrow = volume;
imp.resetSourceName.set_text(name.as_str());
let fraction = (volume_borrow.first().unwrap_or_else(|| &(0 as u32)) / 100) as f64;
imp.resetVolumeMeter.set_fraction(fraction);
let percentage = (fraction * 100 as f64).to_string();
imp.resetVolumePercentage.set_text(&percentage);
}
obj
}
}

View file

@ -1,6 +1,8 @@
use gtk::{Button, CompositeTemplate, glib, Label, ProgressBar, Scale};
use gtk::subclass::prelude::*;
use std::cell::{Cell, RefCell};
use crate::components::output::audioSource;
use gtk::subclass::prelude::*;
use gtk::{glib, Button, CompositeTemplate, DropDown, Label, ProgressBar, Scale};
#[allow(non_snake_case)]
#[derive(Default, CompositeTemplate)]
@ -9,6 +11,8 @@ pub struct AudioSourceEntry {
#[template_child]
pub resetSourceName: TemplateChild<Label>,
#[template_child]
pub resetSelectedSink: TemplateChild<DropDown>,
#[template_child]
pub resetSourceMute: TemplateChild<Button>,
#[template_child]
pub resetVolumeSlider: TemplateChild<Scale>,
@ -16,6 +20,9 @@ pub struct AudioSourceEntry {
pub resetVolumePercentage: TemplateChild<Label>,
#[template_child]
pub resetVolumeMeter: TemplateChild<ProgressBar>,
pub associatedIndex: Cell<u32>,
pub isMuted: Cell<bool>,
pub volume: RefCell<Vec<u32>>,
}
#[glib::object_subclass]