diff --git a/qemu-display-listener/Cargo.toml b/qemu-display-listener/Cargo.toml index 8c1f985..b5f1b1a 100644 --- a/qemu-display-listener/Cargo.toml +++ b/qemu-display-listener/Cargo.toml @@ -19,3 +19,4 @@ serde_repr = "0.1.6" serde_bytes = "0.11.5" futures-util = { version = "0.3.8", features = ["async-await-macro"] } once_cell = "1.5" +futures = "0.3.13" diff --git a/qemu-display-listener/src/audio.rs b/qemu-display-listener/src/audio.rs index 25db3df..db611a9 100644 --- a/qemu-display-listener/src/audio.rs +++ b/qemu-display-listener/src/audio.rs @@ -1,7 +1,6 @@ use once_cell::sync::OnceCell; use std::default::Default; use std::os::unix::net::UnixStream; -use std::str::FromStr; use std::sync::mpsc::{self, Receiver, SendError}; use std::sync::{Arc, Mutex}; use std::{os::unix::io::AsRawFd, thread}; @@ -263,24 +262,6 @@ impl Audio { Ok(Self { proxy }) } - pub async fn available(conn: &zbus::azync::Connection) -> bool { - // TODO: we may want to generalize interface detection - let ip = zbus::fdo::AsyncIntrospectableProxy::builder(conn) - .destination("org.qemu") - .unwrap() - .path("/org/qemu/Display1") - .unwrap() - .build() - .await - .unwrap(); - let introspect = zbus::xml::Node::from_str(&ip.introspect().await.unwrap()).unwrap(); - let has_audio = introspect - .nodes() - .iter() - .any(|n| n.name().map(|n| n == "Audio").unwrap_or(false)); - has_audio - } - pub async fn listen_out(&self) -> Result> { let (p0, p1) = UnixStream::pair()?; let (tx, rx) = mpsc::channel(); diff --git a/qemu-display-listener/src/error.rs b/qemu-display-listener/src/error.rs index 34ddd51..23c398c 100644 --- a/qemu-display-listener/src/error.rs +++ b/qemu-display-listener/src/error.rs @@ -6,7 +6,6 @@ use std::io; pub enum Error { Io(io::Error), Zbus(zbus::Error), - Zvariant(zvariant::Error), Failed(String), } @@ -15,7 +14,6 @@ impl fmt::Display for Error { match self { Error::Io(e) => write!(f, "{}", e), Error::Zbus(e) => write!(f, "{}", e), - Error::Zvariant(e) => write!(f, "{}", e), Error::Failed(e) => write!(f, "{}", e), } } @@ -26,7 +24,6 @@ impl error::Error for Error { match self { Error::Io(e) => Some(e), Error::Zbus(e) => Some(e), - Error::Zvariant(e) => Some(e), Error::Failed(_) => None, } } @@ -44,9 +41,15 @@ impl From for Error { } } +impl From for Error { + fn from(e: zbus::fdo::Error) -> Self { + Error::Zbus(e.into()) + } +} + impl From for Error { fn from(e: zvariant::Error) -> Self { - Error::Zvariant(e) + Error::Zbus(e.into()) } } diff --git a/qemu-display-listener/src/introspect.rs b/qemu-display-listener/src/introspect.rs new file mode 100644 index 0000000..80423c4 --- /dev/null +++ b/qemu-display-listener/src/introspect.rs @@ -0,0 +1,68 @@ +use futures::stream::{self, StreamExt}; +use std::convert::TryFrom; +use zbus::azync::Connection; +use zbus::fdo::ManagedObjects; +use zvariant::OwnedObjectPath; + +use crate::{Audio, Chardev, Result, UsbRedir}; + +pub struct Introspect { + conn: Connection, + objects: ManagedObjects, +} + +impl Introspect { + pub async fn new(conn: &Connection) -> Result { + let objects = zbus::fdo::AsyncObjectManagerProxy::builder(&conn) + .destination("org.qemu")? + .path("/org/qemu/Display1")? + .build() + .await? + .get_managed_objects() + .await?; + // TODO: listen for changes ? + Ok(Self { + conn: conn.clone(), + objects, + }) + } + + pub async fn audio(&self) -> Result> { + if !self + .objects + .contains_key(&OwnedObjectPath::try_from("/org/qemu/Display1/Audio").unwrap()) + { + return Ok(None); + } + + Ok(Some(Audio::new(&self.conn).await?)) + } + + pub async fn chardevs(&self) -> Vec { + stream::iter(&self.objects) + .filter_map(|(p, _ifaces)| async move { + match p.strip_prefix("/org/qemu/Display1/Chardev_") { + Some(id) => Chardev::new(&self.conn, id).await.ok(), + _ => None, + } + }) + .collect() + .await + } + + pub async fn usbredir(&self) -> UsbRedir { + let chardevs = stream::iter(self.chardevs().await) + .filter_map(|c| async move { + if c.proxy.name().await.ok() == Some("org.qemu.usbredir".to_string()) { + Some(c) + } else { + None + } + }) + .collect() + .await; + + let redir = UsbRedir::new(chardevs); + redir + } +} diff --git a/qemu-display-listener/src/lib.rs b/qemu-display-listener/src/lib.rs index e339755..5ef371e 100644 --- a/qemu-display-listener/src/lib.rs +++ b/qemu-display-listener/src/lib.rs @@ -30,6 +30,12 @@ pub use keyboard::*; mod mouse; pub use mouse::*; +mod introspect; +pub use introspect::*; + +mod usbredir; +pub use usbredir::*; + #[cfg(test)] mod tests { #[test] diff --git a/qemu-display-listener/src/usbredir.rs b/qemu-display-listener/src/usbredir.rs new file mode 100644 index 0000000..1f82b4f --- /dev/null +++ b/qemu-display-listener/src/usbredir.rs @@ -0,0 +1,10 @@ +use crate::Chardev; + +pub struct UsbRedir; + +impl UsbRedir { + pub fn new(chardevs: Vec) -> Self { + dbg!(chardevs); + Self + } +} diff --git a/qemu-rdw/src/audio.rs b/qemu-rdw/src/audio.rs index 38f3694..af6b00b 100644 --- a/qemu-rdw/src/audio.rs +++ b/qemu-rdw/src/audio.rs @@ -10,13 +10,7 @@ pub struct Handler { } impl Handler { - pub async fn new(conn: &zbus::azync::Connection) -> Result> { - if !Audio::available(conn).await { - log::debug!("No qemu audio provided on the bus"); - return Ok(Self::default()); - } - - let audio = Audio::new(conn).await?; + pub async fn new(audio: Audio) -> Result> { let rx = audio.listen_out().await?; let mut gst = rdw::GstAudio::new()?; diff --git a/qemu-rdw/src/main.rs b/qemu-rdw/src/main.rs index 87f94d4..cc43fc9 100644 --- a/qemu-rdw/src/main.rs +++ b/qemu-rdw/src/main.rs @@ -2,7 +2,7 @@ use gio::ApplicationFlags; use glib::{clone, MainContext}; use gtk::{gio, glib, prelude::*}; use once_cell::sync::OnceCell; -use qemu_display_listener::{Chardev, Console}; +use qemu_display_listener::{Chardev, Console, Introspect}; use std::os::unix::io::AsRawFd; use std::os::unix::net::UnixStream; use zbus::Connection; @@ -33,13 +33,17 @@ fn main() { let audio_clone = audio.clone(); let clipboard_clone = clipboard.clone(); MainContext::default().spawn_local(clone!(@strong window => async move { + let intro = Introspect::new(&conn).await.unwrap(); + let console = Console::new(&conn, 0).await.expect("Failed to get the QEMU console"); let display = display_qemu::DisplayQemu::new(console); window.set_child(Some(&display)); - match audio::Handler::new(&conn).await { - Ok(handler) => audio_clone.set(handler).unwrap(), - Err(e) => log::warn!("Failed to setup audio: {}", e), + if let Ok(Some(audio)) = intro.audio().await { + match audio::Handler::new(audio).await { + Ok(handler) => audio_clone.set(handler).unwrap(), + Err(e) => log::warn!("Failed to setup audio: {}", e), + } } match clipboard::Handler::new(&conn).await {