diff --git a/Cargo.toml b/Cargo.toml index c6a2adb..cf9d4fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ description = "A wip universal Linux settings application." [dependencies] reset_daemon = "0.1.2" -ReSet-Lib = "0.2.6" +ReSet-Lib = "0.2.8" 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/src/components/input/inputStreamEntry.rs b/src/components/input/inputStreamEntry.rs deleted file mode 100644 index 97a1477..0000000 --- a/src/components/input/inputStreamEntry.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::components::input::inputStreamEntryImpl; -use adw::glib; -use adw::glib::Object; - -glib::wrapper! { - pub struct InputStreamEntry(ObjectSubclass) - @extends gtk::Box, gtk::Widget, - @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Orientable; -} - -impl InputStreamEntry { - pub fn new() -> Self { - Object::builder().build() - } -} diff --git a/src/components/input/mod.rs b/src/components/input/mod.rs index 5dc57f2..83908c2 100644 --- a/src/components/input/mod.rs +++ b/src/components/input/mod.rs @@ -1,5 +1,7 @@ #![allow(non_snake_case)] -pub mod inputStreamEntry; pub mod sourceBox; pub mod sourceBoxImpl; -pub mod inputStreamEntryImpl; \ No newline at end of file +pub mod outputStreamEntry; +pub mod outputStreamEntryImpl; +pub mod sourceEntry; +pub mod sourceEntryImpl; diff --git a/src/components/input/outputStreamEntry.rs b/src/components/input/outputStreamEntry.rs new file mode 100644 index 0000000..4160f97 --- /dev/null +++ b/src/components/input/outputStreamEntry.rs @@ -0,0 +1,102 @@ +use std::cell::RefCell; +use std::sync::Arc; +use std::time::Duration; + +use adw::glib; +use adw::glib::Object; +use adw::prelude::{ButtonExt, RangeExt}; +use dbus::blocking::Connection; +use dbus::Error; +use glib::subclass::types::ObjectSubclassIsExt; +use glib::{clone, Propagation}; +use ReSet_Lib::audio::audio::OutputStream; + +use super::outputStreamEntryImpl; + +glib::wrapper! { + pub struct OutputStreamEntry(ObjectSubclass) + @extends gtk::Box, gtk::Widget, + @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Orientable; +} + +impl OutputStreamEntry { + pub fn new(stream: OutputStream) -> Self { + let obj: Self = Object::builder().build(); + // TODO use event callback for progress bar -> this is the "im speaking" indicator + // TODO map mute to callback + // TODO map dropdown + { + let imp = obj.imp(); + let name = stream.application_name.clone() + ": " + stream.name.as_str(); + imp.resetSourceName.set_text(name.as_str()); + let volume = stream.volume.first().unwrap_or_else(|| &(0 as u32)); + let fraction = (*volume as f64 / 655.36).round(); + let percentage = (fraction).to_string() + "%"; + imp.resetVolumePercentage.set_text(&percentage); + imp.resetVolumeSlider.set_value(*volume as f64); + imp.stream.replace(stream); + imp.resetVolumeSlider.connect_change_value( + clone!(@weak imp => @default-return Propagation::Stop, move |_, _, value| { + let fraction = (value / 655.36).round(); + let percentage = (fraction).to_string() + "%"; + imp.resetVolumePercentage.set_text(&percentage); + let stream = imp.stream.borrow(); + let index = stream.index; + let channels = stream.channels; + set_outputstream_volume(value, index, channels); + Propagation::Proceed + }), + ); + imp.resetSourceMute + .connect_clicked(clone!(@weak imp => move |_| { + let stream = imp.stream.clone(); + let mut stream = stream.borrow_mut(); + stream.muted = !stream.muted; + let muted = stream.muted; + let index = stream.index; + if muted { + imp.resetSourceMute + .set_icon_name("audio-volume-muted-symbolic"); + } else { + imp.resetSourceMute + .set_icon_name("audio-volume-high-symbolic"); + } + toggle_output_stream_mute(index, muted); + })); + } + obj + } +} + +fn set_outputstream_volume(value: f64, index: u32, channels: u16) -> bool { + let conn = Connection::new_session().unwrap(); + let proxy = conn.with_proxy( + "org.xetibo.ReSet", + "/org/xetibo/ReSet", + Duration::from_millis(1000), + ); + let res: Result<(bool,), Error> = proxy.method_call( + "org.xetibo.ReSet", + "SetOutputStreamVolume", + (index, channels, value as u32), + ); + if res.is_err() { + return false; + } + res.unwrap().0 +} + +fn toggle_output_stream_mute(index: u32, muted: bool) -> bool { + let conn = Connection::new_session().unwrap(); + let proxy = conn.with_proxy( + "org.xetibo.ReSet", + "/org/xetibo/ReSet", + Duration::from_millis(1000), + ); + let res: Result<(bool,), Error> = + proxy.method_call("org.xetibo.ReSet", "SetOutputStreamMute", (index, muted)); + if res.is_err() { + return false; + } + res.unwrap().0 +} diff --git a/src/components/output/audioSourceImpl.rs b/src/components/input/outputStreamEntryImpl.rs similarity index 60% rename from src/components/output/audioSourceImpl.rs rename to src/components/input/outputStreamEntryImpl.rs index 58d3dce..88dccfe 100644 --- a/src/components/output/audioSourceImpl.rs +++ b/src/components/input/outputStreamEntryImpl.rs @@ -1,17 +1,19 @@ -use std::cell::{Cell, RefCell}; +use std::cell::RefCell; +use std::sync::Arc; -use crate::components::output::audioSource; +use crate::components::input::outputStreamEntry; +use ReSet_Lib::audio::audio::OutputStream; use gtk::subclass::prelude::*; -use gtk::{glib, Button, CompositeTemplate, DropDown, Label, ProgressBar, Scale}; +use gtk::{glib, Button, CompositeTemplate, Label, ProgressBar, Scale, DropDown}; #[allow(non_snake_case)] #[derive(Default, CompositeTemplate)] #[template(resource = "/org/Xetibo/ReSet/resetOutputStreamEntry.ui")] -pub struct AudioSourceEntry { +pub struct OutputStreamEntry { #[template_child] pub resetSourceName: TemplateChild