diff --git a/data/resources/ui/window.ui b/data/resources/ui/window.ui index f45320d..9a4e0f4 100644 --- a/data/resources/ui/window.ui +++ b/data/resources/ui/window.ui @@ -151,6 +151,47 @@ + + + true + inputdevices + Input Devices + soundcard-symbolic + + + never + 200 + 0 + 1 + + + 1 + + + vertical + 10 + 10 + 5 + 5 + + + none + 1 + + + + + + + + + + + + + true diff --git a/src/backend/manager.rs b/src/backend/manager.rs index 335a0d6..d7f09c1 100644 --- a/src/backend/manager.rs +++ b/src/backend/manager.rs @@ -36,6 +36,7 @@ mod imp { pub wp_object_manager: OnceCell, pub nodemodel: PwNodeModel, + pub sourcemodel: PwNodeModel, pub sinkmodel: PwNodeModel, pub devicemodel: OnceCell, @@ -105,7 +106,7 @@ mod imp { let interest = wp::registry::ObjectInterest::new( wp::pw::Node::static_type(), ); - let variant = glib::Variant::from_str("('Stream/Output/Audio', 'Stream/Input/Audio', 'Audio/Sink')") + let variant = glib::Variant::from_str("('Stream/Output/Audio', 'Stream/Input/Audio', 'Audio/Source', 'Audio/Sink')") .expect("variant"); interest.add_constraint( wp::registry::ConstraintType::PwGlobalProperty, @@ -168,6 +169,7 @@ mod imp { let pwobj = PwNodeObject::new(node); let model = match pwobj.nodetype() { NodeType::Sink => &imp.sinkmodel, + NodeType::Source => &imp.sourcemodel, _ => &imp.nodemodel }; model.append(&pwobj); @@ -187,6 +189,7 @@ mod imp { pwvucontrol_info!("removed: {:?} id: {}", node.name(), node.bound_id()); let model = match pwnodeobject::get_node_type_for_node(node) { NodeType::Sink => &imp.sinkmodel, + NodeType::Source => &imp.sourcemodel, _ => &imp.nodemodel }; model.remove(node.bound_id()); diff --git a/src/backend/pwnodeobject.rs b/src/backend/pwnodeobject.rs index 480c77c..9d40ef1 100644 --- a/src/backend/pwnodeobject.rs +++ b/src/backend/pwnodeobject.rs @@ -220,6 +220,7 @@ pub(crate) fn get_node_type_for_node(node: &wp::pw::Node) -> NodeType { match node.get_pw_property("media.class").as_deref() { Some("Stream/Output/Audio") => NodeType::Output, Some("Stream/Input/Audio") => NodeType::Input, + Some("Audio/Source") => NodeType::Source, Some("Audio/Sink") => NodeType::Sink, _ => NodeType::Undefined, } diff --git a/src/ui/route_dropdown.rs b/src/ui/route_dropdown.rs index b2f4c3b..efee092 100644 --- a/src/ui/route_dropdown.rs +++ b/src/ui/route_dropdown.rs @@ -10,7 +10,7 @@ use crate::ui::PwProfileRow; use crate::macros::*; mod imp { - use crate::backend::{PwNodeObject, PwRouteObject}; + use crate::backend::{NodeType, PwNodeObject, PwRouteFilterModel, PwRouteObject}; use super::*; @@ -44,27 +44,50 @@ mod imp { impl PwRouteDropDown { pub fn update_selected(&self) { + if let Some(index) = self.get_route_index() { + pwvucontrol_info!("update_selected with index {index}"); + self.obj().set_selected_no_send(index); + } + } + + fn get_route_index(&self) -> Option { let nodeobject = self.nodeobject.borrow(); let nodeobject = nodeobject.as_ref().unwrap(); let deviceobject = nodeobject.get_device().expect("device"); + match nodeobject.nodetype() { + NodeType::Input => Some(deviceobject.route_index_input()), + NodeType::Output => Some(deviceobject.route_index_output()), + _ => None + } + } - pwvucontrol_info!("update_selected with index {}", deviceobject.route_index_output()); - self.obj().set_selected_no_send(deviceobject.route_index_output()); + fn get_route_model(&self) -> Option { + let nodeobject = self.nodeobject.borrow(); + let nodeobject = nodeobject.as_ref().unwrap(); + + let deviceobject = nodeobject.get_device().expect("device"); + match nodeobject.nodetype() { + NodeType::Source => Some(deviceobject.routemodel_input()), + NodeType::Sink => Some(deviceobject.routemodel_output()), + _ => None + } } pub fn set_nodeobject(&self, new_nodeobject: Option<&PwNodeObject>) { self.nodeobject.replace(new_nodeobject.cloned()); if let Some(nodeobject) = new_nodeobject { + let deviceobject = nodeobject.get_device().expect("device"); self.block_signal.set(true); pwvucontrol_info!("self.route_dropdown.set_model({});", deviceobject.wpdevice().bound_id()); - self.route_dropdown.set_model(Some(&deviceobject.routemodel_output())); - pwvucontrol_info!("self.route_dropdown.set_selected({});", deviceobject.route_index_output()); - - self.route_dropdown.set_selected(deviceobject.route_index_output()); + self.route_dropdown.set_model(self.get_route_model().as_ref()); + if let Some(index) = self.get_route_index() { + pwvucontrol_info!("self.route_dropdown.set_selected({index});"); + self.route_dropdown.set_selected(index); + } self.block_signal.set(false); diff --git a/src/ui/window.rs b/src/ui/window.rs index 6eee77f..694c63e 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -38,6 +38,8 @@ mod imp { #[template_child] pub recordlist: TemplateChild, #[template_child] + pub inputlist: TemplateChild, + #[template_child] pub outputlist: TemplateChild, #[template_child] pub cardlist: TemplateChild, @@ -56,6 +58,7 @@ mod imp { stack: TemplateChild::default(), playbacklist: TemplateChild::default(), recordlist: TemplateChild::default(), + inputlist: TemplateChild::default(), outputlist: TemplateChild::default(), cardlist: TemplateChild::default(), viewstack: TemplateChild::default(), @@ -95,10 +98,12 @@ mod imp { self.obj().setup_scroll_blocker(&self.playbacklist); self.obj().setup_scroll_blocker(&self.recordlist); + self.obj().setup_scroll_blocker(&self.inputlist); self.obj().setup_scroll_blocker(&self.outputlist); let manager = PwvucontrolManager::default(); let model = &manager.imp().nodemodel; + let sourcemodel = &manager.imp().sourcemodel; let sinkmodel = &manager.imp().sinkmodel; let devicemodel = manager.imp().devicemodel.get().expect("Device model"); let window = self; @@ -141,6 +146,17 @@ mod imp { }), ); + self.inputlist.bind_model( + Some(sourcemodel), + clone!(@weak window => @default-panic, move |item| { + PwSinkBox::new( + item.downcast_ref::() + .expect("RowData is of wrong type"), + ) + .upcast::() + }), + ); + self.outputlist.bind_model( Some(sinkmodel), clone!(@weak window => @default-panic, move |item| {