🚧 Continue rewrite of cosmic-applet-audio
This commit is contained in:
parent
2c591d1ba3
commit
57b44a35d5
4 changed files with 173 additions and 4 deletions
52
applets/cosmic-applet-audio/src/input.rs
Normal file
52
applets/cosmic-applet-audio/src/input.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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(¤t_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(¤t_output);
|
||||
Continue(true)
|
||||
}),
|
||||
);
|
||||
window.show();
|
||||
}
|
||||
|
|
|
|||
52
applets/cosmic-applet-audio/src/output.rs
Normal file
52
applets/cosmic-applet-audio/src/output.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
7
applets/cosmic-applet-audio/src/volume.rs
Normal file
7
applets/cosmic-applet-audio/src/volume.rs
Normal 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.);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue