🚧 Continue rewrite of cosmic-applet-audio

This commit is contained in:
Lucy 2022-04-07 12:36:01 -04:00
parent 2c591d1ba3
commit 57b44a35d5
No known key found for this signature in database
GPG key ID: EBC517FAD666BBF1
4 changed files with 173 additions and 4 deletions

View file

@ -0,0 +1,52 @@
use gtk4::{prelude::*, Button, Label, ListBox};
use libcosmic_widgets::{relm4::RelmContainerExt, LabeledItem};
use pulsectl::controllers::{types::DeviceInfo, DeviceControl, SourceController};
use std::rc::Rc;
fn get_inputs() -> Vec<DeviceInfo> {
SourceController::create()
.expect("failed to create input controller")
.list_devices()
.expect("failed to list input devices")
}
pub fn refresh_default_input(label: &Label) {
let default_input = SourceController::create()
.expect("failed to create input controller")
.get_default_device()
.expect("failed to get default input");
label.set_text(match &default_input.description {
Some(name) => name.as_str(),
None => "Input Device",
});
}
pub fn refresh_input_widgets(inputs: &ListBox) {
while let Some(row) = inputs.row_at_index(0) {
inputs.remove(&row);
}
for input in get_inputs() {
let input = Rc::new(input.clone());
let name = match &input.name {
Some(name) => name.to_owned(),
None => continue, // Why doesn't this have a name? Whatever, it's invalid.
};
view! {
item = LabeledItem {
set_title: input.description
.as_ref()
.unwrap_or(&name),
set_child: set_current_input_device = &Button {
set_label: "Switch",
connect_clicked: move |_| {
SourceController::create()
.expect("failed to create input controller")
.set_default_device(&name)
.expect("failed to set default device");
}
}
}
}
inputs.container_add(&item);
}
}

View file

@ -4,14 +4,22 @@
extern crate relm4_macros;
mod icons;
mod input;
mod output;
mod pa;
mod task;
mod volume;
use gtk4::{
glib::{self, clone},
gio::ApplicationFlags,
glib::{self, clone, MainContext, PRIORITY_DEFAULT},
prelude::*,
Align, Box as GtkBox, Button, Image, Label, ListBox, Orientation, PositionType, Revealer,
RevealerTransitionType, Scale, SelectionMode, Separator, Window,
Align, Application, ApplicationWindow, Box as GtkBox, Button, Image, Label, ListBox,
Orientation, PositionType, Revealer, RevealerTransitionType, Scale, SelectionMode, Separator,
};
use libpulse_binding::{
context::subscribe::{Facility, InterestMaskSet, Operation},
volume::Volume,
};
use once_cell::sync::Lazy;
use pulsectl::Handler;
@ -20,13 +28,46 @@ use tokio::runtime::Runtime;
static RT: Lazy<Runtime> = Lazy::new(|| Runtime::new().expect("failed to build tokio runtime"));
fn main() {
let application = Application::new(
Some("com.system76.cosmic.applets.audio"),
ApplicationFlags::default(),
);
application.connect_activate(app);
application.run();
}
fn app(application: &Application) {
let handler =
Handler::connect("com.system76.cosmic.applets.audio").expect("failed to connect to pulse");
task::spawn_local(clone!(@strong handler.mainloop as main_loop => async move {
pa::drive_main_loop(main_loop).await
}));
let (refresh_output_tx, refresh_output_rx) = MainContext::channel::<()>(PRIORITY_DEFAULT);
let (refresh_input_tx, refresh_input_rx) = MainContext::channel::<()>(PRIORITY_DEFAULT);
handler
.context
.borrow_mut()
.set_subscribe_callback(Some(Box::new(clone!(@strong refresh_output_tx, @strong refresh_input_tx => move |facility, operation, _idx| {
if !matches!(operation, Some(Operation::Changed)) {
return;
}
match facility {
Some(Facility::Sink) => {
refresh_output_tx.send(()).expect("failed to send output refresh message");
}
Some(Facility::Source) => {
refresh_input_tx.send(()).expect("failed to send output refresh message");
}
_ => {}
}
}))));
handler
.context
.borrow_mut()
.subscribe(InterestMaskSet::SINK | InterestMaskSet::SOURCE, |_| {});
view! {
window = Window {
window = ApplicationWindow {
set_application: Some(application),
set_title: Some("COSMIC Network Applet"),
set_default_width: 400,
set_default_height: 300,
@ -116,4 +157,21 @@ fn main() {
}
}
}
refresh_input_rx.attach(
None,
clone!(@weak inputs, @weak current_input => @default-return Continue(true), move |_| {
input::refresh_input_widgets(&inputs);
input::refresh_default_input(&current_input);
Continue(true)
}),
);
refresh_output_rx.attach(
None,
clone!(@weak outputs, @weak current_output => @default-return Continue(true), move |_| {
output::refresh_output_widgets(&outputs);
output::refresh_default_input(&current_output);
Continue(true)
}),
);
window.show();
}

View file

@ -0,0 +1,52 @@
use gtk4::{prelude::*, Button, Label, ListBox};
use libcosmic_widgets::{relm4::RelmContainerExt, LabeledItem};
use pulsectl::controllers::{types::DeviceInfo, DeviceControl, SinkController};
use std::rc::Rc;
fn get_outputs() -> Vec<DeviceInfo> {
SinkController::create()
.expect("failed to create output controller")
.list_devices()
.expect("failed to list output devices")
}
pub fn refresh_default_input(label: &Label) {
let default_output = SinkController::create()
.expect("failed to create output controller")
.get_default_device()
.expect("failed to get default output");
label.set_text(match &default_output.description {
Some(name) => name.as_str(),
None => "Output Device",
});
}
pub fn refresh_output_widgets(outputs: &ListBox) {
while let Some(row) = outputs.row_at_index(0) {
outputs.remove(&row);
}
for output in get_outputs() {
let output = Rc::new(output.clone());
let name = match &output.name {
Some(name) => name.to_owned(),
None => continue, // Why doesn't this have a name? Whatever, it's invalid.
};
view! {
item = LabeledItem {
set_title: output.description
.as_ref()
.unwrap_or(&name),
set_child: set_current_input_device = &Button {
set_label: "Switch",
connect_clicked: move |_| {
SinkController::create()
.expect("failed to create output controller")
.set_default_device(&name)
.expect("failed to set default device");
}
}
}
}
outputs.container_add(&item);
}
}

View file

@ -0,0 +1,7 @@
use gtk4::{prelude::*, Scale};
use libpulse_binding::volume::Volume;
use pulsectl::controllers::types::DeviceInfo;
pub fn update_volume(device: &DeviceInfo, scale: &Scale) {
scale.set_value((device.volume.avg().0 as f64 / Volume::NORMAL.0 as f64) * 100.);
}