🚧 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;
|
extern crate relm4_macros;
|
||||||
|
|
||||||
mod icons;
|
mod icons;
|
||||||
|
mod input;
|
||||||
|
mod output;
|
||||||
mod pa;
|
mod pa;
|
||||||
mod task;
|
mod task;
|
||||||
|
mod volume;
|
||||||
|
|
||||||
use gtk4::{
|
use gtk4::{
|
||||||
glib::{self, clone},
|
gio::ApplicationFlags,
|
||||||
|
glib::{self, clone, MainContext, PRIORITY_DEFAULT},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
Align, Box as GtkBox, Button, Image, Label, ListBox, Orientation, PositionType, Revealer,
|
Align, Application, ApplicationWindow, Box as GtkBox, Button, Image, Label, ListBox,
|
||||||
RevealerTransitionType, Scale, SelectionMode, Separator, Window,
|
Orientation, PositionType, Revealer, RevealerTransitionType, Scale, SelectionMode, Separator,
|
||||||
|
};
|
||||||
|
use libpulse_binding::{
|
||||||
|
context::subscribe::{Facility, InterestMaskSet, Operation},
|
||||||
|
volume::Volume,
|
||||||
};
|
};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use pulsectl::Handler;
|
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"));
|
static RT: Lazy<Runtime> = Lazy::new(|| Runtime::new().expect("failed to build tokio runtime"));
|
||||||
|
|
||||||
fn main() {
|
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 =
|
let handler =
|
||||||
Handler::connect("com.system76.cosmic.applets.audio").expect("failed to connect to pulse");
|
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 {
|
task::spawn_local(clone!(@strong handler.mainloop as main_loop => async move {
|
||||||
pa::drive_main_loop(main_loop).await
|
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! {
|
view! {
|
||||||
window = Window {
|
window = ApplicationWindow {
|
||||||
|
set_application: Some(application),
|
||||||
set_title: Some("COSMIC Network Applet"),
|
set_title: Some("COSMIC Network Applet"),
|
||||||
set_default_width: 400,
|
set_default_width: 400,
|
||||||
set_default_height: 300,
|
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