mirror of
https://gitlab.com/marcandre.lureau/qemu-display.git
synced 2024-12-22 05:35:20 +00:00
qdl: start an Introspect helper
This commit is contained in:
parent
0f653ea6d5
commit
75a0fd7bc7
@ -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"
|
||||
|
@ -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<Receiver<AudioOutEvent>> {
|
||||
let (p0, p1) = UnixStream::pair()?;
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
@ -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<zbus::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<zbus::fdo::Error> for Error {
|
||||
fn from(e: zbus::fdo::Error) -> Self {
|
||||
Error::Zbus(e.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<zvariant::Error> for Error {
|
||||
fn from(e: zvariant::Error) -> Self {
|
||||
Error::Zvariant(e)
|
||||
Error::Zbus(e.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
68
qemu-display-listener/src/introspect.rs
Normal file
68
qemu-display-listener/src/introspect.rs
Normal file
@ -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<Self> {
|
||||
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<Option<Audio>> {
|
||||
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<Chardev> {
|
||||
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
|
||||
}
|
||||
}
|
@ -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]
|
||||
|
10
qemu-display-listener/src/usbredir.rs
Normal file
10
qemu-display-listener/src/usbredir.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use crate::Chardev;
|
||||
|
||||
pub struct UsbRedir;
|
||||
|
||||
impl UsbRedir {
|
||||
pub fn new(chardevs: Vec<Chardev>) -> Self {
|
||||
dbg!(chardevs);
|
||||
Self
|
||||
}
|
||||
}
|
@ -10,13 +10,7 @@ pub struct Handler {
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
pub async fn new(conn: &zbus::azync::Connection) -> Result<Self, Box<dyn Error>> {
|
||||
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<Self, Box<dyn Error>> {
|
||||
let rx = audio.listen_out().await?;
|
||||
let mut gst = rdw::GstAudio::new()?;
|
||||
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user